Flylib.com

Books Software

 
 
 

Improving JIT Debugging


Improving JIT Debugging

Chapter 3 investigated how you can use JIT debugging to intercept a program crash and launch a debugger automatically. When a crash occurs, the CLR presents you with a list of debuggers and asks you to choose one. If your favorite debugger isn't on the list, there's an undocumented and unsupported way to add it to that list. For example, if you sometimes like to use WinDbg, the following command line registers WinDbg for JIT debugging:

vs7jit.exe /RegisterOld "c:\Windbg\windbg.exe" "WinDbg"

To unregister WinDbg for JIT debugging, you can use the following command line:

vs7jit.exe /UnregisterOld "c:\Windbg\windbg.exe"

As I've already stated, this switch is undocumented so I don't know for how long it will continue working.



Coping Without Edit and Continue

As I discussed in some detail during Chapter 1, perhaps the most noticeable loss within the Source window is the inability to edit your code during a debugging run and then continue execution of the new code without restarting the program (a feature sometimes referred to as Edit and Continue, or E&C). If you select the option Tools Options Debugging Edit and Continue Allow me to edit VB files while debugging, you'll be able to edit your source code during program execution, but this revised code is ignored by the compiler until you restart the program. This is actually rather dangerous, as it's very easy to become confused about what code you've changed and what code is actually executing.

If you do want to take advantage of this ability to change code during a program run, I suggest adding all new and revised code into one or more separate procedures that you can then enclose within a collapsible region using the #Region #End Region directive. The #Region directive allows you to remove the new code from your view so that it doesn't interfere with your view of the code that is currently executing.

Placing the new code into separate procedures, maybe including revised copies of current procedures, is necessary because the #Region directive can only be used at a class or namespace level. Unfortunately, you can't place a region directive within a procedure or function. An alternative to creating a new dummy procedure just for gaining the benefits of a collapsible region is to place the new code into a comment code, as a comment block is automatically created as a collapsible region.

Having placed the code into one or more regions and collapsed these regions so that new code can't be seen, you should mark each region with a bookmark by right-clicking the left column of the source window and selecting "Add task list shortcut". You can see and reference these bookmarks in the Task window by opting to show "All tasks " (or just "Shortcuts" if you prefer) from its context menu.

After the current debugging run has finished, double-clicking each of these shortcuts in the Task window takes you directly to the associated region where new code is stored. You can then amend your code to incorporate all of the new changes before restarting your program.



Debugging Common Intermediate Language

One frequent query from new .NET developers is how to view and debug Common Intermediate Language (CIL). The Source window only allows you to step through source code, and the Disassembly window only shows you the source code together with the processor-native code. Although your .exe or .dll contains the CIL, it seems to be impossible to get at it in any useful way.

There is a neat trick that allows you to both view and step through CIL. It's based on using the CIL disassembler utility to dissect your executable into CIL source and then rebuilding the executable again so that the CIL code is bound to the VB .NET source. This may sound a little complicated, but the command-line steps are simple:

  1. VBC MyProgram.vb /debug:full /optimize- /out: MyProgram.exe

  2. ILDASM MyProgram.exe /source /out: MyTest.il

  3. ILASM MyTest.il /debug /out: MyTest.exe

The first step builds your executable as normal, in full debug mode, and without code optimization. The second step disassembles the resulting binary into a CIL source file ”the /source flag adds the original VB .NET source code as CIL comments. The final step builds a new executable from the CIL source, once again in debug mode.

Having completed these command-line compilation steps, load the original project into Visual Studio. In my case this was a console application called DoubleTrouble.sln containing a single project called DoubleTrouble that, in turn , contained a source code file called Module1.vb. Choose the Add Existing Item option from the File menu, change the files filter to be "All files," and select the source code file that was produced by the Ildasm step mentioned previously (the one with a .il suffix). Double-click this CIL file in Solution Explorer to see it displayed in Visual Studio's Source window. If you scroll down past the initial code preamble, you should see your VB .NET source code shown as comments (prefixed with //) and the CIL statements shown as executable code. Figure 4-13 shows how this looks with my selected project.

click to expand
Figure 4-13: Single-stepping and debugging CIL code with VB .NET source

Debugging the new executable that you've just created is simple. First, you place a breakpoint on one of the CIL statements shown in the Source window. Then you need to tell Visual Studio to launch your new executable. To do this, you should right-click your project in Solution Explorer and use the option under Properties Configuration Properties Debugging Start external program to specify the executable that you created in the final command-line step mentioned previously. Now when you press F5, the breakpoint that you set should be hit. If you then jump to the Disassembly window, you can see the CIL code together with its corresponding native code. This is an instructive way of learning about CIL and how it functions.