Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6694 lines
184 KiB

/*++
Copyright (c) 1990-2001 Microsoft Corporation
Module Name:
dump.cpp
Abstract:
This module implements crashdump loading and analysis code
Comments:
There are five basic types of dumps:
User-mode dumps - contains the context and address of the user-mode
program plus all process memory.
User-mode minidumps - contains just thread stacks for
register and stack traces.
Kernel-mode normal dump - contains the context and address space of
the entire kernel at the time of crash.
Kernel-mode summary dump - contains a subset of the kernel-mode
memory plus optionally the user-mode address space.
Kernel-mode triage dump - contains a very small dump with registers,
current process kernel-mode context, current thread kernel-mode
context and some processor information. This dump is very small
(typically 64K) but is designed to contain enough information
to be able to figure out what went wrong when the machine
crashed.
This module also implements the following functions:
Retrieving a normal Kernel-Mode dump from a target machine using 1394
and storing it locally in a crashdump file format
--*/
#include "ntsdp.hpp"
#define HR_DUMP_CORRUPT HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT)
#define DUMP_INITIAL_VIEW_SIZE (1024 * 1024)
#define DUMP_MAXIMUM_VIEW_SIZE (256 * 1024 * 1024)
typedef ULONG PFN_NUMBER32;
//
// Page file dump file information.
//
#define DMPPF_IDENTIFIER "PAGE.DMP"
#define DMPPF_PAGE_NOT_PRESENT 0xffffffff
struct DMPPF_PAGE_FILE_INFO
{
ULONG Size;
ULONG MaximumSize;
};
// Left to its own devices the compiler will add a
// ULONG of padding at the end of this structure to
// keep it an even ULONG64 multiple in size. Force
// it to consider only the declared items.
#pragma pack(4)
struct DMPPF_FILE_HEADER
{
char Id[8];
LARGE_INTEGER BootTime;
ULONG PageData;
DMPPF_PAGE_FILE_INFO PageFiles[16];
};
#pragma pack()
KernelSummary32DumpTargetInfo g_KernelSummary32DumpTarget;
KernelSummary64DumpTargetInfo g_KernelSummary64DumpTarget;
KernelTriage32DumpTargetInfo g_KernelTriage32DumpTarget;
KernelTriage64DumpTargetInfo g_KernelTriage64DumpTarget;
KernelFull32DumpTargetInfo g_KernelFull32DumpTarget;
KernelFull64DumpTargetInfo g_KernelFull64DumpTarget;
UserFull32DumpTargetInfo g_UserFull32DumpTarget;
UserFull64DumpTargetInfo g_UserFull64DumpTarget;
UserMiniPartialDumpTargetInfo g_UserMiniPartialDumpTarget;
UserMiniFullDumpTargetInfo g_UserMiniFullDumpTarget;
// Indexed by DTYPE.
DumpTargetInfo* g_DumpTargets[DTYPE_COUNT] =
{
&g_KernelSummary32DumpTarget,
&g_KernelSummary64DumpTarget,
&g_KernelTriage32DumpTarget,
&g_KernelTriage64DumpTarget,
&g_KernelFull32DumpTarget,
&g_KernelFull64DumpTarget,
&g_UserFull32DumpTarget,
&g_UserFull64DumpTarget,
&g_UserMiniPartialDumpTarget,
&g_UserMiniFullDumpTarget,
};
// Set this value to track down page numbers and contents of a virtual address
// in a dump file.
// Initialized to an address no one will look for.
ULONG64 g_DebugDump_VirtualAddress = 12344321;
//
// Globals
//
#define IndexByByte(Pointer, Index) (&(((CHAR*) (Pointer))) [Index])
#define RtlCheckBit(BMH,BP) ((((BMH)->Buffer[(BP) / 32]) >> ((BP) % 32)) & 0x1)
ULONG64 g_DumpKiProcessors[MAXIMUM_PROCESSORS];
ULONG64 g_DumpKiPcrBaseAddress;
BOOL g_TriageDumpHasDebuggerData;
DTYPE g_DumpType = DTYPE_COUNT;
ULONG g_DumpFormatFlags;
EXCEPTION_RECORD64 g_DumpException;
ULONG g_DumpExceptionFirstChance;
//
// MM Triage information.
//
struct MM_TRIAGE_TRANSLATION
{
ULONG DebuggerDataOffset;
ULONG Triage32Offset;
ULONG Triage64Offset;
ULONG PtrSize:1;
};
MM_TRIAGE_TRANSLATION g_MmTriageTranslations[] =
{
FIELD_OFFSET(KDDEBUGGER_DATA64, MmSpecialPoolTag),
FIELD_OFFSET(DUMP_MM_STORAGE32, MmSpecialPoolTag),
FIELD_OFFSET(DUMP_MM_STORAGE64, MmSpecialPoolTag),
FALSE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmTriageActionTaken),
FIELD_OFFSET(DUMP_MM_STORAGE32, MiTriageActionTaken),
FIELD_OFFSET(DUMP_MM_STORAGE64, MiTriageActionTaken),
FALSE,
FIELD_OFFSET(KDDEBUGGER_DATA64, KernelVerifier),
FIELD_OFFSET(DUMP_MM_STORAGE32, KernelVerifier),
FIELD_OFFSET(DUMP_MM_STORAGE64, KernelVerifier),
FALSE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmAllocatedNonPagedPool),
FIELD_OFFSET(DUMP_MM_STORAGE32, MmAllocatedNonPagedPool),
FIELD_OFFSET(DUMP_MM_STORAGE64, MmAllocatedNonPagedPool),
TRUE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmTotalCommittedPages),
FIELD_OFFSET(DUMP_MM_STORAGE32, CommittedPages),
FIELD_OFFSET(DUMP_MM_STORAGE64, CommittedPages),
TRUE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmPeakCommitment),
FIELD_OFFSET(DUMP_MM_STORAGE32, CommittedPagesPeak),
FIELD_OFFSET(DUMP_MM_STORAGE64, CommittedPagesPeak),
TRUE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmTotalCommitLimitMaximum),
FIELD_OFFSET(DUMP_MM_STORAGE32, CommitLimitMaximum),
FIELD_OFFSET(DUMP_MM_STORAGE64, CommitLimitMaximum),
TRUE,
#if 0
// These MM triage fields are in pages while the corresponding
// debugger data fields are in bytes. There's no way to
// directly map one to the other.
FIELD_OFFSET(KDDEBUGGER_DATA64, MmMaximumNonPagedPoolInBytes),
FIELD_OFFSET(DUMP_MM_STORAGE32, MmMaximumNonPagedPool),
FIELD_OFFSET(DUMP_MM_STORAGE64, MmMaximumNonPagedPool),
TRUE,
FIELD_OFFSET(KDDEBUGGER_DATA64, MmSizeOfPagedPoolInBytes),
FIELD_OFFSET(DUMP_MM_STORAGE32, PagedPoolMaximum),
FIELD_OFFSET(DUMP_MM_STORAGE64, PagedPoolMaximum),
TRUE,
#endif
0, 0, 0, 0, 0,
};
//
// Internal globals.
//
struct DUMP_INFO_FILE
{
HANDLE File;
ULONG64 FileSize;
HANDLE Map;
PVOID MapBase;
ULONG MapSize;
//
// If a page is dirty, it will stay in the cache indefinitely.
// A maximum of MAX_CLEAN_PAGE_RECORD clean pages are kept in
// the LRU list.
//
ULONG ActiveCleanPages;
//
// Cache may be cleaned up after use and reinitialized later.
// This flag records its current state.
//
BOOL Initialized;
//
// This is a list of all mapped pages, dirty and clean.
// This list is not intended to get very big: the page finder
// searches it, so it is not a good data structure for a large
// list. It is expected that MAX_CLEAN_PAGE_RECORD will be a
// small number, and that there aren't going to be very many
// pages dirtied in a typical debugging session, so this will
// work well.
//
LIST_ENTRY InFileOrderList;
//
// This is a list of all clean pages. When the list is full
// and a new page is mapped, the oldest page will be discarded.
//
LIST_ENTRY InLRUOrderList;
};
typedef DUMP_INFO_FILE* PDUMP_INFO_FILE;
DUMP_INFO_FILE g_DumpInfoFiles[DUMP_INFO_COUNT];
PVOID g_DumpBase;
//
// Cache manager
//
// A dump file may be larger than can be mapped into the
// debugger's address space, so we use a simple cached
// paging method to access the data area.
//
//
// Allocation granularity for cache. This will be the
// allocation granularity for the system hosting the
// debugger and not for the system which produced the dump.
//
ULONG g_DumpCacheGranularity;
//
// Cache structure
//
typedef struct _DUMPFILE_CACHE_RECORD
{
LIST_ENTRY InFileOrderList;
LIST_ENTRY InLRUOrderList;
//
// Base address of mapped region.
//
PVOID MappedAddress;
//
// File page number of mapped region. pages are of
// size == FileCacheData.Granularity.
//
// The virtual address of a page is not stored; it is
// translated into a page number by the read routines
// and all access is by page number.
//
ULONG64 PageNumber;
//
// A page may be locked into the cache, either because it is used
// frequently or because it has been modified.
//
BOOL Locked;
} DUMPFILE_CACHE_RECORD, *PDUMPFILE_CACHE_RECORD;
#define MAX_CLEAN_PAGE_RECORD 4
void
DmppDumpFileCacheInit(PDUMP_INFO_FILE File)
{
File->ActiveCleanPages = 0;
InitializeListHead(&File->InFileOrderList);
InitializeListHead(&File->InLRUOrderList);
File->Initialized = TRUE;
}
VOID
DmppDumpFileCacheEmpty(PDUMP_INFO_FILE File)
{
PLIST_ENTRY Next;
PDUMPFILE_CACHE_RECORD CacheRecord;
Next = File->InFileOrderList.Flink;
while (Next != &File->InFileOrderList)
{
CacheRecord = CONTAINING_RECORD(Next,
DUMPFILE_CACHE_RECORD,
InFileOrderList);
Next = Next->Flink;
UnmapViewOfFile(CacheRecord->MappedAddress);
free (CacheRecord);
}
File->ActiveCleanPages = 0;
InitializeListHead(&File->InFileOrderList);
InitializeListHead(&File->InLRUOrderList);
}
VOID
DmppDumpFileCacheCleanup(PDUMP_INFO_FILE File)
{
if (File->Initialized)
{
File->Initialized = FALSE;
DmppDumpFileCacheEmpty(File);
}
}
PDUMPFILE_CACHE_RECORD
DmppReuseOldestCacheRecord(PDUMP_INFO_FILE File, ULONG64 FileByteOffset)
{
PDUMPFILE_CACHE_RECORD CacheRecord;
PLIST_ENTRY Next;
PVOID MappedAddress;
ULONG64 MapOffset;
ULONG Size;
MapOffset = FileByteOffset & ~((ULONG64)g_DumpCacheGranularity - 1);
if ((File->FileSize - MapOffset) < g_DumpCacheGranularity)
{
Size = (ULONG)(File->FileSize - MapOffset);
}
else
{
Size = g_DumpCacheGranularity;
}
MappedAddress = MapViewOfFile(File->Map, FILE_MAP_READ,
(DWORD)(MapOffset >> 32),
(DWORD)MapOffset, Size);
if (MappedAddress == NULL)
{
return NULL;
}
Next = File->InLRUOrderList.Flink;
CacheRecord = CONTAINING_RECORD(Next,
DUMPFILE_CACHE_RECORD,
InLRUOrderList);
UnmapViewOfFile(CacheRecord->MappedAddress);
CacheRecord->PageNumber = FileByteOffset / g_DumpCacheGranularity;
CacheRecord->MappedAddress = MappedAddress;
//
// Move record to end of LRU
//
RemoveEntryList(Next);
InsertTailList(&File->InLRUOrderList, Next);
//
// Move record to correct place in ordered list
//
RemoveEntryList(&CacheRecord->InFileOrderList);
Next = File->InFileOrderList.Flink;
while (Next != &File->InFileOrderList)
{
PDUMPFILE_CACHE_RECORD NextCacheRecord;
NextCacheRecord = CONTAINING_RECORD(Next,
DUMPFILE_CACHE_RECORD,
InFileOrderList);
if (CacheRecord->PageNumber < NextCacheRecord->PageNumber)
{
break;
}
Next = Next->Flink;
}
InsertTailList(Next, &CacheRecord->InFileOrderList);
return CacheRecord;
}
PDUMPFILE_CACHE_RECORD
DmppFindCacheRecordForFileByteOffset(PDUMP_INFO_FILE File,
ULONG64 FileByteOffset)
{
PDUMPFILE_CACHE_RECORD CacheRecord;
PLIST_ENTRY Next;
ULONG64 PageNumber;
PageNumber = FileByteOffset / g_DumpCacheGranularity;
Next = File->InFileOrderList.Flink;
while (Next != &File->InFileOrderList)
{
CacheRecord = CONTAINING_RECORD(Next,
DUMPFILE_CACHE_RECORD,
InFileOrderList);
if (CacheRecord->PageNumber < PageNumber)
{
Next = Next->Flink;
}
else if (CacheRecord->PageNumber == PageNumber)
{
if (!CacheRecord->Locked)
{
RemoveEntryList(&CacheRecord->InLRUOrderList);
InsertTailList(&File->InLRUOrderList,
&CacheRecord->InLRUOrderList);
}
return CacheRecord;
}
else
{
break;
}
}
//
// Can't find it in cache.
//
return NULL;
}
PDUMPFILE_CACHE_RECORD
DmppCreateNewFileCacheRecord(PDUMP_INFO_FILE File, ULONG64 FileByteOffset)
{
PDUMPFILE_CACHE_RECORD CacheRecord;
PDUMPFILE_CACHE_RECORD NextCacheRecord;
PLIST_ENTRY Next;
ULONG64 MapOffset;
ULONG Size;
CacheRecord = (PDUMPFILE_CACHE_RECORD)malloc(sizeof(*CacheRecord));
if (CacheRecord == NULL)
{
return NULL;
}
ZeroMemory(CacheRecord, sizeof(*CacheRecord));
MapOffset = (FileByteOffset / g_DumpCacheGranularity) *
g_DumpCacheGranularity;
if ((File->FileSize - MapOffset) < g_DumpCacheGranularity)
{
Size = (ULONG)(File->FileSize - MapOffset);
}
else
{
Size = g_DumpCacheGranularity;
}
CacheRecord->MappedAddress = MapViewOfFile(File->Map, FILE_MAP_READ,
(DWORD)(MapOffset >> 32),
(DWORD)MapOffset, Size);
if (CacheRecord->MappedAddress == NULL)
{
free(CacheRecord);
return NULL;
}
CacheRecord->PageNumber = FileByteOffset / g_DumpCacheGranularity;
//
// Insert new record in file order list
//
Next = File->InFileOrderList.Flink;
while (Next != &File->InFileOrderList)
{
NextCacheRecord = CONTAINING_RECORD(Next,
DUMPFILE_CACHE_RECORD,
InFileOrderList);
if (CacheRecord->PageNumber < NextCacheRecord->PageNumber)
{
break;
}
Next = Next->Flink;
}
InsertTailList(Next, &CacheRecord->InFileOrderList);
//
// Insert new record at tail of LRU list
//
InsertTailList(&File->InLRUOrderList,
&CacheRecord->InLRUOrderList);
return CacheRecord;
}
PUCHAR
DmppFileOffsetToMappedAddress(IN PDUMP_INFO_FILE File,
IN ULONG64 FileOffset,
IN BOOL LockCacheRecord,
OUT PULONG Avail)
{
PDUMPFILE_CACHE_RECORD CacheRecord;
ULONG64 FileByteOffset;
if (FileOffset == 0)
{
return NULL;
}
// The base view covers the beginning of the file.
if (FileOffset < File->MapSize)
{
*Avail = (ULONG)(File->MapSize - FileOffset);
return (PUCHAR)File->MapBase + FileOffset;
}
FileByteOffset = FileOffset;
CacheRecord = DmppFindCacheRecordForFileByteOffset(File, FileByteOffset);
if (CacheRecord == NULL)
{
if (File->ActiveCleanPages < MAX_CLEAN_PAGE_RECORD)
{
CacheRecord = DmppCreateNewFileCacheRecord(File, FileByteOffset);
if (CacheRecord)
{
File->ActiveCleanPages++;
}
}
else
{
//
// too many pages cached in
// overwrite existing cache
//
CacheRecord = DmppReuseOldestCacheRecord(File, FileByteOffset);
}
}
if (CacheRecord == NULL)
{
return NULL;
}
else
{
if (LockCacheRecord && !CacheRecord->Locked)
{
RemoveEntryList(&CacheRecord->InLRUOrderList);
CacheRecord->Locked = TRUE;
File->ActiveCleanPages--;
}
ULONG PageRemainder =
(ULONG)(FileByteOffset & (g_DumpCacheGranularity - 1));
*Avail = g_DumpCacheGranularity - PageRemainder;
return ((PUCHAR)CacheRecord->MappedAddress) + PageRemainder;
}
}
ULONG
DmppReadFileOffset(ULONG FileIndex,
ULONG64 Offset, PVOID Buffer, ULONG BufferSize)
{
ULONG Done = 0;
ULONG Avail;
PUCHAR Mapping;
PDUMP_INFO_FILE File = &g_DumpInfoFiles[FileIndex];
if (File->File == NULL)
{
// Information for this kind of file wasn't provided.
return 0;
}
__try
{
while (BufferSize > 0)
{
Mapping = DmppFileOffsetToMappedAddress(File, Offset, FALSE,
&Avail);
if (Mapping == NULL)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
memcpy(Buffer, Mapping, Avail);
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
Done += Avail;
}
}
__except(MappingExceptionFilter(GetExceptionInformation()))
{
Done = 0;
}
return Done;
}
ULONG
DmppWriteFileOffset(ULONG FileIndex,
ULONG64 Offset, PVOID Buffer, ULONG BufferSize)
{
ULONG Done = 0;
ULONG Avail;
PUCHAR Mapping;
ULONG Protect;
PDUMP_INFO_FILE File = &g_DumpInfoFiles[FileIndex];
if (File->File == NULL)
{
// Information for this kind of file wasn't provided.
return 0;
}
__try
{
while (BufferSize > 0)
{
Mapping = DmppFileOffsetToMappedAddress(File, Offset, TRUE,
&Avail);
if (Mapping == NULL)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
VirtualProtect(Mapping, Avail, PAGE_WRITECOPY, &Protect);
memcpy(Mapping, Buffer, Avail);
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
Done += Avail;
}
}
__except(MappingExceptionFilter(GetExceptionInformation()))
{
Done = 0;
}
return Done;
}
HRESULT
AddDumpInfoFile(PCSTR FileName, ULONG Index, ULONG InitialView)
{
HRESULT Status;
PDUMP_INFO_FILE File = &g_DumpInfoFiles[Index];
DBG_ASSERT(((g_DumpCacheGranularity - 1) & InitialView) == 0);
if (File->File != NULL)
{
return E_INVALIDARG;
}
// We have to share everything in order to be
// able to reopen already-open temporary files
// expanded from CABs as they are marked as
// delete-on-close.
File->File = CreateFile(FileName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (File->File == INVALID_HANDLE_VALUE)
{
goto LastStatus;
}
//
// Get file size and map initial view.
//
ULONG SizeLow, SizeHigh;
SizeLow = GetFileSize(File->File, &SizeHigh);
File->FileSize = ((ULONG64)SizeHigh << 32) | SizeLow;
File->Map = CreateFileMapping(File->File, NULL, PAGE_READONLY,
0, 0, NULL);
if (File->Map == NULL)
{
goto LastStatus;
}
if (File->FileSize < InitialView)
{
InitialView = (ULONG)File->FileSize;
}
File->MapBase = MapViewOfFile(File->Map, FILE_MAP_READ, 0, 0,
InitialView);
if (File->MapBase == NULL)
{
goto LastStatus;
}
switch(Index)
{
case DUMP_INFO_PAGE_FILE:
if (memcmp(File->MapBase, DMPPF_IDENTIFIER,
sizeof(DMPPF_IDENTIFIER) - 1) != 0)
{
Status = HR_DUMP_CORRUPT;
goto Fail;
}
break;
}
File->MapSize = InitialView;
DmppDumpFileCacheInit(File);
return S_OK;
LastStatus:
Status = WIN32_LAST_STATUS();
Fail:
CloseDumpInfoFile(Index);
return Status;
}
void
CloseDumpInfoFile(ULONG Index)
{
PDUMP_INFO_FILE File = &g_DumpInfoFiles[Index];
if (Index == DUMP_INFO_DUMP)
{
g_DumpBase = NULL;
}
DmppDumpFileCacheCleanup(File);
if (File->MapBase != NULL)
{
UnmapViewOfFile(File->MapBase);
File->MapBase = NULL;
}
if (File->Map != NULL)
{
CloseHandle(File->Map);
File->Map = NULL;
}
if (File->File != NULL && File->File != INVALID_HANDLE_VALUE)
{
CloseHandle(File->File);
}
File->File = NULL;
}
void
CloseDumpInfoFiles(void)
{
ULONG i;
for (i = 0; i < DUMP_INFO_COUNT; i++)
{
CloseDumpInfoFile(i);
}
}
HRESULT
DmpInitialize(LPCSTR FileName)
{
HRESULT Status;
dprintf("\nLoading Dump File [%s]\n", FileName);
if ((Status = AddDumpInfoFile(FileName, DUMP_INFO_DUMP,
DUMP_INITIAL_VIEW_SIZE)) != S_OK)
{
return Status;
}
PDUMP_INFO_FILE File = &g_DumpInfoFiles[DUMP_INFO_DUMP];
ULONG i;
ULONG64 BaseMapSize;
g_DumpBase = File->MapBase;
for (i = 0; i < DTYPE_COUNT; i++)
{
BaseMapSize = File->MapSize;
Status = g_DumpTargets[i]->IdentifyDump(&BaseMapSize);
if (Status != E_NOINTERFACE)
{
break;
}
}
if (Status == E_NOINTERFACE)
{
ErrOut("Could not match Dump File signature - "
"invalid file format\n");
}
else if (Status == S_OK &&
BaseMapSize > File->MapSize)
{
if (BaseMapSize > File->FileSize ||
BaseMapSize > DUMP_MAXIMUM_VIEW_SIZE)
{
ErrOut("Dump file is too large to map\n");
Status = E_INVALIDARG;
}
else
{
// Target requested a larger mapping so
// try and do so. Round up to a multiple
// of the initial view size for cache alignment.
BaseMapSize =
(BaseMapSize + DUMP_INITIAL_VIEW_SIZE - 1) &
~(DUMP_INITIAL_VIEW_SIZE - 1);
if (BaseMapSize > File->FileSize)
{
BaseMapSize = File->FileSize;
}
UnmapViewOfFile(File->MapBase);
File->MapBase = MapViewOfFile(File->Map, FILE_MAP_READ,
0, 0, (ULONG)BaseMapSize);
if (File->MapBase == NULL)
{
Status = WIN32_LAST_STATUS();
}
}
}
if (Status == S_OK)
{
File->MapSize = (ULONG)BaseMapSize;
g_DumpType = (DTYPE)i;
g_DumpBase = File->MapBase;
}
else
{
CloseDumpInfoFile(DUMP_INFO_DUMP);
}
return Status;
}
VOID
DmpUninitialize(VOID)
{
if (g_DumpType < DTYPE_COUNT)
{
g_DumpTargets[g_DumpType]->Uninitialize();
g_DumpType = DTYPE_COUNT;
}
}
HRESULT
DmppInitGlobals(ULONG BuildNumber, ULONG CheckedBuild,
ULONG MachineType, ULONG PlatformId,
ULONG MajorVersion, ULONG MinorVersion)
{
SetTargetSystemVersionAndBuild(BuildNumber, PlatformId);
g_TargetCheckedBuild = CheckedBuild;
DbgKdApi64 = (g_SystemVersion > NT_SVER_NT4);
HRESULT Status = InitializeMachines(MachineType);
if (Status != S_OK)
{
return Status;
}
if (g_TargetMachine == NULL)
{
ErrOut("Dump has an unknown processor type, 0x%X\n", MachineType);
return HR_DUMP_CORRUPT;
}
g_TargetPlatformId = PlatformId;
g_KdVersion.MachineType = (USHORT) MachineType;
g_KdVersion.MajorVersion = (USHORT) MajorVersion;
g_KdVersion.MinorVersion = (USHORT) MinorVersion;
g_KdVersion.Flags = DBGKD_VERS_FLAG_DATA |
(g_TargetMachine->m_Ptr64 ? DBGKD_VERS_FLAG_PTR64 : 0);
return S_OK;
}
//----------------------------------------------------------------------------
//
// DumpTargetInfo.
//
//----------------------------------------------------------------------------
void
DumpTargetInfo::Uninitialize(void)
{
CloseDumpInfoFiles();
g_DumpType = DTYPE_COUNT;
g_DumpFormatFlags = 0;
}
HRESULT
DumpTargetInfo::ReadVirtual(
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesRead
)
{
ULONG Done = 0;
ULONG FileIndex;
ULONG Avail;
ULONG Attempt;
ULONG64 FileOffset;
if (BufferSize == 0)
{
*BytesRead = 0;
return S_OK;
}
while (BufferSize > 0)
{
FileOffset = VirtualToOffset(Offset, &FileIndex, &Avail);
if (FileOffset == 0)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
Attempt = DmppReadFileOffset(FileIndex, FileOffset, Buffer, Avail);
Done += Attempt;
if (Attempt < Avail)
{
break;
}
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
}
*BytesRead = Done;
// If we didn't read anything return an error.
return Done == 0 ? E_FAIL : S_OK;
}
HRESULT
DumpTargetInfo::WriteVirtual(
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesWritten
)
{
ULONG Done = 0;
ULONG FileIndex;
ULONG Avail;
ULONG Attempt;
ULONG64 FileOffset;
if (BufferSize == 0)
{
*BytesWritten = 0;
return S_OK;
}
while (BufferSize > 0)
{
FileOffset = VirtualToOffset(Offset, &FileIndex, &Avail);
if (FileOffset == 0)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
Attempt = DmppWriteFileOffset(FileIndex, FileOffset, Buffer, Avail);
Done += Attempt;
if (Attempt < Avail)
{
break;
}
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
}
*BytesWritten = Done;
// If we didn't write anything return an error.
return Done == 0 ? E_FAIL : S_OK;
}
HRESULT
DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment)
{
ErrOut("Dump file type does not support writing\n");
return E_NOTIMPL;
}
//----------------------------------------------------------------------------
//
// KernelDumpTargetInfo.
//
//----------------------------------------------------------------------------
void
OutputHeaderString(PCSTR Format, PSTR Str)
{
if (*(PULONG)Str == DUMP_SIGNATURE32 ||
Str[0] == 0)
{
// String not present.
return;
}
dprintf(Format, Str);
}
void
KernelDumpTargetInfo::Uninitialize(void)
{
m_HeaderContext = NULL;
ZeroMemory(g_DumpKiProcessors, sizeof(g_DumpKiProcessors));
g_DumpKiPcrBaseAddress = 0;
DumpTargetInfo::Uninitialize();
}
HRESULT
DmppReadControlSpaceAxp(
ULONG Processor,
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesRead
)
{
ULONG64 StartAddr;
ULONG Read = 0;
HRESULT Status;
ULONG PtrSize = g_TargetMachine->m_Ptr64 ?
sizeof(ULONG64) : sizeof(ULONG);
if (BufferSize < PtrSize)
{
return E_INVALIDARG;
}
switch(Offset)
{
case ALPHA_DEBUG_CONTROL_SPACE_PCR:
if (g_DumpKiPcrBaseAddress == 0)
{
WarnOut("Unable to determine KiPcrBaseAddress\n");
Status = E_FAIL;
}
else
{
// Return the PCR address for the current processor.
Status = g_Target->ReadVirtual(g_DumpKiPcrBaseAddress,
Buffer,
PtrSize, &Read);
}
break;
case ALPHA_DEBUG_CONTROL_SPACE_PRCB:
//
// Return the prcb address for the current processor.
//
memcpy(Buffer, &g_DumpKiProcessors[Processor], PtrSize);
Read = PtrSize;
Status = S_OK;
break;
case ALPHA_DEBUG_CONTROL_SPACE_THREAD:
//
// Return the pointer to the current thread address for the
// current processor.
//
StartAddr = g_DumpKiProcessors[Processor] +
(g_TargetMachine->m_Ptr64 ?
KPRCB_CURRENT_THREAD_OFFSET_64 :
KPRCB_CURRENT_THREAD_OFFSET_32);
Status = g_Target->ReadVirtual(StartAddr, Buffer,
PtrSize, &Read);
break;
case ALPHA_DEBUG_CONTROL_SPACE_TEB:
//
// Return the current Thread Environment Block pointer for the
// current thread on the current processor.
//
StartAddr = g_DumpKiProcessors[Processor] +
(g_TargetMachine->m_Ptr64 ?
KPRCB_CURRENT_THREAD_OFFSET_64 :
KPRCB_CURRENT_THREAD_OFFSET_32);
Status = g_Target->ReadPointer(g_TargetMachine, StartAddr, &StartAddr);
if (Status == S_OK)
{
StartAddr += g_TargetMachine->m_OffsetKThreadTeb;
Status = g_Target->ReadVirtual(StartAddr, Buffer,
PtrSize, &Read);
}
break;
}
*BytesRead = Read;
return Status;
}
HRESULT
DmppReadControlSpaceIa64(
ULONG Processor,
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesRead
)
{
ULONG64 StartAddr;
ULONG Read = 0;
HRESULT Status;
if (BufferSize < sizeof(ULONG64))
{
return E_INVALIDARG;
}
switch(Offset)
{
case IA64_DEBUG_CONTROL_SPACE_PCR:
StartAddr = g_DumpKiProcessors[Processor] +
FIELD_OFFSET(IA64_PARTIAL_KPRCB, PcrPage);
Status = g_Target->ReadVirtual(StartAddr, &StartAddr,
sizeof(StartAddr), &Read);
if (Status == S_OK && Read == sizeof(StartAddr))
{
*(PULONG64)Buffer =
(StartAddr << IA64_PAGE_SHIFT) + IA64_PHYSICAL1_START;
}
break;
case IA64_DEBUG_CONTROL_SPACE_PRCB:
*(PULONG64)Buffer = g_DumpKiProcessors[Processor];
Read = sizeof(ULONG64);
Status = S_OK;
break;
case IA64_DEBUG_CONTROL_SPACE_KSPECIAL:
StartAddr = g_DumpKiProcessors[Processor] +
FIELD_OFFSET(IA64_PARTIAL_KPRCB, ProcessorState.SpecialRegisters);
Status = g_Target->ReadVirtual(StartAddr, Buffer, BufferSize, &Read);
break;
case IA64_DEBUG_CONTROL_SPACE_THREAD:
StartAddr = g_DumpKiProcessors[Processor] +
FIELD_OFFSET(IA64_PARTIAL_KPRCB, CurrentThread);
Status = g_Target->ReadVirtual(StartAddr, Buffer,
sizeof(ULONG64), &Read);
break;
}
*BytesRead = Read;
return Status;
}
HRESULT
DmppReadControlSpaceAmd64(
ULONG Processor,
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesRead
)
{
ULONG64 StartAddr;
ULONG Read = 0;
HRESULT Status;
if (BufferSize < sizeof(ULONG64))
{
return E_INVALIDARG;
}
switch(Offset)
{
case AMD64_DEBUG_CONTROL_SPACE_PCR:
*(PULONG64)Buffer = g_DumpKiProcessors[Processor] -
FIELD_OFFSET(AMD64_KPCR, Prcb);
Read = sizeof(ULONG64);
Status = S_OK;
break;
case AMD64_DEBUG_CONTROL_SPACE_PRCB:
*(PULONG64)Buffer = g_DumpKiProcessors[Processor];
Read = sizeof(ULONG64);
Status = S_OK;
break;
case AMD64_DEBUG_CONTROL_SPACE_KSPECIAL:
StartAddr = g_DumpKiProcessors[Processor] +
FIELD_OFFSET(AMD64_PARTIAL_KPRCB, ProcessorState.SpecialRegisters);
Status = g_Target->ReadVirtual(StartAddr, Buffer, BufferSize, &Read);
break;
case AMD64_DEBUG_CONTROL_SPACE_THREAD:
StartAddr = g_DumpKiProcessors[Processor] +
FIELD_OFFSET(AMD64_PARTIAL_KPRCB, CurrentThread);
Status = g_Target->ReadVirtual(StartAddr, Buffer,
sizeof(ULONG64), &Read);
break;
}
*BytesRead = Read;
return Status;
}
HRESULT
KernelDumpTargetInfo::ReadControl(
IN ULONG Processor,
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
ULONG64 StartAddr;
//
// This function will not currently work if the symbols are not loaded.
//
if (!IS_KERNEL_TRIAGE_DUMP() &&
KdDebuggerData.KiProcessorBlock == 0)
{
ErrOut("ReadControl failed - ntoskrnl symbols must be loaded first\n");
return E_FAIL;
}
if (g_DumpKiProcessors[Processor] == 0)
{
// This error message is a little too verbose.
#if 0
ErrOut("No control space information for processor %d\n", Processor);
#endif
return E_FAIL;
}
switch(g_TargetMachineType)
{
case IMAGE_FILE_MACHINE_I386:
StartAddr = Offset +
g_DumpKiProcessors[Processor] +
g_TargetMachine->m_OffsetPrcbProcessorState;
return ReadVirtual(StartAddr, Buffer, BufferSize, BytesRead);
case IMAGE_FILE_MACHINE_ALPHA:
case IMAGE_FILE_MACHINE_AXP64:
return DmppReadControlSpaceAxp(Processor, Offset, Buffer,
BufferSize, BytesRead);
case IMAGE_FILE_MACHINE_IA64:
return DmppReadControlSpaceIa64(Processor, Offset, Buffer,
BufferSize, BytesRead);
case IMAGE_FILE_MACHINE_AMD64:
return DmppReadControlSpaceAmd64(Processor, Offset, Buffer,
BufferSize, BytesRead);
}
return E_FAIL;
}
HRESULT
KernelDumpTargetInfo::GetThreadIdByProcessor(
IN ULONG Processor,
OUT PULONG Id
)
{
*Id = VIRTUAL_THREAD_ID(Processor);
return S_OK;
}
HRESULT
KernelDumpTargetInfo::GetThreadInfoDataOffset(PTHREAD_INFO Thread,
ULONG64 ThreadHandle,
PULONG64 Offset)
{
return KdGetThreadInfoDataOffset(Thread, ThreadHandle, Offset);
}
HRESULT
KernelDumpTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread,
ULONG Processor,
ULONG64 ThreadData,
PULONG64 Offset)
{
return KdGetProcessInfoDataOffset(Thread, Processor, ThreadData, Offset);
}
HRESULT
KernelDumpTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread,
ULONG ThreadIndex,
ULONG64 ThreadData,
PULONG64 Offset)
{
return KdGetThreadInfoTeb(Thread, ThreadIndex, ThreadData, Offset);
}
HRESULT
KernelDumpTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread,
ULONG Processor,
ULONG64 ThreadData,
PULONG64 Offset)
{
return KdGetProcessInfoPeb(Thread, Processor, ThreadData, Offset);
}
ULONG64
KernelDumpTargetInfo::GetCurrentTimeDateN(void)
{
if (g_SystemVersion < NT_SVER_W2K)
{
ULONG64 TimeDate;
// Header time not available. Try and read
// the time saved in the shared user data segment.
if (ReadSharedUserTimeDateN(&TimeDate) == S_OK)
{
return TimeDate;
}
else
{
return 0;
}
}
return g_TargetMachine->m_Ptr64 ?
((PDUMP_HEADER64)g_DumpBase)->SystemTime.QuadPart :
((PDUMP_HEADER32)g_DumpBase)->SystemTime.QuadPart;
}
ULONG64
KernelDumpTargetInfo::GetCurrentSystemUpTimeN(void)
{
ULONG64 page = 'EGAP';
ULONG64 page64 = page | (page << 32);
ULONG64 SystemUpTime = g_TargetMachine->m_Ptr64 ?
((PDUMP_HEADER64)g_DumpBase)->SystemUpTime.QuadPart :
((PDUMP_HEADER32)g_DumpBase)->SystemUpTime.QuadPart;
if (SystemUpTime && (SystemUpTime != page64))
{
return SystemUpTime;
}
// Header time not available. Try and read
// the time saved in the shared user data segment.
if (ReadSharedUserUpTimeN(&SystemUpTime) == S_OK)
{
return SystemUpTime;
}
else
{
return 0;
}
}
HRESULT
KernelDumpTargetInfo::InitGlobals32(PMEMORY_DUMP32 Dump)
{
if (Dump->Header.DirectoryTableBase == 0)
{
ErrOut("Invalid directory table base value 0x%x\n",
Dump->Header.DirectoryTableBase);
return HR_DUMP_CORRUPT;
}
if (Dump->Header.MinorVersion > 1381 &&
Dump->Header.PaeEnabled == TRUE )
{
KdDebuggerData.PaeEnabled = TRUE;
}
else
{
KdDebuggerData.PaeEnabled = FALSE;
}
KdDebuggerData.PsLoadedModuleList =
EXTEND64(Dump->Header.PsLoadedModuleList);
g_TargetNumberProcessors = Dump->Header.NumberProcessors;
ExceptionRecord32To64(&Dump->Header.Exception, &g_DumpException);
g_DumpExceptionFirstChance = FALSE;
m_HeaderContext = Dump->Header.ContextRecord;
// New field in Windows Whistler and NT SP1 and above
if ((Dump->Header.KdDebuggerDataBlock) &&
(Dump->Header.KdDebuggerDataBlock != DUMP_SIGNATURE32))
{
g_KdDebuggerDataBlock = EXTEND64(Dump->Header.KdDebuggerDataBlock);
}
OutputHeaderString("Comment: '%s'\n", Dump->Header.Comment);
HRESULT Status =
DmppInitGlobals(Dump->Header.MinorVersion,
Dump->Header.MajorVersion & 0xFF,
Dump->Header.MachineImageType,
VER_PLATFORM_WIN32_NT,
Dump->Header.MajorVersion,
Dump->Header.MinorVersion);
if (Status != S_OK)
{
return Status;
}
ULONG NextIdx;
return g_TargetMachine->
SetPageDirectory(PAGE_DIR_KERNEL, Dump->Header.DirectoryTableBase,
&NextIdx);
}
HRESULT
KernelDumpTargetInfo::InitGlobals64(PMEMORY_DUMP64 Dump)
{
if (Dump->Header.DirectoryTableBase == 0)
{
ErrOut("Invalid directory table base value 0x%I64x\n",
Dump->Header.DirectoryTableBase);
return HR_DUMP_CORRUPT;
}
KdDebuggerData.PaeEnabled = FALSE;
KdDebuggerData.PsLoadedModuleList =
Dump->Header.PsLoadedModuleList;
g_TargetNumberProcessors = Dump->Header.NumberProcessors;
g_DumpException = Dump->Header.Exception;
g_DumpExceptionFirstChance = FALSE;
m_HeaderContext = Dump->Header.ContextRecord;
// New field in Windows Whistler and NT SP1 and above
if ((Dump->Header.KdDebuggerDataBlock) &&
(Dump->Header.KdDebuggerDataBlock != DUMP_SIGNATURE32))
{
g_KdDebuggerDataBlock = Dump->Header.KdDebuggerDataBlock;
}
OutputHeaderString("Comment: '%s'\n", Dump->Header.Comment);
HRESULT Status =
DmppInitGlobals(Dump->Header.MinorVersion,
Dump->Header.MajorVersion & 0xFF,
Dump->Header.MachineImageType,
VER_PLATFORM_WIN32_NT,
Dump->Header.MajorVersion,
Dump->Header.MinorVersion);
if (Status != S_OK)
{
return Status;
}
ULONG NextIdx;
return g_TargetMachine->
SetPageDirectory(PAGE_DIR_KERNEL, Dump->Header.DirectoryTableBase,
&NextIdx);
}
void
KernelDumpTargetInfo::DumpHeader32(PDUMP_HEADER32 Header)
{
dprintf("\nDUMP_HEADER32:\n");
dprintf("MajorVersion %08lx\n", Header->MajorVersion);
dprintf("MinorVersion %08lx\n", Header->MinorVersion);
dprintf("DirectoryTableBase %08lx\n", Header->DirectoryTableBase);
dprintf("PfnDataBase %08lx\n", Header->PfnDataBase);
dprintf("PsLoadedModuleList %08lx\n", Header->PsLoadedModuleList);
dprintf("PsActiveProcessHead %08lx\n", Header->PsActiveProcessHead);
dprintf("MachineImageType %08lx\n", Header->MachineImageType);
dprintf("NumberProcessors %08lx\n", Header->NumberProcessors);
dprintf("BugCheckCode %08lx\n", Header->BugCheckCode);
dprintf("BugCheckParameter1 %08lx\n", Header->BugCheckParameter1);
dprintf("BugCheckParameter2 %08lx\n", Header->BugCheckParameter2);
dprintf("BugCheckParameter3 %08lx\n", Header->BugCheckParameter3);
dprintf("BugCheckParameter4 %08lx\n", Header->BugCheckParameter4);
OutputHeaderString("VersionUser '%s'\n", Header->VersionUser);
dprintf("PaeEnabled %08lx\n", Header->PaeEnabled);
dprintf("KdDebuggerDataBlock %08lx\n", Header->KdDebuggerDataBlock);
OutputHeaderString("Comment '%s'\n", Header->Comment);
}
void
KernelDumpTargetInfo::DumpHeader64(PDUMP_HEADER64 Header)
{
dprintf("\nDUMP_HEADER64:\n");
dprintf("MajorVersion %08lx\n", Header->MajorVersion);
dprintf("MinorVersion %08lx\n", Header->MinorVersion);
dprintf("DirectoryTableBase %s\n",
FormatAddr64(Header->DirectoryTableBase));
dprintf("PfnDataBase %s\n",
FormatAddr64(Header->PfnDataBase));
dprintf("PsLoadedModuleList %s\n",
FormatAddr64(Header->PsLoadedModuleList));
dprintf("PsActiveProcessHead %s\n",
FormatAddr64(Header->PsActiveProcessHead));
dprintf("MachineImageType %08lx\n", Header->MachineImageType);
dprintf("NumberProcessors %08lx\n", Header->NumberProcessors);
dprintf("BugCheckCode %08lx\n", Header->BugCheckCode);
dprintf("BugCheckParameter1 %s\n",
FormatAddr64(Header->BugCheckParameter1));
dprintf("BugCheckParameter2 %s\n",
FormatAddr64(Header->BugCheckParameter2));
dprintf("BugCheckParameter3 %s\n",
FormatAddr64(Header->BugCheckParameter3));
dprintf("BugCheckParameter4 %s\n",
FormatAddr64(Header->BugCheckParameter4));
OutputHeaderString("VersionUser '%s'\n", Header->VersionUser);
dprintf("KdDebuggerDataBlock %s\n",
FormatAddr64(Header->KdDebuggerDataBlock));
OutputHeaderString("Comment '%s'\n", Header->Comment);
}
//----------------------------------------------------------------------------
//
// KernelFullSumDumpTargetInfo.
//
//----------------------------------------------------------------------------
HRESULT
PageFileOffset(ULONG PfIndex, ULONG64 PfOffset, PULONG64 FileOffset)
{
PDUMP_INFO_FILE File = &g_DumpInfoFiles[DUMP_INFO_PAGE_FILE];
if (File->File == NULL)
{
return HR_PAGE_NOT_AVAILABLE;
}
if (PfIndex > MAX_PAGING_FILE_MASK)
{
return HR_DUMP_CORRUPT;
}
//
// We can safely assume the header information is present
// in the base mapping.
//
DMPPF_FILE_HEADER* Hdr = (DMPPF_FILE_HEADER*)File->MapBase;
DMPPF_PAGE_FILE_INFO* FileInfo = &Hdr->PageFiles[PfIndex];
ULONG64 PfPage = PfOffset >> g_TargetMachine->m_PageShift;
if (PfPage >= FileInfo->MaximumSize)
{
return HR_PAGE_NOT_AVAILABLE;
}
ULONG i;
ULONG PageDirOffs = sizeof(*Hdr) + (ULONG)PfPage * sizeof(ULONG);
for (i = 0; i < PfIndex; i++)
{
PageDirOffs += Hdr->PageFiles[i].MaximumSize * sizeof(ULONG);
}
ULONG PageDirEnt;
if (DmppReadFileOffset(DUMP_INFO_PAGE_FILE, PageDirOffs,
&PageDirEnt, sizeof(PageDirEnt)) !=
sizeof(PageDirEnt))
{
return HR_DUMP_CORRUPT;
}
if (PageDirEnt == DMPPF_PAGE_NOT_PRESENT)
{
return HR_PAGE_NOT_AVAILABLE;
}
*FileOffset = Hdr->PageData +
(PageDirEnt << g_TargetMachine->m_PageShift) +
(PfOffset & (g_Machine->m_PageSize - 1));
return S_OK;
}
HRESULT
KernelFullSumDumpTargetInfo::Initialize(void)
{
InitSelCache();
m_ProvokingVirtAddr = 0;
return S_OK;
}
HRESULT
KernelFullSumDumpTargetInfo::ReadPhysical(
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesRead
)
{
ULONG Done = 0;
ULONG Avail;
ULONG Attempt;
ULONG64 FileOffset;
if (BufferSize == 0)
{
*BytesRead = 0;
return S_OK;
}
while (BufferSize > 0)
{
FileOffset = PhysicalToOffset(Offset, &Avail);
if (FileOffset == 0)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
Attempt = DmppReadFileOffset(DUMP_INFO_DUMP,
FileOffset, Buffer, Avail);
Done += Attempt;
if (Attempt < Avail)
{
break;
}
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
}
*BytesRead = Done;
// If we didn't read anything return an error.
return Done == 0 ? E_FAIL : S_OK;
}
HRESULT
KernelFullSumDumpTargetInfo::WritePhysical(
ULONG64 Offset,
PVOID Buffer,
ULONG BufferSize,
PULONG BytesWritten
)
{
ULONG Done = 0;
ULONG Avail;
ULONG Attempt;
ULONG64 FileOffset;
if (BufferSize == 0)
{
*BytesWritten = 0;
return S_OK;
}
while (BufferSize > 0)
{
FileOffset = PhysicalToOffset(Offset, &Avail);
if (FileOffset == 0)
{
break;
}
if (Avail > BufferSize)
{
Avail = BufferSize;
}
Attempt = DmppWriteFileOffset(DUMP_INFO_DUMP,
FileOffset, Buffer, Avail);
Done += Attempt;
if (Attempt < Avail)
{
break;
}
Offset += Avail;
Buffer = (PUCHAR)Buffer + Avail;
BufferSize -= Avail;
}
*BytesWritten = Done;
// If we didn't write anything return an error.
return Done == 0 ? E_FAIL : S_OK;
}
HRESULT
KernelFullSumDumpTargetInfo::GetProcessorId
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
return g_TargetMachine->ReadKernelProcessorId(Processor, Id);
}
HRESULT
KernelFullSumDumpTargetInfo::ReadPageFile(ULONG PfIndex, ULONG64 PfOffset,
PVOID Buffer, ULONG Size)
{
HRESULT Status;
ULONG64 FileOffset;
if ((Status = PageFileOffset(PfIndex, PfOffset, &FileOffset)) != S_OK)
{
return Status;
}
// It's assumed that all page file reads are for the
// entire amount requested, as there are no situations
// where it's useful to only read part of a page from the
// page file.
if (DmppReadFileOffset(DUMP_INFO_PAGE_FILE, FileOffset,
Buffer, Size) < Size)
{
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
}
else
{
return S_OK;
}
}
HRESULT
KernelFullSumDumpTargetInfo::GetTargetContext(
ULONG64 Thread,
PVOID Context
)
{
ULONG Read;
HRESULT Status;
Status = ReadVirtual(g_DumpKiProcessors[VIRTUAL_THREAD_INDEX(Thread)] +
g_TargetMachine->m_OffsetPrcbProcessorState,
Context, g_TargetMachine->m_SizeTargetContext,
&Read);
if (Status != S_OK)
{
return Status;
}
return Read == g_TargetMachine->m_SizeTargetContext ? S_OK : E_FAIL;
}
HRESULT
KernelFullSumDumpTargetInfo::GetSelDescriptor(MachineInfo* Machine,
ULONG64 Thread,
ULONG Selector,
PDESCRIPTOR64 Desc)
{
return KdGetSelDescriptor(Machine, Thread, Selector, Desc);
}
void
KernelFullSumDumpTargetInfo::DumpDebug(void)
{
ULONG i;
dprintf("\nKiProcessorBlock at %s\n",
FormatAddr64(KdDebuggerData.KiProcessorBlock));
dprintf(" %d KiProcessorBlock entries:\n ", g_TargetNumberProcessors);
for (i = 0; i < g_TargetNumberProcessors; i++)
{
dprintf(" %s", FormatAddr64(g_DumpKiProcessors[i]));
}
dprintf("\n");
PDUMP_INFO_FILE PageDump = &g_DumpInfoFiles[DUMP_INFO_PAGE_FILE];
if (PageDump->File != NULL)
{
// XXX drewb - Display more information when format is understood.
dprintf("\nAdditional page file in use\n");
}
}
ULONG64
KernelFullSumDumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
HRESULT Status;
ULONG Levels;
ULONG PfIndex;
ULONG64 Phys;
*File = DUMP_INFO_DUMP;
if ((Status = g_TargetMachine->
GetVirtualTranslationPhysicalOffsets(Virt, NULL, 0, &Levels,
&PfIndex, &Phys)) != S_OK)
{
// If the virtual address was paged out we got back
// a page file reference for the address. The user
// may have provided a page file in addition to the
// normal dump file so translate the reference into
// a secondary dump information file request.
if (Status == HR_PAGE_IN_PAGE_FILE)
{
if (PageFileOffset(PfIndex, Phys, &Phys) != S_OK)
{
return 0;
}
*File = DUMP_INFO_PAGE_FILE;
// Page files always have complete pages so the amount
// available is always the remainder of the page.
ULONG PageIndex = (ULONG)Virt & (g_TargetMachine->m_PageSize - 1);
*Avail = g_TargetMachine->m_PageSize - PageIndex;
return Phys;
}
else
{
return 0;
}
}
else
{
ULONG64 Offs;
// A summary dump will not contain any pages that
// are mapped by user-mode addresses. The virtual
// translation tables may still have valid mappings,
// though, so VToO will succeed. We want to suppress
// page-not-available messages in this case since
// the dump is known not to contain user pages.
// Record the provoking virtual address so that
// summary dump's PhysicalToOffset will know whether
// a message should be displayed or not.
m_ProvokingVirtAddr = Virt;
Offs = PhysicalToOffset(Phys, Avail);
m_ProvokingVirtAddr = 0;
return Offs;
}
}
ULONG
KernelFullSumDumpTargetInfo::GetCurrentProcessor(void)
{
ULONG i;
for (i = 0; i < g_TargetNumberProcessors; i++)
{
CROSS_PLATFORM_CONTEXT Context;
if (g_Target->GetContext(VIRTUAL_THREAD_HANDLE(i), &Context) == S_OK)
{
switch(g_TargetMachineType)
{
case IMAGE_FILE_MACHINE_I386:
if (Context.X86Nt5Context.Esp ==
((PX86_NT5_CONTEXT)m_HeaderContext)->Esp)
{
return i;
}
break;
case IMAGE_FILE_MACHINE_ALPHA:
if (g_SystemVersion <= NT_SVER_NT4)
{
if (Context.AlphaNt5Context.IntSp ==
(((PALPHA_CONTEXT)m_HeaderContext)->IntSp |
((ULONG64)((PALPHA_CONTEXT)m_HeaderContext)->
HighIntSp << 32)))
{
return i;
}
break;
}
// Fall through.
case IMAGE_FILE_MACHINE_AXP64:
if (Context.AlphaNt5Context.IntSp ==
((PALPHA_NT5_CONTEXT)m_HeaderContext)->IntSp)
{
return i;
}
break;
case IMAGE_FILE_MACHINE_IA64:
if (Context.IA64Context.IntSp ==
((PIA64_CONTEXT)m_HeaderContext)->IntSp)
{
return i;
}
break;
case IMAGE_FILE_MACHINE_AMD64:
if (Context.Amd64Context.Rsp ==
((PAMD64_CONTEXT)m_HeaderContext)->Rsp)
{
return i;
}
break;
}
}
}
// Give up and just pick the default processor.
return 0;
}
//----------------------------------------------------------------------------
//
// KernelSummaryDumpTargetInfo.
//
//----------------------------------------------------------------------------
void
KernelSummaryDumpTargetInfo::Uninitialize(void)
{
delete m_LocationCache;
m_LocationCache = NULL;
m_PageBitmapSize = 0;
ZeroMemory(&m_PageBitmap, sizeof(m_PageBitmap));
KernelDumpTargetInfo::Uninitialize();
}
void
KernelSummaryDumpTargetInfo::ConstructLocationCache(ULONG BitmapSize,
ULONG SizeOfBitMap,
PULONG Buffer)
{
PULONG Cache;
ULONG Index;
ULONG Offset;
m_PageBitmapSize = BitmapSize;
m_PageBitmap.SizeOfBitMap = SizeOfBitMap;
m_PageBitmap.Buffer = Buffer;
//
// Create a direct mapped cache.
//
Cache = new ULONG[BitmapSize];
if (!Cache)
{
// Not a failure; there just won't be a cache.
return;
}
//
// For each bit set in the bitmask fill the appropriate cache
// line location with the correct offset
//
Offset = 0;
for (Index = 0; Index < BitmapSize; Index++)
{
//
// If this page is in the summary dump fill in the offset
//
if ( RtlCheckBit (&m_PageBitmap, Index) )
{
Cache[ Index ] = Offset++;
}
}
//
// Assign back to the global storing the cache data.
//
m_LocationCache = Cache;
}
ULONG64
KernelSummaryDumpTargetInfo::SumPhysicalToOffset(ULONG HeaderSize,
ULONG64 Phys,
PULONG Avail)
{
ULONG Offset, j;
ULONG64 Page = Phys >> g_TargetMachine->m_PageShift;
//
// Make sure this page is included in the dump
//
if ( Page >= m_PageBitmapSize )
{
ErrOut("Page %x too large to be in the dump file.\n", Page);
return 0;
}
if ( !RtlCheckBit ( &m_PageBitmap, Page ) )
{
// If this page lookup is the result of a user-mode
// address translation it's guaranteed that
// the page won't be present since summary dumps
// exclude user pages. Don't bother displaying
// an error message in that case.
if (!m_ProvokingVirtAddr || m_ProvokingVirtAddr >= g_SystemRangeStart)
{
ErrOut("Page %x not present in the dump file.\n", Page);
}
return 0;
}
//
// If the cache exists then find the location the easy way
//
if (m_LocationCache != NULL)
{
Offset = m_LocationCache[ Page ];
}
else
{
//
// CAVEAT This code will never execute unless there is a failure
// creating the summary dump (cache) mapping information
//
//
// The page is in the summary dump locate it's offset
// Note: this is painful. The offset is a count of
// all the bits set up to this page
//
Offset = 0;
for (j = 0; j < m_PageBitmapSize; j++ )
{
if ( RtlCheckBit( &m_PageBitmap, j ) )
{
//
// If the offset is equal to the page were done.
//
if (j == Page)
{
break;
}
Offset++;
}
}
//
// Sanity check that we didn't drop out of the loop.
//
if ( j >= m_PageBitmapSize )
{
return 0;
}
}
//
// The actual address is calculated as follows
// Header size - Size of header plus summary dump header
//
ULONG PageIndex = (ULONG)Phys & (g_TargetMachine->m_PageSize - 1);
*Avail = g_TargetMachine->m_PageSize - PageIndex;
return HeaderSize + (Offset * g_TargetMachine->m_PageSize) + PageIndex;
}
HRESULT
KernelSummary32DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP32)g_DumpBase;
dprintf("Kernel Summary Dump File: "
"Only kernel address space is available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_DUMP;
ConstructLocationCache(m_Dump->Summary.BitmapSize,
m_Dump->Summary.Bitmap.SizeOfBitMap,
m_Dump->Summary.Bitmap.Buffer);
g_DumpInfoFiles[DUMP_INFO_DUMP].FileSize = m_Dump->Header.RequiredDumpSpace.QuadPart;
HRESULT Status = InitGlobals32(m_Dump);
if (Status != S_OK)
{
return Status;
}
return KernelFullSumDumpTargetInfo::Initialize();
}
void
KernelSummary32DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelSummaryDumpTargetInfo::Uninitialize();
}
HRESULT
KernelSummary32DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = EXTEND64(m_Dump->Header.BugCheckParameter1);
Args[1] = EXTEND64(m_Dump->Header.BugCheckParameter2);
Args[2] = EXTEND64(m_Dump->Header.BugCheckParameter3);
Args[3] = EXTEND64(m_Dump->Header.BugCheckParameter4);
return S_OK;
}
HRESULT
KernelSummary32DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status = E_NOINTERFACE;
PMEMORY_DUMP32 Dump = (PMEMORY_DUMP32)g_DumpBase;
if (Dump->Header.Signature != DUMP_SIGNATURE32 ||
Dump->Header.ValidDump != DUMP_VALID_DUMP32)
{
return Status;
}
__try
{
if (Dump->Header.DumpType == DUMP_TYPE_SUMMARY)
{
if (Dump->Summary.Signature != DUMP_SUMMARY_SIGNATURE)
{
// The header says it's a summary dump but
// it doesn't have a valid signature, so assume
// it's not a valid dump.
Status = HR_DUMP_CORRUPT;
}
else
{
Status = S_OK;
m_Dump = Dump;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
return Status;
}
ULONG64
KernelSummary32DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, PULONG Avail)
{
return SumPhysicalToOffset(m_Dump->Summary.HeaderSize, Phys, Avail);
}
HRESULT
KernelSummary64DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP64)g_DumpBase;
dprintf("Kernel Summary Dump File: "
"Only kernel address space is available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_DUMP;
ConstructLocationCache(m_Dump->Summary.BitmapSize,
m_Dump->Summary.Bitmap.SizeOfBitMap,
m_Dump->Summary.Bitmap.Buffer);
g_DumpInfoFiles[DUMP_INFO_DUMP].FileSize = m_Dump->Header.RequiredDumpSpace.QuadPart;
HRESULT Status = InitGlobals64(m_Dump);
if (Status != S_OK)
{
return Status;
}
return KernelFullSumDumpTargetInfo::Initialize();
}
void
KernelSummary32DumpTargetInfo::DumpDebug(void)
{
PSUMMARY_DUMP32 Sum = &m_Dump->Summary;
dprintf("----- 32 bit Kernel Summary Dump Analysis\n");
DumpHeader32(&m_Dump->Header);
dprintf("\nSUMMARY_DUMP32:\n");
dprintf("DumpOptions %08lx\n", Sum->DumpOptions);
dprintf("HeaderSize %08lx\n", Sum->HeaderSize);
dprintf("BitmapSize %08lx\n", Sum->BitmapSize);
dprintf("Pages %08lx\n", Sum->Pages);
dprintf("Bitmap.SizeOfBitMap %08lx\n", Sum->Bitmap.SizeOfBitMap);
KernelFullSumDumpTargetInfo::DumpDebug();
}
void
KernelSummary64DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelSummaryDumpTargetInfo::Uninitialize();
}
HRESULT
KernelSummary64DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = m_Dump->Header.BugCheckParameter1;
Args[1] = m_Dump->Header.BugCheckParameter2;
Args[2] = m_Dump->Header.BugCheckParameter3;
Args[3] = m_Dump->Header.BugCheckParameter4;
return S_OK;
}
HRESULT
KernelSummary64DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status = E_NOINTERFACE;
PMEMORY_DUMP64 Dump = (PMEMORY_DUMP64)g_DumpBase;
if (Dump->Header.Signature != DUMP_SIGNATURE64 ||
Dump->Header.ValidDump != DUMP_VALID_DUMP64)
{
return Status;
}
__try
{
if (Dump->Header.DumpType == DUMP_TYPE_SUMMARY)
{
if (Dump->Summary.Signature != DUMP_SUMMARY_SIGNATURE)
{
// The header says it's a summary dump but
// it doesn't have a valid signature, so assume
// it's not a valid dump.
Status = HR_DUMP_CORRUPT;
}
else
{
Status = S_OK;
m_Dump = Dump;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
return Status;
}
ULONG64
KernelSummary64DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, PULONG Avail)
{
return SumPhysicalToOffset(m_Dump->Summary.HeaderSize, Phys, Avail);
}
void
KernelSummary64DumpTargetInfo::DumpDebug(void)
{
PSUMMARY_DUMP64 Sum = &m_Dump->Summary;
dprintf("----- 64 bit Kernel Summary Dump Analysis\n");
DumpHeader64(&m_Dump->Header);
dprintf("\nSUMMARY_DUMP64:\n");
dprintf("DumpOptions %08lx\n", Sum->DumpOptions);
dprintf("HeaderSize %08lx\n", Sum->HeaderSize);
dprintf("BitmapSize %08lx\n", Sum->BitmapSize);
dprintf("Pages %08lx\n", Sum->Pages);
dprintf("Bitmap.SizeOfBitMap %08lx\n", Sum->Bitmap.SizeOfBitMap);
KernelFullSumDumpTargetInfo::DumpDebug();
}
//----------------------------------------------------------------------------
//
// KernelTriageDumpTargetInfo.
//
//----------------------------------------------------------------------------
void
KernelTriageDumpTargetInfo::Uninitialize(void)
{
m_PrcbOffset = 0;
MemoryMap_Destroy();
KernelDumpTargetInfo::Uninitialize();
g_TriageDumpHasDebuggerData = FALSE;
}
void
KernelTriageDumpTargetInfo::NearestDifferentlyValidOffsets(ULONG64 Offset,
PULONG64 NextOffset,
PULONG64 NextPage)
{
//
// In a minidump there can be memory fragments mapped at
// arbitrary locations so we cannot assume validity
// changes on page boundaries. We could attempt to
// scan the memory list and try to find the closest valid
// chunk of memory but it's rarely important that
// complete accuracy is required. Just return the
// next byte.
//
if (NextOffset != NULL)
{
*NextOffset = Offset + 1;
}
if (NextPage != NULL)
{
*NextPage = (Offset + g_TargetMachine->m_PageSize) &
~((ULONG64)g_TargetMachine->m_PageSize - 1);
}
}
HRESULT
KernelTriageDumpTargetInfo::ReadVirtual(
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
// All virtual memory is contained in the memory map.
return MemoryMap_ReadMemory(Offset, Buffer, BufferSize,
BytesRead) ? S_OK : E_FAIL;
}
HRESULT
KernelTriageDumpTargetInfo::GetProcessorSystemDataOffset(
IN ULONG Processor,
IN ULONG Index,
OUT PULONG64 Offset
)
{
if (Processor != GetCurrentProcessor())
{
return E_INVALIDARG;
}
ULONG64 Prcb = g_DumpKiProcessors[Processor];
HRESULT Status;
switch(Index)
{
case DEBUG_DATA_KPCR_OFFSET:
// We don't have a full PCR, just a PRCB.
return E_FAIL;
case DEBUG_DATA_KPRCB_OFFSET:
*Offset = Prcb;
break;
case DEBUG_DATA_KTHREAD_OFFSET:
if ((Status = ReadPointer(g_TargetMachine,
Prcb + (g_TargetMachine->m_Ptr64 ?
KPRCB_CURRENT_THREAD_OFFSET_64 :
KPRCB_CURRENT_THREAD_OFFSET_32),
Offset)) != S_OK)
{
return Status;
}
break;
}
return S_OK;
}
HRESULT
KernelTriageDumpTargetInfo::GetTargetContext(
ULONG64 Thread,
PVOID Context
)
{
// We only have the current context in a triage dump.
if (VIRTUAL_THREAD_INDEX(Thread) != GetCurrentProcessor())
{
return E_INVALIDARG;
}
// The KPRCB could be used to retrieve context information as in
// KernelFullSumDumpTargetInfo::GetTargetContext but
// for consistency the header context is used since it's
// the officially advertised place.
memcpy(Context, m_HeaderContext, g_TargetMachine->m_SizeTargetContext);
return S_OK;
}
HRESULT
KernelTriageDumpTargetInfo::GetSelDescriptor(MachineInfo* Machine,
ULONG64 Thread,
ULONG Selector,
PDESCRIPTOR64 Desc)
{
return EmulateNtSelDescriptor(Machine, Selector, Desc);
}
ULONG64
KernelTriageDumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
ULONG64 Base;
ULONG Size;
PVOID Mapping, Param;
*File = DUMP_INFO_DUMP;
// ReadVirtual is overridden to read the memory map directly
// so this function will only be called from the generic
// WriteVirtual. We can only write regions mapped out of
// the dump so only return memory regions that don't have
// an image pointer as their user data.
if (MemoryMap_GetRegionInfo(Virt, &Base, &Size, &Mapping, &Param) &&
Param == NULL)
{
*Avail = Size - (ULONG)(Virt - Base);
return (PUCHAR)Mapping - (PUCHAR)g_DumpBase;
}
return 0;
}
ULONG
KernelTriageDumpTargetInfo::GetCurrentProcessor(void)
{
// Extract the processor number from the
// PRCB in the dump.
return *(PUCHAR)
IndexByByte(g_DumpBase, m_PrcbOffset +
g_TargetMachine->m_OffsetPrcbNumber);
}
HRESULT
KernelTriageDumpTargetInfo::MapMemoryRegions(ULONG PrcbOffset,
ULONG ThreadOffset,
ULONG ProcessOffset,
ULONG64 TopOfStack,
ULONG SizeOfCallStack,
ULONG CallStackOffset,
ULONG64 BStoreLimit,
ULONG SizeOfBStore,
ULONG BStoreOffset,
ULONG64 DataPageAddress,
ULONG DataPageOffset,
ULONG DataPageSize,
ULONG64 DebuggerDataAddress,
ULONG DebuggerDataOffset,
ULONG DebuggerDataSize,
ULONG MmDataOffset,
ULONG DataBlocksOffset,
ULONG DataBlocksCount)
{
HRESULT Status;
if (!MemoryMap_Create())
{
return E_OUTOFMEMORY;
}
// Technically a triage dump doesn't have to contain a KPRCB
// but we really want it to have one. Nobody generates them
// without a KPRCB so this is really just a sanity check.
if (PrcbOffset == 0)
{
ErrOut("Dump does not contain KPRCB\n");
return E_FAIL;
}
// Set this first so GetCurrentProcessor works.
m_PrcbOffset = PrcbOffset;
ULONG Processor = GetCurrentProcessor();
// The dump contains one PRCB for the current processor.
// Map the PRCB at the processor-zero location because
// that location should not ever have some other mapping
// for the dump.
g_DumpKiProcessors[Processor] = g_TargetMachine->m_TriagePrcbOffset;
if ((Status = MemoryMap_AddRegion(g_DumpKiProcessors[Processor],
g_TargetMachine->m_SizePrcb,
IndexByByte(g_DumpBase, PrcbOffset),
NULL, FALSE)) != S_OK)
{
return Status;
}
//
// Add ETHREAD and EPROCESS memory regions if available.
//
if (ThreadOffset != 0)
{
PVOID CurThread =
IndexByByte(g_DumpBase, PrcbOffset +
(g_TargetMachine->m_Ptr64 ?
KPRCB_CURRENT_THREAD_OFFSET_64 :
KPRCB_CURRENT_THREAD_OFFSET_32));
ULONG64 ThreadAddr, ProcAddr;
if (g_TargetMachine->m_Ptr64)
{
ThreadAddr = *(PULONG64)CurThread;
ProcAddr = *(PULONG64)
IndexByByte(g_DumpBase, ThreadOffset +
g_TargetMachine->m_OffsetKThreadApcProcess);
}
else
{
ThreadAddr = EXTEND64(*(PULONG)CurThread);
ProcAddr = EXTEND64(*(PULONG)
IndexByByte(g_DumpBase, ThreadOffset +
g_TargetMachine->m_OffsetKThreadApcProcess));
}
if ((Status = MemoryMap_AddRegion(ThreadAddr,
g_TargetMachine->m_SizeEThread,
IndexByByte(g_DumpBase,
ThreadOffset),
NULL, TRUE)) != S_OK)
{
return Status;
}
if (ProcessOffset != 0)
{
if ((Status = MemoryMap_AddRegion(ProcAddr,
g_TargetMachine->m_SizeEProcess,
IndexByByte(g_DumpBase,
ProcessOffset),
NULL, TRUE)) != S_OK)
{
return Status;
}
}
else
{
WarnOut("Mini Kernel Dump does not have "
"process information\n");
}
}
else
{
WarnOut("Mini Kernel Dump does not have thread information\n");
}
// Add the backing store region.
if (g_TargetMachineType == IMAGE_FILE_MACHINE_IA64)
{
if (BStoreOffset != 0)
{
if ((Status = MemoryMap_AddRegion(BStoreLimit - SizeOfBStore,
SizeOfBStore,
IndexByByte(g_DumpBase,
BStoreOffset),
NULL, TRUE)) != S_OK)
{
return Status;
}
}
else
{
WarnOut("Mini Kernel Dump does not have "
"backing store information\n");
}
}
// Add data page if available
if (DataPageAddress)
{
if ((Status = MemoryMap_AddRegion(DataPageAddress, DataPageSize,
IndexByByte(g_DumpBase,
DataPageOffset),
NULL, TRUE)) != S_OK)
{
return Status;
}
}
// Map any debugger data.
if (DebuggerDataAddress)
{
if ((Status = MemoryMap_AddRegion(DebuggerDataAddress,
DebuggerDataSize,
IndexByByte(g_DumpBase,
DebuggerDataOffset),
NULL, TRUE)) != S_OK)
{
return Status;
}
g_TriageDumpHasDebuggerData = TRUE;
if (MmDataOffset)
{
MM_TRIAGE_TRANSLATION* Trans = g_MmTriageTranslations;
// Map memory fragments for MM Triage information
// that equates to entries in the debugger data.
while (Trans->DebuggerDataOffset > 0)
{
ULONG64 DbgData;
ULONG MmData;
ULONG Size;
DbgData = *(ULONG64 UNALIGNED*)
IndexByByte(g_DumpBase, DebuggerDataOffset +
Trans->DebuggerDataOffset);
Size = sizeof(ULONG);
if (g_TargetMachine->m_Ptr64)
{
MmData = MmDataOffset + Trans->Triage64Offset;
if (Trans->PtrSize)
{
Size = sizeof(ULONG64);
}
}
else
{
MmData = MmDataOffset + Trans->Triage32Offset;
DbgData = EXTEND64(DbgData);
}
if ((Status = MemoryMap_AddRegion(DbgData, Size,
IndexByByte(g_DumpBase,
MmData),
NULL, TRUE)) != S_OK)
{
return Status;
}
Trans++;
}
}
}
// Map arbitrary data blocks.
if (DataBlocksCount > 0)
{
PTRIAGE_DATA_BLOCK Block;
Block = (PTRIAGE_DATA_BLOCK)IndexByByte(g_DumpBase, DataBlocksOffset);
while (DataBlocksCount-- > 0)
{
if ((Status = MemoryMap_AddRegion(Block->Address,
Block->Size,
IndexByByte(g_DumpBase,
Block->Offset),
NULL, TRUE)) != S_OK)
{
return Status;
}
Block++;
}
}
// Add the stack to the valid memory region.
return MemoryMap_AddRegion(TopOfStack, SizeOfCallStack,
IndexByByte(g_DumpBase, CallStackOffset),
NULL, TRUE);
}
HRESULT
KernelTriage32DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP32)g_DumpBase;
dprintf("Mini Kernel Dump File: "
"Only registers and stack trace are available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_SMALL_DUMP;
PTRIAGE_DUMP32 Triage = &m_Dump->Triage;
HRESULT Status = InitGlobals32(m_Dump);
if (Status != S_OK)
{
return Status;
}
//
// Optional memory page
//
ULONG64 DataPageAddress = 0;
ULONG DataPageOffset = 0;
ULONG DataPageSize = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATAPAGE))
{
DataPageAddress = Triage->DataPageAddress;
DataPageOffset = Triage->DataPageOffset;
DataPageSize = Triage->DataPageSize;
}
//
// Optional KDDEBUGGER_DATA64.
//
ULONG64 DebuggerDataAddress = 0;
ULONG DebuggerDataOffset = 0;
ULONG DebuggerDataSize = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DEBUGGER_DATA))
{
// DebuggerDataBlock field must be valid if the dump is
// new enough to have a data block in it.
DebuggerDataAddress = EXTEND64(m_Dump->Header.KdDebuggerDataBlock);
DebuggerDataOffset = Triage->DebuggerDataOffset;
DebuggerDataSize = Triage->DebuggerDataSize;
}
//
// Optional data blocks.
//
ULONG DataBlocksOffset = 0;
ULONG DataBlocksCount = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATA_BLOCKS))
{
DataBlocksOffset = Triage->DataBlocksOffset;
DataBlocksCount = Triage->DataBlocksCount;
}
//
// We store the servicepack version in the header because we
// don't store the actual memory
//
SetTargetNtCsdVersion(m_Dump->Triage.ServicePackBuild);
return MapMemoryRegions(Triage->PrcbOffset, Triage->ThreadOffset,
Triage->ProcessOffset,
EXTEND64(Triage->TopOfStack),
Triage->SizeOfCallStack, Triage->CallStackOffset,
0, 0, 0,
DataPageAddress, DataPageOffset, DataPageSize,
DebuggerDataAddress, DebuggerDataOffset,
DebuggerDataSize, Triage->MmOffset,
DataBlocksOffset, DataBlocksCount);
}
void
KernelTriage32DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelTriageDumpTargetInfo::Uninitialize();
}
HRESULT
KernelTriage32DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = EXTEND64(m_Dump->Header.BugCheckParameter1);
Args[1] = EXTEND64(m_Dump->Header.BugCheckParameter2);
Args[2] = EXTEND64(m_Dump->Header.BugCheckParameter3);
Args[3] = EXTEND64(m_Dump->Header.BugCheckParameter4);
return S_OK;
}
HRESULT
KernelTriage32DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status = E_NOINTERFACE;
PMEMORY_DUMP32 Dump = (PMEMORY_DUMP32)g_DumpBase;
if (Dump->Header.Signature != DUMP_SIGNATURE32 ||
Dump->Header.ValidDump != DUMP_VALID_DUMP32)
{
return Status;
}
__try
{
if (Dump->Header.DumpType == DUMP_TYPE_TRIAGE)
{
if (*(PULONG)IndexByByte(Dump, Dump->Triage.SizeOfDump -
sizeof(ULONG)) != TRIAGE_DUMP_VALID)
{
// The header says it's a triage dump but
// it doesn't have a valid signature, so assume
// it's not a valid dump.
Status = HR_DUMP_CORRUPT;
}
else
{
Status = S_OK;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if (Status != S_OK)
{
return Status;
}
// Make sure that the dump has the minimal information that
// we want.
if (Dump->Triage.ContextOffset == 0 ||
Dump->Triage.ExceptionOffset == 0 ||
Dump->Triage.PrcbOffset == 0 ||
Dump->Triage.CallStackOffset == 0)
{
ErrOut("Mini Kernel Dump does not contain enough "
"information to be debugged\n");
return E_FAIL;
}
// We rely on being able to directly access the entire
// content of the dump through the default view so
// ensure that it's possible.
*BaseMapSize = g_DumpInfoFiles[DUMP_INFO_DUMP].FileSize;
m_Dump = Dump;
return Status;
}
ModuleInfo*
KernelTriage32DumpTargetInfo::GetModuleInfo(BOOL UserMode)
{
return UserMode ? NULL : &g_KernelTriage32ModuleIterator;
}
UnloadedModuleInfo*
KernelTriage32DumpTargetInfo::GetUnloadedModuleInfo(void)
{
return &g_KernelTriage32UnloadedModuleIterator;
}
void
KernelTriage32DumpTargetInfo::DumpDebug(void)
{
PTRIAGE_DUMP32 Triage = &m_Dump->Triage;
dprintf("----- 32 bit Kernel Mini Dump Analysis\n");
DumpHeader32(&m_Dump->Header);
dprintf("MiniDumpFields %08lx \n", m_Dump->Header.MiniDumpFields);
dprintf("\nTRIAGE_DUMP32:\n");
dprintf("ServicePackBuild %08lx \n", Triage->ServicePackBuild );
dprintf("SizeOfDump %08lx \n", Triage->SizeOfDump );
dprintf("ValidOffset %08lx \n", Triage->ValidOffset );
dprintf("ContextOffset %08lx \n", Triage->ContextOffset );
dprintf("ExceptionOffset %08lx \n", Triage->ExceptionOffset );
dprintf("MmOffset %08lx \n", Triage->MmOffset );
dprintf("UnloadedDriversOffset %08lx \n", Triage->UnloadedDriversOffset );
dprintf("PrcbOffset %08lx \n", Triage->PrcbOffset );
dprintf("ProcessOffset %08lx \n", Triage->ProcessOffset );
dprintf("ThreadOffset %08lx \n", Triage->ThreadOffset );
dprintf("CallStackOffset %08lx \n", Triage->CallStackOffset );
dprintf("SizeOfCallStack %08lx \n", Triage->SizeOfCallStack );
dprintf("DriverListOffset %08lx \n", Triage->DriverListOffset );
dprintf("DriverCount %08lx \n", Triage->DriverCount );
dprintf("StringPoolOffset %08lx \n", Triage->StringPoolOffset );
dprintf("StringPoolSize %08lx \n", Triage->StringPoolSize );
dprintf("BrokenDriverOffset %08lx \n", Triage->BrokenDriverOffset );
dprintf("TriageOptions %08lx \n", Triage->TriageOptions );
dprintf("TopOfStack %08lx \n", Triage->TopOfStack );
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATAPAGE))
{
dprintf("DataPageAddress %08lx \n", Triage->DataPageAddress );
dprintf("DataPageOffset %08lx \n", Triage->DataPageOffset );
dprintf("DataPageSize %08lx \n", Triage->DataPageSize );
}
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DEBUGGER_DATA))
{
dprintf("DebuggerDataOffset %08lx \n", Triage->DebuggerDataOffset);
dprintf("DebuggerDataSize %08lx \n", Triage->DebuggerDataSize );
}
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATA_BLOCKS))
{
dprintf("DataBlocksOffset %08lx \n", Triage->DataBlocksOffset );
dprintf("DataBlocksCount %08lx \n", Triage->DataBlocksCount );
}
}
HRESULT
KernelTriage64DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP64)g_DumpBase;
dprintf("Mini Kernel Dump File: "
"Only registers and stack trace are available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_SMALL_DUMP;
PTRIAGE_DUMP64 Triage = &m_Dump->Triage;
HRESULT Status = InitGlobals64(m_Dump);
if (Status != S_OK)
{
return Status;
}
//
// Optional memory page
//
ULONG64 DataPageAddress = 0;
ULONG DataPageOffset = 0;
ULONG DataPageSize = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATAPAGE))
{
DataPageAddress = Triage->DataPageAddress;
DataPageOffset = Triage->DataPageOffset;
DataPageSize = Triage->DataPageSize;
}
//
// Optional KDDEBUGGER_DATA64.
//
ULONG64 DebuggerDataAddress = 0;
ULONG DebuggerDataOffset = 0;
ULONG DebuggerDataSize = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DEBUGGER_DATA))
{
// DebuggerDataBlock field must be valid if the dump is
// new enough to have a data block in it.
DebuggerDataAddress = m_Dump->Header.KdDebuggerDataBlock;
DebuggerDataOffset = Triage->DebuggerDataOffset;
DebuggerDataSize = Triage->DebuggerDataSize;
}
//
// Optional data blocks.
//
ULONG DataBlocksOffset = 0;
ULONG DataBlocksCount = 0;
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATA_BLOCKS))
{
DataBlocksOffset = Triage->DataBlocksOffset;
DataBlocksCount = Triage->DataBlocksCount;
}
//
// We store the servicepack version in the header because we
// don't store the actual memory
//
SetTargetNtCsdVersion(m_Dump->Triage.ServicePackBuild);
return MapMemoryRegions(Triage->PrcbOffset, Triage->ThreadOffset,
Triage->ProcessOffset, Triage->TopOfStack,
Triage->SizeOfCallStack, Triage->CallStackOffset,
Triage->ArchitectureSpecific.Ia64.LimitOfBStore,
Triage->ArchitectureSpecific.Ia64.SizeOfBStore,
Triage->ArchitectureSpecific.Ia64.BStoreOffset,
DataPageAddress, DataPageOffset, DataPageSize,
DebuggerDataAddress, DebuggerDataOffset,
DebuggerDataSize, Triage->MmOffset,
DataBlocksOffset, DataBlocksCount);
}
void
KernelTriage64DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelTriageDumpTargetInfo::Uninitialize();
}
HRESULT
KernelTriage64DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = m_Dump->Header.BugCheckParameter1;
Args[1] = m_Dump->Header.BugCheckParameter2;
Args[2] = m_Dump->Header.BugCheckParameter3;
Args[3] = m_Dump->Header.BugCheckParameter4;
return S_OK;
}
HRESULT
KernelTriage64DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status = E_NOINTERFACE;
PMEMORY_DUMP64 Dump = (PMEMORY_DUMP64)g_DumpBase;
if (Dump->Header.Signature != DUMP_SIGNATURE64 ||
Dump->Header.ValidDump != DUMP_VALID_DUMP64)
{
return Status;
}
__try
{
if (Dump->Header.DumpType == DUMP_TYPE_TRIAGE)
{
if (*(PULONG)IndexByByte(Dump, Dump->Triage.SizeOfDump -
sizeof(ULONG)) != TRIAGE_DUMP_VALID)
{
// The header says it's a triage dump but
// it doesn't have a valid signature, so assume
// it's not a valid dump.
Status = HR_DUMP_CORRUPT;
}
else
{
Status = S_OK;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if (Status != S_OK)
{
return Status;
}
// Make sure that the dump has the minimal information that
// we want.
if (Dump->Triage.ContextOffset == 0 ||
Dump->Triage.ExceptionOffset == 0 ||
Dump->Triage.PrcbOffset == 0 ||
Dump->Triage.CallStackOffset == 0)
{
ErrOut("Mini Kernel Dump does not contain enough "
"information to be debugged\n");
return E_FAIL;
}
// We rely on being able to directly access the entire
// content of the dump through the default view so
// ensure that it's possible.
*BaseMapSize = g_DumpInfoFiles[DUMP_INFO_DUMP].FileSize;
m_Dump = Dump;
return Status;
}
ModuleInfo*
KernelTriage64DumpTargetInfo::GetModuleInfo(BOOL UserMode)
{
return UserMode ? NULL : &g_KernelTriage64ModuleIterator;
}
UnloadedModuleInfo*
KernelTriage64DumpTargetInfo::GetUnloadedModuleInfo(void)
{
return &g_KernelTriage64UnloadedModuleIterator;
}
void
KernelTriage64DumpTargetInfo::DumpDebug(void)
{
PTRIAGE_DUMP64 Triage = &m_Dump->Triage;
dprintf("----- 64 bit Kernel Mini Dump Analysis\n");
DumpHeader64(&m_Dump->Header);
dprintf("MiniDumpFields %08lx \n", m_Dump->Header.MiniDumpFields);
dprintf("\nTRIAGE_DUMP64:\n");
dprintf("ServicePackBuild %08lx \n", Triage->ServicePackBuild );
dprintf("SizeOfDump %08lx \n", Triage->SizeOfDump );
dprintf("ValidOffset %08lx \n", Triage->ValidOffset );
dprintf("ContextOffset %08lx \n", Triage->ContextOffset );
dprintf("ExceptionOffset %08lx \n", Triage->ExceptionOffset );
dprintf("MmOffset %08lx \n", Triage->MmOffset );
dprintf("UnloadedDriversOffset %08lx \n", Triage->UnloadedDriversOffset );
dprintf("PrcbOffset %08lx \n", Triage->PrcbOffset );
dprintf("ProcessOffset %08lx \n", Triage->ProcessOffset );
dprintf("ThreadOffset %08lx \n", Triage->ThreadOffset );
dprintf("CallStackOffset %08lx \n", Triage->CallStackOffset );
dprintf("SizeOfCallStack %08lx \n", Triage->SizeOfCallStack );
dprintf("DriverListOffset %08lx \n", Triage->DriverListOffset );
dprintf("DriverCount %08lx \n", Triage->DriverCount );
dprintf("StringPoolOffset %08lx \n", Triage->StringPoolOffset );
dprintf("StringPoolSize %08lx \n", Triage->StringPoolSize );
dprintf("BrokenDriverOffset %08lx \n", Triage->BrokenDriverOffset );
dprintf("TriageOptions %08lx \n", Triage->TriageOptions );
dprintf("TopOfStack %s \n",
FormatAddr64(Triage->TopOfStack));
dprintf("BStoreOffset %08lx \n",
Triage->ArchitectureSpecific.Ia64.BStoreOffset );
dprintf("SizeOfBStore %08lx \n",
Triage->ArchitectureSpecific.Ia64.SizeOfBStore );
dprintf("LimitOfBStore %s \n",
FormatAddr64(Triage->ArchitectureSpecific.Ia64.LimitOfBStore));
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATAPAGE))
{
dprintf("DataPageAddress %s \n",
FormatAddr64(Triage->DataPageAddress));
dprintf("DataPageOffset %08lx \n", Triage->DataPageOffset );
dprintf("DataPageSize %08lx \n", Triage->DataPageSize );
}
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DEBUGGER_DATA))
{
dprintf("DebuggerDataOffset %08lx \n", Triage->DebuggerDataOffset);
dprintf("DebuggerDataSize %08lx \n", Triage->DebuggerDataSize );
}
if (((m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_BASIC_INFO) ==
TRIAGE_DUMP_BASIC_INFO) &&
(m_Dump->Header.MiniDumpFields & TRIAGE_DUMP_DATA_BLOCKS))
{
dprintf("DataBlocksOffset %08lx \n", Triage->DataBlocksOffset );
dprintf("DataBlocksCount %08lx \n", Triage->DataBlocksCount );
}
}
//----------------------------------------------------------------------------
//
// KernelFullDumpTargetInfo.
//
//----------------------------------------------------------------------------
HRESULT
KernelFull32DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP32)g_DumpBase;
dprintf("Kernel Dump File: Full address space is available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_FULL_DUMP;
HRESULT Status = InitGlobals32(m_Dump);
if (Status != S_OK)
{
return Status;
}
return KernelFullSumDumpTargetInfo::Initialize();
}
void
KernelFull32DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelDumpTargetInfo::Uninitialize();
}
HRESULT
KernelFull32DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = EXTEND64(m_Dump->Header.BugCheckParameter1);
Args[1] = EXTEND64(m_Dump->Header.BugCheckParameter2);
Args[2] = EXTEND64(m_Dump->Header.BugCheckParameter3);
Args[3] = EXTEND64(m_Dump->Header.BugCheckParameter4);
return S_OK;
}
HRESULT
KernelFull32DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
m_Dump = (PMEMORY_DUMP32)g_DumpBase;
if (m_Dump->Header.Signature != DUMP_SIGNATURE32 ||
m_Dump->Header.ValidDump != DUMP_VALID_DUMP32 ||
(m_Dump->Header.DumpType != DUMP_SIGNATURE32 &&
m_Dump->Header.DumpType != DUMP_TYPE_FULL))
{
m_Dump = NULL;
return E_NOINTERFACE;
}
// Summary and triage dumps must be checked before this
// so there's nothing left to check.
return S_OK;
}
ULONG64
KernelFull32DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, PULONG Avail)
{
ULONG PageIndex = (ULONG)Phys & (g_TargetMachine->m_PageSize - 1);
*Avail = g_TargetMachine->m_PageSize - PageIndex;
PPHYSICAL_MEMORY_DESCRIPTOR32 PhysDesc = &m_Dump->Header.PhysicalMemoryBlock;
ULONG64 Page = Phys >> g_TargetMachine->m_PageShift;
//
// Memory start after one page.
//
ULONG64 Offset = 1;
ULONG j = 0;
while (j < PhysDesc->NumberOfRuns)
{
if ((Page >= PhysDesc->Run[j].BasePage) &&
(Page < (PhysDesc->Run[j].BasePage +
PhysDesc->Run[j].PageCount)))
{
Offset += Page - PhysDesc->Run[j].BasePage;
return Offset * g_TargetMachine->m_PageSize + PageIndex;
}
Offset += PhysDesc->Run[j].PageCount;
j += 1;
}
KdOut("Physical Memory Address %s is greater than MaxPhysicalAddress\n",
FormatDisp64(Phys));
return 0;
}
void
KernelFull32DumpTargetInfo::DumpDebug(void)
{
PPHYSICAL_MEMORY_DESCRIPTOR32 PhysDesc =
&m_Dump->Header.PhysicalMemoryBlock;
ULONG PageSize = g_TargetMachine->m_PageSize;
dprintf("----- 32 bit Kernel Full Dump Analysis\n");
DumpHeader32(&m_Dump->Header);
dprintf("\nPhysical Memory Description:\n");
dprintf("Number of runs: %d\n", PhysDesc->NumberOfRuns);
dprintf(" FileOffset Start Address Length\n");
ULONG j = 0;
ULONG Offset = 1;
while (j < PhysDesc->NumberOfRuns)
{
dprintf(" %08lx %08lx %08lx\n",
Offset * PageSize,
PhysDesc->Run[j].BasePage * PageSize,
PhysDesc->Run[j].PageCount * PageSize);
Offset += PhysDesc->Run[j].PageCount;
j += 1;
}
j--;
dprintf("Last Page: %08lx %08lx\n",
(Offset - 1) * PageSize,
(PhysDesc->Run[j].BasePage + PhysDesc->Run[j].PageCount - 1) *
PageSize);
KernelFullSumDumpTargetInfo::DumpDebug();
}
HRESULT
KernelFull64DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Dump = (PMEMORY_DUMP64)g_DumpBase;
dprintf("Kernel Dump File: Full address space is available\n\n");
g_TargetClass = DEBUG_CLASS_KERNEL;
g_TargetClassQualifier = DEBUG_KERNEL_FULL_DUMP;
HRESULT Status = InitGlobals64(m_Dump);
if (Status != S_OK)
{
return Status;
}
return KernelFullSumDumpTargetInfo::Initialize();
}
void
KernelFull64DumpTargetInfo::Uninitialize(void)
{
m_Dump = NULL;
KernelDumpTargetInfo::Uninitialize();
}
HRESULT
KernelFull64DumpTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
{
*Code = m_Dump->Header.BugCheckCode;
Args[0] = m_Dump->Header.BugCheckParameter1;
Args[1] = m_Dump->Header.BugCheckParameter2;
Args[2] = m_Dump->Header.BugCheckParameter3;
Args[3] = m_Dump->Header.BugCheckParameter4;
return S_OK;
}
HRESULT
KernelFull64DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
m_Dump = (PMEMORY_DUMP64)g_DumpBase;
if (m_Dump->Header.Signature != DUMP_SIGNATURE64 ||
m_Dump->Header.ValidDump != DUMP_VALID_DUMP64 ||
(m_Dump->Header.DumpType != DUMP_SIGNATURE32 &&
m_Dump->Header.DumpType != DUMP_TYPE_FULL))
{
m_Dump = NULL;
return E_NOINTERFACE;
}
// Summary and triage dumps must be checked before this
// so there's nothing left to check.
return S_OK;
}
ULONG64
KernelFull64DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, PULONG Avail)
{
ULONG PageIndex = (ULONG)Phys & (g_TargetMachine->m_PageSize - 1);
*Avail = g_TargetMachine->m_PageSize - PageIndex;
PPHYSICAL_MEMORY_DESCRIPTOR64 PhysDesc = &m_Dump->Header.PhysicalMemoryBlock;
ULONG64 Page = Phys >> g_TargetMachine->m_PageShift;
//
// Memory start after one page.
//
ULONG64 Offset = 1;
ULONG j = 0;
while (j < PhysDesc->NumberOfRuns)
{
if ((Page >= PhysDesc->Run[j].BasePage) &&
(Page < (PhysDesc->Run[j].BasePage +
PhysDesc->Run[j].PageCount)))
{
Offset += Page - PhysDesc->Run[j].BasePage;
return Offset * g_TargetMachine->m_PageSize + PageIndex;
}
Offset += PhysDesc->Run[j].PageCount;
j += 1;
}
KdOut("Physical Memory Address %I64 is greater than MaxPhysicalAddress\n",
Phys);
return 0;
}
void
KernelFull64DumpTargetInfo::DumpDebug(void)
{
PPHYSICAL_MEMORY_DESCRIPTOR64 PhysDesc =
&m_Dump->Header.PhysicalMemoryBlock;
ULONG PageSize = g_TargetMachine->m_PageSize;
dprintf("----- 64 bit Kernel Full Dump Analysis\n");
DumpHeader64(&m_Dump->Header);
dprintf("\nPhysical Memory Description:\n");
dprintf("Number of runs: %d\n", PhysDesc->NumberOfRuns);
dprintf(" FileOffset Start Address Length\n");
ULONG j = 0;
ULONG64 Offset = 1;
while (j < PhysDesc->NumberOfRuns)
{
dprintf(" %s %s %s\n",
FormatAddr64(Offset * PageSize),
FormatAddr64(PhysDesc->Run[j].BasePage * PageSize),
FormatAddr64(PhysDesc->Run[j].PageCount * PageSize));
Offset += PhysDesc->Run[j].PageCount;
j += 1;
}
j--;
dprintf("Last Page: %s %s\n",
FormatAddr64((Offset - 1) * PageSize),
FormatAddr64((PhysDesc->Run[j].BasePage +
PhysDesc->Run[j].PageCount - 1) *
PageSize));
KernelFullSumDumpTargetInfo::DumpDebug();
}
//----------------------------------------------------------------------------
//
// UserDumpTargetInfo.
//
//----------------------------------------------------------------------------
void
UserDumpTargetInfo::Uninitialize(void)
{
m_HighestMemoryRegion32 = 0;
m_EventProcess = 0;
m_EventThread = 0;
m_ThreadCount = 0;
DumpTargetInfo::Uninitialize();
}
HRESULT
UserDumpTargetInfo::GetThreadInfoDataOffset(PTHREAD_INFO Thread,
ULONG64 ThreadHandle,
PULONG64 Offset)
{
if (Thread != NULL && Thread->DataOffset != 0)
{
*Offset = Thread->DataOffset;
return S_OK;
}
BOOL ContextThread = FALSE;
if (Thread != NULL)
{
ThreadHandle = Thread->Handle;
ContextThread = Thread == g_RegContextThread;
}
else if (ThreadHandle == NULL)
{
ThreadHandle = g_CurrentProcess->CurrentThread->Handle;
ContextThread = g_CurrentProcess->CurrentThread == g_RegContextThread;
}
HRESULT Status;
ULONG64 TebAddr;
ULONG Id, Suspend;
if ((Status = GetThreadInfo(VIRTUAL_THREAD_INDEX(ThreadHandle),
&Id, &Suspend, &TebAddr)) != S_OK)
{
ErrOut("User dump thread %u not available\n",
VIRTUAL_THREAD_INDEX(ThreadHandle));
return Status;
}
if (TebAddr == 0)
{
//
// NT4 dumps have a bug - they do not fill in the TEB value.
// luckily, for pretty much all user mode processes, the
// TEBs start two pages down from the highest user address.
// For example, on x86 we try 0x7FFDE000 (on 3GB systems 0xBFFDE000).
//
if (!g_TargetMachine->m_Ptr64 && m_HighestMemoryRegion32 > 0x80000000)
{
TebAddr = 0xbffe0000;
}
else
{
TebAddr = 0x7ffe0000;
}
TebAddr -= 2 * g_TargetMachine->m_PageSize;
//
// Try and validate that this is really a TEB.
// If it isn't search lower memory addresses for
// a while, but don't get hung up here.
//
ULONG64 TebCheck = TebAddr;
ULONG Attempts = 8;
BOOL IsATeb = FALSE;
while (Attempts > 0)
{
ULONG64 TebSelf;
// Check if this looks like a TEB. TEBs have a
// self pointer in the TIB that's useful for this.
if (ReadPointer(g_TargetMachine,
TebCheck + 6 * (g_TargetMachine->m_Ptr64 ? 8 : 4),
&TebSelf) == S_OK &&
TebSelf == TebCheck)
{
// It looks like it's a TEB. Remember this address
// so that if all searching fails we'll at least
// return some TEB.
TebAddr = TebCheck;
IsATeb = TRUE;
// If the given thread is the current register context
// thread we can check and see if the current SP falls
// within the stack limits in the TEB.
if (ContextThread)
{
ULONG64 StackBase, StackLimit;
ADDR Sp;
g_TargetMachine->GetSP(&Sp);
if (g_TargetMachine->m_Ptr64)
{
StackBase = STACK_BASE_FROM_TEB64;
StackLimit = StackBase + 8;
}
else
{
StackBase = STACK_BASE_FROM_TEB32;
StackLimit = StackBase + 4;
}
if (ReadPointer(g_TargetMachine,
TebCheck + StackBase,
&StackBase) == S_OK &&
ReadPointer(g_TargetMachine,
TebCheck + StackLimit,
&StackLimit) == S_OK &&
Flat(Sp) >= StackLimit &&
Flat(Sp) <= StackBase)
{
// SP is within stack limits, everything
// looks good.
break;
}
}
else
{
// Can't validate SP so just go with it.
break;
}
// As long as we're looking through real TEBs
// we'll continue searching. Otherwise we
// wouldn't be able to locate TEBs in dumps that
// have a lot of threads.
Attempts++;
}
// The memory either wasn't a TEB or was the
// wrong TEB. Drop down a page and try again.
TebCheck -= g_TargetMachine->m_PageSize;
Attempts--;
}
WarnOut("WARNING: Teb %u pointer is NULL - "
"defaulting to %s\n", VIRTUAL_THREAD_INDEX(ThreadHandle),
FormatAddr64(TebAddr));
if (!IsATeb)
{
WarnOut("WARNING: %s does not appear to be a TEB\n",
FormatAddr64(TebAddr));
}
else if (Attempts == 0)
{
WarnOut("WARNING: %s does not appear to be the right TEB\n",
FormatAddr64(TebAddr));
}
}
*Offset = TebAddr;
if (Thread != NULL)
{
Thread->DataOffset = TebAddr;
}
return S_OK;
}
HRESULT
UserDumpTargetInfo::GetProcessInfoDataOffset(PTHREAD_INFO Thread,
ULONG Processor,
ULONG64 ThreadData,
PULONG64 Offset)
{
if (Thread != NULL && Thread->Process->DataOffset != 0)
{
*Offset = Thread->Process->DataOffset;
return S_OK;
}
HRESULT Status;
if (Thread != NULL || ThreadData == 0)
{
if ((Status = GetThreadInfoDataOffset(Thread,
VIRTUAL_THREAD_HANDLE(Processor),
&ThreadData)) != S_OK)
{
return Status;
}
}
ThreadData += g_TargetMachine->m_Ptr64 ? PEB_FROM_TEB64 : PEB_FROM_TEB32;
if ((Status = ReadPointer(g_TargetMachine, ThreadData, Offset)) != S_OK)
{
return Status;
}
if (Thread != NULL)
{
Thread->Process->DataOffset = *Offset;
}
return S_OK;
}
HRESULT
UserDumpTargetInfo::GetThreadInfoTeb(PTHREAD_INFO Thread,
ULONG Processor,
ULONG64 ThreadData,
PULONG64 Offset)
{
return GetThreadInfoDataOffset(Thread, ThreadData, Offset);
}
HRESULT
UserDumpTargetInfo::GetProcessInfoPeb(PTHREAD_INFO Thread,
ULONG Processor,
ULONG64 ThreadData,
PULONG64 Offset)
{
// Thread data is not useful.
return GetProcessInfoDataOffset(Thread, 0, 0, Offset);
}
HRESULT
UserDumpTargetInfo::GetSelDescriptor(MachineInfo* Machine,
ULONG64 Thread, ULONG Selector,
PDESCRIPTOR64 Desc)
{
return EmulateNtSelDescriptor(Machine, Selector, Desc);
}
//----------------------------------------------------------------------------
//
// UserFullDumpTargetInfo.
//
//----------------------------------------------------------------------------
void
UserFullDumpTargetInfo::Uninitialize(void)
{
UserDumpTargetInfo::Uninitialize();
}
HRESULT
UserFullDumpTargetInfo::GetBuildAndPlatform(ULONG MajorVersion,
ULONG MinorVersion,
PULONG BuildNumber,
PULONG PlatformId)
{
//
// The only way to distinguish user dump
// platforms is guessing from the Major/MinorVersion
// and the extra QFE/Hotfix data.
//
switch(MajorVersion)
{
case 4:
switch(MinorVersion & 0xffff)
{
case 0:
// This could be Win95 or NT4. We mostly
// deal with NT dumps so just assume NT.
*BuildNumber = 1381;
*PlatformId = VER_PLATFORM_WIN32_NT;
break;
case 3:
// Win95 OSR releases were 4.03. Treat them
// as Win95 for now.
*BuildNumber = 950;
*PlatformId = VER_PLATFORM_WIN32_WINDOWS;
break;
case 10:
// This could be Win98 or Win98SE. Go with Win98.
*BuildNumber = 1998;
*PlatformId = VER_PLATFORM_WIN32_WINDOWS;
break;
}
break;
case 5:
*PlatformId = VER_PLATFORM_WIN32_NT;
switch(MinorVersion & 0xffff)
{
case 0:
*BuildNumber = 2195;
break;
case 1:
// Just has to be greater than 2195 to
// distinguish it from Win2K RTM.
*BuildNumber = 2196;
break;
}
break;
case 0:
// AV: Busted BETA of the debugger generates a broken dump file
// Guess it's 2195.
WarnOut("Dump file was generated with NULL version - guessing NT5, ");
*PlatformId = VER_PLATFORM_WIN32_NT;
*BuildNumber = 2195;
break;
default:
// Other platforms are not supported.
ErrOut("Dump file was generated by an unsupported system, ");
ErrOut("version %x.%x\n", MajorVersion, MinorVersion & 0xffff);
return E_INVALIDARG;
}
// Newer full user dumps have the actual build number in
// the high word, so use it if it's present.
if (MinorVersion >> 16)
{
*BuildNumber = MinorVersion >> 16;
}
return S_OK;
}
HRESULT
UserFull32DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Header = (PUSERMODE_CRASHDUMP_HEADER32)g_DumpBase;
dprintf("User Dump File: Only application data is available\n\n");
g_TargetClass = DEBUG_CLASS_USER_WINDOWS;
g_TargetClassQualifier = DEBUG_USER_WINDOWS_DUMP;
ULONG BuildNumber;
ULONG PlatformId;
HRESULT Status;
if ((Status = GetBuildAndPlatform(m_Header->MajorVersion,
m_Header->MinorVersion,
&BuildNumber, &PlatformId)) != S_OK)
{
return Status;
}
if ((Status = DmppInitGlobals(BuildNumber, 0,
m_Header->MachineImageType, PlatformId,
m_Header->MajorVersion,
m_Header->MinorVersion & 0xffff)) != S_OK)
{
return Status;
}
// Dump does not contain this information.
g_TargetNumberProcessors = 1;
DEBUG_EVENT32 Event;
if (DmppReadFileOffset(DUMP_INFO_DUMP, m_Header->DebugEventOffset, &Event,
sizeof(Event)) != sizeof(Event))
{
ErrOut("Unable to read debug event at offset %x\n",
m_Header->DebugEventOffset);
return E_FAIL;
}
m_EventProcess = Event.dwProcessId;
m_EventThread = Event.dwThreadId;
if (Event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
ExceptionRecord32To64(&Event.u.Exception.ExceptionRecord,
&g_DumpException);
g_DumpExceptionFirstChance = Event.u.Exception.dwFirstChance;
}
else
{
// Fake an exception.
ZeroMemory(&g_DumpException, sizeof(g_DumpException));
g_DumpException.ExceptionCode = STATUS_BREAKPOINT;
g_DumpExceptionFirstChance = FALSE;
}
m_ThreadCount = m_Header->ThreadCount;
m_Memory = (PMEMORY_BASIC_INFORMATION32)
IndexByByte(m_Header, m_Header->MemoryRegionOffset);
//
// Determine the highest memory region address.
// This helps differentiate 2GB systems from 3GB systems.
//
ULONG i;
PMEMORY_BASIC_INFORMATION32 Mem;
ULONG TotalMemory;
Mem = m_Memory;
m_HighestMemoryRegion32 = 0;
for (i = 0; i < m_Header->MemoryRegionCount; i++)
{
if (Mem->BaseAddress > m_HighestMemoryRegion32)
{
m_HighestMemoryRegion32 = Mem->BaseAddress;
}
Mem++;
}
VerbOut(" Memory regions: %d\n",
m_Header->MemoryRegionCount);
TotalMemory = 0;
Mem = m_Memory;
for (i = 0; i < m_Header->MemoryRegionCount; i++)
{
VerbOut(" %5d: %08X - %08X off %08X, prot %08X, type %08X\n",
i, Mem->BaseAddress,
Mem->BaseAddress + Mem->RegionSize - 1,
TotalMemory + m_Header->DataOffset,
Mem->Protect, Mem->Type);
if ((Mem->Protect & PAGE_GUARD) ||
(Mem->Protect & PAGE_NOACCESS) ||
(Mem->State & MEM_FREE) ||
(Mem->State & MEM_RESERVE))
{
VerbOut(" Region has data-less pages\n");
}
TotalMemory += Mem->RegionSize;
Mem++;
}
VerbOut(" Total memory region size %X, file %08X - %08X\n",
TotalMemory, m_Header->DataOffset,
m_Header->DataOffset + TotalMemory - 1);
//
// Determine whether guard pages are present in
// the dump content or not.
//
// First try with IgnoreGuardPages == TRUE.
//
m_IgnoreGuardPages = TRUE;
if (!VerifyModules())
{
//
// That didn't work, try IgnoreGuardPages == FALSE.
//
m_IgnoreGuardPages = FALSE;
if (!VerifyModules())
{
ErrOut("Module list is corrupt\n");
return E_FAIL;
}
}
return S_OK;
}
void
UserFull32DumpTargetInfo::Uninitialize(void)
{
m_Header = NULL;
m_Memory = NULL;
m_IgnoreGuardPages = TRUE;
UserFullDumpTargetInfo::Uninitialize();
}
HRESULT
UserFull32DumpTargetInfo::GetTargetContext(
ULONG64 Thread,
PVOID Context
)
{
if (VIRTUAL_THREAD_INDEX(Thread) >= m_Header->ThreadCount)
{
return E_INVALIDARG;
}
if (DmppReadFileOffset(DUMP_INFO_DUMP,
m_Header->ThreadOffset +
VIRTUAL_THREAD_INDEX(Thread) *
g_TargetMachine->m_SizeTargetContext,
Context,
g_TargetMachine->m_SizeTargetContext) ==
g_TargetMachine->m_SizeTargetContext)
{
return S_OK;
}
else
{
return E_FAIL;
}
}
HRESULT
UserFull32DumpTargetInfo::GetImageVersionInformation(PCSTR ImagePath,
ULONG64 ImageBase,
PCSTR Item,
PVOID Buffer,
ULONG BufferSize,
PULONG VerInfoSize)
{
HRESULT Status;
IMAGE_DOS_HEADER DosHdr;
IMAGE_NT_HEADERS32 NtHdr;
if ((Status = ReadAllVirtual(ImageBase, &DosHdr, sizeof(DosHdr))) != S_OK)
{
return Status;
}
if (DosHdr.e_magic != IMAGE_DOS_SIGNATURE)
{
return E_FAIL;
}
if ((Status = ReadAllVirtual(ImageBase + DosHdr.e_lfanew,
&NtHdr, sizeof(NtHdr))) != S_OK)
{
return Status;
}
if (NtHdr.Signature != IMAGE_NT_SIGNATURE ||
NtHdr.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
return E_FAIL;
}
if (NtHdr.OptionalHeader.NumberOfRvaAndSizes <=
IMAGE_DIRECTORY_ENTRY_RESOURCE)
{
// No resource information so no version information.
return E_NOINTERFACE;
}
return ReadImageVersionInfo(ImageBase, Item,
Buffer, BufferSize, VerInfoSize,
&NtHdr.OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
}
HRESULT
UserFull32DumpTargetInfo::QueryMemoryRegion(PULONG64 Handle,
BOOL HandleIsOffset,
PMEMORY_BASIC_INFORMATION64 Info)
{
ULONG Index;
if (HandleIsOffset)
{
for (Index = 0; Index < m_Header->MemoryRegionCount; Index++)
{
if ((ULONG)*Handle >= m_Memory[Index].BaseAddress &&
(ULONG)*Handle < m_Memory[Index].BaseAddress +
m_Memory[Index].RegionSize)
{
break;
}
}
if (Index >= m_Header->MemoryRegionCount)
{
return E_NOINTERFACE;
}
}
else
{
Index = (ULONG)*Handle;
for (;;)
{
if (Index >= m_Header->MemoryRegionCount)
{
return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES);
}
if (!(m_Memory[Index].Protect & PAGE_GUARD))
{
break;
}
Index++;
}
}
MemoryBasicInformation32To64(&m_Memory[Index], Info);
*Handle = ++Index;
return S_OK;
}
HRESULT
UserFull32DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
m_Header = (PUSERMODE_CRASHDUMP_HEADER32)g_DumpBase;
if (m_Header->Signature != USERMODE_CRASHDUMP_SIGNATURE ||
m_Header->ValidDump != USERMODE_CRASHDUMP_VALID_DUMP32)
{
m_Header = NULL;
return E_NOINTERFACE;
}
//
// Check for the presence of some basic things.
//
if (m_Header->ThreadCount == 0 ||
m_Header->ModuleCount == 0 ||
m_Header->MemoryRegionCount == 0)
{
ErrOut("Thread, module or memory region count is zero.\n"
"The dump file is probably corrupt.\n");
return HR_DUMP_CORRUPT;
}
if (m_Header->ThreadOffset == 0 ||
m_Header->ModuleOffset == 0 ||
m_Header->DataOffset == 0 ||
m_Header->MemoryRegionOffset == 0 ||
m_Header->DebugEventOffset == 0 ||
m_Header->ThreadStateOffset == 0)
{
ErrOut("A dump header data offset is zero.\n"
"The dump file is probably corrupt.\n");
return HR_DUMP_CORRUPT;
}
// We don't want to have to call DmppReadFileOffset
// every time we check memory ranges so just require
// that the memory descriptors fit in the base view.
*BaseMapSize = m_Header->MemoryRegionOffset +
m_Header->MemoryRegionCount * sizeof(*m_Memory);
return S_OK;
}
void
UserFull32DumpTargetInfo::DumpDebug(void)
{
dprintf("----- 32 bit User Full Dump Analysis\n\n");
dprintf("MajorVersion: %d\n", m_Header->MajorVersion);
dprintf("MinorVersion: %d (Build %d)\n",
m_Header->MinorVersion & 0xffff,
m_Header->MinorVersion >> 16);
dprintf("MachineImageType: %08lx\n", m_Header->MachineImageType);
dprintf("ThreadCount: %08lx\n", m_Header->ThreadCount);
dprintf("ThreadOffset: %08lx\n", m_Header->ThreadOffset);
dprintf("ModuleCount: %08lx\n", m_Header->ModuleCount);
dprintf("ModuleOffset: %08lx\n", m_Header->ModuleOffset);
dprintf("DebugEventOffset: %08lx\n", m_Header->DebugEventOffset);
dprintf("VersionInfoOffset: %08lx\n", m_Header->VersionInfoOffset);
dprintf("\nVirtual Memory Description:\n");
dprintf("Number of regions: %d\n", m_Header->MemoryRegionCount);
dprintf(" FileOffset Start Address Length\n");
ULONG j = 0;
ULONG64 Offset = 0;
BOOL Skip;
while (j < m_Header->MemoryRegionCount)
{
Skip = FALSE;
dprintf(" %12I64lx %08lx %08lx",
Offset,
m_Memory[j].BaseAddress,
m_Memory[j].RegionSize);
if (m_Memory[j].Protect & PAGE_GUARD)
{
dprintf(" Guard Page");
if (m_IgnoreGuardPages)
{
dprintf(" - Ignored");
Skip = TRUE;
}
}
if (!Skip)
{
Offset += m_Memory[j].RegionSize;
}
dprintf("\n");
j += 1;
}
}
ULONG64
UserFull32DumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
ULONG i;
ULONG Offset = 0;
*File = DUMP_INFO_DUMP;
// Ignore the upper 32 bits to avoid getting
// confused by sign extensions in pointer handling
Virt &= 0xffffffff;
for (i = 0; i < m_Header->MemoryRegionCount; i++)
{
if (m_IgnoreGuardPages)
{
//
// Guard pages get reported, but they are not written
// out to the file
//
if (m_Memory[i].Protect & PAGE_GUARD)
{
continue;
}
}
if (Virt >= m_Memory[i].BaseAddress &&
Virt < m_Memory[i].BaseAddress + m_Memory[i].RegionSize)
{
ULONG Frag = (ULONG)Virt - m_Memory[i].BaseAddress;
*Avail = m_Memory[i].RegionSize - Frag;
if (Virt == (g_DebugDump_VirtualAddress & 0xffffffff))
{
g_NtDllCalls.DbgPrint("%X at offset %X\n",
(ULONG)Virt,
m_Header->DataOffset + Offset + Frag);
}
return m_Header->DataOffset + Offset + Frag;
}
Offset += m_Memory[i].RegionSize;
}
return 0;
}
HRESULT
UserFull32DumpTargetInfo::GetThreadInfo(ULONG Index,
PULONG Id, PULONG Suspend,
PULONG64 Teb)
{
if (Index >= m_ThreadCount)
{
return E_INVALIDARG;
}
CRASH_THREAD32 Thread;
if (DmppReadFileOffset(DUMP_INFO_DUMP,
m_Header->ThreadStateOffset +
Index * sizeof(Thread),
&Thread, sizeof(Thread)) != sizeof(Thread))
{
return E_FAIL;
}
*Id = Thread.ThreadId;
*Suspend = Thread.SuspendCount;
*Teb = EXTEND64(Thread.Teb);
return S_OK;
}
// #define DBG_VERIFY_MOD
BOOL
UserFull32DumpTargetInfo::VerifyModules(void)
{
CRASH_MODULE32 CrashModule;
ULONG i;
IMAGE_DOS_HEADER DosHeader;
ULONG Read;
BOOL Succ = TRUE;
ULONG Offset;
PSTR Env;
Env = getenv("DBGENG_VERIFY_MODULES");
if (Env != NULL)
{
return atoi(Env) == m_IgnoreGuardPages;
}
Offset = m_Header->ModuleOffset;
#ifdef DBG_VERIFY_MOD
g_NtDllCalls.DbgPrint("Verify %d modules at offset %X\n",
m_Header->ModuleCount, Offset);
#endif
for (i = 0; i < m_Header->ModuleCount; i++)
{
if (DmppReadFileOffset(DUMP_INFO_DUMP, Offset,
&CrashModule, sizeof(CrashModule)) !=
sizeof(CrashModule))
{
return FALSE;
}
#ifdef DBG_VERIFY_MOD
g_NtDllCalls.DbgPrint("Mod %d of %d offs %X, base %s, ",
i, m_Header->ModuleCount, Offset,
FormatAddr64(CrashModule.BaseOfImage));
if (ReadVirtual(CrashModule.BaseOfImage, &DosHeader,
sizeof(DosHeader), &Read) != S_OK ||
Read != sizeof(DosHeader))
{
g_NtDllCalls.DbgPrint("unable to read header\n");
}
else
{
g_NtDllCalls.DbgPrint("magic %04X\n", DosHeader.e_magic);
}
#endif
//
// It is not strictly a requirement that every image
// begin with an MZ header, though all of our tools
// today produce images like this. Check for it
// as a sanity check since it's so common nowadays.
//
if (ReadVirtual(CrashModule.BaseOfImage, &DosHeader,
sizeof(DosHeader), &Read) != S_OK ||
Read != sizeof(DosHeader) ||
DosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
Succ = FALSE;
break;
}
Offset += sizeof(CrashModule) + CrashModule.ImageNameLength;
}
#ifdef DBG_VERIFY_MOD
g_NtDllCalls.DbgPrint("VerifyModules returning %d, %d of %d mods\n",
Succ, i, m_Header->ModuleCount);
#endif
return Succ;
}
HRESULT
UserFull64DumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Header = (PUSERMODE_CRASHDUMP_HEADER64)g_DumpBase;
dprintf("User Dump File: Only application data is available\n\n");
g_TargetClass = DEBUG_CLASS_USER_WINDOWS;
g_TargetClassQualifier = DEBUG_USER_WINDOWS_DUMP;
ULONG BuildNumber;
ULONG PlatformId;
HRESULT Status;
if ((Status = GetBuildAndPlatform(m_Header->MajorVersion,
m_Header->MinorVersion,
&BuildNumber, &PlatformId)) != S_OK)
{
return Status;
}
if ((Status = DmppInitGlobals(BuildNumber, 0,
m_Header->MachineImageType, PlatformId,
m_Header->MajorVersion,
m_Header->MinorVersion & 0xffff)) != S_OK)
{
return Status;
}
// Dump does not contain this information.
g_TargetNumberProcessors = 1;
DEBUG_EVENT64 Event;
if (DmppReadFileOffset(DUMP_INFO_DUMP, m_Header->DebugEventOffset, &Event,
sizeof(Event)) != sizeof(Event))
{
ErrOut("Unable to read debug event at offset %I64x\n",
m_Header->DebugEventOffset);
return E_FAIL;
}
m_EventProcess = Event.dwProcessId;
m_EventThread = Event.dwThreadId;
if (Event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
g_DumpException = Event.u.Exception.ExceptionRecord;
g_DumpExceptionFirstChance = Event.u.Exception.dwFirstChance;
}
else
{
// Fake an exception.
ZeroMemory(&g_DumpException, sizeof(g_DumpException));
g_DumpException.ExceptionCode = STATUS_BREAKPOINT;
g_DumpExceptionFirstChance = FALSE;
}
m_ThreadCount = m_Header->ThreadCount;
m_Memory = (PMEMORY_BASIC_INFORMATION64)
IndexByByte(m_Header, m_Header->MemoryRegionOffset);
ULONG64 TotalMemory;
ULONG i;
VerbOut(" Memory regions: %d\n",
m_Header->MemoryRegionCount);
TotalMemory = 0;
PMEMORY_BASIC_INFORMATION64 Mem = m_Memory;
for (i = 0; i < m_Header->MemoryRegionCount; i++)
{
VerbOut(" %5d: %s - %s, prot %08X, type %08X\n",
i, FormatAddr64(Mem->BaseAddress),
FormatAddr64(Mem->BaseAddress + Mem->RegionSize - 1),
Mem->Protect, Mem->Type);
if ((Mem->Protect & PAGE_GUARD) ||
(Mem->Protect & PAGE_NOACCESS) ||
(Mem->State & MEM_FREE) ||
(Mem->State & MEM_RESERVE))
{
VerbOut(" Region has data-less pages\n");
}
TotalMemory += Mem->RegionSize;
Mem++;
}
VerbOut(" Total memory region size %s\n",
FormatAddr64(TotalMemory));
return S_OK;
}
void
UserFull64DumpTargetInfo::Uninitialize(void)
{
m_Header = NULL;
m_Memory = NULL;
UserFullDumpTargetInfo::Uninitialize();
}
HRESULT
UserFull64DumpTargetInfo::GetTargetContext(
ULONG64 Thread,
PVOID Context
)
{
if (VIRTUAL_THREAD_INDEX(Thread) >= m_Header->ThreadCount)
{
return E_INVALIDARG;
}
if (DmppReadFileOffset(DUMP_INFO_DUMP,
m_Header->ThreadOffset +
VIRTUAL_THREAD_INDEX(Thread) *
g_TargetMachine->m_SizeTargetContext,
Context,
g_TargetMachine->m_SizeTargetContext) ==
g_TargetMachine->m_SizeTargetContext)
{
return S_OK;
}
else
{
return E_FAIL;
}
}
HRESULT
UserFull64DumpTargetInfo::GetImageVersionInformation(PCSTR ImagePath,
ULONG64 ImageBase,
PCSTR Item,
PVOID Buffer,
ULONG BufferSize,
PULONG VerInfoSize)
{
HRESULT Status;
IMAGE_DOS_HEADER DosHdr;
IMAGE_NT_HEADERS64 NtHdr;
if ((Status = ReadAllVirtual(ImageBase, &DosHdr, sizeof(DosHdr))) != S_OK)
{
return Status;
}
if (DosHdr.e_magic != IMAGE_DOS_SIGNATURE)
{
return E_FAIL;
}
if ((Status = ReadAllVirtual(ImageBase + DosHdr.e_lfanew,
&NtHdr, sizeof(NtHdr))) != S_OK)
{
return Status;
}
if (NtHdr.Signature != IMAGE_NT_SIGNATURE ||
NtHdr.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
return E_FAIL;
}
if (NtHdr.OptionalHeader.NumberOfRvaAndSizes <=
IMAGE_DIRECTORY_ENTRY_RESOURCE)
{
// No resource information so no version information.
return E_NOINTERFACE;
}
return ReadImageVersionInfo(ImageBase, Item,
Buffer, BufferSize, VerInfoSize,
&NtHdr.OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
}
HRESULT
UserFull64DumpTargetInfo::QueryMemoryRegion(PULONG64 Handle,
BOOL HandleIsOffset,
PMEMORY_BASIC_INFORMATION64 Info)
{
ULONG Index;
if (HandleIsOffset)
{
for (Index = 0; Index < m_Header->MemoryRegionCount; Index++)
{
if (*Handle >= m_Memory[Index].BaseAddress &&
*Handle < m_Memory[Index].BaseAddress +
m_Memory[Index].RegionSize)
{
break;
}
}
if (Index >= m_Header->MemoryRegionCount)
{
return E_NOINTERFACE;
}
}
else
{
Index = (ULONG)*Handle;
if (Index >= m_Header->MemoryRegionCount)
{
return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES);
}
// 64-bit user dump support came into being after
// guard pages were suppressed so they never contain them.
}
*Info = m_Memory[Index];
*Handle = ++Index;
return S_OK;
}
HRESULT
UserFull64DumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
m_Header = (PUSERMODE_CRASHDUMP_HEADER64)g_DumpBase;
if (m_Header->Signature != USERMODE_CRASHDUMP_SIGNATURE ||
m_Header->ValidDump != USERMODE_CRASHDUMP_VALID_DUMP64)
{
m_Header = NULL;
return E_NOINTERFACE;
}
//
// Check for the presence of some basic things.
//
if (m_Header->ThreadCount == 0 ||
m_Header->ModuleCount == 0 ||
m_Header->MemoryRegionCount == 0)
{
ErrOut("Thread, module or memory region count is zero.\n"
"The dump file is probably corrupt.\n");
return HR_DUMP_CORRUPT;
}
if (m_Header->ThreadOffset == 0 ||
m_Header->ModuleOffset == 0 ||
m_Header->DataOffset == 0 ||
m_Header->MemoryRegionOffset == 0 ||
m_Header->DebugEventOffset == 0 ||
m_Header->ThreadStateOffset == 0)
{
ErrOut("A dump header data offset is zero.\n"
"The dump file is probably corrupt.\n");
return HR_DUMP_CORRUPT;
}
// We don't want to have to call DmppReadFileOffset
// every time we check memory ranges so just require
// that the memory descriptors fit in the default view.
*BaseMapSize = m_Header->MemoryRegionOffset +
m_Header->MemoryRegionCount * sizeof(*m_Memory);
return S_OK;
}
void
UserFull64DumpTargetInfo::DumpDebug(void)
{
dprintf("----- 64 bit User Full Dump Analysis\n\n");
dprintf("MajorVersion: %d\n", m_Header->MajorVersion);
dprintf("MinorVersion: %d (Build %d)\n",
m_Header->MinorVersion & 0xffff,
m_Header->MinorVersion >> 16);
dprintf("MachineImageType: %08lx\n", m_Header->MachineImageType);
dprintf("ThreadCount: %08lx\n", m_Header->ThreadCount);
dprintf("ThreadOffset: %12I64lx\n", m_Header->ThreadOffset);
dprintf("ModuleCount: %08lx\n", m_Header->ModuleCount);
dprintf("ModuleOffset: %12I64lx\n", m_Header->ModuleOffset);
dprintf("DebugEventOffset: %12I64lx\n", m_Header->DebugEventOffset);
dprintf("VersionInfoOffset: %12I64lx\n", m_Header->VersionInfoOffset);
dprintf("\nVirtual Memory Description:\n");
dprintf("Number of regions: %d\n", m_Header->MemoryRegionCount);
dprintf(" FileOffset Start Address"
" Length\n");
ULONG j = 0;
ULONG64 Offset = 0;
while (j < m_Header->MemoryRegionCount)
{
dprintf(" %12I64lx %s %12I64x",
Offset,
FormatAddr64(m_Memory[j].BaseAddress),
m_Memory[j].RegionSize);
Offset += m_Memory[j].RegionSize;
dprintf("\n");
j += 1;
}
}
ULONG64
UserFull64DumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
ULONG i;
ULONG64 Offset = 0;
*File = DUMP_INFO_DUMP;
for (i = 0; i < m_Header->MemoryRegionCount; i++)
{
//
// Guard pages get reported, but they are not written
// out to the file
//
if (m_Memory[i].Protect & PAGE_GUARD)
{
continue;
}
if (Virt >= m_Memory[i].BaseAddress &&
Virt < m_Memory[i].BaseAddress + m_Memory[i].RegionSize)
{
ULONG64 Frag = Virt - m_Memory[i].BaseAddress;
ULONG64 Avail64 = m_Memory[i].RegionSize - Frag;
// It's extremely unlikely that there'll be a single
// region greater than 4GB, but check anyway. No
// reads should ever require more than 4GB so just
// indicate that 4GB is available.
if (Avail64 > 0xffffffff)
{
*Avail = 0xffffffff;
}
else
{
*Avail = (ULONG)Avail64;
}
return m_Header->DataOffset + Offset + Frag;
}
Offset += m_Memory[i].RegionSize;
}
return 0;
}
HRESULT
UserFull64DumpTargetInfo::GetThreadInfo(ULONG Index,
PULONG Id, PULONG Suspend,
PULONG64 Teb)
{
if (Index >= m_ThreadCount)
{
return E_INVALIDARG;
}
CRASH_THREAD64 Thread;
if (DmppReadFileOffset(DUMP_INFO_DUMP,
m_Header->ThreadStateOffset +
Index * sizeof(Thread),
&Thread, sizeof(Thread)) != sizeof(Thread))
{
return E_FAIL;
}
*Id = Thread.ThreadId;
*Suspend = Thread.SuspendCount;
*Teb = Thread.Teb;
return S_OK;
}
//----------------------------------------------------------------------------
//
// UserMiniDumpTargetInfo.
//
//----------------------------------------------------------------------------
HRESULT
UserMiniDumpTargetInfo::Initialize(void)
{
// Pick up any potentially modified base mapping pointer.
m_Header = (PMINIDUMP_HEADER)g_DumpBase;
// Clear pointers that have already been set so
// that they get picked up again.
m_SysInfo = NULL;
g_TargetClass = DEBUG_CLASS_USER_WINDOWS;
g_TargetClassQualifier = DEBUG_USER_WINDOWS_SMALL_DUMP;
g_DumpFormatFlags = 0;
if (m_Header->Flags & MiniDumpWithFullMemory)
{
g_DumpFormatFlags |= DEBUG_FORMAT_USER_SMALL_FULL_MEMORY;
}
if (m_Header->Flags & MiniDumpWithHandleData)
{
g_DumpFormatFlags |= DEBUG_FORMAT_USER_SMALL_HANDLE_DATA;
}
MINIDUMP_DIRECTORY UNALIGNED *Dir;
ULONG i;
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
IndexRva(m_Header->StreamDirectoryRva,
m_Header->NumberOfStreams * sizeof(*Dir),
"Directory");
if (Dir == NULL)
{
return HR_DUMP_CORRUPT;
}
for (i = 0; i < m_Header->NumberOfStreams; i++)
{
switch(Dir->StreamType)
{
case ThreadListStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_Threads) == NULL)
{
break;
}
m_ActualThreadCount =
((MINIDUMP_THREAD_LIST UNALIGNED *)m_Threads)->NumberOfThreads;
m_ThreadStructSize = sizeof(MINIDUMP_THREAD);
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_THREAD_LIST) +
sizeof(MINIDUMP_THREAD) * m_ActualThreadCount)
{
m_Threads = NULL;
m_ActualThreadCount = 0;
}
else
{
// Move past count to actual thread data.
m_Threads += sizeof(MINIDUMP_THREAD_LIST);
}
break;
case ThreadExListStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_Threads) == NULL)
{
break;
}
m_ActualThreadCount =
((MINIDUMP_THREAD_EX_LIST UNALIGNED *)m_Threads)->
NumberOfThreads;
m_ThreadStructSize = sizeof(MINIDUMP_THREAD_EX);
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_THREAD_EX_LIST) +
sizeof(MINIDUMP_THREAD_EX) * m_ActualThreadCount)
{
m_Threads = NULL;
m_ActualThreadCount = 0;
}
else
{
// Move past count to actual thread data.
m_Threads += sizeof(MINIDUMP_THREAD_EX_LIST);
}
break;
case ModuleListStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_Modules) == NULL)
{
break;
}
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_MODULE_LIST) +
sizeof(MINIDUMP_MODULE) * m_Modules->NumberOfModules)
{
m_Modules = NULL;
}
break;
case MemoryListStream:
if (m_Header->Flags & MiniDumpWithFullMemory)
{
ErrOut("Full memory minidumps can't have MemoryListStreams\n");
return HR_DUMP_CORRUPT;
}
if (IndexDirectory(i, Dir, (PVOID*)&m_Memory) == NULL)
{
break;
}
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_MEMORY_LIST) +
sizeof(MINIDUMP_MEMORY_DESCRIPTOR) *
m_Memory->NumberOfMemoryRanges)
{
m_Memory = NULL;
}
break;
case Memory64ListStream:
if (!(m_Header->Flags & MiniDumpWithFullMemory))
{
ErrOut("Partial memory minidumps can't have "
"Memory64ListStreams\n");
return HR_DUMP_CORRUPT;
}
if (IndexDirectory(i, Dir, (PVOID*)&m_Memory64) == NULL)
{
break;
}
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_MEMORY64_LIST) +
sizeof(MINIDUMP_MEMORY_DESCRIPTOR64) *
m_Memory64->NumberOfMemoryRanges)
{
m_Memory64 = NULL;
}
break;
case ExceptionStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_Exception) == NULL)
{
break;
}
if (Dir->Location.DataSize !=
sizeof(MINIDUMP_EXCEPTION_STREAM))
{
m_Exception = NULL;
}
break;
case SystemInfoStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_SysInfo) == NULL)
{
break;
}
if (Dir->Location.DataSize != sizeof(MINIDUMP_SYSTEM_INFO))
{
m_SysInfo = NULL;
}
break;
case CommentStreamA:
PSTR CommentA;
CommentA = NULL;
if (IndexDirectory(i, Dir, (PVOID*)&CommentA) == NULL)
{
break;
}
dprintf("Comment: '%s'\n", CommentA);
break;
case CommentStreamW:
PWSTR CommentW;
CommentW = NULL;
if (IndexDirectory(i, Dir, (PVOID*)&CommentW) == NULL)
{
break;
}
dprintf("Comment: '%ls'\n", CommentW);
break;
case HandleDataStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_Handles) == NULL)
{
break;
}
if (Dir->Location.DataSize !=
m_Handles->SizeOfHeader +
m_Handles->SizeOfDescriptor *
m_Handles->NumberOfDescriptors)
{
m_Handles = NULL;
}
break;
case FunctionTableStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_FunctionTables) == NULL)
{
break;
}
// Don't bother walking every table to verify the size,
// just do a simple minimum size check.
if (Dir->Location.DataSize <
m_FunctionTables->SizeOfHeader +
m_FunctionTables->SizeOfDescriptor *
m_FunctionTables->NumberOfDescriptors)
{
m_FunctionTables = NULL;
}
break;
case UnusedStream:
// Nothing to do.
break;
default:
WarnOut("WARNING: Minidump contains unknown stream type 0x%x\n",
Dir->StreamType);
break;
}
Dir++;
}
// This was already checked in Identify but check
// again just in case something went wrong.
if (m_SysInfo == NULL)
{
ErrOut("Unable to locate system info\n");
return HR_DUMP_CORRUPT;
}
HRESULT Status;
if ((Status = DmppInitGlobals(m_SysInfo->BuildNumber, 0,
m_ImageType, m_SysInfo->PlatformId,
m_SysInfo->MajorVersion,
m_SysInfo->MinorVersion)) != S_OK)
{
return Status;
}
// Dump does not contain this information.
g_TargetNumberProcessors = 1;
if (m_SysInfo->CSDVersionRva != 0)
{
MINIDUMP_STRING UNALIGNED *CsdString = (MINIDUMP_STRING UNALIGNED *)
IndexRva(m_SysInfo->CSDVersionRva, sizeof(*CsdString),
"CSD string");
if (CsdString != NULL && CsdString->Length > 0)
{
WCHAR UNALIGNED *WideStr = CsdString->Buffer;
ULONG WideLen = wcslen((PWSTR)WideStr);
if (g_ActualSystemVersion > W9X_SVER_START &&
g_ActualSystemVersion < W9X_SVER_END)
{
WCHAR UNALIGNED *Str;
//
// Win9x CSD strings are usually just a single
// letter surrounded by whitespace, so clean them
// up a little bit.
//
while (iswspace(*WideStr))
{
WideStr++;
}
Str = WideStr;
WideLen = 0;
while (*Str && !iswspace(*Str))
{
WideLen++;
Str++;
}
}
sprintf(g_TargetServicePackString, "%.*S", WideLen, WideStr);
}
}
// Minidumps don't store the process ID.
m_EventProcess = VIRTUAL_PROCESS_ID;
if (m_Exception != NULL)
{
m_EventThread = m_Exception->ThreadId;
C_ASSERT(sizeof(m_Exception->ExceptionRecord) ==
sizeof(EXCEPTION_RECORD64));
g_DumpException = *(EXCEPTION_RECORD64 UNALIGNED *)
&m_Exception->ExceptionRecord;
}
else
{
m_EventThread = VIRTUAL_THREAD_ID(0);
// Fake an exception.
ZeroMemory(&g_DumpException, sizeof(g_DumpException));
g_DumpException.ExceptionCode = STATUS_BREAKPOINT;
}
g_DumpExceptionFirstChance = FALSE;
if (m_Threads != NULL)
{
m_ThreadCount = m_ActualThreadCount;
if (m_Exception == NULL)
{
m_EventThread = IndexThreads(0)->ThreadId;
}
}
else
{
m_ThreadCount = 1;
}
return S_OK;
}
void
UserMiniDumpTargetInfo::Uninitialize(void)
{
MemoryMap_Destroy();
m_Header = NULL;
m_SysInfo = NULL;
m_ActualThreadCount = 0;
m_ThreadStructSize = 0;
m_Threads = NULL;
m_Modules = NULL;
m_Memory = NULL;
m_Memory64 = NULL;
m_Memory64DataBase = 0;
m_Exception = NULL;
m_Handles = NULL;
m_FunctionTables = NULL;
m_ImageType = IMAGE_FILE_MACHINE_UNKNOWN;
UserDumpTargetInfo::Uninitialize();
}
void
UserMiniDumpTargetInfo::NearestDifferentlyValidOffsets(ULONG64 Offset,
PULONG64 NextOffset,
PULONG64 NextPage)
{
//
// In a minidump there can be memory fragments mapped at
// arbitrary locations so we cannot assume validity
// changes on page boundaries. We could attempt to
// scan the memory list and try to find the closest valid
// chunk of memory but it's rarely important that
// complete accuracy is required. Just return the
// next byte.
//
if (NextOffset != NULL)
{
*NextOffset = Offset + 1;
}
if (NextPage != NULL)
{
*NextPage = (Offset + g_TargetMachine->m_PageSize) &
~((ULONG64)g_TargetMachine->m_PageSize - 1);
}
}
HRESULT
UserMiniDumpTargetInfo::ReadHandleData(
IN ULONG64 Handle,
IN ULONG DataType,
OUT OPTIONAL PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG DataSize
)
{
if (m_Handles == NULL)
{
return E_FAIL;
}
MINIDUMP_HANDLE_DESCRIPTOR UNALIGNED *Desc;
if (DataType != DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT)
{
PUCHAR RawDesc = (PUCHAR)m_Handles + m_Handles->SizeOfHeader;
ULONG i;
for (i = 0; i < m_Handles->NumberOfDescriptors; i++)
{
Desc = (MINIDUMP_HANDLE_DESCRIPTOR UNALIGNED *)RawDesc;
if (Desc->Handle == Handle)
{
break;
}
RawDesc += m_Handles->SizeOfDescriptor;
}
if (i >= m_Handles->NumberOfDescriptors)
{
return E_NOINTERFACE;
}
}
ULONG Used;
RVA StrRva;
switch(DataType)
{
case DEBUG_HANDLE_DATA_TYPE_BASIC:
Used = sizeof(DEBUG_HANDLE_DATA_BASIC);
if (Buffer == NULL)
{
break;
}
if (BufferSize < Used)
{
return E_INVALIDARG;
}
PDEBUG_HANDLE_DATA_BASIC Basic;
Basic = (PDEBUG_HANDLE_DATA_BASIC)Buffer;
Basic->TypeNameSize = Desc->TypeNameRva == 0 ? 0 :
((MINIDUMP_STRING UNALIGNED *)
IndexByByte(m_Header, Desc->TypeNameRva))->
Length / sizeof(WCHAR) + 1;
Basic->ObjectNameSize = Desc->ObjectNameRva == 0 ? 0 :
((MINIDUMP_STRING UNALIGNED *)
IndexByByte(m_Header, Desc->ObjectNameRva))->
Length / sizeof(WCHAR) + 1;
Basic->Attributes = Desc->Attributes;
Basic->GrantedAccess = Desc->GrantedAccess;
Basic->HandleCount = Desc->HandleCount;
Basic->PointerCount = Desc->PointerCount;
break;
case DEBUG_HANDLE_DATA_TYPE_TYPE_NAME:
StrRva = Desc->TypeNameRva;
break;
case DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME:
StrRva = Desc->ObjectNameRva;
break;
case DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT:
Used = sizeof(ULONG);
if (Buffer == NULL)
{
break;
}
if (BufferSize < Used)
{
return E_INVALIDARG;
}
*(PULONG)Buffer = m_Handles->NumberOfDescriptors;
break;
}
if (DataType == DEBUG_HANDLE_DATA_TYPE_TYPE_NAME ||
DataType == DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME)
{
if (StrRva == 0)
{
Used = 1;
if (Buffer != NULL && BufferSize < Used)
{
return E_INVALIDARG;
}
*(PCHAR)Buffer = 0;
}
else
{
MINIDUMP_STRING UNALIGNED *Str = (MINIDUMP_STRING UNALIGNED *)
IndexRva(StrRva, sizeof(*Str), "Handle name string");
if (Str == NULL)
{
return HR_DUMP_CORRUPT;
}
Used = Str->Length / sizeof(WCHAR) + 1;
if (Buffer != NULL &&
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)Str->Buffer,
-1, (LPSTR)Buffer, BufferSize,
NULL, NULL) == 0)
{
return WIN32_LAST_STATUS();
}
}
}
if (DataSize != NULL)
{
*DataSize = Used;
}
return S_OK;
}
HRESULT
UserMiniDumpTargetInfo::GetProcessorId
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
{
LPTSTR vendor = "<unavailable>";
if (Processor != 0)
{
return E_INVALIDARG;
}
if (m_SysInfo == NULL)
{
return E_UNEXPECTED;
}
switch(m_SysInfo->ProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
Id->X86.Family = m_SysInfo->ProcessorLevel;
Id->X86.Model = (m_SysInfo->ProcessorRevision >> 8) & 0xf;
Id->X86.Stepping = m_SysInfo->ProcessorRevision & 0xf;
strcpy(&(Id->X86.VendorString[0]), vendor);
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
Id->Alpha.Type = m_SysInfo->ProcessorLevel;
Id->Alpha.Revision = m_SysInfo->ProcessorRevision;
break;
case PROCESSOR_ARCHITECTURE_IA64:
Id->Ia64.Model = m_SysInfo->ProcessorLevel;
Id->Ia64.Revision = m_SysInfo->ProcessorRevision;
strcpy(&(Id->Ia64.VendorString[0]), vendor);
break;
case PROCESSOR_ARCHITECTURE_AMD64:
Id->Amd64.Family = m_SysInfo->ProcessorLevel;
Id->Amd64.Model = (m_SysInfo->ProcessorRevision >> 8) & 0xf;
Id->Amd64.Stepping = m_SysInfo->ProcessorRevision & 0xf;
strcpy(&(Id->Amd64.VendorString[0]), vendor);
break;
}
return S_OK;
}
PVOID
UserMiniDumpTargetInfo::FindDynamicFunctionEntry(MachineInfo* Machine,
ULONG64 Address)
{
if (m_FunctionTables == NULL)
{
return NULL;
}
PUCHAR StreamData =
(PUCHAR)m_FunctionTables + m_FunctionTables->SizeOfHeader +
m_FunctionTables->SizeOfAlignPad;
ULONG TableIdx;
for (TableIdx = 0;
TableIdx < m_FunctionTables->NumberOfDescriptors;
TableIdx++)
{
// Stream structure contents are guaranteed to be
// properly aligned.
PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR Desc =
(PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR)StreamData;
StreamData += m_FunctionTables->SizeOfDescriptor;
PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable =
(PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE)StreamData;
StreamData += m_FunctionTables->SizeOfNativeDescriptor;
PVOID TableData = (PVOID)StreamData;
StreamData += Desc->EntryCount *
m_FunctionTables->SizeOfFunctionEntry +
Desc->SizeOfAlignPad;
if (Address >= Desc->MinimumAddress && Address < Desc->MaximumAddress)
{
PVOID Entry = Machine->FindDynamicFunctionEntry
(RawTable, Address, TableData,
Desc->EntryCount * m_FunctionTables->SizeOfFunctionEntry);
if (Entry)
{
return Entry;
}
}
}
return NULL;
}
ULONG64
UserMiniDumpTargetInfo::GetDynamicFunctionTableBase(MachineInfo* Machine,
ULONG64 Address)
{
if (m_FunctionTables == NULL)
{
return 0;
}
PUCHAR StreamData =
(PUCHAR)m_FunctionTables + m_FunctionTables->SizeOfHeader +
m_FunctionTables->SizeOfAlignPad;
ULONG TableIdx;
for (TableIdx = 0;
TableIdx < m_FunctionTables->NumberOfDescriptors;
TableIdx++)
{
// Stream structure contents are guaranteed to be
// properly aligned.
PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR Desc =
(PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR)StreamData;
StreamData +=
m_FunctionTables->SizeOfDescriptor +
m_FunctionTables->SizeOfNativeDescriptor +
Desc->EntryCount * m_FunctionTables->SizeOfFunctionEntry +
Desc->SizeOfAlignPad;
if (Address >= Desc->MinimumAddress && Address < Desc->MaximumAddress)
{
return Desc->BaseAddress;
}
}
return 0;
}
HRESULT
UserMiniDumpTargetInfo::GetTargetContext(
ULONG64 Thread,
PVOID Context
)
{
if (m_Threads == NULL ||
VIRTUAL_THREAD_INDEX(Thread) >= m_ActualThreadCount)
{
return E_INVALIDARG;
}
PVOID ContextData =
IndexRva(IndexThreads(VIRTUAL_THREAD_INDEX(Thread))->ThreadContext.Rva,
g_TargetMachine->m_SizeTargetContext,
"Thread context data");
if (ContextData == NULL)
{
return HR_DUMP_CORRUPT;
}
memcpy(Context, ContextData, g_TargetMachine->m_SizeTargetContext);
return S_OK;
}
HRESULT
UserMiniDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
m_Header = (PMINIDUMP_HEADER)g_DumpBase;
if (m_Header->Signature != MINIDUMP_SIGNATURE ||
(m_Header->Version & 0xffff) != MINIDUMP_VERSION)
{
m_Header = NULL;
return E_NOINTERFACE;
}
MINIDUMP_DIRECTORY UNALIGNED *Dir;
ULONG i;
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
IndexRva(m_Header->StreamDirectoryRva,
m_Header->NumberOfStreams * sizeof(*Dir),
"Directory");
if (Dir == NULL)
{
return HR_DUMP_CORRUPT;
}
for (i = 0; i < m_Header->NumberOfStreams; i++)
{
switch(Dir->StreamType)
{
case SystemInfoStream:
if (IndexDirectory(i, Dir, (PVOID*)&m_SysInfo) == NULL)
{
break;
}
if (Dir->Location.DataSize != sizeof(MINIDUMP_SYSTEM_INFO))
{
m_SysInfo = NULL;
}
break;
case Memory64ListStream:
MINIDUMP_MEMORY64_LIST Mem64;
// The memory for the full memory list may not
// fit within the initial mapping used at identify
// time so do not directly index. Instead, use
// the adaptive read to get the data so we can
// determine the data base.
if (DmppReadFileOffset(DUMP_INFO_DUMP, Dir->Location.Rva,
&Mem64, sizeof(Mem64)) == sizeof(Mem64) &&
Dir->Location.DataSize ==
sizeof(MINIDUMP_MEMORY64_LIST) +
sizeof(MINIDUMP_MEMORY_DESCRIPTOR64) *
Mem64.NumberOfMemoryRanges)
{
m_Memory64DataBase = Mem64.BaseRva;
}
// Clear any cache entries that may have been
// added by the above read so that only the
// identify mapping is active.
DmppDumpFileCacheEmpty(&g_DumpInfoFiles[DUMP_INFO_DUMP]);
break;
}
Dir++;
}
if (m_SysInfo == NULL)
{
ErrOut("Minidump does not have system info\n");
return E_FAIL;
}
switch(m_SysInfo->ProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
m_ImageType = IMAGE_FILE_MACHINE_I386;
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
m_ImageType = IMAGE_FILE_MACHINE_ALPHA;
break;
case PROCESSOR_ARCHITECTURE_IA64:
m_ImageType = IMAGE_FILE_MACHINE_IA64;
break;
case PROCESSOR_ARCHITECTURE_ALPHA64:
m_ImageType = IMAGE_FILE_MACHINE_AXP64;
break;
case PROCESSOR_ARCHITECTURE_AMD64:
m_ImageType = IMAGE_FILE_MACHINE_AMD64;
break;
default:
return E_FAIL;
}
// We rely on being able to directly access the entire
// content of the dump through the default view so
// ensure that it's possible.
*BaseMapSize = g_DumpInfoFiles[DUMP_INFO_DUMP].FileSize;
return S_OK;
}
ModuleInfo*
UserMiniDumpTargetInfo::GetModuleInfo(BOOL UserMode)
{
DBG_ASSERT(UserMode);
return &g_UserMiniModuleIterator;
}
HRESULT
UserMiniDumpTargetInfo::GetImageVersionInformation(PCSTR ImagePath,
ULONG64 ImageBase,
PCSTR Item,
PVOID Buffer,
ULONG BufferSize,
PULONG VerInfoSize)
{
//
// Find the image in the dump module list.
//
if (m_Modules == NULL)
{
return E_NOINTERFACE;
}
ULONG i;
MINIDUMP_MODULE UNALIGNED *Mod = m_Modules->Modules;
for (i = 0; i < m_Modules->NumberOfModules; i++)
{
if (ImageBase == Mod->BaseOfImage)
{
break;
}
Mod++;
}
if (i == m_Modules->NumberOfModules)
{
return E_NOINTERFACE;
}
PVOID Data = NULL;
ULONG DataSize = 0;
if (Item[0] == '\\' && Item[1] == 0)
{
Data = &Mod->VersionInfo;
DataSize = sizeof(Mod->VersionInfo);
}
else
{
return E_INVALIDARG;
}
return FillDataBuffer(Data, DataSize, Buffer, BufferSize, VerInfoSize);
}
HRESULT
UserMiniDumpTargetInfo::GetExceptionContext(PCROSS_PLATFORM_CONTEXT Context)
{
if (m_Exception != NULL)
{
PVOID ContextData;
if (m_Exception->ThreadContext.DataSize <
g_TargetMachine->m_SizeTargetContext ||
(ContextData = IndexRva(m_Exception->ThreadContext.Rva,
g_TargetMachine->m_SizeTargetContext,
"Exception context")) == NULL)
{
return E_FAIL;
}
memcpy(Context, ContextData, g_TargetMachine->m_SizeTargetContext);
return S_OK;
}
else
{
ErrOut("Minidump doesn't have an exception context\n");
return E_FAIL;
}
}
ULONG64
UserMiniDumpTargetInfo::GetCurrentTimeDateN(void)
{
return TimeDateStampToFileTime(m_Header->TimeDateStamp);
}
HRESULT
UserMiniDumpTargetInfo::GetThreadInfo(ULONG Index,
PULONG Id, PULONG Suspend, PULONG64 Teb)
{
if (m_Threads == NULL || Index >= m_ActualThreadCount)
{
return E_INVALIDARG;
}
MINIDUMP_THREAD_EX UNALIGNED *Thread = IndexThreads(Index);
*Id = Thread->ThreadId;
*Suspend = Thread->SuspendCount;
*Teb = Thread->Teb;
return S_OK;
}
PSTR g_MiniStreamNames[] =
{
"UnusedStream", "ReservedStream0", "ReservedStream1", "ThreadListStream",
"ModuleListStream", "MemoryListStream", "ExceptionStream",
"SystemInfoStream", "ThreadExListStream", "Memory64ListStream",
"CommentStreamA", "CommentStreamW", "HandleDataStream",
"FunctionTableStream",
};
PSTR
MiniStreamTypeName(ULONG32 Type)
{
if (Type < sizeof(g_MiniStreamNames) / sizeof(g_MiniStreamNames[0]))
{
return g_MiniStreamNames[Type];
}
else
{
return "???";
}
}
PVOID
UserMiniDumpTargetInfo::IndexRva(RVA Rva, ULONG Size, PCSTR Title)
{
if (Rva >= g_DumpInfoFiles[DUMP_INFO_DUMP].MapSize)
{
ErrOut("ERROR: %s not present in dump (RVA 0x%X)\n",
Title, Rva);
return NULL;
}
else if (Rva + Size > g_DumpInfoFiles[DUMP_INFO_DUMP].MapSize)
{
ErrOut("ERROR: %s only partially present in dump "
"(RVA 0x%X, size 0x%X)\n",
Title, Rva, Size);
return NULL;
}
return IndexByByte(m_Header, Rva);
}
PVOID
UserMiniDumpTargetInfo::IndexDirectory(ULONG Index,
MINIDUMP_DIRECTORY UNALIGNED *Dir,
PVOID* Store)
{
if (*Store != NULL)
{
WarnOut("WARNING: Ignoring extra %s stream, dir entry %d\n",
MiniStreamTypeName(Dir->StreamType), Index);
return NULL;
}
char Msg[128];
sprintf(Msg, "Dir entry %d, %s stream",
Index, MiniStreamTypeName(Dir->StreamType));
PVOID Ptr = IndexRva(Dir->Location.Rva, Dir->Location.DataSize, Msg);
if (Ptr != NULL)
{
*Store = Ptr;
}
return Ptr;
}
void
UserMiniDumpTargetInfo::DumpDebug(void)
{
ULONG i;
dprintf("----- User Mini Dump Analysis\n");
dprintf("\nMINIDUMP_HEADER:\n");
dprintf("Version %X (%X)\n",
m_Header->Version & 0xffff, m_Header->Version >> 16);
dprintf("NumberOfStreams %d\n", m_Header->NumberOfStreams);
dprintf("Flags %X\n", m_Header->Flags);
MINIDUMP_DIRECTORY UNALIGNED *Dir;
dprintf("\nStreams:\n");
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
IndexRva(m_Header->StreamDirectoryRva,
m_Header->NumberOfStreams * sizeof(*Dir),
"Directory");
if (Dir == NULL)
{
return;
}
PVOID Data;
for (i = 0; i < m_Header->NumberOfStreams; i++)
{
dprintf("Stream %d: type %s (%d), size %08X, RVA %08X\n",
i, MiniStreamTypeName(Dir->StreamType), Dir->StreamType,
Dir->Location.DataSize, Dir->Location.Rva);
Data = NULL;
if (IndexDirectory(i, Dir, &Data) == NULL)
{
continue;
}
ULONG j;
RVA Rva;
Rva = Dir->Location.Rva;
switch(Dir->StreamType)
{
case ModuleListStream:
MINIDUMP_MODULE_LIST UNALIGNED *ModList;
MINIDUMP_MODULE UNALIGNED *Mod;
ModList = (MINIDUMP_MODULE_LIST UNALIGNED *)Data;
Mod = ModList->Modules;
dprintf(" %d modules\n", ModList->NumberOfModules);
Rva += FIELD_OFFSET(MINIDUMP_MODULE_LIST, Modules);
for (j = 0; j < ModList->NumberOfModules; j++)
{
PVOID Str = IndexRva(Mod->ModuleNameRva,
sizeof(MINIDUMP_STRING),
"Module entry name");
dprintf(" RVA %08X, %s - %s: '%S'\n",
Rva,
FormatAddr64(Mod->BaseOfImage),
FormatAddr64(Mod->BaseOfImage + Mod->SizeOfImage),
Str != NULL ?
((MINIDUMP_STRING UNALIGNED *)Str)->Buffer :
L"** Invalid **");
Mod++;
Rva += sizeof(*Mod);
}
break;
case MemoryListStream:
{
MINIDUMP_MEMORY_LIST UNALIGNED *MemList;
MemList = (MINIDUMP_MEMORY_LIST UNALIGNED *)Data;
dprintf(" %d memory ranges\n", MemList->NumberOfMemoryRanges);
dprintf(" range# Address %sSize\n",
g_TargetMachine->m_Ptr64 ? " " : "");
for (j = 0; j < MemList->NumberOfMemoryRanges; j++)
{
dprintf(" %4d %s %s\n",
j,
FormatAddr64(MemList->MemoryRanges[j].StartOfMemoryRange),
FormatAddr64(MemList->MemoryRanges[j].Memory.DataSize));
}
break;
}
case Memory64ListStream:
{
MINIDUMP_MEMORY64_LIST UNALIGNED *MemList;
MemList = (MINIDUMP_MEMORY64_LIST UNALIGNED *)Data;
dprintf(" %d memory ranges\n", MemList->NumberOfMemoryRanges);
dprintf(" RVA 0x%X BaseRva\n", (ULONG)(MemList->BaseRva));
dprintf(" range# Address %sSize\n",
g_TargetMachine->m_Ptr64 ? " " : "");
for (j = 0; j < MemList->NumberOfMemoryRanges; j++)
{
dprintf(" %4d %s %s\n",
j,
FormatAddr64(MemList->MemoryRanges[j].StartOfMemoryRange),
FormatAddr64(MemList->MemoryRanges[j].DataSize));
}
break;
}
case CommentStreamA:
dprintf(" '%s'\n", Data);
break;
case CommentStreamW:
dprintf(" '%ls'\n", Data);
break;
}
Dir++;
}
}
//----------------------------------------------------------------------------
//
// UserMiniPartialDumpTargetInfo.
//
//----------------------------------------------------------------------------
HRESULT
UserMiniPartialDumpTargetInfo::Initialize(void)
{
HRESULT Status;
dprintf("User Mini Dump File: Only registers and stack "
"trace are available\n\n");
if ((Status = UserMiniDumpTargetInfo::Initialize()) != S_OK)
{
return Status;
}
if (m_Memory != NULL)
{
//
// Map every piece of memory in the dump. This makes
// ReadVirtual very simple and there shouldn't be that
// many ranges so it doesn't require that many map regions.
//
if (!MemoryMap_Create())
{
return E_OUTOFMEMORY;
}
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *Mem;
ULONG i;
ULONG64 TotalMemory;
Mem = m_Memory->MemoryRanges;
for (i = 0; i < m_Memory->NumberOfMemoryRanges; i++)
{
PVOID Data = IndexRva(Mem->Memory.Rva, Mem->Memory.DataSize,
"Memory range data");
if (Data == NULL)
{
return HR_DUMP_CORRUPT;
}
if ((Status = MemoryMap_AddRegion(Mem->StartOfMemoryRange,
Mem->Memory.DataSize, Data,
NULL, FALSE)) != S_OK)
{
return Status;
}
Mem++;
}
VerbOut(" Memory regions: %d\n",
m_Memory->NumberOfMemoryRanges);
Mem = m_Memory->MemoryRanges;
TotalMemory = 0;
for (i = 0; i < m_Memory->NumberOfMemoryRanges; i++)
{
VerbOut(" %5d: %s - %s\n",
i, FormatAddr64(Mem->StartOfMemoryRange),
FormatAddr64(Mem->StartOfMemoryRange +
Mem->Memory.DataSize - 1));
TotalMemory += Mem->Memory.DataSize;
Mem++;
}
VerbOut(" Total memory region size %s\n",
FormatAddr64(TotalMemory));
}
return S_OK;
}
HRESULT
UserMiniPartialDumpTargetInfo::ReadVirtual(
IN ULONG64 Offset,
OUT PVOID Buffer,
IN ULONG BufferSize,
OUT OPTIONAL PULONG BytesRead
)
{
// All virtual memory is contained in the memory map.
return MemoryMap_ReadMemory(Offset, Buffer, BufferSize,
BytesRead) ? S_OK : E_FAIL;
}
HRESULT
UserMiniPartialDumpTargetInfo::QueryMemoryRegion
(PULONG64 Handle,
BOOL HandleIsOffset,
PMEMORY_BASIC_INFORMATION64 Info)
{
ULONG Index;
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *Mem;
if (HandleIsOffset)
{
if (m_Memory == NULL)
{
return E_NOINTERFACE;
}
Mem = m_Memory->MemoryRanges;
for (Index = 0; Index < m_Memory->NumberOfMemoryRanges; Index++)
{
if (*Handle >= Mem->StartOfMemoryRange &&
*Handle < Mem->StartOfMemoryRange + Mem->Memory.DataSize)
{
break;
}
Mem++;
}
if (Index >= m_Memory->NumberOfMemoryRanges)
{
return E_NOINTERFACE;
}
}
else
{
Index = (ULONG)*Handle;
if (m_Memory == NULL || Index >= m_Memory->NumberOfMemoryRanges)
{
return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES);
}
Mem = m_Memory->MemoryRanges + Index;
}
Info->BaseAddress = Mem->StartOfMemoryRange;
Info->AllocationBase = Mem->StartOfMemoryRange;
Info->AllocationProtect = PAGE_READWRITE;
Info->__alignment1 = 0;
Info->RegionSize = Mem->Memory.DataSize;
Info->State = MEM_COMMIT;
Info->Protect = PAGE_READWRITE;
Info->Type = MEM_PRIVATE;
Info->__alignment2 = 0;
*Handle = ++Index;
return S_OK;
}
HRESULT
UserMiniPartialDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status;
if ((Status = UserMiniDumpTargetInfo::IdentifyDump(BaseMapSize)) != S_OK)
{
return Status;
}
if (m_Header->Flags & MiniDumpWithFullMemory)
{
return E_NOINTERFACE;
}
return S_OK;
}
ULONG64
UserMiniPartialDumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
*File = DUMP_INFO_DUMP;
if (m_Memory == NULL)
{
return 0;
}
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *Mem = m_Memory->MemoryRanges;
ULONG i;
for (i = 0; i < m_Memory->NumberOfMemoryRanges; i++)
{
if (Virt >= Mem->StartOfMemoryRange &&
Virt < Mem->StartOfMemoryRange + Mem->Memory.DataSize)
{
ULONG Frag = (ULONG)(Virt - Mem->StartOfMemoryRange);
*Avail = Mem->Memory.DataSize - Frag;
return Mem->Memory.Rva + Frag;
}
Mem++;
}
return 0;
}
//----------------------------------------------------------------------------
//
// UserMiniFullDumpTargetInfo.
//
//----------------------------------------------------------------------------
HRESULT
UserMiniFullDumpTargetInfo::Initialize(void)
{
HRESULT Status;
dprintf("User Mini Dump File with Full Memory: Only application "
"data is available\n\n");
if ((Status = UserMiniDumpTargetInfo::Initialize()) != S_OK)
{
return Status;
}
if (m_Memory != NULL)
{
ULONG64 TotalMemory;
ULONG i;
MINIDUMP_MEMORY_DESCRIPTOR64 UNALIGNED *Mem;
VerbOut(" Memory regions: %d\n",
m_Memory64->NumberOfMemoryRanges);
Mem = m_Memory64->MemoryRanges;
TotalMemory = 0;
for (i = 0; i < m_Memory64->NumberOfMemoryRanges; i++)
{
VerbOut(" %5d: %s - %s\n",
i, FormatAddr64(Mem->StartOfMemoryRange),
FormatAddr64(Mem->StartOfMemoryRange +
Mem->DataSize - 1));
TotalMemory += Mem->DataSize;
Mem++;
}
VerbOut(" Total memory region size %s\n",
FormatAddr64(TotalMemory));
}
return S_OK;
}
HRESULT
UserMiniFullDumpTargetInfo::QueryMemoryRegion
(PULONG64 Handle,
BOOL HandleIsOffset,
PMEMORY_BASIC_INFORMATION64 Info)
{
ULONG Index;
MINIDUMP_MEMORY_DESCRIPTOR64 UNALIGNED *Mem;
if (HandleIsOffset)
{
if (m_Memory64 == NULL)
{
return E_NOINTERFACE;
}
Mem = m_Memory64->MemoryRanges;
for (Index = 0; Index < m_Memory64->NumberOfMemoryRanges; Index++)
{
if (*Handle >= Mem->StartOfMemoryRange &&
*Handle < Mem->StartOfMemoryRange + Mem->DataSize)
{
break;
}
Mem++;
}
if (Index >= m_Memory64->NumberOfMemoryRanges)
{
return E_NOINTERFACE;
}
}
else
{
Index = (ULONG)*Handle;
if (m_Memory64 == NULL || Index >= m_Memory64->NumberOfMemoryRanges)
{
return HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES);
}
Mem = m_Memory64->MemoryRanges + Index;
}
Info->BaseAddress = Mem->StartOfMemoryRange;
Info->AllocationBase = Mem->StartOfMemoryRange;
Info->AllocationProtect = PAGE_READWRITE;
Info->__alignment1 = 0;
Info->RegionSize = Mem->DataSize;
Info->State = MEM_COMMIT;
Info->Protect = PAGE_READWRITE;
Info->Type = MEM_PRIVATE;
Info->__alignment2 = 0;
*Handle = ++Index;
return S_OK;
}
HRESULT
UserMiniFullDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
{
HRESULT Status;
if ((Status = UserMiniDumpTargetInfo::IdentifyDump(BaseMapSize)) != S_OK)
{
return Status;
}
if (!(m_Header->Flags & MiniDumpWithFullMemory))
{
return E_NOINTERFACE;
}
if (m_Memory64DataBase == 0)
{
ErrOut("Full-memory minidump must have a Memory64ListStream\n");
return E_FAIL;
}
// In the case of a full memory minidump we don't
// want to map the entire dump as it can be very large.
// Fortunately, we are guaranteed that all of the raw
// memory data in a full memory minidump will be at the
// end of the dump, so we can just map the dump up
// to the memory content and stop.
*BaseMapSize = m_Memory64DataBase;
return S_OK;
}
ULONG64
UserMiniFullDumpTargetInfo::VirtualToOffset(ULONG64 Virt,
PULONG File, PULONG Avail)
{
*File = DUMP_INFO_DUMP;
if (m_Memory64 == NULL)
{
return 0;
}
MINIDUMP_MEMORY_DESCRIPTOR64 UNALIGNED *Mem = m_Memory64->MemoryRanges;
ULONG i;
ULONG64 Offset = m_Memory64->BaseRva;
for (i = 0; i < m_Memory64->NumberOfMemoryRanges; i++)
{
if (Virt >= Mem->StartOfMemoryRange &&
Virt < Mem->StartOfMemoryRange + Mem->DataSize)
{
ULONG64 Frag = Virt - Mem->StartOfMemoryRange;
ULONG64 Avail64 = Mem->DataSize - Frag;
if (Avail64 > 0xffffffff)
{
*Avail = 0xffffffff;
}
else
{
*Avail = (ULONG)Avail64;
}
return Offset + Frag;
}
Offset += Mem->DataSize;
Mem++;
}
return 0;
}
//----------------------------------------------------------------------------
//
// ModuleInfo implementations.
//
//----------------------------------------------------------------------------
HRESULT
KernelTriage32ModuleInfo::Initialize(void)
{
m_Target = (KernelTriage32DumpTargetInfo*)g_Target;
if (m_Target->m_Dump->Triage.DriverListOffset != 0)
{
m_Head = m_Target->m_Dump->Triage.DriverCount;
m_Cur = 0;
return S_OK;
}
else
{
dprintf("Mini Kernel Dump does not contain driver list\n");
return S_FALSE;
}
}
HRESULT
KernelTriage32ModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
if (m_Cur == m_Head)
{
return S_FALSE;
}
PDUMP_DRIVER_ENTRY32 DriverEntry;
PDUMP_STRING DriverName;
DBG_ASSERT(m_Target->m_Dump->Triage.DriverListOffset != 0);
DriverEntry = (PDUMP_DRIVER_ENTRY32)
IndexByByte(m_Target->m_Dump,
m_Target->m_Dump->Triage.DriverListOffset +
m_Cur * sizeof(*DriverEntry));
DriverName = (PDUMP_STRING)
IndexByByte(m_Target->m_Dump, DriverEntry->DriverNameOffset);
Entry->NamePtr = (PSTR)DriverName->Buffer;
Entry->UnicodeNamePtr = 1;
Entry->NameLength = DriverName->Length * sizeof(WCHAR);
Entry->Base = EXTEND64(DriverEntry->LdrEntry.DllBase);
Entry->Size = DriverEntry->LdrEntry.SizeOfImage;
Entry->ImageInfoValid = TRUE;
Entry->CheckSum = DriverEntry->LdrEntry.CheckSum;
Entry->TimeDateStamp = DriverEntry->LdrEntry.TimeDateStamp;
m_Cur++;
return S_OK;
}
KernelTriage32ModuleInfo g_KernelTriage32ModuleIterator;
HRESULT
KernelTriage64ModuleInfo::Initialize(void)
{
m_Target = (KernelTriage64DumpTargetInfo*)g_Target;
if (m_Target->m_Dump->Triage.DriverListOffset != 0)
{
m_Head = m_Target->m_Dump->Triage.DriverCount;
m_Cur = 0;
return S_OK;
}
else
{
dprintf("Mini Kernel Dump does not contain driver list\n");
return S_FALSE;
}
}
HRESULT
KernelTriage64ModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
if (m_Cur == m_Head)
{
return S_FALSE;
}
PDUMP_DRIVER_ENTRY64 DriverEntry;
PDUMP_STRING DriverName;
DBG_ASSERT(m_Target->m_Dump->Triage.DriverListOffset != 0);
DriverEntry = (PDUMP_DRIVER_ENTRY64)
IndexByByte(m_Target->m_Dump,
m_Target->m_Dump->Triage.DriverListOffset +
m_Cur * sizeof(*DriverEntry));
DriverName = (PDUMP_STRING)
IndexByByte(m_Target->m_Dump, DriverEntry->DriverNameOffset);
Entry->NamePtr = (PSTR)DriverName->Buffer;
Entry->UnicodeNamePtr = 1;
Entry->NameLength = DriverName->Length * sizeof(WCHAR);
Entry->Base = DriverEntry->LdrEntry.DllBase;
Entry->Size = DriverEntry->LdrEntry.SizeOfImage;
Entry->ImageInfoValid = TRUE;
Entry->CheckSum = DriverEntry->LdrEntry.CheckSum;
Entry->TimeDateStamp = DriverEntry->LdrEntry.TimeDateStamp;
m_Cur++;
return S_OK;
}
KernelTriage64ModuleInfo g_KernelTriage64ModuleIterator;
HRESULT
UserMiniModuleInfo::Initialize(void)
{
m_Target = (UserMiniDumpTargetInfo*)g_Target;
if (m_Target->m_Modules != NULL)
{
m_Head = m_Target->m_Modules->NumberOfModules;
m_Cur = 0;
return S_OK;
}
else
{
m_Head = 0;
m_Cur = 0;
dprintf("User Mode Mini Dump does not have a module list\n");
return S_FALSE;
}
}
HRESULT
UserMiniModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
{
if (m_Cur == m_Head)
{
return S_FALSE;
}
MINIDUMP_MODULE UNALIGNED *Mod;
MINIDUMP_STRING UNALIGNED *ModName;
DBG_ASSERT(m_Target->m_Modules != NULL);
Mod = m_Target->m_Modules->Modules + m_Cur;
ModName = (MINIDUMP_STRING UNALIGNED *)
m_Target->IndexRva(Mod->ModuleNameRva, sizeof(*ModName),
"Module entry name");
if (ModName == NULL)
{
return HR_DUMP_CORRUPT;
}
Entry->NamePtr = (PSTR)ModName->Buffer;
Entry->UnicodeNamePtr = 1;
Entry->NameLength = ModName->Length;
// Some dumps do not have properly sign-extended addresses,
// so force the extension on 32-bit platforms.
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386 ||
g_TargetMachineType == IMAGE_FILE_MACHINE_ALPHA)
{
Entry->Base = EXTEND64(Mod->BaseOfImage);
}
else
{
Entry->Base = Mod->BaseOfImage;
}
Entry->Size = Mod->SizeOfImage;
Entry->ImageInfoValid = TRUE;
Entry->CheckSum = Mod->CheckSum;
Entry->TimeDateStamp = Mod->TimeDateStamp;
m_Cur++;
return S_OK;
}
UserMiniModuleInfo g_UserMiniModuleIterator;
HRESULT
KernelTriage32UnloadedModuleInfo::Initialize(void)
{
m_Target = (KernelTriage32DumpTargetInfo*)g_Target;
if (m_Target->m_Dump->Triage.UnloadedDriversOffset != 0)
{
PVOID Data = IndexByByte
(m_Target->m_Dump, m_Target->m_Dump->Triage.UnloadedDriversOffset);
m_Cur = (PDUMP_UNLOADED_DRIVERS32)((PULONG)Data + 1);
m_End = m_Cur + *(PULONG)Data;
return S_OK;
}
else
{
dprintf("Mini Kernel Dump does not contain unloaded driver list\n");
return S_FALSE;
}
}
HRESULT
KernelTriage32UnloadedModuleInfo::GetEntry(PSTR Name,
PDEBUG_MODULE_PARAMETERS Params)
{
if (m_Cur == m_End)
{
return S_FALSE;
}
ZeroMemory(Params, sizeof(*Params));
Params->Base = EXTEND64(m_Cur->StartAddress);
Params->Size = m_Cur->EndAddress - m_Cur->StartAddress;
Params->Flags = DEBUG_MODULE_UNLOADED;
if (Name != NULL)
{
USHORT NameLen = m_Cur->Name.Length;
if (NameLen > MAX_UNLOADED_NAME_LENGTH)
{
NameLen = MAX_UNLOADED_NAME_LENGTH;
}
if (WideCharToMultiByte(CP_ACP, 0, m_Cur->DriverName,
NameLen / sizeof(WCHAR),
Name,
MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR) + 1,
NULL, NULL) == 0)
{
return WIN32_LAST_STATUS();
}
Name[NameLen / sizeof(WCHAR)] = 0;
}
m_Cur++;
return S_OK;
}
KernelTriage32UnloadedModuleInfo g_KernelTriage32UnloadedModuleIterator;
HRESULT
KernelTriage64UnloadedModuleInfo::Initialize(void)
{
m_Target = (KernelTriage64DumpTargetInfo*)g_Target;
if (m_Target->m_Dump->Triage.UnloadedDriversOffset != 0)
{
PVOID Data = IndexByByte
(m_Target->m_Dump, m_Target->m_Dump->Triage.UnloadedDriversOffset);
m_Cur = (PDUMP_UNLOADED_DRIVERS64)((PULONG64)Data + 1);
m_End = m_Cur + *(PULONG)Data;
return S_OK;
}
else
{
dprintf("Mini Kernel Dump does not contain unloaded driver list\n");
return S_FALSE;
}
}
HRESULT
KernelTriage64UnloadedModuleInfo::GetEntry(PSTR Name,
PDEBUG_MODULE_PARAMETERS Params)
{
if (m_Cur == m_End)
{
return S_FALSE;
}
ZeroMemory(Params, sizeof(*Params));
Params->Base = m_Cur->StartAddress;
Params->Size = (ULONG)(m_Cur->EndAddress - m_Cur->StartAddress);
Params->Flags = DEBUG_MODULE_UNLOADED;
if (Name != NULL)
{
USHORT NameLen = m_Cur->Name.Length;
if (NameLen > MAX_UNLOADED_NAME_LENGTH)
{
NameLen = MAX_UNLOADED_NAME_LENGTH;
}
if (WideCharToMultiByte(CP_ACP, 0, m_Cur->DriverName,
NameLen / sizeof(WCHAR),
Name,
MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR) + 1,
NULL, NULL) == 0)
{
return WIN32_LAST_STATUS();
}
Name[NameLen / sizeof(WCHAR)] = 0;
}
m_Cur++;
return S_OK;
}
KernelTriage64UnloadedModuleInfo g_KernelTriage64UnloadedModuleIterator;
//----------------------------------------------------------------------------
//
// Validation code.
//
//----------------------------------------------------------------------------
#if DBG
VOID
DumpPageMap (
IN PRTL_BITMAP PageMap
)
{
PFN_NUMBER32 StartOfRun;
BOOL Mapped;
ULONG LineCount;
ULONG i;
printf ("VERBOSE:\nSummary Dump Page Map (not mapped pages): \n\n");
LineCount = 0;
StartOfRun = 0;
Mapped = RtlCheckBit (PageMap, 0);
for (i = 1; i < PageMap->SizeOfBitMap; i++) {
if (RtlCheckBit (PageMap, i)) {
printf ("%4.4x ", (SHORT) i);
if (LineCount == 11) {
printf ("\n");
LineCount = 0;
}
LineCount++;
}
}
printf ("\n\n");
}
#endif
// XXX drewb - Verification disabled until we decide what we want.
#ifdef VALIDATE_DUMPS
BOOL
DmppVerifyPagePresentInDumpFile(
ULONG Page,
ULONG Size
)
{
CHAR * Address;
CHAR ch;
BOOL ret = FALSE;
__try {
Address = DmppFileOffsetToMappedAddress (
DmppPageToOffset( Page ), FALSE);
if (!Address) {
ret = FALSE;
__leave;
}
ch = *Address;
Address += Size - 1;
ch = *Address;
ret = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ret = FALSE;
}
return ret;
}
BOOL
WINAPI
DmpValidateDumpFile(
BOOL ThoroughCheck
)
/*++
Routine Description:
Validate that a dump file is valid. This will do the work of checking
if the file is a user-mode or kernel-mode dump file and verifying
that it is correct.
Arguments:
ThoroughCheck - If TRUE, instructs DmpValidateDumpFile to do a thorough
(significantly slower) check of the dump file. Otherwise a qicker,
less complete check is done.
Return Values:
TRUE - The dumpfile is valid.
FALSE - The dumpfile is invalid. Extended error status is available using
GetLastError.
--*/
{
PPHYSICAL_MEMORY_DESCRIPTOR32 PhysicalMemoryBlock;
BOOL ret = TRUE;
if (IS_FULL_USER_DUMP()) {
// ret = DmppUserModeTestHeader ();
// if (ret == 1 && ThoroughCheck) {
// ret = DmppUserModeTestContents ();
// }
} else {
PhysicalMemoryBlock = &g_DumpHeaderKernel32->Header.PhysicalMemoryBlock;
if (IS_SUMMARY_DUMP()) {
//
// The summary dump will have holes in the page table, so it's
// useless to even check it.
//
ret = TRUE;
} else {
if (g_DumpAddressExtensions) {
//
// This function is broken.
//
return TRUE;
#if 0
ULONG i;
ULONG j;
BOOL ret;
X86PAE_HARDWARE_PDPTE * PageDirectoryPointerTableBase;
X86PAE_HARDWARE_PDPTE * PageDirectoryPointerTableEntry;
HARDWARE_PDE_X86PAE * PageDirectoryBase;
HARDWARE_PDE_X86PAE * PageDirectoryEntry;
HARDWARE_PTE_X86PAE * PageTableBase;
//
// On a PAE kernel DmpPdePage is actually a pointer to the
// Page-Directory-Pointer-Table, not the Page-Directory.
//
//ASSERT ( DmpPdePage );
PageDirectoryPointerTableBase = (X86PAE_HARDWARE_PDPTE *) DmpPdePage;
//
// Loop through the top-level Page-Directory-Dointer Table.
//
ret = TRUE;
for (i = 0; i < 4; i++) {
PageDirectoryPointerTableEntry = &PageDirectoryPointerTableBase [ i ];
if (!PageDirectoryPointerTableEntry->Valid) {
//
// All Page-Directory-Pointer Table Entries must be present.
//
ret = FALSE;
break;
}
PageDirectoryBase = DmppFileOffsetToMappedAddress(
DmppPageToOffset(PageDirectoryPointerTableEntry->PageFrameNumber),
TRUE);
if (!PageDirectoryBase) {
//
// The specified page frame number did not map to a valid page.
//
ret = FALSE;
break;
}
//
// Loop through the Page-Directory Table.
//
for (j = 0; (j < 512) && (ret == TRUE) ; j++) {
PageDirectoryEntry = &PageDirectoryBase [ j ];
if (!PageDirectoryEntry->Valid) {
//
// The specific Page Table is not present. Not an error.
//
continue;
}
PageTableBase = DmppFileOffsetToMappedAddress(
DmppPageToOffset(PageDirectoryEntry->PageFrameNumber),
TRUE);
if (!PageTableBase) {
//
// The Page Table did not map to a valid address.
//
ret = FALSE;
break;
}
}
}
#endif
} else {
// NOT implemented
ret = TRUE;
}
//
// Dump validation routine. This routine verifies that any
// memory present in the MmPhysicalMemoryBlock was actually
// written to the dump file.
if (ret == 0 && ThoroughCheck) {
PFN_NUMBER32 i;
PFN_NUMBER32 j;
PFN_NUMBER32 Page;
for (i = 0; i < PhysicalMemoryBlock->NumberOfRuns; i++) {
for (j = 0; j < PhysicalMemoryBlock->Run [ i ].PageCount; j++) {
Page = PhysicalMemoryBlock->Run [ i ].BasePage + j;
if (!DmppVerifyPagePresentInDumpFile (Page, PageSize)) {
ret = FALSE;
break;
}
}
}
}
}
}
return ret;
}
#endif // #ifdef VALIDATE_DUMPS