Introduction to Debugging

Source code is compiled and run in the .NET Framework using a two-stage process. First, source code is compiled to Microsoft intermediate language (MSIL) code using a .NET Framework–compatible compiler, such as that for Visual Basic .NET or Visual C#. Second, MSIL code is compiled to native code.

Essentially, the process of debugging goes in the reverse direction. As you execute code, the debugger first maps the native code back to MSIL, and then maps the MSIL code back to source code using programmer database (PDB) files. To enable debugging, it is thus necessary to generate the required mapping information at each stage in the compilation process.

The first step of enabling debugging is to create the PDB file that maps MSIL to source code. When developing precompiled components with either Visual Basic .NET or Visual C#, this is specified using the /debug switch. When using code-behind pages in ASP.NET, this is specified with a <compilation debug="true"/> setting in the computer's or application's .config file.

The second step required for debugging is to instruct the just-in-time (JIT), MSIL to native code compiler to create mapping information between the MSIL and native code. This is accomplished by setting the JITTracking attribute in your assembly. This attribute, which is essentially a flag, is automatically set when a process to be debugged is loaded from within a debugger. However, if the process is loaded (and JIT-compiled) outside the debugger, and the debugger is later attached, it is necessary to set this attribute beforehand using a compiler switch. If this attribute is not set, a debugger attaching to a running program cannot work back from the native code to the MSIL, and it will not be possible to map farther back to the source code in that debugging session.

Whereas Visual Studio .NET can be used for compiling and debugging, it is important to realize that a debugger, such as the CorDbg and DbgClr debuggers included with the .NET Framework SDK, does not compile MSIL code. When using a stand-alone debugger, it is necessary to first compile the code to MSIL using a separate compiler. An important side effect of this process is that if your source code changes while debugging, a simple reload of the source code file will not suffice, and you will need to recompile. ASP.NET, of course, is a special case — code behind ASP.NET pages is actually compiled dynamically by ASP.NET using the command-line compilers in the .NET runtime directory.

Compiler Switches

Both the Visual C# and Visual Basic .NET compilers support the same set of debug compiler switches. Although there appear to be many possible combinations, only the following three configurations are actually possible.

  1. Do not include debugging information:
    • (nothing)
    • /debug-
  2. Create a programmer database (PDB) file:
    • /debug:pdbonly
  3. Create a PDB file and set the JITTracking attribute:
    • /debug
    • /debug+
    • /debug:full

As mentioned earlier in this tutorial, there are two steps in the compilation and debugging process. When compiling from source code to MSIL, any of the debug switches except /debug- will result in a PDB file being created. Three of the remaining options — specifically, /debug, /debug+, and /debug:full — all do the same thing: they create the PDB file and cause the JITTracking attribute to be set, which allows a debugger to map native code back to source code when the debugger attaches to a running program.

See Also

Debugging .NET Framework Applications | Debugging ASP.NET Web Applications | Appendix A: For Additional Information | Appendix B: Runtime Debugger (CorDbg.exe)