Over a million developers have joined DZone.

Obscure WinDbg Commands, Part 1

DZone 's Guide to

Obscure WinDbg Commands, Part 1

· ·
Free Resource

I’m starting a short post series today covering some obscure WinDbg commands. Some of these are fairly useful, others are mostly good for extracting “wows” from your coworkers. Still, there’s a lot of ground to cover.

Today’s two commands deal with inspecting thread call stacks. Real-world processes might have hundreds of threads in them, and figuring out who’s who can be pretty time-consuming. This is especially true if you are using the thread pool (.NET, Win32, or even your own), which often means you have dozens of thread pool threads waiting idly for work.

The !uniqstack command can help in these situations. It enumerates all the thread call stacks and eliminates duplicates, so that you can understand at a glance what these hundreds of threads are doing.

0:021> !uniqstack 
Processing 22 threads, please wait 

.  0  Id: 464.ca8 Suspend: 1 Teb: 7f9dd000 Unfrozen 
  Start: SillyThreadPool!ILT+120(_wmainCRTStartup) (00ed107d) 
  Priority: 0  Priority class: 32  Affinity: ff 
ChildEBP RetAddr 
00a6f510 76e0cfb2 ntdll!NtReadFile+0xc 
00a6f578 5285d9de KERNELBASE!ReadFile+0x10e 
00a6f62c 5285d19c MSVCR110D!_read_nolock+0x7be 
00a6f684 527a4246 MSVCR110D!_read+0x24c 
00a6f6b4 527a26b3 MSVCR110D!_filbuf+0x126 
00a6f714 527a2708 MSVCR110D!getc+0x223 
00a6f720 527a2718 MSVCR110D!_fgetchar+0x18 
00a6f728 00ed14ca MSVCR110D!getchar+0x8 
00a6f808 00ed1a49 SillyThreadPool!wmain+0x7a 
00a6f858 00ed1c3d SillyThreadPool!__tmainCRTStartup+0x199 
00a6f860 7755850d SillyThreadPool!wmainCRTStartup+0xd 
00a6f86c 77d1bf39 KERNEL32!BaseThreadInitThunk+0xe 
00a6f8b0 77d1bf0c ntdll!__RtlUserThreadStart+0x72 
00a6f8c8 00000000 ntdll!_RtlUserThreadStart+0x1b

.  1  Id: 464.13d4 Suspend: 1 Teb: 7f9da000 Unfrozen 
  Start: SillyThreadPool!ILT+265(?MyThreadPoolWorkerYGKPAXZ) (00ed110e) 
  Priority: 0  Priority class: 32  Affinity: ff 
ChildEBP RetAddr 
00d3f7b8 76e01129 ntdll!NtWaitForSingleObject+0xc 
00d3f824 76e010b4 KERNELBASE!WaitForSingleObjectEx+0x8f 
00d3f838 00ed141e KERNELBASE!WaitForSingleObject+0x12 
00d3f914 7755850d SillyThreadPool!MyThreadPoolWorker+0x2e 
00d3f920 77d1bf39 KERNEL32!BaseThreadInitThunk+0xe 
00d3f964 77d1bf0c ntdll!__RtlUserThreadStart+0x72 
00d3f97c 00000000 ntdll!_RtlUserThreadStart+0x1b

. 21  Id: 464.1574 Suspend: 1 Teb: 7f879000 Unfrozen 
  Start: ntdll!DbgUiRemoteBreakin (77d5dbeb) 
  Priority: 0  Priority class: 32  Affinity: ff 
ChildEBP RetAddr 
0279faa4 77d5dc24 ntdll!DbgBreakPoint 
0279fad4 7755850d ntdll!DbgUiRemoteBreakin+0x39 
0279fae0 77d1bf39 KERNEL32!BaseThreadInitThunk+0xe 
0279fb24 77d1bf0c ntdll!__RtlUserThreadStart+0x72 
0279fb3c 00000000 ntdll!_RtlUserThreadStart+0x1b

Total threads: 22 
Duplicate callstacks: 19 (windbg thread #s follow): 
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

In other cases, you’re specifically looking for one particular thread among many. Of course debugger automation can come handy here – you can dump the call stacks for all the threads (e.g. using the ~* e kncommand), and then parse the output. But there is an easier way: the !findstack command. This command searches thread call stacks for a specific symbol and displays matching threads.

0:021> !findstack kernelbase!WaitForSingleObject 
Thread 001, 2 frame(s) match 
  * 01 00d3f824 76e010b4 KERNELBASE!WaitForSingleObjectEx+0x8f 
  * 02 00d3f838 00ed141e KERNELBASE!WaitForSingleObject+0x12 

... snipped ... 

Thread 020, 2 frame(s) match 
  * 01 0265fac4 76e010b4 KERNELBASE!WaitForSingleObjectEx+0x8f 
  * 02 0265fad8 00ed141e KERNELBASE!WaitForSingleObject+0x12

I am posting short links and updates on Twitter as well as on this blog. You can follow me: @goldshtn


Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}