Managed Debugger

Managed Debugger

MDbg supports user-mode debugging of managed applications and does not support mixed-mode debugging. Because MDbg is dedicated to managed code, it has an expanded repertoire of debugging commands specific to managed code. As mentioned previously, it is a console debugger. MDbg supersedes the Runtime Debugger (CorDbg), which is the previous managed debugger. CorDbg remains available and continues to be distributed with Visual Studio .NET and the .NET Framework.

Conveniently, MDbg and CorDbg share many of the same commands. However, MDbg commands are different from those of WinDbg and SOS. For developers who switch between these tools frequently, leveraging the benefits of each, the nonstandardization of the public interface of these tools is sometimes frustrating. For example, here are three distinct commands to display a call stack. In WinDbg, you have the k commands, such as k, kb, and kp. SOS offers the !clrstack command. MDbg has the most original name for a stack trace command, which is the w (w for where) command.

The following walkthrough demonstrates and highlights many of the unique features of MDbg. As mentioned, Store.exe is the demonstration application provided for walkthroughs in this chapter. In this application, you record sales as transactions. To add a transaction, enter the number of transactions in the # of Transactions text box and then click the Add Transactions button. The default number of transactions is 1. The Transaction dialog box appears next, in which you select the items sold in this transaction. After accepting, the new transaction is added to the list of transactions, and the display is updated. The Store application is modified minimally to facilitate some of the walkthroughs. Each walkthrough has its own version of the Store application. The generic Store application is shown in Figure 13-5.

Image from book
Figure 13-5: The user interface of the Store application

MDbg Walkthrough

The ability to create instances of new objects or invoke managed functions is one of the coolest features included in MDbg. You can seed an application with classes and functions that are used during debugging sessions exclusively. They are never actually called in the application during normal operation, but are instead called only from the debugger. A Debug class has been added to the Store application for this purpose. Debug.Reset is a static method and the sole member of the Debug class. The Reset method resets the Store application. The Debug class and Reset method are a normal class and method. There are no hooks in them especially for the debugger. This is the Debug class:

public class Debug
    public static void Reset()
        Form1.formMessage.Text = "Grand Total:";
        Form1.formSummary.Text = "Summary: No Transactions";
        Console.WriteLine("Application reset.");

