Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Release Mode Breakpoint Using Reflection Emit

0.00/5 (No votes)
9 Oct 2002 1  
C#'s release mode doesn't allow break points to be set. This function emits an IL break, forcing a breakpoint in the debugger.

Introduction

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 Debug and 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.

/*
    *    Credits:
    *        Visual Studio Magazine
    *        Octobor 2002 Issue
    *        "Generate Assemblies With Reflection Emit" by Randy Holloway
    *        http://www.fawcette.com/vsm/2002_10/magazine/columns/blackbelt/ 
*/
// the interface serves as a stub to invoke the generated function

public interface IRMBP
{
    void Break();
}

public class ReleaseMode
{
    static public void Break()
    {
        Assembly asm=new ReleaseMode().GenerateBreak();
        IRMBP bp=null;
        bp=(IRMBP)asm.CreateInstance("RMBP");
        bp.Break();
    }

    private Assembly GenerateBreak()
    {
        // create an instance of the assembly name...

        AssemblyName asmbName=new AssemblyName();
        // ... and name it.

        asmbName.Name="ReleaseModeBreakPoint";

        // get an instance of the assembly builder,

        // indicating that the assembly can be saved and executed

        // If we don't specify "RunAndSave", we get the error

        // "cannot have a persistent module in a transient assembly"

        AssemblyBuilder asmbBuilder=
          AppDomain.CurrentDomain.DefineDynamicAssembly(asmbName, 
          AssemblyBuilderAccess.RunAndSave);

        // create a module builder, giving it the module name,

        // the filename, and whether or not symbolic information is written

        ModuleBuilder mdlBuilder=
          asmbBuilder.DefineDynamicModule("ReleaseModeBreakPoint", 
          "rmbp.dll", false);

        // create a type.  As in "public class RMBP"

        TypeBuilder typBuilder=mdlBuilder.DefineType("RMBP", 
                                      TypeAttributes.Public);

        // add an interface.  As in "public class RMBP : IRMBP"

        typBuilder.AddInterfaceImplementation(typeof(IRMBP));

        // create the "Break" method.  "public virtual void Break();"

        Type[] paramTypes=new Type[0];
        Type returnType=typeof(void);
        MethodBuilder mthdBuilder=typBuilder.DefineMethod("Break", 
           MethodAttributes.Public | MethodAttributes.Virtual, 
           returnType, paramTypes);

        // generate the code:

        ILGenerator ilg=mthdBuilder.GetILGenerator(); // get the generator

        ilg.Emit(OpCodes.Break); // issue a break statement

        ilg.Emit(OpCodes.Ret);    // return afterwards


        // get info about our Break method in the interface class IRMBP

        MethodInfo mthdInfo=typeof(IRMBP).GetMethod("Break");

        // associate our generated method with the interface method

        typBuilder.DefineMethodOverride(mthdBuilder, mthdInfo);

        // create the type

        typBuilder.CreateType();

        return asmbBuilder;
    }
}

Usage

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here