Home > Sample chapters

How Windows Debuggers Work

Remote Debugging

Remote debugging is a convenient feature that allows you to control a target remotely using a debugger instance that’s running on a different machine on the network. This section provides an inside look at how this feature is implemented by debugger programs in Windows, as well as how you can use it in the case of the WinDbg and Visual Studio debuggers.

Architecture Overview

At a high level, two conceptual models are used to support remote debugging: remote sessions and remote stubs. In both cases, a process needs to be running on the same machine as the target so that it can carry out the commands that are typed in the remote debugger. In the case of a remote session, the debugger session is entirely on the target machine and the remote debugger instance simply acts as a “dumb” client to send commands to the local debugger instance. In the case of a remote stub, the debugger session is running remotely, with a “stub” broker process running locally on the target machine and acting as a gateway to get information in and out of the target.

Remote sessions are used when collaboration among multiple engineers is needed to investigate a certain failure. In that case, a local debugger instance runs on the repro machine, and developers are then able to start typing commands remotely from their respective machines, take a look at the failure, and even leave comments and see each other’s debugging attempts and commands as they get typed in. WinDbg supports this very useful form of remote debugging, which is illustrated in Figure 3-11.

Figure 3-11

Figure 3-11 Remote debugging using remote sessions.

Remote stubs are used when it’s important to set up the debugging environment on a remote computer, either because the full debugging environment can’t be installed or because the symbols and sources cannot be accessed directly on the target computer. Both the Windows and Visual Studio debuggers support this form of remote debugging, though this architecture is more useful in the case of the Visual Studio debugger. (In fact, remote stubs are the only type of remote debugging that’s supported by Visual Studio.) This is because a full Visual Studio debugging environment installation on the target machine is heavy handed, both in terms of the disk space it requires and the time it takes to complete, often making it an inadequate option for production environments. By comparison, the Visual Studio remote-debugging component, which also includes the remote stub process, is more lightweight and can be installed much faster when you need it on the target machine. Figure 3-12 illustrates this second form of remote debugging.

Figure 3-12

Figure 3-12 Remote debugging using remote stubs.

Remote Debugging in WinDbg

As mentioned earlier, WinDbg supports both remote sessions and remote stubs. This section will walk you through the steps for making use of either setup in your remote WinDbg debugging experiments.

Remote Sessions

Starting a WinDbg remote debugging session is straightforward. You use the .server debugger command, as illustrated by the following procedure.

Using Remote Sessions in WinDbg
  1. From the local WinDbg instance controlling the target process, start a TCP/IP remote session by using the .server command, as illustrated in the following listing. This example uses port 4445, but any TCP port that’s not currently in use would also work. Using the .server command with no arguments reminds you of its syntax.

    0:000> vercommand
    command line: '"c:\Program Files\Debugging Tools for Windows (x86)\windbg.exe"
    notepad.exe'
    0:000> $ .server without any arguments displays the usage...
    0:000> .server
    Usage: .server tcp:port=<Socket>  OR  .server npipe:pipe=<PipeName>
    0:000> $ Open a remote debugging server session using port 4445
    0:000> .server tcp:port=4445
    Server started.  Client can connect with any of these command lines
    0: <debugger> -remote tcp:Port=4445,Server=TARIKS-LP03
  2. On the remote machine where you plan to control the target, start a new WinDbg instance (the remote debugger) and connect to the previous TCP port remotely by using the –remote command-line option. You can also use the connection string provided in the output from the .server command in the preceding listing to remind you of the syntax. Note that the remote and target machines can be the same, so you can also execute this step on the same machine as step 1.

    windbg.exe -remote tcp:Port=4445,Server=TARIKS-LP03

    Notice how any commands you type in the new WinDbg instance’s command window (the “remote” debugger) also appear in the first WinDbg instance on the target machine (the “local” debugger). The remote debugger is essentially acting only as a terminal for typing commands that get transferred and, ultimately, processed by the local debugger instance on the target machine.

  3. You can terminate the entire debugging session (both the remote and local debugger instances) from the remote machine using the qq command. By contrast, the q command terminates only the remote instance and leaves the local debugger instance intact.

    0:000> $ Remote debugger command prompt
    0:000> qq

In addition to their obvious benefits in remote debugging scenarios, WinDbg remote sessions can be useful even when debugging on the same machine. An example is when you start debugging using one of the command-line Windows debuggers (cdb.exe or ntsd.exe) and later decide to switch to using WinDbg as a front-end UI to that same session.

Remote Stubs

Commands typed in the remote debugger command window in a remote debugging session are executed as if they were typed on the target machine. In particular, this means that debugger extensions are also loaded into the debugger process on the target machine and that symbols need to be accessible from that machine too. Although remote sessions are by far the more common scenario in WinDbg remote debugging, remote stubs can be used if you don’t want to copy the symbols or otherwise make them available over the network so that they’re accessible from the target machine.

You can use remote stubs in both user-mode and kernel-mode remote debugging with the Windows debuggers. The dbgsrv.exe process, which comes as part of the Windows debuggers package, is used as a stub process in remote user-mode debugging. The kdsrv.exe process, also included in the Windows debuggers package, is used in remote kernel-mode debugging.

