
lldb-server had limited support for single-stepping through the lr/sc atomic sequence. This patch enhances that support for all possible atomic sequences. The previous version contained an incorrect regex pattern in the test, causing the riscv-specific test to run on other platforms. This reland fixes the regex (see lldb/test/API/riscv/step/TestSoftwareStep.py)
80 lines
3.2 KiB
Python
80 lines
3.2 KiB
Python
"""
|
|
Test software step-inst, also known as instruction level single step, in risc-v atomic sequence.
|
|
For more information about atomic sequences, see the RISC-V Unprivileged ISA specification.
|
|
"""
|
|
|
|
import lldb
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
|
|
|
|
class TestSoftwareStep(TestBase):
|
|
def do_sequence_test(self, filename, bkpt_name):
|
|
source_file = filename + ".c"
|
|
exe_file = filename + ".x"
|
|
|
|
self.build(dictionary={"C_SOURCES": source_file, "EXE": exe_file})
|
|
(target, process, cur_thread, bkpt) = lldbutil.run_to_name_breakpoint(
|
|
self, bkpt_name, exe_name=exe_file
|
|
)
|
|
entry_pc = cur_thread.GetFrameAtIndex(0).GetPC()
|
|
|
|
self.runCmd("thread step-inst")
|
|
self.expect(
|
|
"thread list",
|
|
substrs=["stopped", "stop reason = instruction step into"],
|
|
)
|
|
|
|
pc = cur_thread.GetFrameAtIndex(0).GetPC()
|
|
|
|
return pc - entry_pc
|
|
|
|
@skipIf(archs=no_match("^rv.*"))
|
|
def test_cas(self):
|
|
"""
|
|
This test verifies LLDB instruction step handling of a proper lr/sc pair.
|
|
"""
|
|
difference = self.do_sequence_test("main", "cas")
|
|
self.assertEqual(difference, 0x1A)
|
|
|
|
@skipIf(archs=no_match("^rv.*"))
|
|
def test_branch_cas(self):
|
|
"""
|
|
LLDB cannot predict the actual state of registers within a critical section (i.e., inside an atomic
|
|
sequence). Therefore, it should identify all forward branches inside the atomic sequence and set
|
|
breakpoints at every jump address that lies beyond the end of the sequence (after the sc instruction).
|
|
This ensures that if any such branch is taken, execution will pause at its target address.
|
|
|
|
This test includes an lr/sc sequence containing an active forward branch with a jump address located
|
|
after the end of the atomic section. LLDB should correctly stop at this branch's target address. The
|
|
test is nearly identical to the previous one, except for the branch condition, which is inverted and
|
|
will result in a taken jump.
|
|
"""
|
|
difference = self.do_sequence_test("branch", "branch_cas")
|
|
self.assertEqual(difference, 0x1A)
|
|
|
|
@skipIf(archs=no_match("^rv.*"))
|
|
def test_incomplete_sequence_without_lr(self):
|
|
"""
|
|
This test verifies the behavior of a standalone sc instruction without a preceding lr. Since the sc
|
|
lacks the required lr pairing, LLDB should treat it as a non-atomic store rather than part of an
|
|
atomic sequence.
|
|
"""
|
|
difference = self.do_sequence_test(
|
|
"incomplete_sequence_without_lr", "incomplete_cas"
|
|
)
|
|
self.assertEqual(difference, 0x4)
|
|
|
|
@skipIf(archs=no_match("^rv.*"))
|
|
def test_incomplete_sequence_without_sc(self):
|
|
"""
|
|
This test checks the behavior of a standalone lr instruction without a subsequent sc. Since the lr
|
|
lacks its required sc counterpart, LLDB should treat it as a non-atomic load rather than part of an
|
|
atomic sequence.
|
|
"""
|
|
difference = self.do_sequence_test(
|
|
"incomplete_sequence_without_sc", "incomplete_cas"
|
|
)
|
|
self.assertEqual(difference, 0x4)
|