なぜGDBは行間を予期せずジャンプし、変数を「<valueoptimizedout>」として出力するのですか?


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 908910に対して同じことを再度実行するのはなぜですか。

もう1つfoundは、bool-type変数ですが、なぜ表示されるのvalue optimized outですか?の値も設定できませんfound

これはコンパイラの最適化のようです(この場合は-O2)。どうすればまだ値を設定できfoundますか?


8
デバッグするときは、最適化するとそのような問題が発生するため、通常は-O0でコンパイルすることをお勧めします。
liraNuna

回答:


115

最適化されたコードをデバッグするには、アセンブリ/機械語を学びます。

GDBTUIモードを使用します。マイナスを入力してEnterキーを押すと、GDBのコピーで有効になります。次に、Cx 2と入力します(つまり、Controlを押しながらXを押し、両方を放してから2を押します)。これにより、分割ソースと逆アセンブリ表示になります。次に、stepinextiを使用して、一度に1つの機械命令を移動します。Cx oを使用して、TUIウィンドウを切り替えます。

CPUの機械語と関数呼び出し規約に関するPDFをダウンロードしてください。関数の引数と戻り値で何が行われているのかをすぐに認識できるようになります。

次のようなGDBコマンドを使用して、レジスタの値を表示できます。 p $eax


私はまだ「最適化」の問題を抱えており、変数値は他のウィンドウに表示されませんが、それでも、それは素晴らしい情報です、ありがとう!
トム・ブリト

16
@TomBrito:最適化されているということは、変数がメモリにないことを意味します。おそらくCPUレジスタのみにあります。つまり、逆アセンブリを読み取り、レジスタ値を出力して見つける必要があります。
Zan Lynx 2013

@Zan Lynx:あなたの分析に同意するかどうかはわかりません。DWARFシンボルには、レジスタから値を抽出するのに十分な情報があります。おそらくここで意味するのは、実行が現在の行に到達するまでに変数を安全に破棄できるとコンパイラーが判断したことです。その場合、変数が存在するストレージはおそらく他の目的で再利用されています。ただし、これは通常、変数が登録されている場合にのみ発生するというのは正しいと思います。
Ian Ni-Lewis

@ IanNi-Lewis:使用しているDWARFのバージョンはわかりませんが、私の経験では、GDBはレジスターに格納されている変数を出力できません。
Zan Lynx 2014

あなたが正しいと確信しています。DWARFでの私の経験は、gdbを使用することからではなく、独自のパーサーを作成することから来ているので、gdbが何ができるのか本当にわかりません。
Ian Ni-Lewis

75

最適化せずに再コンパイルします(gccでは-O0)。


17
-O0でさえ、最適化されたコードを生成できます(今、これに苦労しようとしています)が、理由はわかりません。
クリス・グレッグ

@ChrisGregg同じ問題が発生しました!何が問題なのかわかりましたか?
パオロM

1
@paolomはclangの問題のようですので、残念ながらデバッグ目的で代わりにg ++でコンパイルしています。
クリス・グレッグ

多くの場合、これは解決策ではありません。特に、本番環境からのコアダンプがある場合や、開発環境で問題を再現できない場合はそうです。
smbear 2017年

39

「揮発性」として検出されたと宣言します。これは、コンパイラに最適化しないように指示する必要があります。

volatile int found = 0;

1
gdbデバッガーでいくつかの変数を「揮発性」と宣言しても、最適化された変数として表示されます。これにはもうありますか?
M.Rez 2016

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

「bool」が実際にはどこにも保存されないことに注意してください。


4

foundの値を設定することはほとんどできません。最適化されたプログラムのデバッグに問題が発生することはめったにありません。コンパイラーは、ソースコードに対応しないようにコードを再配置できるため(同じ結果を生成する以外)、デバッガーを混乱させます。


4

最適化されたプログラムをデバッグする場合(バグがデバッグビルドに表示されない場合に必要になる場合があります)、生成されたアセンブリコンパイラを理解する必要があることがよくあります。

特定のケースでは、の戻り値はcpnd_find_exact_ckptinfo、プラットフォームで戻り値として使用されるレジスタに格納されます。でix86、それはになります%eax。オンx86_64%rax、などあなたは「[あなたプロセッサ]手続き呼び出し規約」のためのgoogleする必要があるかもしれない場合は、上記のそれのどれも。

そのレジスタを調べてGDB設定することができます。例ix86

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

0

gdbでQtCreatorを使用しています。

追加

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

私にとってはうまくいく

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.