为什么GDB会在行与行之间意外地跳转并将变量打印为“ <值优化输出>”?


84

谁能解释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


8
调试时,最好使用-O0进行编译,因为优化会导致此类问题。
LiraNuna

Answers:


115

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

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

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

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


我仍然遇到“最优化”问题,并且变量值未在其他窗口中显示,但是,这仍然是一个很好的信息,谢谢!
汤姆·布里托

16
@TomBrito:优化意味着变量不在内存中。它可能仅在CPU寄存器中,这意味着您需要阅读反汇编并打印寄存器值才能找到它。
Zan Lynx

@Zan Lynx:我不确定我是否同意你的分析。DWARF符号具有足够的信息以从寄存器中提取值。也许这意味着编译器已确定在执行到当前行时可以安全地丢弃该变量。在这种情况下,变量所在的存储可能已被其他用途重用。我认为您是正确的,但是通常只有在变量被注册后才会发生。
伊恩·尼·刘易斯

@ IanNi-Lewis:我不知道您正在使用哪个版本的DWARF,但以我的经验,GDB无法打印已存储到寄存器中的变量。
Zan Lynx 2014年

我确定你是对的。我对DWARF的经验来自于编写自己的解析器,而不是来自于使用gdb,因此我真的不知道gdb的功能。
伊恩·尼·刘易斯

75

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


17
甚至-O0都可以生成优化的输出代码(现在尝试与此进行斗争),尽管我不确定为什么。
克里斯·格雷格

@ChrisGregg我遇到了同样的问题!您找出问题所在了吗?
Paolo M

1
@paolom看起来可能是个clang问题,因此不幸的是,我一直在使用g ++进行编译,以进行调试。
克里斯·格雷格

通常,这不是解决方案-尤其是当您从生产中获得核心转储和/或无法在开发环境中重现该问题时。

39

声明为“挥发性”。这应该告诉编译器不要对其进行优化。

volatile int found = 0;

1
即使当我在gdb调试器中将某些变量声明为“ volatile”时,它也将其显示为优化输出变量!还有这个吗?
M.Rez '16

11

启用优化后,编译器将开始做非常聪明的事情。由于变量存储在寄存器中的优化方式,调试器将显示代码大量前进和后退。这可能是您无法设置变量(或在某些情况下看到其值)的原因,因为它已经出于速度的考虑而巧妙地分配在寄存器之间,而不是拥有调试器可以访问的直接内存位置。

没有优化就编译?


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

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


4

您几乎无法设置found的值。调试优化的程序很少会遇到麻烦,编译器可以以根本不与源代码相对应的方式重新排列代码(除了产生相同的结果外),从而使调试器无休止地陷入混乱。


4

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

在您的特定情况下,的返回值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

对我来说很好

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.