OK, for some silly reason, I needed to look at the code generated in release mode vs. debug mode. Why? Because I'm writing an article about the differences using the
Trace classes, and I wanted to see what really is going on behind the scenes when I compile code in release mode. Also, I figure that just because C# is "managed", that doesn't mean that I won't get weird behavior when I switch over to a release version, and I may in the future want to set a breakpoint.
As was recently pointed out to me, you can't set a breakpoint in release mode because there's no symbolic information. This is silly, because I should still be able to set a breakpoint based on the instruction address. However, living with the inevitable, the following code emits a "
Break" opcode in a module created on the fly and invokes the module's function. Whether in release or debug mode, this results in the application breaking when run under the debugger.
This was also a fun exercise in dynamic code generation and shows some of the nightmarish steps necessary to generate code dynamically. Still, I think it's a really awesome feature of the language!
The Easy Way
As Fredrik Tonn pointed out in a message, adding a breakpoint in release mode can be easily accomplished by inserting
System.Diagnostics.Debugger.Break() in your code where you wish the breakpoint to occur.
What else is there to say? So much for the easy way.
The Hard Way
This requires generating some IL on the fly, which invokes the IL opcode
Break. The following code documents how to do this.
public interface IRMBP
public class ReleaseMode
static public void Break()
Assembly asm=new ReleaseMode().GenerateBreak();
private Assembly GenerateBreak()
AssemblyName asmbName=new AssemblyName();
Type paramTypes=new Type;
MethodAttributes.Public | MethodAttributes.Virtual,
ILGenerator ilg=mthdBuilder.GetILGenerator(); ilg.Emit(OpCodes.Break); ilg.Emit(OpCodes.Ret);
Usage is quite simple. Put the statement
ReleaseMode.Break(); where you want the debugger to break. Once the debugger breaks, you can single step through the two
returns and then inspect your program's assembly code.