Skip to content

Commit f66a5e2

Browse files
authored
[lldb] Fix SBThread::StepOverUntil for discontinuous functions (#123046)
I think the only issue here was that we would erroneously consider functions which are "in the middle" of the function were stepping to as a part of the function, and would try to step into them (likely stepping out of the function instead) instead of giving up early.
1 parent 0d7c8c0 commit f66a5e2

File tree

6 files changed

+154
-2
lines changed

6 files changed

+154
-2
lines changed

lldb/include/lldb/Symbol/Function.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ class Function : public UserID, public SymbolContextScope {
454454
/// and variables).
455455
const Address &GetAddress() const { return m_address; }
456456

457+
bool GetRangeContainingLoadAddress(lldb::addr_t load_addr, Target &target,
458+
AddressRange &range) {
459+
return m_block.GetRangeContainingLoadAddress(load_addr, target, range);
460+
}
461+
457462
lldb::LanguageType GetLanguage() const;
458463
/// Find the file and line number of the source location of the start of the
459464
/// function. This will use the declaration if present and fall back on the

lldb/source/API/SBThread.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,6 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
842842
// appropriate error message.
843843

844844
bool all_in_function = true;
845-
AddressRange fun_range = frame_sc.function->GetAddressRange();
846845

847846
std::vector<addr_t> step_over_until_addrs;
848847
const bool abort_other_plans = false;
@@ -859,7 +858,9 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
859858
addr_t step_addr =
860859
sc.line_entry.range.GetBaseAddress().GetLoadAddress(target);
861860
if (step_addr != LLDB_INVALID_ADDRESS) {
862-
if (fun_range.ContainsLoadAddress(step_addr, target))
861+
AddressRange unused_range;
862+
if (frame_sc.function->GetRangeContainingLoadAddress(step_addr, *target,
863+
unused_range))
863864
step_over_until_addrs.push_back(step_addr);
864865
else
865866
all_in_function = false;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class TestStepUntilAPI(TestBase):
8+
NO_DEBUG_INFO_TESTCASE = True
9+
10+
def setUp(self):
11+
super().setUp()
12+
13+
self.main_source = "main.c"
14+
self.main_spec = lldb.SBFileSpec(self.main_source)
15+
self.less_than_two = line_number("main.c", "Less than 2")
16+
self.greater_than_two = line_number("main.c", "Greater than or equal to 2.")
17+
self.back_out_in_main = line_number("main.c", "Back out in main")
18+
self.in_foo = line_number("main.c", "In foo")
19+
20+
def _build_dict_for_discontinuity(self):
21+
return dict(
22+
CFLAGS_EXTRAS="-funique-basic-block-section-names "
23+
+ "-ffunction-sections -fbasic-block-sections=list="
24+
+ self.getSourcePath("function.list"),
25+
LD_EXTRAS="-Wl,--script=" + self.getSourcePath("symbol.order"),
26+
)
27+
28+
def _do_until(self, build_dict, args, until_line, expected_line):
29+
self.build(dictionary=build_dict)
30+
launch_info = lldb.SBLaunchInfo(args)
31+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
32+
self, "At the start", self.main_spec, launch_info
33+
)
34+
35+
self.assertSuccess(
36+
thread.StepOverUntil(self.frame(), self.main_spec, until_line)
37+
)
38+
39+
self.runCmd("process status")
40+
41+
line = self.frame().GetLineEntry().GetLine()
42+
self.assertEqual(
43+
line, expected_line, "Did not get the expected stop line number"
44+
)
45+
46+
def _assertDiscontinuity(self):
47+
target = self.target()
48+
foo = target.FindFunctions("foo")
49+
self.assertEqual(len(foo), 1)
50+
foo = foo[0]
51+
52+
call_me = self.target().FindFunctions("call_me")
53+
self.assertEqual(len(call_me), 1)
54+
call_me = call_me[0]
55+
56+
foo_addr = foo.function.GetStartAddress().GetLoadAddress(target)
57+
found_before = False
58+
found_after = False
59+
for range in call_me.function.GetRanges():
60+
addr = range.GetBaseAddress().GetLoadAddress(target)
61+
if addr < foo_addr:
62+
found_before = True
63+
if addr > foo_addr:
64+
found_after = True
65+
66+
self.assertTrue(
67+
found_before and found_after,
68+
"'foo' is not between 'call_me'" + str(foo) + str(call_me),
69+
)
70+
71+
def test_hitting(self):
72+
"""Test SBThread.StepOverUntil - targeting a line and hitting it."""
73+
self._do_until(None, None, self.less_than_two, self.less_than_two)
74+
75+
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
76+
def test_hitting_discontinuous(self):
77+
"""Test SBThread.StepOverUntil - targeting a line and hitting it -- with
78+
discontinuous functions"""
79+
self._do_until(
80+
self._build_dict_for_discontinuity(),
81+
None,
82+
self.less_than_two,
83+
self.less_than_two,
84+
)
85+
self._assertDiscontinuity()
86+
87+
def test_missing(self):
88+
"""Test SBThread.StepOverUntil - targeting a line and missing it by stepping out to call site"""
89+
self._do_until(
90+
None, ["foo", "bar", "baz"], self.less_than_two, self.back_out_in_main
91+
)
92+
93+
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
94+
def test_missing_discontinuous(self):
95+
"""Test SBThread.StepOverUntil - targeting a line and missing it by
96+
stepping out to call site -- with discontinuous functions"""
97+
self._do_until(
98+
self._build_dict_for_discontinuity(),
99+
["foo", "bar", "baz"],
100+
self.less_than_two,
101+
self.back_out_in_main,
102+
)
103+
self._assertDiscontinuity()
104+
105+
def test_bad_line(self):
106+
"""Test that we get an error if attempting to step outside the current
107+
function"""
108+
self.build()
109+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
110+
self, "At the start", self.main_spec
111+
)
112+
self.assertIn(
113+
"step until target not in current function",
114+
thread.StepOverUntil(
115+
self.frame(), self.main_spec, self.in_foo
116+
).GetCString(),
117+
)
118+
119+
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
120+
def test_bad_line_discontinuous(self):
121+
"""Test that we get an error if attempting to step outside the current
122+
function -- and the function is discontinuous"""
123+
self.build(dictionary=self._build_dict_for_discontinuity())
124+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
125+
self, "At the start", self.main_spec
126+
)
127+
self.assertIn(
128+
"step until target not in current function",
129+
thread.StepOverUntil(
130+
self.frame(), self.main_spec, self.in_foo
131+
).GetCString(),
132+
)
133+
self._assertDiscontinuity()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!call_me

lldb/test/API/functionalities/thread/step_until/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
* unrelated to the program, just to achieve consistent
55
* debug line tables, across platforms, that are not
66
* dependent on compiler optimzations. */
7+
8+
int foo(int x) { return x; /* In foo */ }
9+
710
int call_me(int argc) {
811
printf ("At the start, argc: %d.\n", argc);
912

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
SECTIONS {
2+
.text.ordered : {
3+
*(.text.call_me)
4+
*(.text.foo)
5+
*(.text.call_me.call_me.__part.1)
6+
*(.text.call_me.call_me.__part.2)
7+
*(.text.call_me.call_me.__part.3)
8+
}
9+
} INSERT BEFORE .text;

0 commit comments

Comments
 (0)