LLDB未显示源代码。

27

我正在尝试调试我写的C++程序,但当我在LLDB中运行它并停止程序时,它只显示汇编代码,而不是原始源代码。 例如,在崩溃后我想要调试:

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

我正在使用-O0 -g编译。当我通过Xcode(我在OSX上)或从命令行运行调试器时,我看到相同的情况。

我还需要做什么才能让源代码出现在LLDB中?

附加说明

这是典型构建命令的示例:

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

之前有 -O2,因为我使用的是默认值,但我认为后面的 -O0 会覆盖它,对吗?

我尝试过

  1. 我用同样的构建设置创建了一个简单的“hello world”程序来重现这个问题。

  2. 经过一些搜索,我尝试运行 dsymutil main.o,它显示 warning: no debug symbols in executable (-arch x86_64),所以也许我的构建命令没有生成调试符号?

  3. 我还尝试将 -gsplit-dwarf 添加到构建命令中,但没有效果。

  4. 这是我“hello world”版本的链接命令:

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

  5. 我在可执行文件和目标文件上运行了 dwarfdump我在这里读到的)。在我的生疏的眼中,它看起来像调试符号在目标文件中是存在的,但在可执行文件本身中不存在(除非 dwarfdump 只能在目标文件上工作,这是可能的)。所以也许连接阶段存在问题。或者可能出现了 DWARF 的问题。

  6. 现在,我已经通过在终端逐个发出构建命令来在“hello world”程序中解决了这个问题。因此,我猜这可能是我的构建系统(Tup)的问题,可能会以不同的工作目录运行命令,因此路径会出现问题或其他什么问题。


Xcode有一个选项可以_'strip the executable'_,我猜想是删除调试符号。请检查此选项是否已设置。 - ForceBru
1
我正在使用命令行构建,而不是在Xcode内部。这种情况任何可能发生吗?也许剥离可执行文件是在链接期间发生的?我会在上面添加我的链接命令。 - Leo
请注意:我刚看到您正在使用命令行,所以这段内容可能不适用。但如果您决定使用 Xcode,可以参考这里。 - Brandon
谢谢!有没有办法看到Xcode所做的CLI等效操作?我会尝试一下,看看能否做到这一点,可能会提供更多线索。 - Leo
根据我上面的实验/编辑,这绝对是Tup构建工具。我已经使用了dwarfdump与各种基于Tup和非Tup构建命令,并且似乎它正在搞乱DWARF输出中的路径。Tup使用一个虚拟的FUSE文件系统来确定构建命令的输出,所以很可能是这个原因。我会进一步更新这个问题,并在找到答案时提供一个解决方案。如果有人看到这篇文章并知道Tup的解决方案,请让我知道,但我知道这是一个不太知名的工具。 - Leo
2个回答

32

当你在clang命令行上添加-g选项时,DWARF调试信息会被放入.o文件中。当你将目标文件(.o、ranlib存档文件也就是静态库文件.a)链接成可执行文件/dylib/framework/bundle时,“调试笔记”会被放入可执行文件中,以指示(1)包含调试信息的.o等文件的位置,以及(2)可执行二进制文件中函数/变量的最终地址。优化标志(-O0、-O2等)不会影响调试信息的生成——尽管使用优化编译的调试代码比使用-O0编译的调试代码更加困难。

如果你在没有任何其他修改的情况下运行调试器来调试该可执行二进制文件,在构建可执行文件时,只要这些.o等文件仍然位于相同的文件路径下,调试器就会从这些文件中读取调试信息。这使得迭代开发变得快速——没有工具需要读取、更新和输出(庞大的)调试信息。你可以通过运行“nm -pa exename”查找OSO条目(以及其他条目)来在可执行文件中看到这些“调试笔记”。这些是stabs nlist条目,对可执行文件运行strip(1)会将它们删除。

如果您想将所有的调试信息(在.o文件中)收集到一个独立的捆绑包中,那么您需要在可执行文件上运行dsymutil。此操作使用调试注释(假设:(1).o文件仍处于其原始位置,且(2)未对可执行文件进行剪裁),创建一个"dSYM捆绑包"。如果二进制文件是exename,则dSYM捆绑包是exename.dSYM。当调试器在exename上运行时,它将在该二进制文件旁边查找dSYM捆绑包。如果没有找到,则会在计算机上的Spotlight索引位置上进行搜索。

您可以在.o文件或dSYM捆绑包上运行dwarfdump--它们都包含调试信息。 dwarfdump无法在输出可执行文件中找到任何调试信息。

因此,正常的工作流程是:使用-g选项编译,链接可执行映像。如果进行迭代开发,则运行调试器。如果发布/存档二进制文件,则创建dSYM,剪裁可执行文件。


谢谢Jason,这是有用的信息。我现在认为这是因为我的构建系统将不同的构建类型(生产、调试)放在子目录中,路径信息(可执行文件=> .o或.o => 源)不正确。有没有办法在.o文件中进行简单的查找/替换路径?如果没有,我想我必须想办法调整构建工具Tup。 - Leo
1
我不知道有任何工具可以更改OSO nlist条目中的路径。如果您要在.o文件名之前添加一些字符串,则dsymutil具有-oso-prepend-path =选项,但我尚未测试该功能(并且我认为它不会做您需要的事情)。 - Jason Molenda
再次感谢。很抱歉回复慢了,这只是一个副业项目,所以我没有太多时间在上面。我将标记此为已接受,因为它解决了为什么会发生这种情况的问题。有机会时,我会前往Tup社区寻找具体的解决方案。 - Leo
对于其他遇到此问题的人,这只是一个提示,正如Jason所说,这是由于路径不正确(构建工具在子目录中执行所有操作)导致的。我使用了这里概述的方法解决了这个问题:https://dev59.com/Amcs5IYBdhLWcg3wSiFo 这也是Jason的答案之一 ;) - Leo

9

我通过使用(lldb) target symbols add a.out.dSYM命令,将包含在a.out.dSYM目录中的调试符号路径添加进去来解决了这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接