Spook
Spook

Reputation: 25927

Debugger shows invalid instruction

Sometimes it happens, that Visual Studio debugger shows invalid current instruction. The simplest possible reasons are:

But my case was different. Here's a piece of source code (C#):

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

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

I debugged the source. Debugger stopped at the conditional statement and shown, that match.Groups.Count actually is equal to 4. Obviously, the throw statement should have been skipped, but surprisingly, was not. However, after "executing" the throw statement, debugger went to the next valid statement and continued executing as it was supposed to.

What happened?

Upvotes: 1

Views: 182

Answers (1)

Spook
Spook

Reputation: 25927

In such cases it is usually worth to look at the disassembly. In my case it looked like:

    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)

I set the breakpoint at the jne instruction (1). For those, who don't know the assembler much (not, that I actually do), conditional statements are often compiled to a pair: test and some kind of conditional jump, such as jne (jump if not equal). So the place, where I set the breakpoint was actually a final decision, whether to execute the throw statement or not.

After a step-over (F10), debugger jumped to the (2) location, so it correctly skipped the throw statement. Again for those, who doesn't know that, the nop (No operation) is an assembler instruction, which actually does nothing. Compiler usually uses it to align the assembly code, such that it performs better (low level processor stuff, I guess).

But the compiler screwed up and saved the information in .pdb file, that mentioned nop is a part of the throw statement. Debugger read that and positioned the current instruction marker on the throw statement. But then, it just executed it (did nothing) and kept on executing, because the actual assembly code was correct.

I leave this example in case, that someone finds himself stuck with similar problem - hopefully it'll help to find the cause. It's rather rare to encounter, but who knows?...

Upvotes: 1

Related Questions