If you look back at the Open Executable dialog box in Figure 8-2, you'll notice that the very bottom of the dialog box has a check box titled Debug Child Processes Also. By checking it, you're telling WinDBG that you also want to debug any processes started by debuggees. When running Microsoft Windows XP or Microsoft Windows Server 2003, if you forget to check that box when opening a process you can use the .childdbg (Debug Child Processes) command to change the option on the fly. By itself, .childdbg will tell you the current state. Issuing a .childdbg l command will turn on debugging child processes. Issue .childdbg 0 to turn it off.
To show you some of the multiple process and thread options, in the next section I'll provide some of the output resulting from debugging the command prompt, CMD.EXE, and choosing to debug child processes as well. After I get CMD.EXE loaded up and executing, I'll start NOTEPAD.EXE. If you follow the same steps and have child debugging enabled, as soon as you start NOTEPAD.EXE, WinDBG will stop at the loader breakpoint for NOTEPAD.EXE. It makes sense that WinDBG stopped NOTEPAD.EXE, but that also stops CMD.EXE because both processes are now sharing the debugger loop.
To see in the UI the processes that are currently running, choose Processes And Threads from the View menu. You'll see a layout similar to that in Figure 8-3. In the Processes And Threads window, the processes are all the root nodes, with each processes' threads as their children. The numbers next to CMD.EXE, 000:9AC, are the WinDBG process number followed by the Win32 process ID. In CMD.EXE, the thread 000:9B0 indicates the WinDBG thread ID and the Win32 thread ID. The WinDBG process and thread numbers are unique the entire time WinDBG is running. That means there can never be another process number 1 until I restart WinDBG. The WinDBG process and thread numbers are important because they are used to set per-process and per-thread breakpoints and can be used as modifiers to various commands.
As with anything in WinDBG, if WinDBG displays it in a window, there's a Command window command to get at the same information. To view the processes being debugged, the | (Process Status) command does the trick. The output for the two processes shown in Figure 8-3 is as follows:
0 id: 9ac create
. 1 id: 3d0 child name: cmd.exe name: notepad.exe
The dot in the far left column indicates the active process, meaning that any commands you execute will be working on that process. The other interesting field is the one that tells how the process came to run under the debugger. "Create" means WinDBG created the process, and "child" indicates a process that was spawned by a parent process.
The overloaded s command—|s for Set Current Process and ~s for Set Current Thread— does the work to change which process is active. You can also use the Processes And Threads window and double-click on the process you'd like to make active. The bold font indicates the active process. When using the s command, you need to specify the process as a prefix to the command. For example, to switch from the second process to the first, you'd issue |0s. To quickly see which process is active, look at the numbers to the left of the Command window input line. As you swap between the processes, you'll see the numbers update. When I switched to the first process using the CMD.EXE and NOTEPAD.EXE examples and issued the | command again, the output looked a little different:
create name: cmd.exe child name: notepad.exe
The difference is the octothorpe in front of the NOTEPAD.EXE process. The octothorpe indicates the process that caused the exception to stop in WinDBG. Since NOTEPAD.EXE is sitting at its loader breakpoint, the exception was a breakpoint.
Viewing threads is almost identical to viewing processes. I'm going to let NOTEPAD.EXE start, so I'll press F5 in WinDBG. When NOTEPAD.EXE appears, I'll open the File Open dialog box because it creates a bunch of threads, and in WinDBG I'll press Ctrl+Break to break into the debugger. If you do the same and have the Processes And Threads window open, you should see that NOTEPAD.EXE has four threads in it and CMD.EXE has two threads.
The ~ (Thread Status) command shows the active threads in the current process. Switching to the NOTEPAD.EXE process and issuing the ~ command creates the following output on Windows XP (SP1):
3d0.39c Suspend: 1 Teb: 7ffde000 Unfrozen
3d0.1a4 Suspend: 1 Teb: 7ffdd000 Unfrozen
3d0.8f0 Suspend: 1 Teb: 7ffdc000 Unfrozen
3d0.950 Suspend: 1 Teb: 7ffdb000 Unfrozen
As with the | command, the ~ command uses a dot to indicate the current thread and an octothorpe to signify the thread that either caused the exception or was active when the debugger attached. The WinDBG thread number is the next displayed item. As with process numbers, there will only ever be one thread number 2 for the life of the WinDBG instance. Next come the ID values, which are the Win32 process ID followed by the thread ID. The suspend count is a little confusing. A suspend count of 1 indicates the thread is NOT suspended. The help on ~ shows a suspend count of 0, which I've never seen in the output. After the suspend count is the linear address of the Thread Environment Block (TEB) for the thread. The TEB is the same thing as the Thread Information Block (TIB) I
discussed in Chapter 7 and is the address of the per-thread data block that contains the thread instance information such as the stack and COM initialization. Finally, Unfrozen indicates whether you've used the ~F (Freeze Thread) command to freeze a thread. (Freezing a thread from the debugger is akin to calling SuspendThread on that thread from your program. You'll stop that thread from executing until it is unfrozen.)
A command will work on the current thread by default, but sometimes you'll want to see information about a different thread. For example, to see the registers of a different thread, you use the thread modifier in front of the r (Registers) command: ~2r. If you have multiple processes open, you can also apply the process modifier to the commands. The command |0~0r shows the registers for the first process and first thread no matter which process and thread are active.
Was this article helpful?