Skipping over the last statement Of a function of an optimized Assembly in the visual studio debugger

Posted in: , , by . No comments

Let’s imagine that we are debugging a third party module (possibly using this cool dotPeek feature), and want to skip the last line of the executing function. While it’s well known that it’s possible to skip over lines of code in the debugger, and it’s easily done in an unoptimized assembly by simply setting the closing curly bracket “}” as the next statement. That method doesn't work with an optimized build because because the compiler doesn’t emit IL NOP instruction at the end of the function whose debugging information can be pointing to the closing curly bracket (that’s how it works with an unoptimized build).

JIT optimization

Before we can set the next instruction to any instruction, we need to prevent JIT optimization
When an assembly is compiled with optimizations it also usually means that it’s going to be JIT optimized as well, which would make the debugging experience really bad with messages like “Cannot evaluate expression because the code of the current method is optimized” and also prevent changing the next instruction.

Preventing JIT optimization

The information on reliably preventing JIT optimization exists on the internet but it’s scattered across a few sources so It took me a while to find it. I’ll sum it up here:

  1. In case where a native image of the assembly is installed on the machine (as most framework assemblies like mscorlib.dll and System.dll are) the optimized native image would be loaded by default and we can prevent that by setting the environment variable COMPLUS_ZapDisable to 1.
  2. For each assembly that we want to prevent JIT optimization we should create an .ini file with the same name as the dll containing:
    [.NET Framework Debugging Control]
    GenerateTrackingInfo=1
    AllowOptimize=0

    note that the .ini file should be placed at the same directory as the assembly that it refers to loaded from (including the GAC and Temporary ASP.Net Files if needed).

The final result can be verified by seeing the assembly not optimized in the modules window during debug:
image

Skipping the last statement of a function

Let’s assume that our function is the following (now it would be obvious why I wanted to skip the last line).

private static void FailFast(string message, string detailMessage)
{
if (Invariant.IsDialogOverrideEnabled)
Debugger.Break();
Environment.FailFast(MS.Internal.WindowsBase.SR.Get("InvariantFailure"));
}

While preventing JIT Optimization allows us setting the next instruction, we still can’t skip the last source code line (because there is still no IL NOP that can be pointing to the closing curly bracket as it’s a C# compiler optimization)

The trick is using the disassembly window to control the execution:
image 
The image contains the disassembly window when execution is broken at the first line of our function.

What we can do in this case is as with source code, set the next statement to be the beginning of the function epilogue (mov esp, ebp) thus skipping the instruction that causes the application to terminate (Environment.FailFast).

Obviously as with source code, we should be careful of where we directing the execution to prevent corruption (for example not skipping the function epilogue and corrupting the stack, supplying return value when needed). Also preventing the application from crashing when it wants to, like I did, is probably not a good idea but I wanted to check something quickly and it did the job.

Posted in: , , by . No comments

The permalink

Leave a Reply