Hello,
I noticed this behavior for LLDB under Linux when setting a breakpoint on a file and a function name:
When doing “breakpoint set --file --name ”, the is that of the compile unit (CU) and not necessarily where the function is defined. This is not what an end-user expects.
Take this simple example program:
$ cat foo.h
int foo(){ return 42; }
$ cat main.c
#include “foo.h”
int main(){return foo();}
$ clang -g main.c
As you can see, the function foo is defined in foo.h so it seems natural to set a breakpoint on foo.h, doesn’t it?
$ lldb -x -b -o “breakpoint set --file foo.h --name foo” ./a.out
(lldb) target create “./a.out”
Current executable set to ‘./a.out’ (x86_64).
(lldb) breakpoint set --file foo.h --name foo
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.
Apparently, LLDB cannot find the symbol like this. Let’s try the only other file that we have in the project:
$ lldb -x -b -o “breakpoint set --file main.c --name foo” ./a.out
(lldb) target create “./a.out”
Current executable set to ‘./a.out’ (x86_64).
(lldb) breakpoint set --file main.c --name foo
Breakpoint 1: where = a.out`foo + 4 at foo.h:1:12, address = 0x0000000000401114
Isn’t that remarkable? LLDB uses main.c as the file to search in but then finds it in foo.h.
Let’s recall what the parameters --file and --name mean:
-n ( --name )
Set the breakpoint by function name. Can be repeated multiple times to make one breakpoint for multiple names
-f ( --file )
Specifies the source file in which to set this breakpoint. Note, by default lldb only looks for files that are #included if they use the standard include file extensions. To
set breakpoints on .c/.cpp/.m/.mm files that are #included, set target.inline-breakpoint-strategy to “always”.
Let’s check if setting the target.inline-breakpoint strategy to “always” changes something:
$ lldb -x -b -o “settings set target.inline-breakpoint-strategy always” -o “breakpoint set --file foo.h --name foo” ./a.out
(lldb) target create “./a.out”
Current executable set to ‘./a.out’ (x86_64).
(lldb) settings set target.inline-breakpoint-strategy always
(lldb) breakpoint set --file foo.h --name foo
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.
No, it didn’t change anything.
The only evidence for my assumption that LLDB uses the CU’s name for --file is the DWARF dump:
$ llvm-dwarfdump a.out
a.out: file format ELF64-x86-64
.debug_info contents:
0x00000000: Compile Unit: length = 0x00000060 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000064)
0x0000000b: DW_TAG_compile_unit
DW_AT_producer (“clang version 8.0.0 (Fedora 8.0.0-3.fc30)”)
DW_AT_language (DW_LANG_C99)
DW_AT_name (“main.c”)
DW_AT_stmt_list (0x00000000)
DW_AT_comp_dir (“/home/kkleine”)
DW_AT_low_pc (0x0000000000401110)
DW_AT_high_pc (0x000000000040113a)
0x0000002a: DW_TAG_subprogram
DW_AT_low_pc (0x0000000000401110)
DW_AT_high_pc (0x000000000040111b)
DW_AT_frame_base (DW_OP_reg6 RBP)
DW_AT_name (“foo”)
DW_AT_decl_file (“/home/kkleine/./foo.h”)
DW_AT_decl_line (1)
DW_AT_type (0x0000005c “int”)
DW_AT_external (true)
0x00000043: DW_TAG_subprogram
DW_AT_low_pc (0x0000000000401120)
DW_AT_high_pc (0x000000000040113a)
DW_AT_frame_base (DW_OP_reg6 RBP)
DW_AT_name (“main”)
DW_AT_decl_file (“/home/kkleine/main.c”)
DW_AT_decl_line (2)
DW_AT_type (0x0000005c “int”)
DW_AT_external (true)
0x0000005c: DW_TAG_base_type
DW_AT_name (“int”)
DW_AT_encoding (DW_ATE_signed)
DW_AT_byte_size (0x04)
As you can see, the DWARF is very small and simply. The function foo has a DW_AT_decl_file which is probably used to report the breakpoint location but for the actual filtering, it seems as if the CU is crucial for the --file argument.
The only reasonable implementation for --file to me seems to be when combined with the line number:
$ lldb -x -b -o “breakpoint set --file foo.h --line 1” ./a.out
(lldb) target create “./a.out”
Current executable set to ‘./a.out’ (x86_64).
(lldb) breakpoint set --file foo.h --line 1
Breakpoint 1: where = a.out`foo + 4 at foo.h:1:12, address = 0x0000000000401114
This works as expected.
For myself I think that the --file --name combination works not like an end-user expects because in bigger projects you typically look at the definition of foo and want to pause, when execution reaches this. You don’t care if a function is inlined or in which CU the function is located. Moreover I think the DWARF actually supports more than enough information with the DW_TAG_subprogram DIE’s attributes for the file and line number. Enough to filter by file and line number.
IMHO we should come up with a very strong argument to justify that --file limits by a CU’s DW_AT_name. To me the only reasonable argument is speed. But speed doesn’t justify such a drastic limitation. Shouldn’t we rather change what --file currently does and keep the old behavior when using --cu instead of --file?
- Konrad