Now that you've been introduced to the Visual Studio .NET debugger, it's time to look at some other tools that can help you to make sense of what's going on inside your applications. The CLR, the .NET Framework, the compilers, and Visual Studio itself are all vast and imposing beasts, which means that you're going to need some powerful weapons to tackle them properly. This chapter introduces some useful tools, shows you when and how you might want to use them, and warns you about any potential problems that they have.
Cordbg is a framework command-line debugger for managed code only. Whereas the Visual Studio debugger has an easy-to-use graphical interface and swaggers along with the power to debug almost any situation it encounters, Cordbg is its ugly sibling lurking in a corner. There are, however, some situations where you will want to dispense with the all- dancing cool gadget and instead get to know some more primitive and usually hidden details about your application.
Cordbg ships with the .NET Framework SDK, so you can use it in situations where Visual Studio can't be installed or would consume too much memory. Cordbg consists of only four components , namely Cordbg.exe, msdis130.dll, msvcp70.dll, and msvcr70.dll. This means that Cordbg is small enough and has few enough dependencies that even paranoid management can be persuaded to allow its installation on a production server.
You can also use Cordbg to debug window painting and drawing code that a graphical debugger chokes on because of interference from its own windows . Another benefit is that Cordbg has some low-level functionality that isn't available from within Visual Studio. For instance, attaching the Visual Studio debugger to your managed code automatically disables JIT optimization, which can make it very difficult to debug a problem that appears in a release build, but not in a debug build. With Cordbg , you can switch JIT optimization on and off at will, even in the middle of a debug session.
The two major caveats with Cordbg are that it can only debug managed code and it's less straightforward to use than the Visual Studio debugger. Cordbg also has a few minor bugs ”see the Microsoft Knowledge Base article Q307169 for more details about these.
If you create a simple "Hello, VB .NET World" program and then compile it, you can then load it into Cordbg and show its source code. It's important that you have debug symbols available during debugging, so you should either compile the program as a debug build or compile it as a release build, but change the Release configuration to produce debug symbols, as explained in Chapter 3. Figure 5-1 shows such a program after it's been compiled and loaded into Cordbg . Note the run command that loads and starts the specified executable and the show command that shows you the program source. The asterisk ( ˆ— ) next to line 003 shows the current execution point, where the program has been paused by the debugger.
You can ignore the warning message in this case ”the debugger is just informing you that it can't locate the debug symbols for mscorlib.dll, the CLR's primary runtime library. If you want to load debug symbols for the .NET Framework, you should copy them from C:\Program Files\Microsoft .Net\ FrameworkSDK\symbols and put them into the same folder used by the Windows symbol files, normally C:\Windows\Symbols\Dll, although the path might be different on your computer.
Cordbg has many commands, all of which are fully documented. Typing /? at a Cordbg command line shows you these commands and their usage. One of the most useful commands is mode , which allows you to set various debugger features. For example, mode AppDomainLoads 1 (or mode app 1 for short) tells the debugger to inform you about every application domain and assembly load event. Another useful mode command is mode JitOptimizations 1 (or mode jit 1 for short), which tells Cordbg to turn the JIT compiler code optimization on. You can use this and another command called wt to see what effect code optimization has on the HelloWorld program.
First type wt . This command steps through the entire program, printing the call tree as it goes. At the end of the trace, when the return instruction is reached for the function in which the wt command was executed, you will see a line showing the total number of native instructions executed. As the program is currently running with the JIT optimizer off, you can see that this number is very substantial for such a simple program ”3,385 native code instructions with my version of Windows 2000 Professional and the .NET Framework. Now type go to continue and finish program execution.
So what happens if the JIT optimizer is turned on and you run the same test? If you look at Figure 5-2, you can see the results. Note that you need to add a breakpoint on line 3 first to make sure that the program halts; otherwise , the optimization causes the program to run straight through without stopping.
As you can see, the difference between nonoptimized code (3,385 native instructions) and optimized code (374 native instructions) is quite dramatic. This type of information is much more difficult to retrieve when you use the Visual Studio debugger.
Before I leave Cordbg , one final tip. You can prefix most Cordbg commands with an asterisk ( ˆ— ), which causes the command to execute once for every managed thread in the process. If you want to explore Cordbg and its commands further, you can view both the documentation and the complete source code of this debugger, as it comes as one of the sample programs.