Dot Net Thoughts

November 29, 2007

Debug and Release Builds

Filed under: csharp,Debugging,Uncategorized — dotnetthoughts @ 9:41 pm
Tags: , ,

Occassionally, I find old code concepts rattling around in my head that just don’t apply in today’s world. My most recent one was the difference between release and debug builds.

Back in VB6 days, one of the big reasons for creating a debug build was the generation of the pdb file. The pdb (portable database) file contained information about the names of items such as variables, classes and methods. Furthermore, it contained information about where these values were located in the code. Without the pdb file, all of this information was unavailable.

Imagine my surprise when I ran some code without a pdb file the other day, and I received a stack trace containing method names. With a little further thought, though, I realized it wasn’t that surprising. When disassembling a dll with ILDASM, I can see all of the method and variable names with or without a pdb file present. It turns out that the inclusion of a pdb file will allow you to trace a bug to a module and line number, but it is no longer needed for artifact names.

Release builds now contain pdb files by default. So what is the difference between the two build types? To help figure this out, I generated a very simple HelloWorld class and compiled it as both a release and a debug build. This class contained two methods.


        static void Main(string[] args) 

        static string SayHello(string name) 
            return String.Format("Hello {0}!", name); 

Opening the Main method of the release dll in ILDasm yields a pretty straight forward implementation of the code. In the decompilation, you can see the creation of the string, the loading of the parameter, the string formatting, and the return of the value.


  .method private hidebysig static string 
          SayHello(string name) cil managed 
    // Code size       12 (0xc) 
    .maxstack  8 
    IL_0000:  ldstr      "Hello {0}!" 
    IL_0005:  ldarg.0 
    IL_0006:  call       string [mscorlib]System.String::Format(string, 
    IL_000b:  ret 
  } // end of method Program::SayHello       

The debug code looks almost exactly the same, but it includes a few differences to help assist in debugging. A local variable is initialized to hold the value of parameter so that it is available to a debugger. Also added are several nop (no operation) and a br_s (branch) method. These set points within the application on which a breakpoint can be set.


  .method private hidebysig static string 
          SayHello(string name) cil managed 
    // Code size       17 (0x11) 
    .maxstack  2 
    .locals init ([0] string CS$1$0000) 
    IL_0000:  nop 
    IL_0001:  ldstr      "Hello {0}!" 
    IL_0006:  ldarg.0 
    IL_0007:  call       string [mscorlib]System.String::Format(string, 
    IL_000c:  stloc.0 
    IL_000d:  br.s       IL_000f    IL_000f:  ldloc.0 
    IL_0010:  ret 
  } // end of method Program::SayHello       

I expected that tweaking the code to call a few subroutines would at least lead to some inlining optimizations within the IL. The differences between the Debug and Release versions of the IL were similar to those above. So where were the optimizations?

The only other major difference between the IL versions are the values stored in the DebuggingModes attribute. This attribute links back to the debug compilation flags that can be set when compiling a project. Since these are the only differences in the IL, the optimizations must occur within the JIT compiler itself. Scott Hanselman has an excellent post on compiled release and debug that is well worth reading.

That would be it for today! Good luck and code safe!



  1. Thanks for this nice article.

    Two thinks which were really helpful:
    1. You mentioned the IL code using ILDasm
    2. The link given to scott’s article.

    Atreya A

    Comment by Atreya A — September 26, 2008 @ 1:42 pm | Reply

  2. Hello!

    Do you know if the “Release mode” treats numbers differently from the “Debug mode”?

    I have to do a conversion from char* to double and it does it correctly in the “Debug mode” but incorrectly in the “Release mode”.

    – Debug: 9.0377972222222
    – Release: 9.0397277777778

    Santi24 needs help! Please!

    Comment by Santi24 — November 27, 2008 @ 4:27 am | Reply

  3. Its worth reading this post.. well explained. Thanks.

    Comment by Nimmi — May 4, 2010 @ 9:34 pm | Reply

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at

%d bloggers like this: