1

有时会发生,Visual Studio 调试器显示无效的当前指令。最简单的可能原因是:

  • 过时的 .pdb 文件。这些文件包含有关二进制文件的哪个偏移量对应于哪一行代码的信息。如果这些文件已过时,显然调试器将显示无效行。
  • 以发布模式而不是调试模式编译的程序。发布模式通常会开启优化,以便优化器可以将代码修改得更快(或更小)。在这种情况下,二进制文件不再对应源代码,因此调试器无法显示实际执行的行。

但我的情况不同。这是一段源代码(C#):

if (match.Groups.Count != 4)
    throw new InvalidOperationException("Internal error: Invalid regular expression!");

MyEnum myEnum;
try
{
    // (...)

我调试了源代码。调试器在条件语句处停止并显示,match.Groups.Count实际上等于 4。显然,应该跳过 throw 语句,但令人惊讶的是,没有。但是,在“执行”throw语句之后,调试器转到下一个有效语句并继续按预期执行。

发生了什么?

4

1 回答 1

1

在这种情况下,通常值得查看拆卸。就我而言,它看起来像:

    if (match.Groups.Count != 4)

00000344  mov         rax,qword ptr [rbp+30h] 
00000348  mov         rax,qword ptr [rax] 
(...)
00000399  test        eax,eax 
0000039b  jne         00000000000003ED // (1)

    throw new InvalidOperationException("Internal error: Invalid regular expression!");

0000039d  lea         rdx,[00049AC0h] 
000003a4  mov         ecx,7000024Eh 
000003a9  call        000000005F7CC994 
(...)
000003dc  call        000000005ED32730 
000003e1  mov         rcx,qword ptr [rbp+000000F8h] 
000003e8  call        000000005F7CC64C 
000003ed  nop // (2)

    MyEnum myEnum;
    try
    {

000003ee  nop // (3)

jne我在指令 (1)处设置断点。对于那些不太了解汇编程序的人(不是我实际上知道的),条件语句通常被编译成一对:test和某种条件跳转,例如jnejump if not e qual )。所以我设置断点的地方实际上是一个最终决定,是否执行 throw 语句。

经过一步 (F10) 后,调试器跳转到 (2) 位置,因此它正确地跳过了 throw 语句。对于那些不知道这一点的人来说,nopN o操作)是一个汇编指令,实际上什么都不做。编译器通常使用它来对齐汇编代码,使其性能更好(我猜是低级处理器的东西)。

但是编译器搞砸了并将信息保存在 .pdb 文件中,提到nop的是throw语句的一部分。调试器读取它并将当前指令标记定位在throw语句上。但是,它只是执行它(什么也没做)并继续执行,因为实际的汇编代码是正确的。

我留下这个例子,以防有人发现自己遇到了类似的问题——希望它有助于找到原因。很少遇到,但谁知道呢?...

于 2013-05-23T10:20:16.927 回答