C# 调试器显示无效指令

C# 调试器显示无效指令,c#,visual-studio,debugging,compiler-construction,C#,Visual Studio,Debugging,Compiler Construction,有时,VisualStudio调试器会显示无效的当前指令。最简单的可能原因是: 过期的.pdb文件。这些文件包含有关二进制文件的哪个偏移量对应于哪个代码行的信息。若这些文件过时,调试器显然会显示无效行 在发布模式下编译的程序,而不是调试。发布模式通常启用了优化,这样优化器可以修改代码以更快(或更小)。在这种情况下,二进制文件不再与源代码对应,因此调试器无法显示实际执行的行 但我的情况不同。下面是一段源代码(C#): 我调试了源代码。调试器在条件语句处停止并显示,match.Groups.Co

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

  • 过期的.pdb文件。这些文件包含有关二进制文件的哪个偏移量对应于哪个代码行的信息。若这些文件过时,调试器显然会显示无效行
  • 在发布模式下编译的程序,而不是调试。发布模式通常启用了优化,这样优化器可以修改代码以更快(或更小)。在这种情况下,二进制文件不再与源代码对应,因此调试器无法显示实际执行的行
但我的情况不同。下面是一段源代码(C#):

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


发生了什么?

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

    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
和某种条件跳转,例如
jne
(如果不相等,则跳转)。因此,设置断点的位置实际上是最终决定,是否执行throw语句

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

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


我留下这个例子以防万一,有人发现自己陷入了类似的问题——希望这能帮助找到原因。这种情况很少见,但谁知道呢?…

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

    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
和某种条件跳转,例如
jne
(如果不相等,则跳转)。因此,设置断点的位置实际上是最终决定,是否执行throw语句

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

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


我留下这个例子以防万一,有人发现自己陷入了类似的问题——希望这能帮助找到原因。这种情况很少见,但谁知道呢?…

FYI:当您要回答自己的问题时,屏幕底部附近有一个“回答自己的问题”复选框。我想这会阻止人们插嘴回答。是的,我忘了:)谢谢。仅供参考:当你要回答你自己的问题时,屏幕底部附近有一个“回答你自己的问题”复选框。我想这会阻止人们插嘴回答。是的,我忘了:)谢谢。