Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lldb] Fix SBThread::StepOverUntil for discontinuous functions #123046

Merged
merged 7 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lldb/include/lldb/Symbol/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,11 @@ class Function : public UserID, public SymbolContextScope {
/// and variables).
const Address &GetAddress() const { return m_address; }

bool GetRangeContainingLoadAddress(lldb::addr_t load_addr, Target &target,
AddressRange &range) {
return m_block.GetRangeContainingLoadAddress(load_addr, target, range);
}

lldb::LanguageType GetLanguage() const;
/// Find the file and line number of the source location of the start of the
/// function. This will use the declaration if present and fall back on the
Expand Down
5 changes: 3 additions & 2 deletions lldb/source/API/SBThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,6 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
// appropriate error message.

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

std::vector<addr_t> step_over_until_addrs;
const bool abort_other_plans = false;
Expand All @@ -859,7 +858,9 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
addr_t step_addr =
sc.line_entry.range.GetBaseAddress().GetLoadAddress(target);
if (step_addr != LLDB_INVALID_ADDRESS) {
if (fun_range.ContainsLoadAddress(step_addr, target))
AddressRange unused_range;
if (frame_sc.function->GetRangeContainingLoadAddress(step_addr, *target,
unused_range))
step_over_until_addrs.push_back(step_addr);
else
all_in_function = false;
Expand Down
133 changes: 133 additions & 0 deletions lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestStepUntilAPI(TestBase):
NO_DEBUG_INFO_TESTCASE = True

def setUp(self):
super().setUp()

self.main_source = "main.c"
self.main_spec = lldb.SBFileSpec(self.main_source)
self.less_than_two = line_number("main.c", "Less than 2")
self.greater_than_two = line_number("main.c", "Greater than or equal to 2.")
self.back_out_in_main = line_number("main.c", "Back out in main")
self.in_foo = line_number("main.c", "In foo")

def _build_dict_for_discontinuity(self):
return dict(
CFLAGS_EXTRAS="-funique-basic-block-section-names "
+ "-ffunction-sections -fbasic-block-sections=list="
+ self.getSourcePath("function.list"),
LD_EXTRAS="-Wl,--script=" + self.getSourcePath("symbol.order"),
)

def _do_until(self, build_dict, args, until_line, expected_line):
self.build(dictionary=build_dict)
launch_info = lldb.SBLaunchInfo(args)
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
self, "At the start", self.main_spec, launch_info
)

self.assertSuccess(
thread.StepOverUntil(self.frame(), self.main_spec, until_line)
)

self.runCmd("process status")

line = self.frame().GetLineEntry().GetLine()
self.assertEqual(
line, expected_line, "Did not get the expected stop line number"
)

def _assertDiscontinuity(self):
target = self.target()
foo = target.FindFunctions("foo")
self.assertEqual(len(foo), 1)
foo = foo[0]

call_me = self.target().FindFunctions("call_me")
self.assertEqual(len(call_me), 1)
call_me = call_me[0]

foo_addr = foo.function.GetStartAddress().GetLoadAddress(target)
found_before = False
found_after = False
for range in call_me.function.GetRanges():
addr = range.GetBaseAddress().GetLoadAddress(target)
if addr < foo_addr:
found_before = True
if addr > foo_addr:
found_after = True

self.assertTrue(
found_before and found_after,
"'foo' is not between 'call_me'" + str(foo) + str(call_me),
)

def test_hitting(self):
"""Test SBThread.StepOverUntil - targeting a line and hitting it."""
self._do_until(None, None, self.less_than_two, self.less_than_two)

@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
def test_hitting_discontinuous(self):
"""Test SBThread.StepOverUntil - targeting a line and hitting it -- with
discontinuous functions"""
self._do_until(
self._build_dict_for_discontinuity(),
None,
self.less_than_two,
self.less_than_two,
)
self._assertDiscontinuity()

def test_missing(self):
"""Test SBThread.StepOverUntil - targeting a line and missing it by stepping out to call site"""
self._do_until(
None, ["foo", "bar", "baz"], self.less_than_two, self.back_out_in_main
)

@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
def test_missing_discontinuous(self):
"""Test SBThread.StepOverUntil - targeting a line and missing it by
stepping out to call site -- with discontinuous functions"""
self._do_until(
self._build_dict_for_discontinuity(),
["foo", "bar", "baz"],
self.less_than_two,
self.back_out_in_main,
)
self._assertDiscontinuity()

def test_bad_line(self):
"""Test that we get an error if attempting to step outside the current
function"""
self.build()
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
self, "At the start", self.main_spec
)
self.assertIn(
"step until target not in current function",
thread.StepOverUntil(
self.frame(), self.main_spec, self.in_foo
).GetCString(),
)

@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
def test_bad_line_discontinuous(self):
"""Test that we get an error if attempting to step outside the current
function -- and the function is discontinuous"""
self.build(dictionary=self._build_dict_for_discontinuity())
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
self, "At the start", self.main_spec
)
self.assertIn(
"step until target not in current function",
thread.StepOverUntil(
self.frame(), self.main_spec, self.in_foo
).GetCString(),
)
self._assertDiscontinuity()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!call_me
3 changes: 3 additions & 0 deletions lldb/test/API/functionalities/thread/step_until/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
* unrelated to the program, just to achieve consistent
* debug line tables, across platforms, that are not
* dependent on compiler optimzations. */

int foo(int x) { return x; /* In foo */ }

int call_me(int argc) {
printf ("At the start, argc: %d.\n", argc);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SECTIONS {
.text.ordered : {
*(.text.call_me)
*(.text.foo)
*(.text.call_me.call_me.__part.1)
*(.text.call_me.call_me.__part.2)
*(.text.call_me.call_me.__part.3)
}
} INSERT BEFORE .text;
Loading