Debug vs Release

When I first started out this article I thought I’d just present you with a simple solution to the problem, just a “how to” article so to speak. Several posts have already discussed this matter and if you’ve ever come across it, you know that you should check the DebuggableAttribute of the Manifest belonging to the assembly belonging to the type. Perhaps not trivial, but not too hard either. But as with many such approaches you dig up from blogs around the Net is that they don’t always work. If you want to know why or if you want to know what approach is best, then this article is for you.

What is Debug or Release build

When you build your application, you can set the target build configuration in Visual Studio to Debug or Release or any other configuration that has been specified. Usually, while your debugging and testing your application, you want it to run and compile in Debug mode. But Debug mode adds all kinds of tests and safety checks to your code, as well ass debugging symbols and and a PDB database, which contains a complex lookup table of all your classes, functions, variables etc. When your code breaks, the debugger can use this information to find the correct location in the source code that corresponds to the binary executing code. There’s much more to say about Debug builds and debugging in general, but I assume in this article that you are well aware of it. And if not, there are plenty of books on the subject.

Advanced compilation settings in Visual Studio 2008

Technically, a Debug build means that your code is augmented in various ways. You can use several settings in Visual Studio to tell the compiler what you want to be augmented. Under Advanced Build Settings you can select Debug Info “full” or “pdb-only” (see screenshot) and contrary to what you might think, “full” is generally faster then pdb-only when running the code because of the overhead of having to lookup unwinding stacks in the pdb, but that’s another story. Adding checks for artithmetic overflow/underflow, adding code for allowing edit & continue, all these result in extra code and a higher granularity for debugging. Some of these settings will add an attribute to your Assembly’s Manifest, some of these will not.
As it turns out, it is perfectly legal to change your settings for Release build such that you include debugging symbols and unwinding information. It is also perfectly legal to add a new configuration which changes some other settings resulting in a debug build but not having the DEBUG constant set. I will not consider these practices here, as I believe they are a result of poor settings management. Just as you will not call a method “EraseRecords” when it actually adds records, you shouldn’t change the settings in such a way that a Release build contains debugging symbols or a Debug build is optimized for speed and size.
Later in this article we’ll define what a more accurate definition of a Debug build should be and we’ll use that for determining how a certain assembly was build.

Two challenges

Finding out how a certain assembly was originally build is very different whether you have to check that from and for your current assembly or type or whether you have to check that for an already compiled assembly. Obviously, if you have control over your assemblies and it is necessary to expose the method of how it was compiled, you should expose that using a standard method in any of your classes. Yet, this can be tedious if you have many assemblies and you don’t want to expose this method for each and every type. Knowing how a build is done is something that’s seldomly necessary — shouldn’t be necessary at all, actually — in release builds, because you should only link release assemblies together and not mix them with debug builds.

So much for theory, but there’s also this other school of thought that says that you should try to keep your methods together, sticking to the DRY principle and all. In tracing or logging contexts, you may need to be able to jack in a debug build of one of the assemblies and not all of them together, or you simply need to know whether a build you’re logging about is a debug build or not. I’ve also seen situations where it wasn’t clear whether a downloaded third party component was build using optimizations off or on. In such cases, a simple utility method would help a lot and saves you a roundtrip to the mailing list of the company you downloaded it from.

Doubtlessly there are other use cases that extend beyond mere curiosity. Point being that we really have two distinct situations to look out for:

  1. Determining whether your currently executing code is in debug or release build;
  2. Determining whether an already build assembly of unknown origin is build with debug or release build.

In the following sections I’ll dive deeper in each of these, but before we go head-first, let’s emphasize what the end-result has to be about so we all know what we’re heading for.

Requirements for a good method for determining debug or release build

  1. Must work with existing assemblies or with assemblies still to be compiled;
  2. Should not need extra coding for each assembly we want to test;
  3. Must work in mixed environments where debug and release builds are used together;
  4. Must work when other configurations are used;
  5. Must work without knowing the DEBUG constant;
  6. Must work on a Type, an Assembly, a Module or the current code block;
  7. Works only with managed types or assemblies;
  8. Cannot work on any other variable (see below).

The last point may need a bit of extra explanation. When you instantiate a variable and you’d ask the question “is this variable in debug build”, it is hard to answer: would you mean the type of the variable, it’s execution context or where the original contents of the variable came from (marshalled types don’t even need to be on the current machine!). If it’s type Object, would you want to infer the type? By abstracting this away we make the caller responsible for telling what Type he actually wants to investigate, which is where this decision belongs.

Determine whether you’re in debug or release build

I already exclaimed that this would be easy. Any C# IDE (Visual Studio, SharpDeveloper and MonoDeveloper) sets the DEBUG conditional compilation constant. This constant is not made mandatory from the ECMA specifications though it is mentioned encouraged there and all compilers stick to it (a side note: the ECMA specification uses “Debug” in its examples, VS and other IDE’s use “DEBUG”, be aware of this with C#, as these constants are case sensitive). This makes cross-platform checking for debug or release builds genuinely straightforward:

1
2
3
4
5
6
7
8
9
// CSHARP code
public static readonly bool IsDebugMode =
    #if DEBUG
         true;       // Debug Build is selected
    #else
         false;      // Release or other Build is selected
    #endif
public static readonly bool IsReleaseMode = !IsDebugMode;
1
2
3
4
5
6
7
8
9
' VB.NET code
#If DEBUG Then
    ' will compile when when Debug Build is selected
    Public Shared ReadOnly IsDebugMode As Boolean = True
#Else
    ' will compile when release build is selected
    Public Shared ReadOnly IsDebugMode As Boolean = False
#End If
Public Shared ReadOnly IsReleaseMode As Boolean = Not IsDebugMode

You can use this code anywhere in any class, but I suggest you place it inside a utility class. The downside of this approach is that you cannot easily put it in a separate utility assembly: what would happen if your utility assembly were compiled using Release mode and your client assembly or your main app were compiled using Debug mode. That would simply wreak havoc to this method and return the reverse from what you’d expect.

A note on VB.Net vs. C#.Net conditional compilation constants in the IDE

Conditional compilation constants have been around in VB ever since VB5, but they’ve never really taken off. In VB, the need for such constants has traditionally been much lower then in lower level languages like C and C++, where they have created a whole subculture of macros and expressions. Now both C# and VB.Net support the same semantics, but the VB IDE has been left behind a bit over the years. Where in C++ and later in C# the non-compilable sections were grey-colored to emphasize that these would not be hit by the compiler, in VB.Net IDE’s, the code coloring simply continued in the uncompilable blocks (see screenshots).

Screenshot CSharp.Net coloring of conditional compilation blocks VB.Net coloring of conditional compilation

This odd coloring sometimes left me frowning on whether I had used the correct constant or not. A simple workaround I then apply is making the statement illegal. If that doesn’t show the dreaded red line for illegal syntax, you know that the line or block is ignored.

Another notable difference that always jumps  at me when I do this type of development is how better readable C# is an how much easier it is in C# with its multiline support compare to VB, where I have to repeat the whole line. It is not possible to conditionally compile a part of a line in Visual Basic.

The road to determining whether an Assembly was build in Debug or Release build

The real challenge is not finding out whether the DEBUG constant is set or not in your current code, the real challenge is finding a way of determining this for any assembly, as well as creating a method inside a utility assembly that you can use throughout your code without the need of recompilation.

Revisited: what is a Debug build

In the opening of this article I gave a high level — and for I assume well known — overview of what a Debug build vs. a Release build is. We know that the compiler augments the code with lots of magic to help us debug the code, but how does the CLI (or CLR for that matter) determine whether it is a Debug build or not? Does it need to know? The answer is: yes. It needs to know, because the JIT compiler (the Just In Time compiler which kicks in to compile the IL code into machine language when your code is run by the CLR) will have to change its way to treat the code and prevent certain optimizations to allow for proper debugging.

Assembly's DebuggableAttributes in Reflector

The JIT compiler simply checks some attributes on the assembly’s manifest. If you useReflector to view the code of your Debug assembly you may see something similar to the screenshot.  The attribute of major importance here is the DebuggableAttribute, but CompilationRelaxationsAttribute and DebuggerVisualizerAttribute are also of some interest. For understanding the solution later on it is not necessary to understand each and every of these attributes, however, I’ll explain them shortly so you know what we’re talking about when I discuss these attributes later on.

Debuggable attributes used in debug builds