These are the steps of the walkthrough:

  1. Start the demonstration by running the Store application and adding multiple transactions.

  2. Start MDbg.

  3. Display the active processes by using the pro(cessnum) command. The command lists the process identifier, executable, and domain of each process. The Store application is included in the list.

      mdbg> pro
    Active processes on current machine:
    (PID: 3636) C:\codew\debugging\memory\bin\Release\Store.vshost.exe
            (ID: 1) Store.vshost.exe
    (PID: 3812) C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe
            (ID: 1) DefaultDomain
    (PID: 2812) C:\store\Store.exe
            (ID: 1) Store.exe
  4. The a(ttach) command attaches MDbg to a running application. The prompt will change to indicate that you are debugging an application. As part of attaching to the program, the debugger interrupts the application.

    mdbg> a 2812
    [p#:0, t#:0] mdbg>
  5. How is the Reset method described in the debugger? That would help in calling that method. The x command displays functions in a module. Without parameters, the x command lists the modules in the application. List the modules:

    [p#:0, t#:0] mdbg> x
    :0      mscorlib.dll#0 (no symbols loaded)
    :1      Store.exe#0
    :2      System.Windows.Forms.dll#0 (no symbols loaded)
    :3      System.dll#0 (no symbols loaded)
    :4      System.Drawing.dll#0 (no symbols loaded)
    :5      System.Configuration.dll#0 (no symbols loaded)
    :6      System.Xml.dll#0 (no symbols loaded)
  6. Module ":1" is Store.exe. With that information, the functions in that module can be listed using the x command again, this time with the ":1" parameter:

    [p#:0, t#:0] mdbg> x :1
    ~0. Store.Transaction.Dispose(disposing)
    ~1. Store.Transaction.InitializeComponent()
    ~2. Store.Transaction..ctor()
    ~3. Store.Transaction.btnAdd_Click(sender,e)
    ~4. Store.Transaction.chkComputer_CheckedChanged(sender,e)
    ~5. Store.Transaction.chkLaptop_CheckedChanged(sender,e)
    ~6. Store.Transaction.chkPrinter_CheckedChanged(sender,e)
    ~7. Store.Transaction.chkSoftware_CheckedChanged(sender,e)
    ~8. Store.Transaction.get_newItem()
    ~9. Store.Transaction.btnCancel_Click(sender,e)
    ~10. Store.Form1.Dispose(disposing)
    ~11. Store.Form1.InitializeComponent()
    ~12. Store.Form1..ctor()
    ~13. Store.Form1.get_formItems()
    ~14. Store.Form1.get_formMessage()
    ~15. Store.Form1.get_formSummary()
    ~16. Store.Form1.get_formListBox()
    ~17. Store.Form1.btnTransaction_Click(sender,e)
    ~18. Store.Form1.btnBad_Click(sender,e)
    ~19. Store.Form1..cctor()
    ~20. Store.Properties.Resources..ctor()
    ~21. Store.Properties.Resources.get_ResourceManager()
    ~22. Store.Properties.Resources.get_Culture()
    ~23. Store.Properties.Resources.set_Culture(value)
    ~24. Store.Program.Main()
    ~25. Store.Properties.Settings.get_Default()
    ~26. Store.Properties.Settings..ctor()
    ~27. Store.Properties.Settings..cctor()
    ~28. Store.Item..ctor()
    ~29. Store.Item.get_Products()
    ~30. Store.Item.set_Products(value)
    ~31. Store.Item.get_ItemId()
    ~32. Store.Item.Dispose()
    ~33. Store.Item..cctor()
    ~34. Store.Debug.Reset()
    ~35. Store.Debug..ctor()
  7. The Store.Debug.Reset method is found at the bottom of the list. Store is the namespace, Debug is the class, and Reset is the method. The next time a transaction is added, we want to call this method to reset the application. The button-click handler for adding transactions is Store.Form1.btnTransaction_Click, which is also found in the preceding list. Set a breakpoint on this method and resume the Store application. The b(reakpoint) command sets breakpoints, and the g(o) command resumes an interrupted application:

    [p#:0, t#:0] mdbg> b Store.Form1.btnTransaction_Click
    Breakpoint #1 bound (Store.Form1::btnTransaction_Click(+0))
    [p#:0, t#:0] mdbg> g
  8. Click the Add Transaction button in the Store application. The breakpoint is hit, and the MDbg debugger interrupts the application. The source line where the program is interrupted is displayed. We no longer need this breakpoint. The b command without parameters lists all breakpoints, whereas the del(ete) command removes a breakpoint:

    STOP: Breakpoint 1 Hit
    64:        {
    [p#:0, t#:0] mdbg> b
    Current breakpoints:
    Breakpoint #1 bound (Store.Form1::btnTransaction_Click(+0))
    [p#:0, t#:0] mdbg> del 1
  9. The newo(bj) command creates new instances of classes. However, Reset is a static method and does not require an instance. The method is called directly on the class. Invoke the method with the f(unceval) command:

    [p#:0, t#:0] mdbg> f Store.Debug.Reset
    STOP EvalComplete
  10. Resume the Store application with the g command, and the application should be reinitialized.

Here is a second demonstration of the MDbg debugger. This walkthrough highlights exception management. When a second-chance exception is raised in an attached application, MDbg does not intercede. Interesting, this is contrary to the behavior of most debuggers. Second-chance exceptions are catastrophic. The exception will terminate the application or prompt JIT debugging. If you want the debugger to intercede, you can request that MDbg catch second-chance exceptions.

  1. Restart the Store application.

  2. Attach MDbg to the application and resume the program.

  3. Click the Bad Action button. As advertised, a bad action occurs, which is an unhandled exception. The Just-in-Time Debugging dialog box will most likely appear where the application can be terminated. Terminate the application. The Bad Action button is not particularly creative in manifesting an exception. The method has filler code but eventually raises a divide-by-zero exception:

    private void btnBad_Click(object sender, EventArgs e)
        int a = 5, b = 0;
        a /= 2;
        a /= b;
  4. Restart the Store application.

  5. Attach the MDbg debugger.

  6. Ask the MDbg debugger to catch all second-chance exceptions with the ca(tch) ex(ception) command:

    [p#:0, t#:0] mdbg> ca ex
  7. Resume the application and then click the Bad Action button. This time, the exception is trapped in MDbg, and execution transfers to the debugger. In the debugger, the exception type and the properties of the current exception object are dumped, providing important information on the exception. The source code at the infraction is also displayed and shows the divide-by-zero operation. You now have plenty of information to debug the problem.

    [p#:0, t#:0] mdbg> g
    STOP: Exception thrown
            _message="Attempted to divide by zero."
            _stackTrace=array [24]
    134:            a /= b;

MDbg Commands

MDbg has a full complement of commands for debugging managed applications. Some of the commands were demonstrated in the previous walkthroughs. Table 13-1 lists the MDbg commands.

Table 13-1: MDbg Commands



? and h(elp)

These are the help commands and display the MDbg commands with a brief description.


This command switches to another managed process, which is currently being debugged. Without parameters, the command displays the attached processes. MDbg can simultaneously debug multiple applications.


This command attaches the MDbg debugger to a managed process. Without parameters, the command lists the available managed processes.


This command sets a specific breakpoint or displays all the breakpoints.


This command stipulates which events to catch. It can also display the events.


This command sets a particular configuration or displays the configuration options.


This command deletes a breakpoint.


This command detaches the debugger from the current debugged application.


This command moves the stack frame down.


This command echoes text to the display.

ex(it) and q(uit)

These commands exit the debugger.


This command executes an action on all threads.


This command calls a method.


This command resumes execution of a debugged application.


This command displays or stipulates which events to ignore. This command complements the ca(tch) command.


This command intercepts exception at the specified stack frame.


This command kills an active process.


This command lists loaded modules, AppDomains, or assemblies.


This command loads an MDbg extension.


This command sets a specific MDbg option or displays all the options.


This command creates an instance of a type.


This commands steps over the next instruction.


This command steps out of a function.


This command sets or displays the source path.


This command displays the values of the local or debug variables.


This command enumerates the available managed processes.


This command resumes a suspended thread.


This command runs a program with the MDbg debugger immediately attached.


This command sets a local or debug variable to a new value.


This command moves the instruction pointer within the current function.


This command shows the source code at the current instruction.


This command steps into a function.


This command suspends a running thread.


This command sets or displays the symbol path.


This command switches to a specified thread or display all threads.


This command moves up the stack frame.


This command displays the object specified by the GC handle.


This command executes a command based on a debugger event.


This command displays a stack trace.


This command displays the functions in a module or lists all the modules.


This keystroke interrupts a running application.


This keystroke terminates the running application and exits the debugger.