diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 4e2c4c1126bc3..cd3d2d89333f1 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -959,7 +959,6 @@ class CommandObjectThreadUntil : public CommandObjectParsed { } LineEntry function_start; - uint32_t index_ptr = 0, end_ptr = UINT32_MAX; std::vector address_list; // Find the beginning & end index of the function, but first make @@ -970,19 +969,14 @@ 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; + RangeVector line_idx_ranges; + for (const AddressRange &range : sc.function->GetAddressRanges()) { + auto [begin, end] = line_table->GetLineEntryIndexRange(range); + line_idx_ranges.Append(begin, end - begin); + } + line_idx_ranges.Sort(); - 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 @@ -991,45 +985,43 @@ 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) { - start_idx_ptr = sc.comp_unit->FindLineEntry( - start_idx_ptr, line_number, nullptr, exact, &line_entry); - if (start_idx_ptr == UINT32_MAX) - break; - - addr_t address = - line_entry.range.GetBaseAddress().GetLoadAddress(target); - if (address != LLDB_INVALID_ADDRESS) { - if (fun_addr_range.ContainsLoadAddress(address, target)) + uint32_t end_func_idx = line_idx_ranges.GetMaxRangeEnd(0); + uint32_t idx = sc.comp_unit->FindLineEntry( + line_idx_ranges.GetMinRangeBase(UINT32_MAX), line_number, nullptr, + exact, &line_entry); + while (idx < end_func_idx) { + if (line_idx_ranges.FindEntryIndexThatContains(idx) != UINT32_MAX) { + addr_t address = + line_entry.range.GetBaseAddress().GetLoadAddress(target); + if (address != LLDB_INVALID_ADDRESS) address_list.push_back(address); - else - all_in_function = false; } - start_idx_ptr++; + idx = sc.comp_unit->FindLineEntry(idx + 1, line_number, nullptr, + exact, &line_entry); } } 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()) { - 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; } diff --git a/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py b/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py index 1fbb404aeae58..965da02ed0f98 100644 --- a/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py +++ b/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py @@ -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) @@ -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() @@ -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, + )