2 uint64_t func_1(void) 3 { 4 uint32_t l_19 = 7UL; 5 uint16_t l_115...">

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.