使用lldb打印变量内容

3
好的,这可能听起来像是一个愚蠢的问题,但我却卡住了:在lldb调试会话期间,我无法读取一个变量的值(使用gdb时一切正常)。
我找到了其他人遇到过与我相同的错误信息的帖子,但不同之处在于,我甚至无法弄清如何打印出最简单形式的变量的值。
为了更好地表达我的问题,我将在这里考虑一个非常简单的例子。我们有一个文件"main.c",内容如下:
#include <stdlib.h>
#include <stdio.h>

int main(void) {
  int a = 1;
  int b = 2;
  int c = 0;
  c = a + b;
  c = c*b;
  printf("c = %d\n", c);
  return 0;
}

我使用以下方式进行编译:

user@machine ~ $ gcc -g main.c

生成了一个名为"a.out"的二进制文件。
然后我调用了lldb:
user@machine ~ $ lldb-3.4 ./a.out

我想在第9行停下来并读取c的值。因此,我首先添加一个断点:

(lldb) breakpoint set -f main.c -l 9

然后我运行代码:
(lldb) run

到目前为止,一切都按预期进行。现在来到棘手的部分:我想读取变量c的值。因此,我写下:

(lldb) print c

而lldb返回给我:

error: use of undeclared identifier 'c'
error: 1 errors parsing expression

当然:
(lldb) expression c

返回完全相同的错误信息。

我有什么遗漏吗?非常感谢您的帮助。

我的设置:

  • lldb:"lldb版本3.4(修订版)"(软件包v。:"3.4~svn183914-1ubuntu1")
  • gcc:"gcc(Ubuntu/Linaro 4.8.1-10ubuntu9)4.8.1"(软件包v。:"4.8.1-2ubuntu3")
  • (我的存储库是由Linux Mint 16默认提供的存储库)

根据@Sean Perry的答案,提供更多信息:

1:似乎附加选项-O0并不改变调试器的行为。

2:我还尝试使用以下虚拟代码代替以前的代码

#include <stdlib.h>
#include <stdio.h>

int main(void) {
  long a = 1;
  long b = 2;
  long c = 0;
  c = a + b;
  c += (long) &c;
  printf("c = %ld\n", c);
  return 0;
}

我不确定@Sean Perry所说的“使用指针”是否是这个意思,但我认为无论如何它都会防止代码优化,因为变量c的地址在每次运行二进制文件时(或多或少地)随机更改。
3:最终,我发现了一些有趣的事情:
  • 使用gcc [-g -O0]编译,然后使用gdb进行调试:可以工作
  • 使用gcc [-g -O0]编译,然后使用lldb进行调试:无法工作
  • 使用clang [-g -O0]编译,然后使用gdb进行调试:可以工作
  • 使用clang [-g -O0]编译,然后使用lldb进行调试:可以工作
编辑1:回复@SeanPerry 编辑2:区分软件版本和包版本

