Life Support for WinDbg - New Windows NT Support Tools
New NT Support Tools The NT Insider, Vol 5, Issue 5, Sep-Oct 1998 | Published: 15-Oct-98| Modified: 20-Aug-02
Recently Microsoft released its Windows NT Support Tools to help support engineers and driver writers troubleshoot problems with Windows NT. The tools, which can work with NT 3.51 and NT 4.0 are:
- A new set of WinDbg extensions to help examine and analyze a wider range of kernel data structures;
- A set of tools for adding memory allocation/deallocation, caller tracking, tail-checking, and statistics gathering to your system; and
- A heuristics based kernel memory crash dump analyzer
Describing all of these new tools would take a good size book (you should see the documentation for the tools), so our focus here will be on the new WinDbg extensions and using the new commands to your advantage. This is not to say that there isn’t other useful information in this new tool package from Microsoft, only that anything remotely related to improving the efficiency of debugging NT device drivers with WinDbg should understandably take priority. Be sure to check out the documentation and other facilitators covered at your convenience.
New WinDbg Extension Dll
The new WinDbg extension DLL, Kdex2x86.dll (or Kdex2alp.dll for Alpha), contains 11 new Bang “!” commands to enhance debugging. These commands are:
· Help
· Version
· Strct
· Apc
· Dpc
· Ethread
· Kthread
· Idt
· Kqueue
· List/Singlelist
· Smb
The commands help you dump a wealth of information that was previously unavailable. While these commands may not all solve your needs, the addition of the !strct and !list/singlelist commands alone are a big win. But before we get into a discussion of these new commands, let’s talk about how to get the new extension DLL for WinDbg loaded.
Getting Started
As with any extension DLL, you must first get WinDbg to load it. To do this, you must first establish a WinDbg debugging session and then type in one of the following 2 commands, which will cause the new DLL to be loaded:
· !kdex2x86. command [arguments]
When you issue this command WinDbg will load kdex2x86.dll, call the command specified, and pass into it any arguments that you specified. Once this DLL has been loaded you can run any other command by typing:
!command [arguments]
· !load kdex2x86.dll
This method also loads the extension DLL. As in the above description, once this DLL has been loaded, you can run any other command by typing:
!command [arguments]
The New Commands
Now that you’ve got the new extension DLL loaded, it’s time to explore the new commands. So let’s go.
Help
This command displays the standard help information for the new extension DLL. If you need to find out all the new commands and their options, this is the command for you. The standard output for this command is shown in Figure 1.
Help |
-Display this message |
Version |
-Display extension DLL version |
Apc [-?h] [expression] |
-Dump APC or all APCs |
Dpc [-?h] [expression] |
-Dump DPC or all DPCs |
Ethread [-?h] [expression] |
-Display ETHREAD structure |
Kthread [-?h] [expression] |
-Display KTHREAD structure |
Idt [-?h] [processornumber [ interruptnumber]] |
-Dump information about IDT and handlers |
Kqueue [-?h] [expression] |
-Display queue of worker threads |
[single]list [-?h] <expression> [count] [structname[.listnodemembername]] |
-Display chain of LIST_ENTRY or SINGLE_LIST_ENTRY structures |
Smb [-?hd] <expression> |
-Display SMB structure from header |
Strct [-?h] <structname> [fieldname] [expression] |
-Display member offset and structure data |
As you can see the commands contained within the new extension DLL are quite powerful and will return you a wealth of new information
Version
This command displays the version number of the new extension DLL. Here at OSR, we usually use this command to make sure that we’re using the checked kdextx86.dll with a checked version of the O/S.
Strct
Probably the most valuable command added to WinDbg to date is the !Strct command. This command displays structure member offsets and dumps structures for approximately 142 of the structures found in ntddk.h! When data is dumped it includes nested structures and named values for various flags contained in the structures. Figure 2 shows what we consider the most important data structures that are supported. For a more complete list consult the Windows NT Support Tools documentation.
CM_RESOURCE_LIST |
DEVICE_DESCRIPTION |
DEVICE_FLAGS |
DEVICE_OBJECT |
ERESOURCE |
FAST_IO_DISPATCH |
GUID |
IO_STACK_LOCATION |
IRP |
KAPC |
KEVENT |
KDPC |
MDL |
SECTION_OBJECT_POINTERS |
VPB |
WORK_QUEUE_ITEM |
CONFIGURATION_INFORMATION |
FILE_OBJECT |
DRIVER_OBJECT |
EXCEPTION_RECORD |
This command can either dump the field offsets of the fields within the structure or will dump the actual data of the named structure if an address is supplied. Imagine how much you’ll learn about the internals of NT by dumping some of these structures… For example, a dump of a File Object is shown in Figure 3. Note that from this list dump you can see everything you need to know about an open instance of a file. This information is invaluable for file system writers.
KDx86> !strct FILE_OBJECT 80854548
+0000 Structure FILE_OBJECT (Size:0x70) at 0x80854548:
+0000 Type = 0005
+0002 Size = 0070
+0004 DeviceObject = 808a4030
+0008 Vpb = 808a53e8
+000c FsContext = e11e2758
+0010 FsContext2 = e11e2898
+0014 SectionObjectPointer = 8086c6b0
+0018 PrivateCacheMap = 00000000
+001c FinalStatus(NTSTATUS) = 0(STATUS_SUCCESS)
+0020 RelatedFileObject = 80850868
+0024 LockOperation = 00
+0025 DeletePending = 00
+0026 ReadAccess = 01
+0027 WriteAccess = 00
+0028 DeleteAccess = 00
+0029 SharedRead = 01
+002a SharedWrite = 00
+002b SharedDelete = 01
+002c Flags = 00044042
+0030 FileName(UNICODE_STRING struct) = following
+0030 Length = 003a
+0032 MaximumLength = 003a
+0034 Buffer = e11fff88
+0038 CurrentByteOffset(LARGE_INTEGER/ULARGE_INTEGER union) = following
+0038 None(Anonymous struct) = following
+0038 LowPart = 00000000
+003c HighPart = 00000000
+0040 Waiters = 00000000
+0044 Busy = 00000000
+0048 LastLock = 00000000
+004c Lock(KEVENT struct) = following
+004c Header(DISPATCHER_HEADER struct) = following
+004c Type = 01
+004d Absolute = 00
+004e Size = 04
+004f Inserted = 00
+0050 SignalState = 00000000
+0054 WaitListHead(LIST_ENTRY struct) = following
+0054 Flink = 8085459c
+0058 Blink = 8085459c
+005c Event(KEVENT struct) = following
+005c Header(DISPATCHER_HEADER struct) = following
+005c Type = 00
+005d Absolute = 00
+005e Size = 04
+005f Inserted = 00
+0060 SignalState = 00000000
+0064 WaitListHead(LIST_ENTRY struct) = following
+0064 Flink = 808545ac
+0068 Blink = 808545ac
+006c CompletionContext = 00000000
Apc
This command dumps a Kernel Asynchronous Procedure Call (KAPC) structure at a given address or all KAPC structures in the system. While most driver writers will not deal with this structure, looking at the list of APCs in the system can be quite enlightening.
Dpc
This command dumps a Kernel Deferred Procedure Call (KDPC) structure at a given address or all KDPC structures in the system for a given processor or all processors. If your having some problem with getting to your DPC, having this command gives you the opportunity to look at your DPC object and if queued, where it is current resides in the DPC queue. Figure 4 shows a dump of a DPC. As you can see, this is a medium importance DPC. You could probably figure out who this DPC belongs to by issuing a !drivers command (!kdextx86.drivers)
KDx86> !dpc
+0000 Structure KDPC (Size:0x20) at 0x808b409c:
+0000 Type = 0013
+0002 Number = 00
+0003 Importance = 01
+0004 DpcListEntry(LIST_ENTRY struct) = following
+0004 Flink = 808c8600
+0008 Blink = 808c8600
+000c DeferredRoutine = 80016f9a
+0010 DeferredContext = 808b4028
+0014 SystemArgument1 = 00000000
+0018 SystemArgument2 = 00000000
+001c Lock = 808c8648
Ethread
This command dumps the fields for an undocumented ETHREAD (Executive Thread) structure at a given address. We got the address of the ETHREAD dump in Figure 5 out of the PETHREAD pointer in an IRP that was queued to a driver. As you can see there are lots of interesting fields to wonder about…
KDx86> !ETHREAD 8078a9c0
+0000 Structure ETHREAD (Size:0x240) at 0x8078a9c0:
+0000 Tcb(KTHREAD struct) = following
+0000 Header(DISPATCHER_HEADER struct) = following
+0000 Type = 06
+0001 Absolute = 00
+0002 Size = 6c
+0003 Inserted = 00
+0004 SignalState = 00000000
+0008 WaitListHead(LIST_ENTRY struct) = following
+0008 Flink = 8078a9c8
+000c Blink = 8078a9c8
+0010 MutantListHead(LIST_ENTRY struct) = following
+0010 Flink = 8078a9d0
+0014 Blink = 8078a9d0
+0018 InitialStack = f7150000
+001c StackLimit = f714b000
+0020 Teb = 7ffde000
+0024 TlsArray = 00000000
+0028 KernelStack = f714fbd4
+002c DebugActive = 00
+002d State = 02
+002e Alerted = 00 00
+0030 Iopl = 00
+0031 NpxState = 0a
+0032 Saturation = 00
+0033 Priority = 0e
+0034 ApcState(KAPC_STATE struct) = following
+0034 ApcListHead[2](LIST_ENTRY struct) = following
+0034 Flink = 8078a9f4
+0038 Blink = 8078a9f4
+003c Flink = 8078a9fc
+0040 Blink = 8078a9fc
+0044 Process = 8078a020
+0048 KernelApcInProgress = 00
+0049 KernelApcPending = 00
+004a UserApcPending = 00
+004c ContextSwitches = 0000011e
+0050 WaitStatus(NTSTATUS) = 0(STATUS_SUCCESS)
+0054 WaitIrql = 00
+0055 WaitMode = 00
+0056 WaitNext = 00
+0057 WaitReason = 00
+0058 WaitBlockList = 8078aa2c
+005c WaitListEntry(LIST_ENTRY struct) = following
+005c Flink = 801850d0
+0060 Blink = 8085d8fc
+0064 WaitTime = 00002e80
+0068 BasePriority = 08
+0069 DecrementCount = 07
+006a PriorityDecrement = 06
+006b Quantum = 17
+006c WaitBlock[4](KWAIT_BLOCK struct) = following
+006c WaitListEntry(LIST_ENTRY struct) = following
+006c Flink = 8078a84c
+0070 Blink = 8078a84c
+0074 Thread = 8078a9c0
+0078 Object = 8078a844
+007c NextWaitBlock = 8078aa2c
+0080 WaitKey = 0000
+0082 WaitType = 0001
+0084 WaitListEntry(LIST_ENTRY struct) = following
+0084 Flink = f714faa0
+0088 Blink = f714faa0
+008c Thread = 8078a9c0
+0090 Object = f714fa98
+0094 NextWaitBlock = 8078aa74
+0098 WaitKey = 0001
+009a WaitType = 0000
+009c WaitListEntry(LIST_ENTRY struct) = following
+009c Flink = 8078abb0
+00a0 Blink = 8078abb0
+00a4 Thread = 8078a9c0
+00a8 Object = 8078aba8
+00ac NextWaitBlock = 8078aa5c
+00b0 WaitKey = 0000
+00b2 WaitType = 0001
+00b4 WaitListEntry(LIST_ENTRY struct) = following
+00b4 Flink = 8078aab0
+00b8 Blink = 8078aab0
+00bc Thread = 8078a9c0
+00c0 Object = 8078aaa8
+00c4 NextWaitBlock = 8078aa74
+00c8 WaitKey = 0102
+00ca WaitType = 0001
+00cc LegoData = 00000000
+00d0 KernelApcDisable = 00000000
+00d4 UserAffinity = 00000003
+00d8 SystemAffinityActive = 00
+00d9 Pad = 00 00 00 10 4a 18 80
+00e0 Queue = 00000000
+00e4 ApcQueueLock = 00000000
+00e8 Timer(KTIMER struct) = following
+00e8 Header(DISPATCHER_HEADER struct) = following
+00e8 Type = 08
+00e9 Absolute = 00
+00ea Size = 0a
+00eb Inserted = 00
+00ec SignalState = 00000001
+00f0 WaitListHead(LIST_ENTRY struct) = following
+00f0 Flink = 8078aab0
+00f4 Blink = 8078aab0
+00f8 DueTime(LARGE_INTEGER/ULARGE_INTEGER union) = following
+00f8 None(Anonymous struct) = following
+00f8 LowPart = 6e910dc0
+00fc HighPart = 00000000
+0100 TimerListEntry(LIST_ENTRY struct) = following
+0100 Flink = 00000000
+0104 Blink = 00000000
+0108 Dpc = 00000000
+010c Period = 00000000
+0110 QueueListEntry(LIST_ENTRY struct) = following
+0110 Flink = 00000000
+0114 Blink = 00000000
+0118 Affinity = 00000003
+011c Preempted = 00
+011d ProcessReadyQueue = 00
+011e KernelStackResident = 01
+011f NextProcessor = 01
+0120 CallbackStack = 00000000
+0124 Win32Thread = e15675c8
+0128 TrapFrame = f714ff04
+012c ApcStatePointer = 8078a9f4 8078ab00
+0134 EnableStackSwap = 01
+0135 LargeStack = 01
+0136 ResourceIndex = 02
+0137 PreviousMode = 01
+0138 KernelTime = 00000017
+013c UserTime = 00000005
+0140 SavedApcState(KAPC_STATE struct) = following
+0140 ApcListHead[2](LIST_ENTRY struct) = following
+0140 Flink = 00000000
+0144 Blink = 00000000
+0148 Flink = 00000000
+014c Blink = 00000000
+0150 Process = 00000000
+0154 KernelApcInProgress = 00
+0155 KernelApcPending = 00
+0156 UserApcPending = 00
+0158 Alertable = 01
+0159 ApcStateIndex = 00
+015a ApcQueueable = 01
+015b AutoAlignment = 00
+015c StackBase = f7150000
+0160 SuspendApc(KAPC struct) = following
+0160 Type = 0012
+0162 Size = 0030
+0164 Spare0 = 00000000
+0168 Thread = 8078a9c0
+016c ApcListEntry(LIST_ENTRY struct) = following
+016c Flink = 8078a9f4
+0170 Blink = 8078a9f4
+0174 KernelRoutine = 8012c964
+0178 RundownRoutine = 00000000
+017c NormalRoutine = 8012cfe8
+0180 NormalContext = 00000000
+0184 SystemArgument1 = 00000000
+0188 SystemArgument2 = 00000000
+018c ApcStateIndex = 00
+018d ApcMode = 00
+018e Inserted = 00
+0190 SuspendSemaphore(KSEMAPHORE struct) = following
+0190 Header(DISPATCHER_HEADER struct) = following
+0190 Type = 05
+0191 Absolute = 00
+0192 Size = 05
+0193 Inserted = 00
+0194 SignalState = 00000000
+0198 WaitListHead(LIST_ENTRY struct) = following
+0198 Flink = 8078ab58
+019c Blink = 8078ab58
+01a0 Limit = 00000002
+01a4 ThreadListEntry(LIST_ENTRY struct) = following
+01a4 Flink = 8078a070
+01a8 Blink = 8078a070
+01ac FreezeCount = 00
+01ad SuspendCount = 00
+01ae IdealProcessor = 01
+01af DisableBoost = 00
+01b0 CreateTime(LARGE_INTEGER/ULARGE_INTEGER union) = following
+01b0 None(Anonymous struct) = following
+01b0 LowPart = 2b28a268
+01b4 HighPart = 01bdebdd
+01b8 ExitTime(LARGE_INTEGER/ULARGE_INTEGER union) = following
+01b8 None(Anonymous struct) = following
+01b8 LowPart = 8078ab78
+01bc HighPart = 8078ab78
+01b8 LpcReplyChain(LIST_ENTRY struct) = following
+01b8 Flink = 8078ab78
+01bc Blink = 8078ab78
+01c0 ExitStatus(NTSTATUS) = 0(STATUS_SUCCESS)
+01c0 OfsChain = 00000000
+01c4 PostBlockList(LIST_ENTRY struct) = following
+01c4 Flink = 8078ab84
+01c8 Blink = 8078ab84
+01cc TerminationPortList(LIST_ENTRY struct) = following
+01cc Flink = e150e388
+01d0 Blink = e150e388
+01d4 ActiveTimerListLock = 00000000
+01d8 ActiveTimerListHead(LIST_ENTRY struct) = following
+01d8 Flink = 8078ab98
+01dc Blink = 8078ab98
+01e0 Cid(CLIENT_ID struct) = following
+01e0 UniqueProcess = 00000075
+01e4 UniqueThread = 0000006f
+01e8 LpcReplySemaphore(KSEMAPHORE struct) = following
+01e8 Header(DISPATCHER_HEADER struct) = following
+01e8 Type = 05
+01e9 Absolute = 00
+01ea Size = 05
+01eb Inserted = 00
+01ec SignalState = 00000000
+01f0 WaitListHead(LIST_ENTRY struct) = following
+01f0 Flink = 8078abb0
+01f4 Blink = 8078abb0
+01f8 Limit = 00000001
+01fc LpcReplyMessage = 00000000
+0200 LpcReplyMessageId = 00000000
+0204 PerformanceCountLow = 00000000
+0208 ImpersonationInfo = 00000000
+020c IrpList(LIST_ENTRY struct) = following
+020c Flink = 80868858
+0210 Blink = 80868858
+0214 TopLevelIrp = 00000000
+0218 DeviceToVerify = 00000000
+021c ReadClusterSize = 00000007
+0220 ForwardClusterOnly = 00
+0221 DisablePageFaultClustering = 00
+0222 DeadThread = 00
+0223 HasTerminated = 00
+0224 EventPair = 00000000
+0228 GrantedAccess(ACCESS_MASK) = 001f03ff( STANDARD_RIGHTS_ALL )
+022c ThreadsProcess = 8078a020
+0230 StartAddress = 77f052cc
+0234 Win32StartAddress = 00410240
+0234 LpcReceivedMessageId = 00410240
+0238 LpcExitThreadCalled = 00
+0239 HardErrorsAreDisabled = 00
+023a LpcReceivedMsgIdValid = 00
+023b ActiveImpersonationInfo = 00
+023c PerformanceCountHigh = 00000000
Kthread
This command dumps the fields for an undocumented KTHREAD (Kernel Thread) structure at a given address.
Idt
This command dumps the Interrupt Descriptor Table (IDT) for a given processor, including the handler for each interrupt. Figure 6 shows a partial dump of the IDT for processor #0 on a development machine here in the OSR Lab.
KDx86> !idt 0
00: 801753fc (_KiTrap00)
01: 80175580 (_KiTrap01)
02: 000012de
03: 80175890 (_KiTrap03)
04: 80175a08 (_KiTrap04)
05: 80175b68 (_KiTrap05)
06: 80175ce0 (_KiTrap06)
07: 80176250 (_KiTrap07)
08: 00001338
09: 801765d0 (_KiTrap09)
0a: 801766f4 (_KiTrap0A)
0b: 80176838 (_KiTrap0B)
0c: 80176bac (_KiTrap0C)
0d: 80176dd0 (_KiTrap0D)
0e: 80177838 (_KiTrap0E)
0f: 80177bcc (_KiTrap0F)
10: 80177cf0 (_KiTrap10)
11: 80177e28 (_KiTrap11)
12: 80177bcc (_KiTrap0F)
13: 80177bcc (_KiTrap0F)
14: 80177bcc (_KiTrap0F)
15: 80177bcc (_KiTrap0F)
16: 80177bcc (_KiTrap0F)
17: 80177bcc (_KiTrap0F)
18: 80177bcc (_KiTrap0F)
19: 80177bcc (_KiTrap0F)
1a: 80177bcc (_KiTrap0F)
1b: 80177bcc (_KiTrap0F)
1c: 80177bcc (_KiTrap0F)
1d: 80177bcc (_KiTrap0F)
1e: 80177bcc (_KiTrap0F)
1f: 80003618
20: 00000000
21: 00000000
22: 00000000
23: 00000000
24: 00000000
25: 00000000
26: 00000000
27: 00000000
28: 00000000
29: 00000000
2a: 80174716 (_KiGetTickCount)
2b: 80174820 (_KiCallbackReturn)
2c: 80174930 (_KiSetLowWaitHighThread)
2d: 80175768 (_KiDebugService)
2e: 80174130 (_KiSystemService)
2f: 80177bcc (_KiTrap0F)
30: 80173820 (_KiUnexpectedInterrupt0)
31: 8017382a (_KiUnexpectedInterrupt1)
32: 80173834 (_KiUnexpectedInterrupt2)
33: 8017383e (_KiUnexpectedInterrupt3)
34: 80173848 (_KiUnexpectedInterrupt4)
35: 80173852 (_KiUnexpectedInterrupt5)
36: 8017385c (_KiUnexpectedInterrupt6)
37: 80002e6c
38: 80173870 (_KiUnexpectedInterrupt8)
Kqueue
This command dumps the fields of the undocumented KQUEUE structure. I went looking around in nttdk.h to see if I could find a KQUEUE structure to dump and came up empty. I suspect that this is used for Lookaside lists, but I am not positive. Anyway, now that I can dump one, I’ll just have to find one.
List/SingleList
These commands understand the NT Kernel LIST_ENTRY and SINGLE_LIST_ENTRY constructs and will allow you to dump chains of structures starting from a given node forward, for singly linked lists, and/or backward, in the case of doubly linked lists.
If you use Own queuing (driver queuing) instead of system queuing, this is a great command to allow you to dump the contents of your queue(s).
Smb
The Smb command dumps NT/Lanman-style network communications blocks starting at an SMB header and chaining through all the commands associated with it. There is no support for starting at an arbitrary point in the command chain or dumping out a particular command structure at an arbitrary place in memory. If you’re doing network work, I’m sure you’ll have a use for this command.
Xpool
The Xpool command dumps information about managed pool blocks in the system when used in conjunction with the Pool Enhancement facility included in this package. This command only works if you have installed the pool enhancements and the pool block management is enabled for the module(s) that you are analyzing with the command. If you’re interested in finding out more about what this command can do for you, definitely pour over the documentation.
Where to get Support Tools (V1.0)
The Microsoft Windows NT Support Tools can be obtained from the Microsoft web site at URL ftp://ftp.microsoft.com/bussys/winnt/winnt-public/tools/OEMSupportTools/. You’ll need to read the documentation about how to install and use the included tools, but you’ll find it is time well spent.
Summary
As we’ve attempted to outline above, there are a multitude of new and powerful commands contained within kdex3x86.dll in the new Microsoft Windows NT Support Tool package. Using WinDbg can be “entertaining” to say the least, so this new extension is a welcome addition to any NT driver writer’s repertoire. Bang away!
Related Articles
Enabling Debugging on the Local Machine for Windows XP®
You're Testing Me - Testing WDM/Win2K Drivers
Analyze This - Analyzing a Crash Dump
More on Kernel Debugging - KMODE_EXCEPTION_NOT_HANDLED
Making WinDbg Your Friend - Creating Debugger Extensions
Life After Death? - Understanding Blue Screens
Special Win2K PnP Tracing and Checks
All About Lint - PC Lint and Windows Drivers
Bagging Bugs — Avoidance and Detection Tips to Consider
Choose Your Weapon: Kernel Mode Debuggers - a Choice at Last
User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment
"Great Article!"
WinDbg is great! But with the information from OSR online it's twice as good. When I find the problem in a DPC I am experiencing, I'm looking forward to the next crash! Thanks!!!
Rating:
06-Dec-05, Pascal Damman
|