为什么GDB在行之间无法预测地跳转并将变量打印为"<value optimized out>"?

Arp*_*pit 83 c compiler-construction optimization gdb compilation

任何人都可以解释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.
Run Code Online (Sandbox Code Playgroud)

为什么在执行第903行之后它再次为905 908 910执行相同的操作?

另一件事是found一个bool-type变量,为什么它显示value optimized out?我也无法设置值found.

这似乎是编译器优化(在这种情况下是它的-O2); 我怎么还能设定值found

Zan*_*ynx 114

要调试优化代码,请学习汇编/机器语言.

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

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

您可以使用GDB命令显示寄存器的值 p $eax

  • @TomBrito:Optimized out表示变量不在内存中.它可能只在CPU寄存器中,这意味着您需要读取反汇编并打印寄存器值才能找到它. (15认同)

D'N*_*bre 74

无需优化即可重新编译(在gcc上为-O0).

  • 甚至-O0也可以产生优化的代码(现在试图解决这个问题),虽然我不知道为什么. (15认同)

Ben*_*enB 38

声明发现为"易变".这应该告诉编译器不要优化它.

volatile int found = 0;
Run Code Online (Sandbox Code Playgroud)


kjf*_*tch 11

编译器将在启用优化的情况下开始执行非常聪明的操作.由于优化的方式变量存储在寄存器中,调试器将显示向前和向后跳跃的代码.这可能是您无法设置变量(或在某些情况下看到它的值)的原因,因为它已经在寄存器之间巧妙地分配以提高速度,而不是具有调试器可以访问的直接存储器位置.

编译没有优化?


Cra*_*rks 6

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

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

通常编译为:

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
Run Code Online (Sandbox Code Playgroud)

注意"bool"从未实际存储在任何地方.


nos*_*nos 5

您几乎无法设置 found 的值。调试优化的程序很少值得麻烦,编译器可以以与源代码完全不对应的方式重新排列代码(除了产生相同的结果),从而使调试器迷惑不解。


Emp*_*ian 5

在调试优化程序时(如果错误没有出现在调试版本中,这可能是必要的),您通常必须了解生成的汇编编译器。

在您的特定情况下,返回值cpnd_find_exact_ckptinfo将存储在您的平台上用于返回值的寄存器中。在ix86,那将是%eax。在x86_64:%rax等。如果不是上述任何一项,您可能需要在 google 上搜索“[您的处理器] 过程调用约定”。

您可以检查该寄存器GDB并进行设置。例如ix86

(gdb) p $eax
(gdb) set $eax = 0 
Run Code Online (Sandbox Code Playgroud)