Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
65 changes: 36 additions & 29 deletions lldb/source/Commands/CommandObjectThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,6 @@ class CommandObjectThreadUntil : public CommandObjectParsed {
}

LineEntry function_start;
uint32_t index_ptr = 0, end_ptr = UINT32_MAX;
std::vector<addr_t> address_list;

// Find the beginning & end index of the function, but first make
Expand All @@ -970,19 +969,26 @@ class CommandObjectThreadUntil : public CommandObjectParsed {
return;
}

AddressRange fun_addr_range = sc.function->GetAddressRange();
Address fun_start_addr = fun_addr_range.GetBaseAddress();
line_table->FindLineEntryByAddress(fun_start_addr, function_start,
&index_ptr);

Address fun_end_addr(fun_start_addr.GetSection(),
fun_start_addr.GetOffset() +
fun_addr_range.GetByteSize());

bool all_in_function = true;
uint32_t lowest_func_idx = UINT32_MAX;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be useful anywhere else? It's asking a Function what its highest and lowest indexes are in the linetable from its CU.

uint32_t highest_func_idx = 0;
for (AddressRange range : sc.function->GetAddressRanges()) {
uint32_t idx;
LineEntry unused;
Address addr = range.GetBaseAddress();
if (line_table->FindLineEntryByAddress(addr, unused, &idx))
lowest_func_idx = std::min(lowest_func_idx, idx);

addr.Slide(range.GetByteSize());
if (line_table->FindLineEntryByAddress(addr, unused, &idx)) {
highest_func_idx = std::max(highest_func_idx, idx);
} else {
// No line entry after the current function. The function is the
// last in the file, so we can just search until the end.
highest_func_idx = UINT32_MAX;
}
}

line_table->FindLineEntryByAddress(fun_end_addr, function_start,
&end_ptr);
bool found_something = false;

// Since not all source lines will contribute code, check if we are
// setting the breakpoint on the exact line number or the nearest
Expand All @@ -991,14 +997,15 @@ class CommandObjectThreadUntil : public CommandObjectParsed {
for (uint32_t line_number : line_numbers) {
LineEntry line_entry;
bool exact = false;
uint32_t start_idx_ptr = index_ptr;
start_idx_ptr = sc.comp_unit->FindLineEntry(
index_ptr, line_number, nullptr, exact, &line_entry);
if (start_idx_ptr != UINT32_MAX)
line_number = line_entry.line;
if (sc.comp_unit->FindLineEntry(0, line_number, nullptr, exact,
&line_entry) == UINT32_MAX)
continue;

found_something = true;
line_number = line_entry.line;
exact = true;
start_idx_ptr = index_ptr;
while (start_idx_ptr <= end_ptr) {
uint32_t start_idx_ptr = lowest_func_idx;
while (start_idx_ptr <= highest_func_idx) {
start_idx_ptr = sc.comp_unit->FindLineEntry(
start_idx_ptr, line_number, nullptr, exact, &line_entry);
if (start_idx_ptr == UINT32_MAX)
Expand All @@ -1007,29 +1014,29 @@ class CommandObjectThreadUntil : public CommandObjectParsed {
addr_t address =
line_entry.range.GetBaseAddress().GetLoadAddress(target);
if (address != LLDB_INVALID_ADDRESS) {
if (fun_addr_range.ContainsLoadAddress(address, target))
AddressRange unused;
if (sc.function->GetRangeContainingLoadAddress(address, *target,
unused))
address_list.push_back(address);
else
all_in_function = false;
}
start_idx_ptr++;
}
}

for (lldb::addr_t address : m_options.m_until_addrs) {
if (fun_addr_range.ContainsLoadAddress(address, target))
AddressRange unused;
if (sc.function->GetRangeContainingLoadAddress(address, *target,
unused))
address_list.push_back(address);
else
all_in_function = false;
}

if (address_list.empty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you only care here about found_something and address_list.size > 0 could you short-circuit this search when you find the first address that matches the line?

Copy link
Collaborator Author

@labath labath Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. address_list being empty is the "error" case. In case of success, the full list of addresses is used for stepping (line 1040)

if (all_in_function)
if (found_something)
result.AppendErrorWithFormat(
"No line entries matching until target.\n");
"Until target outside of the current function.\n");
else
result.AppendErrorWithFormat(
"Until target outside of the current function.\n");
"No line entries matching until target.\n");

return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ def setUp(self):
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 common_setup(self, args):
self.build()
def _common_setup(self, build_dict, args):
self.build(dictionary=build_dict)
exe = self.getBuildArtifact("a.out")

target = self.dbg.CreateTarget(exe)
Expand All @@ -45,7 +54,7 @@ def common_setup(self, args):
return thread

def do_until(self, args, until_lines, expected_linenum):
thread = self.common_setup(args)
thread = self._common_setup(None, args)

cmd_interp = self.dbg.GetCommandInterpreter()
ret_obj = lldb.SBCommandReturnObject()
Expand Down Expand Up @@ -88,3 +97,30 @@ def test_missing_one(self):
self.do_until(
["foo", "bar", "baz"], [self.less_than_two], self.back_out_in_main
)

@no_debug_info_test
def test_bad_line(self):
"""Test that we get an error if attempting to step outside the current
function"""
thread = self._common_setup(None, None)
self.expect(
f"thread until {self.in_foo}",
substrs=["Until target outside of the current function"],
error=True,
)

@no_debug_info_test
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
@skipIf(archs=no_match(["x86_64", "aarch64"]))
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", lldb.SBFileSpec(self.main_source)
)
self.expect(
f"thread until {self.in_foo}",
substrs=["Until target outside of the current function"],
error=True,
)