WinDbg support !analyze
command for analyzing crash dump . This command analyzes exception information in the crash dump, determines the place where the exception occurred, the call stack, and displays detailed report. Whenever something has gone terribly wrong and that the system has been stopped either because OS itself is baffled, or continuing may lead to data loss or corruption, The screen is thrown with a call to one of two functions, KeBugCheck or KeBugCheckEx, both of which are exported for use by device drivers and file systems.
VOID KeBugCheck( IN ULONG BugCheckCode ); VOID KeBugCheckEx( IN ULONG BugCheckCode, IN ULONG BugCheckParameter1, IN ULONG BugCheckParameter2, IN ULONG BugCheckParameter3, IN ULONG BugCheckParameter4 );
Both bug check calls take a BugCheckCode parameter. KeBugCheckEx
takes 4 additional parameters that are simply printed on the Blue Screen along with the stop code. KeBugCheckEx
calls any Bug Check handlers that drivers may have registered. A handler is registered by calling KeRegisterBugCheckCallback(…). KeBugCheckEx
tries to connect with a debugger if the system has kernel debugging enabled.
Dumping the Stack
WinDbg’s “k” command displays the stack of the thread that was being executed on the current processor. You can modify WinDbg’s concept of the current processor by using the “~n” command where “n” is the zero-based index of the processor whose context you wish to examine. If the system that crashed utilizes multiple processors, you can usually find the thread that called KeBugCheckEx() by starting with processor zero and dumping the stack of each processor’s current thread.
// Partial crash dump kd> !analyze -v ********************************************************************************* * * * Bugcheck Analysis * * * * * ********************************************************************************* IRQL_NOT_LESS_OR_EQUAL (a) An attempt was made to access a pagable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses. If a kernel debugger is available get the stack backtrace. Arguments: Arg1: 8b000054, memory referenced Arg2: 00000002, IRQL Arg3: 00000001, value 0 = read operation, 1 = write operation Arg4: 804ec962, address which referenced memory kd> kv ChildEBP RetAddr Args to Child 80541d28 804dce53 0000000a 8b000054 00000002 nt!KeBugCheckEx+0x19 (FPO: [Non-Fpo]) 80541d28 804ec962 0000000a 8b000054 00000002 nt!KiTrap0E+0x2ad (FPO: [0,0] TrapFrame @ 80541d44) 80541db4 804ec78a f65d6e80 f65d6e88 00000100 nt!KiUnlinkThread (FPO: [0,0,1]) // Analyzing this API 80541dc8 804ecf28 00000000 80541de4 f65d6ec0 nt!KiUnwaitThread+0x10 (FPO: [Non-Fpo]) 80541df4 804ec1dc 471470a0 0000000b 80541f1c nt!KiWaitTest+0xac (FPO: [Non-Fpo]) 80541f00 804ec448 8054a900 8054a6a0 ffdff000 nt!KiTimerListExpire+0x78 (FPO: [Non-Fpo]) 80541f2c 804f1e02 8054ace0 00000000 0000d60a nt!KiTimerExpiration+0xb0 (FPO: [Non-Fpo]) 80541f48 804f1d67 00000000 804f1d7b 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0]) 80541f50 804f1d7b 00000000 0000000e 00000000 nt!KiIdleLoop+0x12 (FPO: [0,0,0])
Dumping function argument
Making wild guess that the first parameter to KiUnlinkThread
was the dispatcher object (timer) which would start with a DISPATCHER_HEADER, and the address of the first three argument can be found using ‘kv
‘ , variable dump is given below
kd> dt nt!_DISPATCHER_HEADER f65d6e80 // Adress of first argument to KiUnlinkThread +0x000 Type : 0x8 '' +0x001 Absolute : 0 '' +0x002 Size : 0xa '' +0x003 Inserted : 0 '' +0x004 SignalState : 1 +0x008 WaitListHead : _LIST_ENTRY [ 0xf65f6e88 - 0xf65d6e88 ] // Address of the list kd> dt nt!_KWAIT_BLOCK f65f6e88 // Adress of second argument to KiUnlinkThread +0x000 WaitListEntry : _LIST_ENTRY [ 0x8bf66107 - 0x66e853d8 ] +0x008 Thread : 0x8b000000 +0x00c Object : 0x74f685f0 +0x010 NextWaitBlock : 0x0c558b46 +0x014 WaitKey : 0x9689 +0x016 WaitType : 0xc8
Finding nearest symbol
We can use ln to find what driver does timer object f65d6e80 belong to
kd> ln f65d6e80 (f65d6e80) rdbss!s_RxTimer | (f65d7858) rdbss!s_RxRecurrentWorkItemsList Exact matches: rdbss!s_RxTimer
Finding crash context
Crash dump also tells about the place where the exception occurred. It’s time to get the values of function parameters and local variables. Before we start, let’s pay attention to .ecxr and kb
. ‘kb’ command (it displays the call stack). .ecxr
command asks the debugger to switch the current context to the one stored in the crash dump’s exception information. After we have executed .ecxr, and only after that, we can reliably get access to the call stack and the values of local variables at the moment when the exception was raised.
Dumping the variables in Call stack
We can use ‘dv’ command to display the values of function parameters and local variables. Since we usually want to see this information for every function on the call stack, we should actually use ‘!for_each_frame dv /t
‘ command (/t option asks ‘dv’ to show type information). And here is the sample output of ‘!for_each_frame dv /t’ command:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 00 006afe88 0043096e CrashDemo!TestFunc+0x2e [c:\tests\crashdemo\crashdemo.cpp @ 124] int * pParam = 0x00000000 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 01 006aff6c 00430f31 CrashDemo!WorkerThread+0x5e [c:\tests\crashdemo\crashdemo.cpp @ 115] void * lpParam = 0x00000000 int * TempPtr = 0x00000000 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 02 006affa8 00430ea2 CrashDemo!_callthreadstartex+0x51 [f:\rtm\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348] struct _tiddata * ptd = 0x00355188
Determine the address of a symbol
Sometimes we might need to determine the address of a symbol (function or variable). If we know the exact name of the symbol, we can enter it into Disassembly window of Visual Studio debugger and get the address. But what if we don’t remember the exact name? Or want to find the addresses of a set of symbols with the same pattern in the name (for example, all member functions of a class)? We can use ‘x’ command to list all symbols whose names match the specified mask. The following command tries to locate the address of UnhandledExceptionFilter function, located in kernel32.dll:
0:000> x kernel32!UnhandledExceptionFilter;q 7c862b8a kernel32!UnhandledExceptionFilter = <no type information>
Dumping the structure
With the help of ‘dt
‘ command, we can display the exact layout of a data structure or a class. If we simply want to know the layout of a data type, we can use this command as follows:
// (-b option enables recursive display of embedded data structures for members whose type is also a structure or a class). dt -b TypeName
If you want to display the layout of a particular variable, you can pass its address to ‘dt’ command. For example
// Syntax dt -b TypeName Address // Example dt -b CSymbolInfoPackage 0x0012f6d0 0:000> dt /b CSymbolInfoPackage 0x0012f6d0;q +0x000 si : _SYMBOL_INFO +0x000 structObj : 0x58 +0x004 TypeIndex : 2 // Find out whats inside structObj.Appended "." at the end. dt -b CSymbolInfoPackage 0x0012f6d0 structObj.