Debugger stops at a non-exist line when debugging unoptimized code (original) (raw)
I am spliting a large C file into several small files. However, when debugging the program (compiled and linked without any optimization) after splitting, LLDB shows a non-existing line as shown in the following:
(lldb) si
Process 28554 stopped
* thread #1, name = 'split', stop reason = instruction step into
frame #0: 0x00005555555552e5 split`func_1 at fn0.c:0:318
1 #include "split.h"
-> 2 uint64_t func_1(void)
3 {
4 uint32_t l_19 = 7UL;
5 uint16_t l_1155 = 0xF25AL;
6 int32_t l_1168 = 0x9953B479L;
7 int64_t l_1169 = 1L;
note: This address is not associated with a specific line of code. This may be due to compiler optimizations.
Debugging the original program would not yield such information. Is it a bug of LLDB? Or does the debug information in the split program go wrong (caused by linking maybe)?
How can I analyze it?
jmorse April 2, 2025, 10:03am 2
Interesting – I believe that message comes from the line-number of an instruction being zero. That’s usually done by the compiler to indicate that it’s compiler-generated code, or doesn’t correspond to any single line, which is surprising as you say it’s without optimisations.
You can analyse further using llvm-dwarfdump --debug-line
to examine the line table for the object file, to confirm whether there are any instructions that are assigned line-zero by LLVM.
ShanH April 2, 2025, 12:09pm 3
@jmorse Thanks! I have attached LLDB to the original program (compiled from a single C file using clang -g -O0
) and recorded all the lines, at which the debugger can stop. The recored lines show that the debugger has also stopped at a line 0 (I missed this before). So, I used llvm-dwarfdump
to dump the line table of the original program and I found that there are a lot of 0 lines:
$ llvm-dwarfdump --debug-line splited/origin-dbg | grep -E '^\S+\s+0(\s|$)'
Address Line Column File ISA Discriminator OpIndex Flags
------------------ ------ ------ ------ --- ------------- ------- -------------
0x0000000000001887 0 108 0 0 0 0
0x00000000000018d4 0 388 0 0 0 0
0x00000000000019f6 0 10 5 0 0 0
0x0000000000001a3c 0 11 5 0 0 0
0x0000000000001a9f 0 11 5 0 0 0
0x0000000000001be7 0 266 0 0 0 0
0x0000000000001cfa 0 714 0 0 0 0
0x0000000000001fb9 0 293 0 0 0 0
0x0000000000002054 0 398 0 0 0 0
0x0000000000002183 0 10 5 0 0 0
0x00000000000021d5 0 11 5 0 0 0
...
Example: When debugging the original program, LLDB has stopped at location 0:108 shown in the above line table.
I have also checked the LLVM IR of the original program, and found that there are some !DILocation(line: 0, scope: ...)
attached to some phi
instructions. The LLVM IR is obtained by clang -S -emit-llvm -g -O0
. But I don’t think this is the root cause.
Example:
land.end160: ; preds = %land.rhs153, %for.body138
%58 = phi i1 [ false, %for.body138 ], [ %tobool159, %land.rhs153 ], !dbg !9772
!9772 = !DILocation(line: 0, scope: !9758)
Line 0 is typically used to mean “code the compiler added, but is not attached to user code”.
If I were looking into this I’d look at the linetable output for the split case, and use that to look at the disassembly, then compare that to the linetable/disassembly of the unsplit case.