LLDB 不显示源代码

Posted

技术标签:

【中文标题】LLDB 不显示源代码【英文标题】:LLDB not showing source code 【发布时间】:2016-08-26 09:48:05 【问题描述】:

我正在尝试调试我正在编写的 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 会覆盖它,对吧?

我的尝试

    我用一个简单的“hello world”程序使用相同的构建设置重新创建了这个问题。

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

    我也尝试将-gsplit-dwarf 添加到构建命令中,但没有任何效果。

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

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

    我在可执行文件和目标文件上运行了dwarfdump (I read about it here)。在我未经训练的眼睛看来,调试符号 出现在目标文件中,但不在可执行文件本身中(除非dwarfdump 仅适用于目标文件,这是可能的)。所以也许链接阶段是问题所在。或者也许 DWARF 有问题。

    我现在可以在“hello world”程序中使用它,方法是在终端中逐一发出构建命令。因此,我猜测这可能是我的构建系统 (Tup) 的问题,可能是使用不同的工作目录运行命令,因此路径会被破坏或其他原因。

【问题讨论】:

Xcode 有一个选项来'strip the executable',我想这会删除调试符号。检查是否设置了此选项。 我是从命令行构建的,而不是在 Xcode 中。有什么方法可能还会发生这种情况吗?也许在链接期间剥离可执行文件?我也会在上面添加我的链接命令... imgur.com/a/idI6C 编辑:刚刚看到您正在使用命令行.. nvm.. 但我将把它留在这里以防万一您决定使用 Xcode。 谢谢!有什么方法可以查看与 Xcode 相同的 CLI 功能吗?我会戳一下,看看我是否可以这样做,可能会提供更多线索。 根据我上面的实验/编辑,这绝对是 Tup 构建工具。我使用了dwarfdump 以及各种基于 Tup 和非 Tup 的构建命令,它似乎正在破坏 DWARF 输出中的路径。 Tup 使用虚拟 FUSE 文件系统来确定构建命令的输出,所以可能就是这样。我会进一步更新这个问题,并在找到答案时提​​供答案。如果有人看到这个并知道 Tup 的解决方案,请告诉我,但我知道它是一个鲜为人知的工具。 【参考方案1】:

当您将 -g 命令行选项添加到 clang 时,DWARF 调试信息将放在 .o 文件中。当您将您的目标文件(.o,ranlib 档案又名静态库又名.a 文件)链接到可执行文件/dylib/framework/bundle 中时,“调试说明”会放在可执行文件中以说明(1) .o 带有调试信息的 etc 文件,以及 (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 搜索以查看 dSYM 是否位于您计算机上的 Spotlight 索引位置。

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

所以,正常的工作流程:使用 -g 编译。链接可执行映像。如果是迭代开发,运行调试器。如果发送/归档二进制文件,请创建 dSYM,剥离可执行文件。

【讨论】:

感谢 Jason,这是有用的信息。我现在认为这是因为我的构建系统将不同的构建类型(生产、调试)放在子目录中,路径信息(可执行文件 => .o 或 .o => 源代码)不正确。有什么方法可以直接查找/替换 .o 文件中的路径?如果不是,我想我必须弄清楚如何调整构建工具,Tup。 我不知道有任何工具可以更改 OSO nlist 条目中的路径。 dsymutil 有一个 -oso-prepend-path= 选项,如果您要在 .o 文件名前添加一些字符串,但我尚未测试该功能(而且我认为它不会满足您的需求)。 再次感谢。抱歉回复慢,这是一个副项目,所以我没有太多时间。我将把它标记为已接受,因为它已经解决了为什么会发生这种情况的问题。如果有机会,我会前往 Tup 社区寻找具体的解决方案。 只是给最后来到这里的任何人的注释,正如 Jason 所说,这是因为路径不正确(构建工具在子目录中执行所有操作)。我使用此处概述的方法解决了它:***.com/questions/12973633/… 这也是 Jason 的答案之一;)【参考方案2】:

我通过使用(lldb) target symbols add a.out.dSYM 命令添加调试符号的路径来解决它,这些符号存在于 a.out.dSYM 目录中。

【讨论】:

以上是关于LLDB 不显示源代码的主要内容,如果未能解决你的问题,请参考以下文章

Swift之深入解析如何将代码添加为自定义LLDB命令

如何使用 lldb 跳过几行代码?

LLDB原理与调试实践

在lldb调试中调用c++函数 - 如何使用QuartzCore里面的日志消息

如何在 xcode 中设置 lldb 的发现路径

多个命令为一个的 LLDB 别名