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.