575 lines
15 KiB
C
575 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
dmpstate.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the architecture specific routine that dumps
|
||
the machine state when a bug check occurs and no debugger is hooked
|
||
to the system. It is assumed that it is called from bug check.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 17-Jan-1992
|
||
|
||
Environment:
|
||
|
||
Kernel mode.
|
||
|
||
Revision History:
|
||
|
||
Joe Notarangelo 04-Feb-1992 Alpha-adaptation
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
|
||
//
|
||
// Define forward referenced prototypes.
|
||
//
|
||
|
||
PRUNTIME_FUNCTION
|
||
KiLookupFunctionEntry (
|
||
IN ULONG ControlPc
|
||
);
|
||
|
||
PVOID
|
||
KiPcToFileHeader(
|
||
IN PVOID PcValue,
|
||
OUT PVOID *BaseOfImage,
|
||
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
|
||
);
|
||
|
||
VOID
|
||
KiMachineCheck (
|
||
IN PEXCEPTION_RECORD ExceptionRecord,
|
||
IN PKEXCEPTION_FRAME ExceptionFrame,
|
||
IN PKTRAP_FRAME TrapFrame
|
||
);
|
||
|
||
//
|
||
// Define external data.
|
||
//
|
||
|
||
extern LIST_ENTRY PsLoadedModuleList;
|
||
|
||
VOID
|
||
KeDumpMachineState (
|
||
IN PKPROCESSOR_STATE ProcessorState,
|
||
IN PCHAR Buffer,
|
||
IN PULONG BugCheckParameters,
|
||
IN ULONG NumberOfParameters,
|
||
IN PKE_BUGCHECK_UNICODE_TO_ANSI UnicodeToAnsiRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function formats and displays the machine state at the time of the
|
||
to bug check.
|
||
|
||
Arguments:
|
||
|
||
ProcessorState - Supplies a pointer to a processor state record.
|
||
|
||
Buffer - Supplies a pointer to a buffer to be used to output machine
|
||
state information.
|
||
|
||
BugCheckParameters - Supplies additional bugcheck information
|
||
|
||
NumberOfParameters - sizeof BugCheckParameters array
|
||
|
||
UnicodeToAnsiRoutine - Supplies a pointer to a routine to convert Unicode strings
|
||
to Ansi strings without touching paged translation tables.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PCONTEXT ContextRecord;
|
||
ULONG ControlPc;
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
UNICODE_STRING DllName;
|
||
FRAME_POINTERS EstablisherFrame;
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
PVOID ImageBase;
|
||
ULONG Index;
|
||
BOOLEAN InFunction;
|
||
ULONG LastStack;
|
||
ULONG NextPc;
|
||
ULONG StackLimit;
|
||
UCHAR AnsiBuffer[ 32 ];
|
||
|
||
//
|
||
// Virtually unwind to the caller of bug check.
|
||
//
|
||
|
||
ContextRecord = &ProcessorState->ContextFrame;
|
||
LastStack = (ULONG)ContextRecord->IntSp;
|
||
ControlPc = (ULONG)ContextRecord->IntRa - 4;
|
||
NextPc = ControlPc;
|
||
FunctionEntry = KiLookupFunctionEntry(ControlPc);
|
||
if (FunctionEntry != NULL) {
|
||
NextPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
ContextRecord,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
}
|
||
|
||
//
|
||
// At this point the context record contains the machine state at the
|
||
// call to bug check.
|
||
//
|
||
// Put out the system version and the title line with the PSR and FSR.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"\nMicrosoft Windows NT [0x%08x]\n", NtBuildNumber);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"Machine State at Call to Bug Check PC : %08lX PSR : %08lX\n\n",
|
||
ContextRecord->IntRa,
|
||
ContextRecord->Psr);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
#ifdef DUMP_INTEGER_STATE
|
||
|
||
//
|
||
// Format and output the integer registers.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"V0 : 0x%016Lx T0 : 0x%016Lx T1 : 0x%016Lx\n",
|
||
ContextRecord->IntV0,
|
||
ContextRecord->IntT0,
|
||
ContextRecord->IntT1);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T2 : 0x%016Lx T3 : 0x%016Lx T4 : 0x%016Lx\n",
|
||
ContextRecord->IntT2,
|
||
ContextRecord->IntT3,
|
||
ContextRecord->IntT4);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T5 : 0x%016Lx T6 : 0x%016Lx T7 : 0x%016Lx\n",
|
||
ContextRecord->IntT5,
|
||
ContextRecord->IntT6,
|
||
ContextRecord->IntT7);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"S0 : 0x%016Lx S1 : 0x%016Lx S2 : 0x%016Lx\n",
|
||
ContextRecord->IntS0,
|
||
ContextRecord->IntS1,
|
||
ContextRecord->IntS2);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"S3 : 0x%016Lx S4 : 0x%016Lx S5 : 0x%016Lx\n",
|
||
ContextRecord->IntS3,
|
||
ContextRecord->IntS4,
|
||
ContextRecord->IntS5);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"Fp : 0x%016Lx A0 : 0x%016Lx A1 : 0x%016Lx\n",
|
||
ContextRecord->IntFp,
|
||
ContextRecord->IntA0,
|
||
ContextRecord->IntA1);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"A2 : 0x%016Lx A3 : 0x%016Lx A4 : 0x%016Lx\n",
|
||
ContextRecord->IntA2,
|
||
ContextRecord->IntA3,
|
||
ContextRecord->IntA4);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"A5 : 0x%016Lx T8 : 0x%016Lx T9 : 0x%016Lx\n",
|
||
ContextRecord->IntA5,
|
||
ContextRecord->IntT8,
|
||
ContextRecord->IntT9);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"T10: 0x%016Lx T11: 0x%016Lx T12: 0x%016Lx\n",
|
||
ContextRecord->IntT10,
|
||
ContextRecord->IntT11,
|
||
ContextRecord->IntT12);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"At : 0x%016Lx Gp : 0x%016Lx Sp : 0x%016Lx\n",
|
||
ContextRecord->IntAt,
|
||
ContextRecord->IntGp,
|
||
ContextRecord->IntSp);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
#endif //DUMP_INTEGER_STATE
|
||
|
||
#ifdef DUMP_FLOATING_STATE
|
||
|
||
//
|
||
// Format and output the floating registers.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"F0 : 0x%016Lx F1 : 0x%016Lx F2 : 0x%016Lx\n",
|
||
ContextRecord->FltF0,
|
||
ContextRecord->FltF1,
|
||
ContextRecord->FltF2);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F3 : 0x%016Lx F4 : 0x%016Lx F5 : 0x%016Lx\n",
|
||
ContextRecord->FltF3,
|
||
ContextRecord->FltF4,
|
||
ContextRecord->FltF5);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F6 : 0x%016Lx F7 : 0x%016Lx F8 : 0x%016Lx\n",
|
||
ContextRecord->FltF6,
|
||
ContextRecord->FltF7,
|
||
ContextRecord->FltF8);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F9 : 0x%016Lx F10: 0x%016Lx F11: 0x%016Lx\n",
|
||
ContextRecord->FltF9,
|
||
ContextRecord->FltF10,
|
||
ContextRecord->FltF11);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F12: 0x%016Lx F13: 0x%016Lx F14: 0x%016Lx\n",
|
||
ContextRecord->FltF12,
|
||
ContextRecord->FltF13,
|
||
ContextRecord->FltF14);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F15: 0x%016Lx F16: 0x%016Lx F17: 0x%016Lx\n",
|
||
ContextRecord->FltF15,
|
||
ContextRecord->FltF16,
|
||
ContextRecord->FltF17);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F18: 0x%016Lx F19: 0x%016Lx F20: 0x%016Lx\n",
|
||
ContextRecord->FltF18,
|
||
ContextRecord->FltF19,
|
||
ContextRecord->FltF20);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F21: 0x%016Lx F22: 0x%016Lx F23: 0x%016Lx\n",
|
||
ContextRecord->FltF21,
|
||
ContextRecord->FltF22,
|
||
ContextRecord->FltF23);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F24: 0x%016Lx F25: 0x%016Lx F26: 0x%016Lx\n",
|
||
ContextRecord->FltF24,
|
||
ContextRecord->FltF25,
|
||
ContextRecord->FltF26);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F27: 0x%016Lx F28: 0x%016Lx F29: 0x%016Lx\n",
|
||
ContextRecord->FltF27,
|
||
ContextRecord->FltF28,
|
||
ContextRecord->FltF29);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
sprintf(Buffer,
|
||
"F30: 0x%016Lx F31: 0x%016Lx\n",
|
||
ContextRecord->FltF30,
|
||
ContextRecord->FltF31);
|
||
|
||
HalDisplayString(Buffer);
|
||
|
||
#endif //DUMP_FLOATING_STATE
|
||
|
||
//
|
||
// Output short stack back trace with base address.
|
||
//
|
||
|
||
DllName.Length = 0;
|
||
DllName.Buffer = L"";
|
||
if (FunctionEntry != NULL) {
|
||
StackLimit =(ULONG)KeGetCurrentThread()->KernelStack;
|
||
HalDisplayString("Callee-Sp Return-Ra Dll Base - Name\n");
|
||
for (Index = 0; Index < 8; Index += 1) {
|
||
ImageBase = KiPcToFileHeader((PVOID)ControlPc,
|
||
&ImageBase,
|
||
&DataTableEntry);
|
||
|
||
sprintf(Buffer,
|
||
" %08lX %08lX : %08lX - %s\n",
|
||
ContextRecord->IntSp,
|
||
NextPc + 4,
|
||
ImageBase,
|
||
(*UnicodeToAnsiRoutine)( (ImageBase != NULL) ? &DataTableEntry->BaseDllName : &DllName,
|
||
AnsiBuffer, sizeof( AnsiBuffer )));
|
||
|
||
|
||
|
||
HalDisplayString(Buffer);
|
||
if ((NextPc != ControlPc) || (ContextRecord->IntSp != LastStack)) {
|
||
ControlPc = NextPc;
|
||
LastStack = (ULONG)ContextRecord->IntSp;
|
||
FunctionEntry = KiLookupFunctionEntry(ControlPc);
|
||
if ((FunctionEntry != NULL) && (LastStack < StackLimit)) {
|
||
NextPc = RtlVirtualUnwind(ControlPc,
|
||
FunctionEntry,
|
||
ContextRecord,
|
||
&InFunction,
|
||
&EstablisherFrame,
|
||
NULL);
|
||
} else {
|
||
NextPc = (ULONG)ContextRecord->IntRa;
|
||
}
|
||
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Output other useful information.
|
||
//
|
||
|
||
sprintf(Buffer,
|
||
"\nIRQL : %d, DPC Active : %s\n",
|
||
KeGetCurrentIrql(),
|
||
KeIsExecutingDpc() ? "TRUE" : "FALSE");
|
||
|
||
HalDisplayString(Buffer);
|
||
return;
|
||
}
|
||
|
||
PRUNTIME_FUNCTION
|
||
KiLookupFunctionEntry (
|
||
IN ULONG ControlPc
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function searches the currently active function tables for an entry
|
||
that corresponds to the specified PC value.
|
||
|
||
Arguments:
|
||
|
||
ControlPc - Supplies the address of an instruction within the specified
|
||
function.
|
||
|
||
Return Value:
|
||
|
||
If there is no entry in the function table for the specified PC, then
|
||
NULL is returned. Otherwise, the address of the function table entry
|
||
that corresponds to the specified PC is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PRUNTIME_FUNCTION FunctionEntry;
|
||
PRUNTIME_FUNCTION FunctionTable;
|
||
ULONG SizeOfExceptionTable;
|
||
LONG High;
|
||
PVOID ImageBase;
|
||
LONG Low;
|
||
LONG Middle;
|
||
USHORT i;
|
||
|
||
//
|
||
// Search for the image that includes the specified PC value.
|
||
//
|
||
|
||
ImageBase = KiPcToFileHeader((PVOID)ControlPc,
|
||
&ImageBase,
|
||
&DataTableEntry);
|
||
|
||
//
|
||
// If an image is found that includes the specified PC, then locate the
|
||
// function table for the image.
|
||
//
|
||
|
||
if (ImageBase != NULL) {
|
||
FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
|
||
ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
|
||
&SizeOfExceptionTable);
|
||
|
||
//
|
||
// If a function table is located, then search the function table
|
||
// for a function table entry for the specified PC.
|
||
//
|
||
|
||
if (FunctionTable != NULL) {
|
||
|
||
//
|
||
// Initialize search indicies.
|
||
//
|
||
|
||
Low = 0;
|
||
High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
|
||
|
||
//
|
||
// Perform binary search on the function table for a function table
|
||
// entry that subsumes the specified PC.
|
||
//
|
||
|
||
while (High >= Low) {
|
||
|
||
//
|
||
// Compute next probe index and test entry. If the specified PC
|
||
// is greater than of equal to the beginning address and less
|
||
// than the ending address of the function table entry, then
|
||
// return the address of the function table entry. Otherwise,
|
||
// continue the search.
|
||
//
|
||
|
||
Middle = (Low + High) >> 1;
|
||
FunctionEntry = &FunctionTable[Middle];
|
||
if (ControlPc < FunctionEntry->BeginAddress) {
|
||
High = Middle - 1;
|
||
|
||
} else if (ControlPc >= FunctionEntry->EndAddress) {
|
||
Low = Middle + 1;
|
||
|
||
} else {
|
||
return FunctionEntry;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// A function table entry for the specified PC was not found.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PVOID
|
||
KiPcToFileHeader(
|
||
IN PVOID PcValue,
|
||
OUT PVOID *BaseOfImage,
|
||
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the base of an image that contains the
|
||
specified PcValue. An image contains the PcValue if the PcValue
|
||
is within the ImageBase, and the ImageBase plus the size of the
|
||
virtual image.
|
||
|
||
Arguments:
|
||
|
||
PcValue - Supplies a PcValue.
|
||
|
||
BaseOfImage - Returns the base address for the image containing the
|
||
PcValue. This value must be added to any relative addresses in
|
||
the headers to locate portions of the image.
|
||
|
||
DataTableEntry - Suppies a pointer to a variable that receives the
|
||
address of the data table entry that describes the image.
|
||
|
||
Return Value:
|
||
|
||
NULL - No image was found that contains the PcValue.
|
||
|
||
NON-NULL - Returns the base address of the image that contain the
|
||
PcValue.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PLIST_ENTRY ModuleListHead;
|
||
PLDR_DATA_TABLE_ENTRY Entry;
|
||
PLIST_ENTRY Next;
|
||
ULONG Bounds;
|
||
PVOID ReturnBase, Base;
|
||
|
||
//
|
||
// If the module list has been initialized, then scan the list to
|
||
// locate the appropriate entry.
|
||
//
|
||
|
||
if (KeLoaderBlock != NULL) {
|
||
ModuleListHead = &KeLoaderBlock->LoadOrderListHead;
|
||
|
||
} else {
|
||
ModuleListHead = &PsLoadedModuleList;
|
||
}
|
||
|
||
ReturnBase = NULL;
|
||
Next = ModuleListHead->Flink;
|
||
if (Next != NULL) {
|
||
while (Next != ModuleListHead) {
|
||
Entry = CONTAINING_RECORD(Next,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
Next = Next->Flink;
|
||
Base = Entry->DllBase;
|
||
Bounds = (ULONG)Base + Entry->SizeOfImage;
|
||
if ((ULONG)PcValue >= (ULONG)Base && (ULONG)PcValue < Bounds) {
|
||
*DataTableEntry = Entry;
|
||
ReturnBase = Base;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
*BaseOfImage = ReturnBase;
|
||
return ReturnBase;
|
||
}
|