为什么GDB在跳转行并打印变量时会出现不可预测的情况,显示为"<value optimized out>"?

88

有人能解释一下gdb的这种行为吗?

900         memset(&new_ckpt_info,'\0',sizeof(CKPT_INFO));
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_** HDR),i_offset);
(gdb)
**903         prev_offset   = cp_node->offset;**
(gdb)
**905         m_CPND_CKPTINFO_READ(ckpt_info,(char *)cb->shm_addr.ckpt_addr+sizeof(CKPT_ HDR),i_offset);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
**908         bitmap_offset  = client_hdl/32;**
(gdb)
**910         bitmap_value = cpnd_client_bitmap_set(client_hdl%32);**
(gdb)
913         found = cpnd_find_exact_ckptinfo(cb , &ckpt_info , bitmap_offset , &offset , &prev_offset);
(gdb)
916         if(!found)
(gdb) p found
$1 = <value optimized out>
(gdb) set found=0
Left operand of assignment is not an lvalue.
为什么在执行903行后,它又执行了905、908和910行?
另外一个问题是:变量“found”是一个布尔类型的变量,为什么显示为“value optimized out”?我也无法设置“found”的值。
这似乎是编译器优化(在这种情况下是-O2);我如何设置“found”的值呢?

9
调试时,通常最好使用 -O0 编译,因为优化可能会导致此类问题。 - LiraNuna
8个回答

115

为了调试代码,学习汇编/机器语言。

使用GDB TUI模式。当我键入减号和Enter时,我的GDB就启用它了。然后键入C-x 2(即按住Control并按X,释放两个按键,然后按2)。这将把它放入拆分源和反汇编显示中。然后使用 stepinexti 一次移动一个机器指令。使用 C-x o 在TUI窗口之间切换。

下载有关您的CPU的机器语言和函数调用约定的PDF文件。您将很快学会识别对函数参数和返回值的操作。

可以使用类似于p $eax的GDB命令来显示寄存器的值。


我仍然遇到了“优化掉”的问题,而且变量值在其他窗口中也没有显示出来,但是这仍然是非常有用的信息,谢谢! - The Student
17
"Optimized out" 的意思是该变量未在内存中,很可能仅存在于 CPU 寄存器中。因此,您需要阅读反汇编代码并打印寄存器的值以找到它。 - Zan Lynx
@Zan Lynx:我不确定我同意你的分析。DWARF符号具有足够的信息来从寄存器中提取值。也许这里的意思是编译器已经确定变量在执行到达当前行时可以安全地丢弃。在这种情况下,变量所在的存储空间可能已被重新用于其他用途。我认为你是正确的,这通常只会发生在变量被注册的情况下。 - Ian Ni-Lewis
@IanNi-Lewis:我不知道你使用的DWARF版本是什么,但根据我的经验,GDB无法打印已存储到寄存器中的变量。 - Zan Lynx
@IanNi-Lewis 这很可能是由于GCC没有为仅寄存器变量发出符号的结果。 - Ruslan
显示剩余3条评论

76

重新编译代码,去掉优化选项 (-O0 在 gcc 中)。


18
即使使用 -O0 编译选项,仍可能会产生被优化的代码(我现在正在努力应对这个问题),尽管我不确定原因。 - Chris Gregg
@ChrisGregg 我也遇到了同样的问题!你找出问题了吗? - Paolo M
1
@paolom看起来可能是一个clang问题,所以出于调试目的,我不得不改为使用g++进行编译。 - Chris Gregg
通常这并不是一个解决方案 - 特别是当你从生产环境中得到了核心转储文件,或者你无法在开发环境中重现问题时。 - smbear

40

found 声明为 "volatile"。这应该告诉编译器不要对它进行优化处理。

volatile int found = 0;

1
即使我在 gdb 调试器中将某些变量声明为“volatile”,它仍然显示为优化变量!这里还有什么需要注意的吗? - M.Rez

12
编译器在开启优化后将会进行非常聪明的优化处理。由于变量被存储在寄存器中的优化方式,调试器将展示代码前后跳跃很多次。这可能就是你无法设置变量(或在某些情况下看到其值)的原因,因为它已经被巧妙地分配到了寄存器中以提高速度,而不是有一个调试器可以访问的直接内存位置。
没有优化编译吗?

6

通常,像这样在计算后立即用于分支的布尔值实际上不会存储在变量中。相反,编译器直接从前面比较设置的条件码进行分支。例如,

int a = SomeFunction();
bool result = --a >= 0; // use subtraction as example computation
if ( result ) 
{
   foo(); 
}
else
{
   bar();
}
return;

通常编译成类似以下内容:
call .SomeFunction  ; calls to SomeFunction(), which stores its return value in eax
sub eax, 1 ; subtract 1 from eax and store in eax, set S (sign) flag if result is negative
jl ELSEBLOCK ; GOTO label "ELSEBLOCK" if S flag is set
call .foo ; this is the "if" black, call foo()
j FINISH ; GOTO FINISH; skip over the "else" block
ELSEBLOCK: ; label this location to the assembler
call .bar
FINISH: ; both paths end up here
ret ; return

请注意,“bool”实际上从未存储在任何地方。

6

你基本上无法设置found的值。调试优化过的程序很少有意义,编译器可以以与源代码完全不同的方式重新排列代码(除了产生相同的结果),从而使调试器变得困惑。


5

当调试优化程序时(如果错误在调试构建中不显示,则可能需要这样做),您经常必须理解汇编编译器生成的内容。

在您的特定情况下,cpnd_find_exact_ckptinfo 的返回值将存储在用于返回值的寄存器中,该寄存器在您的平台上使用。在 ix86 上,它将是 %eax。在 x86_64 上: %rax 等。如果不是以上任何一种,请搜索“[您的处理器]过程调用约定”。

您可以在 GDB 中检查并设置该寄存器。例如,在 ix86 上:

(gdb) p $eax
(gdb) set $eax = 0 

0

我正在使用带有gdb的QtCreator。

添加

QMAKE_CXXFLAGS += -O0
QMAKE_CXXFLAGS -= -O1
QMAKE_CXXFLAGS -= -O2
QMAKE_CXXFLAGS -= -O3

对我来说很有效


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