为什么 GDB 在行之间不可预测地跳转并将变量打印为“<value optimized out>”?

问题描述 投票:0回答:8

谁能解释一下 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
是一个
bool
类型变量,那么为什么它显示
value optimized out
呢? 我也无法设置
found
的值。

这似乎是编译器优化(在本例中是

-O2
);我怎样才能设置
found
的值?

c optimization compilation gdb
8个回答
115
投票

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

使用GDB TUI模式。我的 GDB 副本在我键入减号和 Enter 时启用它。然后键入 C-x 2(即按住 Control 并按 X,松开两者,然后按 2)。这将把它放入拆分源和反汇编显示中。然后使用

stepi
nexti
一次移动一条机器指令。使用 C-x o 在 TUI 窗口之间切换。

下载有关 CPU 机器语言和函数调用约定的 PDF。您将很快学会识别函数参数和返回值正在做什么。

您可以使用 GDB 命令来显示寄存器的值,例如

p $eax


76
投票

重新编译而不优化(gcc 上的 -O0)。


40
投票

声明 found 为“易失性”。 这应该告诉编译器不要优化它。

volatile int found = 0;

12
投票

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

未经优化就编译?


6
投票

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


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”实际上从未存储在任何地方。


5
投票

在您的特定情况下,

cpnd_find_exact_ckptinfo

的返回值将存储在您平台上用于返回值的寄存器中。在

ix86
上,那就是
%eax
。关于
x86_64
%rax
等。如果不是上述情况,您可能需要搜索“[您的处理器]过程调用约定”。

您可以在

GDB

中检查该寄存器并进行设置。例如。于

ix86

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



0
投票

添加

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

对我来说效果很好

© www.soinside.com 2019 - 2024. All rights reserved.