我已经尝试复制粘贴您的代码,并在Ubuntu 4.8.2-19ubuntu1Ubuntu/Linaro 4.7.3-12ubuntu1上编译它,并尝试使用lldb-3.4(稳定分支)进行调试,结果成功了(即(lldb) print c导致(int) $0 = 3)。 - Scis
@Scis:有点尴尬...你用的是32位还是64位机器?(只是想知道我使用的64位gcc版本是否会出现故障)。我的gcc版本是Ubuntu/Linaro 4.8.1-10ubuntu9,我的lldb版本是“lldb version 3.4 (revision)”。我有点困惑这是一个稳定版还是不稳定版...一方面,我的repo中的lldb包标记为“svn”,另一方面,我通过输入“aptitude install lldb-3.4”来安装它,这正是你建议我阅读的[链接](http://llvm.org/apt/)上给出的指令。你知道我怎么能找到这个信息吗? - Gael Lorieul
我正在使用64位的Ubuntu 14.04 LTS。至于安装,看起来我们两个都是用同样的方式安装的(顺便问一下,你是否也按照建议安装了clang-3.4?尝试再次运行apt-get install clang-3.4 lldb-3.4,也许存在某些版本不匹配的问题 - 当然这只是一种猜测,但这确实是一种奇怪的行为)。 - Scis
似乎我已经安装了clang-3.2.*版本的clang。我还安装了libllvm-3.3libllvm-3.3:i386,以及libllvm-3.4(似乎3.3版本的库是其他软件的依赖项,包括x.org)。然而,仅安装clang-3.4lldb-3.4似乎没有任何区别。再次强调,clang+lldb组合运行良好,而gcc+lldb组合失败。当我第一次发布我的问题时,我认为我无法正确使用lldb,但随着时间的推移,我越来越感觉问题来自于我特定的gcc版本中的故障... - Gael Lorieul
@Scis 确实,问题出在我的gcc版本(4.8.1)。使用gcc 4.7.3则没有问题。我猜测这可能是在我的r. 4.8.1和你的r. 4.8.2之间被修复的故障。这么点小问题就解决了...(^.^') 感谢您的帮助! - Gael Lorieul
我使用 -O0 -g -DDEBUG=2 - MrE
3个回答

2
似乎这是在使用 gcc 4.8.1lldb-3.4 时出现的特定问题。如果使用 gcc-4.8.2gcc-4.7.3,则没有问题。

1
你比我更快地发布了答案!(^。-) - Gael Lorieul

1
调查您看到的行为的最佳方法是查看调试信息。在Mac OS X系统上,如果您没有创建dSYM,则可以在.dSYM包或.o文件上运行dwarfdump。调试信息具有编译器传递给调试器的指令,用于查找变量的位置。
在实时进程中,使用lldb,您可以让lldb显示所有本地变量存储的位置(以DWARF位置表达式语言表示),方法是使用image lookup -v -a $pc(或im loo -va $pc简写)。
如果有一个程序无法打印变量,并且lldb在相同的PC地址处也不能打印,那听起来可能是lldb的错误。调试信息是关于变量存储位置和其“活动时间”(就调试器而言)的终极真相。在优化代码中,它们可能只在函数的非常短的部分中活动。

从黑客的角度看,真正的真相源头是阅读汇编代码。通常,在优化过程中,编译器不能像它应该的那样跟踪变量的位置 - 它可能会“说”一个变量在给定的 PC 地址不可用,但是如果你足够仔细地阅读汇编代码,你可能会发现上一次值的副本仍保存在堆栈上等。


谢谢提供这些信息! 显然,我对所有这些都很新,并且从未如此深入地研究过二进制代码,所以你所说的对我来说非常有趣。我查找了$pc是什么(我有点找到了),但是我想知道:是否还有其他“特殊”的变量与lldb调试相关? - Gael Lorieul
另一个问题:虽然DWARF格式是一种标准的调试格式,但生成*.dSYM文件是一种普遍的做法还是主要与Mac相关?为什么您似乎更喜欢将调试信息与编译后的代码分开?奇怪的是,我找不到生成这些*.dSYM文件的gccclang选项,但我可能已经忽略了它... - Gael Lorieul

0

这段代码非常简单,我敢打赌LLVM已经完全删除了变量。尝试禁用优化(-O0)进行编译,看看是否有所帮助。除此之外,请使用指针或者做一些更复杂的操作,以免编译器将您的数学计算替换为预计算的值。


一个简单的方法来测试这个可能是使用objdump在二进制文件上,看看它生成了什么代码。不过,如果它在gdb下工作的话... - cnicutar
谢谢回复!我按照你的答案尝试了几件事情,但不幸的是我并没有取得很大的成功...我已经在原来的问题中加入了今天尝试的结果。 - Gael Lorieul
@cnicutar 我使用 objdump 反汇编了二进制文件,发现在二进制文件中有一个加法和一个乘法运算:000000000040052d <main>: [...] 400551: 48 8b 55 e8 mov -0x18(%rbp),%rdx 400555: 48 01 d0 add %rdx,%rax 400558: 48 89 45 f8 mov %rax,-0x8(%rbp) [...] 40055c: 48 8b 45 f8 mov -0x8(%rbp),%rax 400560: 48 0f af 45 f0 imul -0x10(%rbp),%rax 400565: 48 89 45 f8 mov %rax,-0x8(%rbp) [...]这不是“严谨”的证明,但我认为这意味着必须在某个地方有一个 c 变量。 - Gael Lorieul

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