27

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

  1. I’ve recreated this problem with a simple ‘hello world’ program using the same build settings.

  2. 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?

  3. I also tried adding -gsplit-dwarf to the build commands but with no effect.

  4. Here is the link command from my ‘hello world’ version:

    clang++ main.o -L/usr/local/lib -g -o hello

  5. 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.

  6. 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.

Community
  • 1
  • 1
Leo
  • 4,217
  • 4
  • 25
  • 41
  • Xcode has an option to _'strip the executable'_, which, I suppose, removes debug symbols. Check whether this option is set. – ForceBru May 01 '16 at 16:46
  • 1
    I’m building from the command line, not inside Xcode. Is there any way this could still be happening? Maybe stripping the executable happen during linking? I’ll add my link command above too... – Leo May 01 '16 at 16:50
  • http://imgur.com/a/idI6C EDIT: Just saw you're using command line.. nvm.. but I'll leave this here just in case you decide to use Xcode. – Brandon May 01 '16 at 16:51
  • Thanks! Is there any way to see the CLI equivalent of what Xcode does? I’ll have a poke about and see if I can do that, might give some more clues. – Leo May 01 '16 at 16:53
  • As per my experiments/edits above, this is definitely the Tup build tool. I’ve used `dwarfdump` alongside various Tup-based and non-Tup build commands, and it seems that it’s mangling the paths in the DWARF output. Tup uses a dummy FUSE filesystem to determine what the outputs are from build commands, so it’s probably that. I’ll update this question further, and provide an answer when I find one. If anyone sees this and knows a solution for Tup then let me know, but I’m aware it’s a less-well-known tool. – Leo May 01 '16 at 22:02

2 Answers2

32

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.

Jason Molenda
  • 14,835
  • 1
  • 59
  • 61
  • Thanks Jason, this is useful information. I now think that this is happening because my build system is putting different build types (production, debug) in subdirectories, the path information (either executable => .o or .o => source) is incorrect. Is there any way to do a straightforward find/replace on paths in the .o files? If not I think I’ll have to figure out how to adjust the build tool, Tup. – Leo May 03 '16 at 13:10
  • 1
    I don't know of any tool that will change the paths in the OSO nlist entries. `dsymutil` has a `-oso-prepend-path=` option if you were going to prepend some string to the .o filenames but I haven't tested that feature (and I don't think it'll do what you need). – Jason Molenda May 04 '16 at 02:01
  • Thanks again. Sorry for slow response, this is a side-project so I don’t get much time on it. I’m going to mark this as accepted because it has resolved the question of why this has happened. When I get a chance I’ll head over to the Tup community and look for a specific solution. – Leo May 06 '16 at 09:52
  • Just a note to anyone else who ends up here, this was, as Jason says, because of the paths being incorrect (the build tool was doing everything in a subdirectory). I solved it using the approach outlined here: http://stackoverflow.com/questions/12973633/lldb-equivalent-of-gdb-directory-command-for-specifying-source-search-path which is also one of Jason's answers ;) – Leo May 08 '16 at 18:07
9

I solved it by adding the path to debug symbols which are present in a.out.dSYM directory using (lldb) target symbols add a.out.dSYM command.