Author: Vy Nguyen Date: 2025-05-19T11:04:01-04:00 New Revision: dc25ab389c2d441ba378d4db56a4a61e3eedb889
URL: https://github.com/llvm/llvm-project/commit/dc25ab389c2d441ba378d4db56a4a61e3eedb889 DIFF: https://github.com/llvm/llvm-project/commit/dc25ab389c2d441ba378d4db56a4a61e3eedb889.diff LOG: [lldb]Make `list` command work with headers when possible. (#139002) Given this simple test case: ``` // foo.h extern int* ptr; inline void g(int x) { *ptr = x; // should raise a SIGILL } //-------------- // foo.cc #include "foo.h" int* ptr; //-------------- // main.cc #include "foo.h" int main() { g(123); // Call the inlined function and crash return 0; } $ clang -g main.cc foo.cc -o main.out $ lldb main.out ``` When you run `main.out` under lldb, it'd stop inside `void g(int)` because of the crash. The stack trace would show that it had crashed in `foo.h`, but if you do `list foo.h:2`, lldb would complain that it could not find the source file, which is confusing. This patch make `list` work with headers. Added: lldb/test/Shell/Commands/list-header.test Modified: lldb/source/Commands/CommandObjectSource.cpp Removed: ################################################################################ diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 8c87af590a372..7e7d3f065b622 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1108,9 +1108,15 @@ class CommandObjectSourceList : public CommandObjectParsed { } } } else { - const char *filename = m_options.file_name.c_str(); - + // const char *filename = m_options.file_name.c_str(); + FileSpec file_spec(m_options.file_name); bool check_inlines = false; + const InlineStrategy inline_strategy = target.GetInlineStrategy(); + if (inline_strategy == eInlineBreakpointsAlways || + (inline_strategy == eInlineBreakpointsHeaders && + !file_spec.IsSourceImplementationFile())) + check_inlines = true; + SymbolContextList sc_list; size_t num_matches = 0; @@ -1122,17 +1128,20 @@ class CommandObjectSourceList : public CommandObjectParsed { ModuleSpec module_spec(module_file_spec); matching_modules.Clear(); target.GetImages().FindModules(module_spec, matching_modules); - num_matches += matching_modules.ResolveSymbolContextForFilePath( - filename, 0, check_inlines, + num_matches += matching_modules.ResolveSymbolContextsForFileSpec( + file_spec, 1, check_inlines, SymbolContextItem(eSymbolContextModule | - eSymbolContextCompUnit), + eSymbolContextCompUnit | + eSymbolContextLineEntry), sc_list); } } } else { - num_matches = target.GetImages().ResolveSymbolContextForFilePath( - filename, 0, check_inlines, - eSymbolContextModule | eSymbolContextCompUnit, sc_list); + num_matches = target.GetImages().ResolveSymbolContextsForFileSpec( + file_spec, 1, check_inlines, + eSymbolContextModule | eSymbolContextCompUnit | + eSymbolContextLineEntry, + sc_list); } if (num_matches == 0) { @@ -1179,10 +1188,18 @@ class CommandObjectSourceList : public CommandObjectParsed { if (m_options.num_lines == 0) m_options.num_lines = 10; const uint32_t column = 0; + + // Headers aren't always in the DWARF but if they have + // executable code (eg., inlined-functions) then the callsite's + // file(s) will be found and assigned to + // sc.comp_unit->GetPrimarySupportFile, which is NOT what we want to + // print. Instead, we want to print the one from the line entry. + lldb::SupportFileSP found_file_sp = sc.line_entry.file_sp; + target.GetSourceManager().DisplaySourceLinesWithLineNumbers( - sc.comp_unit->GetPrimarySupportFile(), - m_options.start_line, column, 0, m_options.num_lines, "", - &result.GetOutputStream(), GetBreakpointLocations()); + found_file_sp, m_options.start_line, column, 0, + m_options.num_lines, "", &result.GetOutputStream(), + GetBreakpointLocations()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { diff --git a/lldb/test/Shell/Commands/list-header.test b/lldb/test/Shell/Commands/list-header.test new file mode 100644 index 0000000000000..08bcedd3fc946 --- /dev/null +++ b/lldb/test/Shell/Commands/list-header.test @@ -0,0 +1,57 @@ +## Test that `list header.h:<line>` works correctly when header is available. +## +# RUN: split-file %s %t + +# RUN: %clang_host -g %t/main_with_inlined.cc %t/foo.cc -o %t/main_with_inlined.out +# RUN: %clang_host -g %t/main_no_inlined.cc %t/foo.cc -o %t/main_no_inlined.out + +# RUN: %lldb %t/main_with_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-INLINED + +## Would be nice if this listed the header too - but probably not something +## we want to support right now. +# RUN: echo quit | %lldb %t/main_no_inlined.out -o "list foo.h:2" -o "exit" 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-INLINED + +# CHECK-INLINED: 2 extern int* ptr; +# CHECK-INLINED: 3 void f(int x); +# CHECK-INLINED: 4 +# CHECK-INLINED: 5 inline void g(int x) { +# CHECK-INLINED: 6 *ptr = x; // should crash here +# CHECK-INLINED: 7 } + +# CHECK-NO-INLINED: error: Could not find source file "foo.h". + +#--- foo.h +// foo.h +extern int* ptr; +void f(int x); + +inline void g(int x) { + *ptr = x; // should crash here +} + +#--- foo.cc +#include "foo.h" + +int* ptr; + +void f(int x) { + *ptr = x; +} + +#--- main_with_inlined.cc +#include "foo.h" + +int main() { + g(123); // Call the inlined function + return 0; +} + +#--- main_no_inlined.cc +#include "foo.h" + +int main() { + f(234); + return 0; +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits