Making an Image Easier to Debug

When compiling unmanaged code, you can configure an executable image for debugging by setting IDE switches or command-line options. For example, you can use the /Zi command-line option in Visual C++ to ask it to emit debug symbol files (file extension .pdb). Similarly, the /Od command-line option tells the compiler to disable optimization. The resulting code runs more slowly, but is easier to debug, should this be necessary.

When compiling .NET Framework managed code, compilers such as Visual C++, Visual Basic, and C# compile their source program into Microsoft Intermediate Language (MSIL). MSIL is subsequently JIT-compiled, just before execution, into native machine code. As with unmanaged code, you can configure an executable image for debugging by setting IDE switches or command-line options. In addition, you can configure the JIT compilation for debugging in much the same way.

This JIT configuration has two aspects:

  • You can request the JIT-compiler to generate tracking information. This makes it possible for the debugger to match up a chain of MSIL with its machine code counterpart, and to track where local variables and function arguments are stored.
  • You can request the JIT-compiler to not optimize the resulting machine code.

Normally, the compiler that generates the MSIL sets these JIT-compiler options appropriately, based upon the IDE switches or command-line options you specify, for example, /Od.

In some cases, you might want to change the behavior of the JIT-compiler so that the machine code it generates is easier to debug. For example, you might want to generate JIT tracking information for a retail build. You do so with a simple text configuration file.

For example, if the assembly you want to debug is called MyApp.exe, then you create a text file called MyApp.ini in the same folder as MyApp.exe, which contains these three lines:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

You can set the value of each option to 0 or 1, and any absent option defaults to 0. Setting GenerateTrackingInfo to 1 and AllowOptimize to 0 provides the easiest debugging.

Note   The System.Diagnostics.DebuggableAttribute controls the settings for an assembly. DebuggableAttribute includes two fields that record the settings for whether the JIT compiler should optimize, and/or generate tracking info.

For a retail build, compilers do not set any DebuggableAttribute. The JIT-compiler default behavior is to generate the highest performance, hardest to debug machine code. Enabling JIT tracking lowers performance a little, and disabling optimization lowers performance a lot.

The DebuggableAttribute applies to a whole assembly at a time, not to individual modules within the assembly. Development tools must therefore attach custom attributes to the assembly metadata token, if an assembly has already been created, or to the class called System.CompilerServices.AssemblyAttributesGoHere. The ALink tool will then promote these DebuggableAttribute attributes from each module to the assembly they become a part of. If there is a conflict, the ALink operation will fail.

In version 1.0 of the .NET Framework, the Microsoft Visual C++ compiler adds the DebuggableAttribute when the /clr and /Zi compiler options are specified. In version 1.1 of the .NET Framework, you must either add the DebugabbleAttribute manually in your code or use the /ASSEMBLYDEBUG linker option.

See Also

Debugging and Profiling | Enabling JIT-attach Debugging | Enabling Profiling