All the following attributes are used on Assembly level. Debuggable attributes that are used on a higher granularity level, i.e., on classes or methods, are left out because they won’t bring us closer to a solution.

  • DebuggableAttribute, or Debuggable for short has a few methods and properties.
    • DebuggingFlags readonly property returns a DebuggingMode enumeration which. These modes influences how the JIT compiler optimizes the executable and how the MSIL is augmented. These settings are new since .NET 2.0
      • None: all optimizations present, but JIT tracking information is generated in .NET 2.0, but disabled in .NET 3.5  (see below)
      • Default: Same as None, but not JIT Tracking information is always generated, all other optimizations are on.
      • DisableOptimizations: prevent rearrangement of code for optimization purposes.
      • IgnoreSymbolStoreSequencePoints: if set, it means do not use the Program Database (PDB), instead use the MSIL sequence points to look up symbols.
      • EnableEditAndContinue: augments the code to allow for edit and continue support.
    • IsJITOptimizerDisabled returns true if the optimizer for JIT is disabled. The compiler can reorganize code when this setting is set to false. By default, this is true. This setting is equal to setting DebuggingModes.DisableOptimizations (information from Reflector). This setting is available since .NET 1.0.
    • IsJITTrackingEnabled is true when the JIT compiler keeps track of code execution for benefit of the debugger. If this is on, the JIT compiler will add extra code to be able to vind the correct IL instruction from a certain native code instruction. When you debug, this setting should be true. This setting is equal to DebuggingModes.Default. This setting is available since .NET 1.0.
  • CompilationRelaxationsAttribute, or CompilationRelaxations for short is an attribute that controls whether or not string interning is preferred. String interning means that strings that are immutable will be placed on a global stack for the running assembly. This increases string usage, in particular constant and static readonly strings. Certain strings will, however, always be interned (String.Empty for instance is interned by default).
  • DebuggerVisualizerAttribute, or DebuggerVisualizer for short is an attribute that tells the debugger what visualizer it can use for a certain type. By default, Visual Studio comes with three visualizers: a Text Visualizer, an XML Visualizer and a Default Visualizer. It is quite straightforward to create your own visualizer, when you are in break mode, you’ll see a list of registered visualizers and you can click on them to make it easier to inspect your variables. I’ll discuss visulizers more deeply in another article. Because visualizers are only added at the assembly level for debug builds but only on user request, they are not useful for our purpose.
Debuggable attributes in Debug vs. Release builds

Originally I assumed that when you’d create a Release build, there’d be no Debuggable attribute at all. How was I surprised to find out that it is there after all, sitting in my way of finding a simple way to determine the build type of an assembly. Using Reflector once more, you can see how this looks in disassembly in the screenshot. I had expected not to see the Debuggable attribute anymore, but I had also expected to see the CompilationRelaxations gone.

Assembly's DebuggableAttributes in Reflector of a Release Build

Time to do a bit more research, I thought. So I delved into the documentation again and googled a bit while I came across this post by Rick Byers on the Microsoft Developer Network. He explains why IgnoreSymbolStoreSequencePoints is a setting that tells the JIT compiler not to lookup any sequence points (which means so much as a point in the IL code that maps to a point in the source files) that are stored in a possible Program Database (PDB). Instead, if available, it should only use the sequence points inside the IL code.

But why is that mode set on Release Builds? When you compile your code with optimizations on, there will be no inserted sequence points inside the MSIL code and there will be no need at all for the JIT compiler to jump to the corresponding source code, because there simply isn’t any anymore: it is optimized away, the order of the code may have changed and even some blocks that would otherwise never execute may have been removed. Other semi-constant expressions (property gets) may have been turned into constants etc. So why then, you keep asking, is this setting on? It has no effect, it seems.

Advanced build settings Debug Info

Looking back into the Advanced Build Settings, it appears that by default, Visual Studio sets the Debug Info to “pdb-only”. Sounds like “use pdb if available” or “create pdb for debug info” to me. But the opposite is true. It means setting the “IgnoreSymbolStoresequencePoints attribute on the assembly and it will not create pdb information. Let me repeat that: selecting pdb-only prevents PDB generation and enforces looking up debugging info in the Assembly’s MSIL code. That’s quite the opposite of what’s useful and expected. The only thing I can think of is that for non-optimized methods or dynamic methods, classes or assemblies, it will not try to load the PDB when an exception occurs or a debugger is attached. For me, this is a hoax and this setting can be ignored and switched off for release builds as it means nothing and adds nothing.

