13

この可能性のあるバグの簡単なクリーンルームの例があります。

   static void Main(string[] args)
    {
        bool MyFalse = false;

        if (MyFalse)
        {
            throw new Exception();
        }
        try
        {
            int i = 0;
        }
        catch (Exception e)
        {
            Console.Write(e);
        }

        Console.Read();
    }

x64 または AnyCPU でコンパイルされた場合 (VS2012 で 32 ビット優先が false に設定されている場合)、if ブロックにブレークポイントを設定すると、常にヒットします。

VS2012、VS2010、VS2008 で試してみたところ、64 ビットでコンパイルするとすべて if ブロックが起動しましたが、32 ビットでは if ブロックが起動しません。

32 ビット バージョンと 64 ビット バージョンの IL を調べたところ、同じように見えました。

if ブロックが実行されていて、boolean 変数の値に関係なく例外がスローされていたため、これは製品コードで見つかりましたが、単純な例では例外をスローできないように見えますが、製品コードで発生しています。

これは製品コードで発生しているため、デバッガーだけの問題ではありません。

非常に奇妙な動作ですが、実際には if ブロックでコードを実行していないようです。開発者は、それが彼が見た例外であると仮定して銃を飛ばしました。

(すべてのデバッグはデバッグ モードです - プロダクションはリリース中です)

スローがコメントアウトされている場合 - if ブロックに到達していません。

4

2 に答える 2

20

わかりました。これは実際、64 ビット デバッガーのデバッグ ビルドではうまくいきません。重要なのは、if() ステートメントにブレークポイントを正確に設定してから、ステップ実行を開始することです。throw ステートメントが実行されているように見えます。しかし、それが実際には起こらなくても、実際のコードの実行は適切です。

何が起こっているかを確認するには、throw ステートメントの行にステップインさせます。次に、Debug + Disassembly を使用して、実際の場所を確認します。私のマシンでは、次のようになります。

       if (MyFalse)
00000040  movzx       ecx,byte ptr [rbp+8] 
00000044  xor         eax,eax 
00000046  test        ecx,ecx 
00000048  sete        al 
0000004b  mov         dword ptr [rbp+1Ch],eax 
0000004e  movzx       eax,byte ptr [rbp+1Ch] 
00000052  mov         byte ptr [rbp+18h],al 
00000055  movzx       eax,byte ptr [rbp+18h] 
00000059  test        eax,eax 
0000005b  jne         0000000000000088            // <=== Note this jump
        {
0000005d  nop 
            throw new Exception();
0000005e  lea         rcx,[5B848928h] 
00000065  call        000000005F65E9E0 
0000006a  mov         qword ptr [rbp+20h],rax 
0000006e  mov         rax,qword ptr [rbp+20h] 
00000072  mov         qword ptr [rbp+28h],rax 
00000076  mov         rcx,qword ptr [rbp+28h] 
0000007a  call        000000005BE4A5D0 
0000007f  mov         rcx,qword ptr [rbp+28h] 
00000083  call        000000005F73E36C 
00000088  nop                                     // <=== yellow arrow here
        }
        try
        {
00000089  nop 
            int i = 0;

これは、デバッガーがマシン コード命令を C# ステートメントとグループ化する方法からもわかります。アドレス 0088 の NOP について、デバッガーがどのように混乱しているかに注意してください。デバッガーは、複合 if() ステートメントに属していると考えています。そのため、ブロック内に黄色のハイライトが配置されます。しかし、プログラムは実際にはアドレス 005b でジャンプし、throw ステートメント (アドレス 005e から 0083) をスキップしています。

これは 32 ビット モードで正しく動作するため、C# コンパイラや PDB ファイルを責めることはできません。ジッターの問題のような匂いがします。注目すべきは、x86 ジッターが NOP 命令を生成しないことです。また、ジッターによってアドレス 0089 にジャンプする JNE 命令が生成されたはずであると主張することもできます。

返信があるまで、またはサービス パックで更新プログラムが提供されるまで、この癖を覚えておいてください。コードは実際には正しく実行されているため、当惑することはほとんどありません。

于 2013-05-03T20:11:37.840 に答える
10

最適化されたコードでは、MSIL とネイティブ マシン コードおよびソース コードの間に厳密な相関関係はありません。これにより、デバッガーは実行中のコードとは異なるコードを強調表示したり、単一のステップ実行中に同じ行を複数回強調表示したり、意図した場所とは異なる場所にブレークポイントを配置したりします。

これは、最適化されたコードのデバッグに関する基本的な問題であり、デバッグ情報の形式が不適切であることを表しています。コンパイラにもデバッガにもバグはありません。逆アセンブル ビューでのデバッグに頼る必要がある場合があります。

于 2013-05-03T19:07:35.507 に答える