Just as in the remote session case, you can follow the procedure described here by also running the remote debugger on the target machine, in case you don’t have two separate machines.

Using Remote Stubs in WinDbg
  1. Open a TCP/IP communication port on the target machine using the dbgsrv.exe stub process. This command displays a dialog box when it fails, but it won’t show any messages on success.

    C:\Program Files\Debugging Tools for Windows (x86)\dbgsrv.exe -t tcp:port=4445

    The dbgsrv.exe stub process is running in the background at this point. Using the netstat.exe tool that comes with Windows under the system32 directory, you can display the open network ports on the machine and confirm that this stub process is listening for connections from a remote debugger on TCP port 4445:

    C:\windows\system32\netstat.exe -a
    Active Connections
      Proto  Local Address          Foreign Address        State
    ...
      TCP    0.0.0.0:4445           TARIKS-LP03:0          LISTENING
    ...
  2. On the remote machine, start a new windbg.exe instance and connect to the stub process on the target machine using the File\Connect to a Remote Stub menu action. Leave the Connection String combo box empty, and enter the target machine name in the new Browse dialog box. WinDbg then automatically enumerates all the open stub sessions on the target machine for you, as demonstrated in the following screen shot:

  3. After you connect to the remote stub, you can attach to processes running on the target machine by using the familiar File\Attach To A Process menu command (the F6 shortcut). However, this option now shows processes from the target machine, which is exactly what you want in this case. Once attached to a process, you can debug it as if it were running locally, with symbols and debugger extensions located relative to the same remote debugger machine (and not the target machine, as was the case in remote sessions).

  4. When you no longer need the remote debugging channel, make sure you terminate the stub process on the target machine so that you release the TCP port it opened earlier for other uses on the machine. You can do that in the Windows task manager UI or by using the kill.exe command-line utility from the Windows debuggers package.

    C:\Program Files\Debugging Tools for Windows (x86)>kill.exe dbgsrv
    process dbgsrv.exe (4188) - '' killed

This same approach can be used for remote kernel-mode debugging, except you should use the kdsrv.exe stub instead of the dbgsrv.exe stub. Note that in that case, there are actually three machines involved: the regular target and host kernel debugger machines, and the remote machine you are using to run the debugger instance. The kdsrv.exe process is started as a remote stub on the kernel host debugger machine, not the target machine that is being debugged with the kernel debugger. Symbols and extensions are again resolved relative to the remote debugger machine.

Remote Debugging in Visual Studio

Unfortunately, Visual Studio does not support the concept of remote debugging sessions or remote connection strings, which makes it difficult to use it for sharing a debugging session with another developer at the exact point of a particular failure in the way that WinDbg’s .server command works. Instead, Visual Studio remote debugging uses the remote stub idea, with the msvsmon.exe process acting as the remote stub process to which the Visual Studio debugger process (devenv.exe) then connects from the remote machine. MSVSMON is also sometimes called the Visual Studio debug monitor, highlighting the fact that it controls the execution of the target on the local machine on behalf of the Visual Studio debugger on the remote machine.

The default communication protocol between the remote and the target machines uses Distributed COM (DCOM) with Windows authentication, so you need to configure the user running the debugger with correct security permissions on the target machine. If the account you’re using is a domain user, for example, it will need to be an administrator on the target machine or a member of the Debugger Users security group created by Visual Studio.

Using Remote Debugging in Visual Studio

  1. Start the following C# sample program from the companion source code (and leave it waiting for user input) on the target machine.

    C:\book\code\chapter_03\HelloWorld>test.exe
    Hello World!
    Press any key to continue...
  2. Install the Visual Studio 2010 remote-debugging components on the target machine from http://www.microsoft.com/download/en/details.aspx?id=475. This setup installation shouldn’t take too long (at least compared to the full Visual Studio installation!) because the download is only a few megabytes large. Cancel the configuration wizard that comes up at the end of the installation.

  3. Start the msvsmon.exe stub process on the target machine. Use the Tools\Options menu action of MSVSMON to change the server name for the connection, or simply leave the default value unchanged, as shown in the following screen shot.

    C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\Remote Debugger\x86>msvsmon.exe
  4. On the remote machine, which has the full Visual Studio development environment, use the Tools\Attach To Process menu action to attach to the test.exe process remotely. In the Qualifier text box, specify the server name you chose in step 3.

  5. If your Windows firewall is enabled (the default behavior), a consent dialog box appears on the target machine. After authorizing the firewall exception, you’ll see a list of all the processes that are currently running on the target machine, and you can then attach to the managed test.exe process.

You might also see the warning shown in Figure 3-13 as Visual Studio tries to locate the symbols for the managed process. This is because managed-code symbols are resolved relative to the target machine.

Figure 3-13

Figure 3-13 Managed-code symbols path resolution.

Unlike native code symbols, which are resolved in Visual Studio remote debugging relative to the remote machine (as was the case in the WinDbg stub-based remote debugging case), managed-code symbols are resolved relative to the target machine because of the in-process nature of managed-code debugging. Keep this in mind when you use Visual Studio and you want to perform remote source-level debugging of .NET applications because you need to copy the symbols to the target machine for the debugger to locate them successfully.