Debug Info set to none: no DebuggableAttribute

And what happens if we turn the Debug Info switch to “None”? The result is that the DebuggableAttribute is completely gone, as expected for a Release build in the first place.

And what about the CompilationRelaxations? Why is this value “8″, which means Interning is disabled, still selected? I have no idea, to be honest. If you ask me, the “Optimizations” setting for a compiled assembly does not correctly honor this attribute, preventing optimizations involving interning of strings. Trying to mess with different types of compilation settings did not give me any change of this assembly attribute. For determining whether an assembly is build with debugging or not this setting is unfortunately useless.

Debuggable attributes in .NET prior to version 2.0 and in Compact Framework

The Compact Framework and .NET version 1.0 and 1.1 do not support the DebuggingMode enumeration. They do support the booleans IsJITOptimizerDisabledand IsJITTrackingEnabled, which is what we can use for our approach to make it cross-platform and cross-version compliant. Basically, in most case the DebuggableAttribute is gone from the manifest. And if it’s there, it will have the IsJITOptimizerDisabledproperty set to true.

Formalization of what constitutes a Debug Build

This section has looked deeply in what settings the compiler uses and must use to enable an MSIL assembly to be debuggable. For the rest of the discussion we’ll use the following formalization of these findings (backed up by the CLI standard documentation):

  1. A debug build contains the DebuggableAttribute with one or more debug flags set:
    1. DebuggingMode.Default,
    2. DebuggingMode.DisableOptimizations,
    3. DebuggingMode.EnableEditAndContinue;
  2. A debug build may combine these flags with the flags from point 3;
  3. A release build may contain the DebuggableAttribute with selected debug flags set:
    1. DebuggingMode.None,
    2. DebuggingMode.IgnoreSymbolStoreSequencePoints;
  4. A debug build prior to .NET 2.0 must have IsJITOptimizerDisabled set to true and may have IsJITTrackingEnabled set;
  5. A release build may have IsJITTrackingEnabled set to true but may not haveIsJITTrackingEnabled set to true;
  6. Frameworks prior to .NET 2.0 do not support the DebuggingMode flags;
  7. JIT tracking (IsJITOptimizerDisabled or DebuggingMode.Default) may not be disabled in .NET 2.0 and higher;
  8. The Portable.NET compiler supports the .NET 1.0 settings;
  9. The Mono.NET compiler supports the .NET 2.0 settings.

Revisited: what is a Release build

Though the explanation for what constitutes a debug build was rather lengthy, about what constitutes a Release build I can be rather brief: everything that’s not a debug build is considered a release build.

It is possible to create as many types of configurations as you want and it is impossible by whatever means to determine — as soon as your application leaves the confines of the IDE — what configuration was originally chosen. But that wouldn’t be of any importance anyway. Whether you would call your release build “Release”, “Business”, “Compact”, “Roll-out”, “General Availability Release” or “Community Preview”, the name is just a placeholder for configuration settings and definitions of various conditional compilation directives. The settings are what makes the build. If these settings contain any of the settings mentioned in the formalization above, then it’s a build with optimizations disabled (at the very least) and we will consider that a debug build. If not, it is a release build.

Conclusion

This section explained how I came to the solution in the next section and why I chose this approach. Understanding this section may have showed you a tiny little bit of what goes on under the hood when you randomly select some debugging settings to be able to actually, well, debug your application. The DebuggableAttribute is vital in this process and it has proved an attribute we can rely upon.

Solution for determining the build mode: Debug or Release

If you want to know the background to this solution I invite you to read the previous section. I won’t go too deep into the details here, as I already did that above; I’ll only emphasize a bit on the approach I took and how to make the method robust, future-proof and backwards compatible. The download section contains binaries for the different .NET versions. The source code I present here is .NET 3.5 source code to keep the presentation and the explanation brief. The downloadable source code (soon!) will compile correctly on any version of .NET.

Note: a bug prevented this post from being published, it turned out empty. The problem proofed hard to solve so I dedicated a little post to it in case others encounter similar problems.

Source

And a little bonus – a Video about Debug/Release 🙂

Advertisements

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s