I am trying to debug a C++ program I am writing, but when I run it in LLDB and stop the program, it only shows me the assembler, not the original source. e.g. after the crash I’m trying to debug:
Process 86122 stopped
* thread #13: tid = 0x142181, 0x0000000100006ec1 debug_build`game::update() + 10961, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000100006ec1 debug_build`game::update() + 10961
debug_build`game::update:
-> 0x100006ec1 <+10961>: movq (%rdx), %rdx
0x100006ec4 <+10964>: movq %rax, -0xb28(%rbp)
0x100006ecb <+10971>: movq -0x1130(%rbp), %rax
0x100006ed2 <+10978>: movq 0x8(%rax), %rsi
I am compiling with -O0 -g
. I see the same thing when running the debugger via Xcode (I’m on OSX) or from the command line.
What else might I need to do to get the source code to show up in LLDB?
Additional notes
Here is an example of a typical build command:
clang++ -std=c++1y -stdlib=libc++ -fexceptions -I/usr/local/include -c -O2 -Wall -ferror-limit=5 -g -O0 -ftrapv lib/format.cpp -o format.o
The earlier -O2
is there because that’s the default I’m using, but I believe the later -O0
overrides it, right?
What I’ve tried
I’ve recreated this problem with a simple ‘hello world’ program using the same build settings.
After some searching, I tried running dsymutil main.o
which said warning: no debug symbols in executable (-arch x86_64)
, so perhaps the debug symbols are not being generated by my build commands?
I also tried adding -gsplit-dwarf
to the build commands but with no effect.
Here is the link command from my ‘hello world’ version:
clang++ main.o -L/usr/local/lib -g -o hello
I ran dwarfdump
(I read about it here) on the executable and object files. It looks to my untrained eye like the debug symbols are present in the object files, but not in the executable itself (unless dwarfdump
only works on object files, which is possible). So maybe the linking stage is the issue. Or maybe there’s a problem with the DWARF.
I have now got this working in the ‘hello world’ program, through issuing build commands one-by-one in the terminal. I am therefore guessing this may be an issue with my build system (Tup), possibly running the commands with a different working directory so the paths get mangled or something.
When you add the -g command line option to clang, DWARF debug information is put in the .o
file. When you link your object files (.o
, ranlib archives aka static libraries aka .a
files) into an executable/dylib/framework/bundle, "debug notes" are put in the executable to say (1) the location of the .o
etc files with the debug information, and (2) the final addresses of the functions/variables in the executable binary. Optimization flags (-O0
, -O2
etc) do not have an impact on debug information generation - although debugging code compiled with optimization is much more difficult than debugging code built at -O0
.
If you run the debugger on that executable binary -- without any other modification -- the debugger will read the debug information from the .o
etc files as long as they're still on the filesystem at the same file path when you built the executable. This makes iterative development quick - no tool needs to read, update, and output the (large) debug information. You can see these "debug notes" in the executable by running nm -pa exename
and looking for OSO
entries (among others). These are stabs nlist entries and running strip(1)
on your executable will remove them.
If you want to collect all of the debug information (in the .o
files) into a standalone bundle, then you run dsymutil
on the executable. This uses the debug notes (assumptions: (1) the .o
files are still in their orig location, and (2) the executable has not been stripped) to create a "dSYM
bundle". If the binary is exename, the dSYM bundle is exename.dSYM. When the debugger is run on exename, it will look next to that binary for the dSYM bundle. If not found there, it will do a Spotlight search to see if the dSYM is in a spotlight-indexed location on your computer.
You can run dwarfdump
on .o
files, or on the dSYM bundle -- they both have debug information in them. dwarfdump
won't find any debug information in your output executable.
So, the normal workflow: Compile with -g. Link executable image. If iterative development, run debugger. If shipping/archiving the binary, create dSYM, strip executable.