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.
7953 lines
230 KiB
7953 lines
230 KiB
/*++
|
|
|
|
Copyright (c) 1990-2002 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 DUMP_INITIAL_VIEW_SIZE (1024 * 1024)
|
|
#define DUMP_MAXIMUM_VIEW_SIZE (256 * 1024 * 1024)
|
|
|
|
typedef ULONG PFN_NUMBER32;
|
|
|
|
ULONG g_DumpApiTypes[] =
|
|
{
|
|
DEBUG_DUMP_FILE_BASE,
|
|
DEBUG_DUMP_FILE_PAGE_FILE_DUMP,
|
|
};
|
|
|
|
//
|
|
// 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()
|
|
|
|
// 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;
|
|
|
|
|
|
#define RtlCheckBit(BMH,BP) ((((BMH)->Buffer[(BP) / 32]) >> ((BP) % 32)) & 0x1)
|
|
|
|
|
|
//
|
|
// 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,
|
|
};
|
|
|
|
//
|
|
// AddDumpInformationFile and OpenDumpFile work together
|
|
// to establish the full set of files to use for a particular
|
|
// dump target. As the files are given before the target
|
|
// exists, they are collected in this global array until
|
|
// the target takes them over.
|
|
//
|
|
|
|
ViewMappedFile g_PendingDumpInfoFiles[DUMP_INFO_COUNT];
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ViewMappedFile.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define MAX_CLEAN_PAGE_RECORD 4
|
|
|
|
ViewMappedFile::ViewMappedFile(void)
|
|
{
|
|
// No need to specialize this right now so just pick
|
|
// up the global setting.
|
|
m_CacheGranularity = g_SystemAllocGranularity;
|
|
|
|
ResetCache();
|
|
ResetFile();
|
|
}
|
|
|
|
ViewMappedFile::~ViewMappedFile(void)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void
|
|
ViewMappedFile::ResetCache(void)
|
|
{
|
|
m_ActiveCleanPages = 0;
|
|
InitializeListHead(&m_InFileOrderList);
|
|
InitializeListHead(&m_InLRUOrderList);
|
|
}
|
|
|
|
void
|
|
ViewMappedFile::ResetFile(void)
|
|
{
|
|
m_FileNameW = NULL;
|
|
m_FileNameA = NULL;
|
|
m_File = NULL;
|
|
m_FileSize = 0;
|
|
m_Map = NULL;
|
|
m_MapBase = NULL;
|
|
m_MapSize = 0;
|
|
}
|
|
|
|
void
|
|
ViewMappedFile::EmptyCache(void)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
CacheRecord* CacheRec;
|
|
|
|
Next = m_InFileOrderList.Flink;
|
|
|
|
while (Next != &m_InFileOrderList)
|
|
{
|
|
CacheRec = CONTAINING_RECORD(Next, CacheRecord, InFileOrderList);
|
|
Next = Next->Flink;
|
|
|
|
UnmapViewOfFile(CacheRec->MappedAddress);
|
|
|
|
free(CacheRec);
|
|
}
|
|
|
|
ResetCache();
|
|
}
|
|
|
|
ViewMappedFile::CacheRecord*
|
|
ViewMappedFile::ReuseOldestCacheRecord(ULONG64 FileByteOffset)
|
|
{
|
|
CacheRecord* CacheRec;
|
|
PLIST_ENTRY Next;
|
|
PVOID MappedAddress;
|
|
ULONG64 MapOffset;
|
|
ULONG Size;
|
|
|
|
MapOffset = FileByteOffset & ~((ULONG64)m_CacheGranularity - 1);
|
|
|
|
if ((m_FileSize - MapOffset) < m_CacheGranularity)
|
|
{
|
|
Size = (ULONG)(m_FileSize - MapOffset);
|
|
}
|
|
else
|
|
{
|
|
Size = m_CacheGranularity;
|
|
}
|
|
|
|
MappedAddress = MapViewOfFile(m_Map, FILE_MAP_READ,
|
|
(DWORD)(MapOffset >> 32),
|
|
(DWORD)MapOffset, Size);
|
|
if (MappedAddress == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Next = m_InLRUOrderList.Flink;
|
|
|
|
CacheRec = CONTAINING_RECORD(Next, CacheRecord, InLRUOrderList);
|
|
|
|
UnmapViewOfFile(CacheRec->MappedAddress);
|
|
|
|
CacheRec->PageNumber = FileByteOffset / m_CacheGranularity;
|
|
CacheRec->MappedAddress = MappedAddress;
|
|
|
|
//
|
|
// Move record to end of LRU
|
|
//
|
|
|
|
RemoveEntryList(Next);
|
|
InsertTailList(&m_InLRUOrderList, Next);
|
|
|
|
//
|
|
// Move record to correct place in ordered list
|
|
//
|
|
|
|
RemoveEntryList(&CacheRec->InFileOrderList);
|
|
Next = m_InFileOrderList.Flink;
|
|
while (Next != &m_InFileOrderList)
|
|
{
|
|
CacheRecord* NextCacheRec;
|
|
NextCacheRec = CONTAINING_RECORD(Next, CacheRecord, InFileOrderList);
|
|
if (CacheRec->PageNumber < NextCacheRec->PageNumber)
|
|
{
|
|
break;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
InsertTailList(Next, &CacheRec->InFileOrderList);
|
|
|
|
return CacheRec;
|
|
}
|
|
|
|
ViewMappedFile::CacheRecord*
|
|
ViewMappedFile::FindCacheRecordForFileByteOffset(ULONG64 FileByteOffset)
|
|
{
|
|
CacheRecord* CacheRec;
|
|
PLIST_ENTRY Next;
|
|
ULONG64 PageNumber;
|
|
|
|
PageNumber = FileByteOffset / m_CacheGranularity;
|
|
Next = m_InFileOrderList.Flink;
|
|
while (Next != &m_InFileOrderList)
|
|
{
|
|
CacheRec = CONTAINING_RECORD(Next, CacheRecord, InFileOrderList);
|
|
|
|
if (CacheRec->PageNumber < PageNumber)
|
|
{
|
|
Next = Next->Flink;
|
|
}
|
|
else if (CacheRec->PageNumber == PageNumber)
|
|
{
|
|
if (!CacheRec->Locked)
|
|
{
|
|
RemoveEntryList(&CacheRec->InLRUOrderList);
|
|
InsertTailList(&m_InLRUOrderList,
|
|
&CacheRec->InLRUOrderList);
|
|
}
|
|
|
|
return CacheRec;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Can't find it in cache.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ViewMappedFile::CacheRecord*
|
|
ViewMappedFile::CreateNewFileCacheRecord(ULONG64 FileByteOffset)
|
|
{
|
|
CacheRecord* CacheRec;
|
|
CacheRecord* NextCacheRec;
|
|
PLIST_ENTRY Next;
|
|
ULONG64 MapOffset;
|
|
ULONG Size;
|
|
|
|
CacheRec = (CacheRecord*)malloc(sizeof(*CacheRec));
|
|
if (CacheRec == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(CacheRec, sizeof(*CacheRec));
|
|
|
|
MapOffset = (FileByteOffset / m_CacheGranularity) *
|
|
m_CacheGranularity;
|
|
|
|
if ((m_FileSize - MapOffset) < m_CacheGranularity)
|
|
{
|
|
Size = (ULONG)(m_FileSize - MapOffset);
|
|
}
|
|
else
|
|
{
|
|
Size = m_CacheGranularity;
|
|
}
|
|
|
|
CacheRec->MappedAddress = MapViewOfFile(m_Map, FILE_MAP_READ,
|
|
(DWORD)(MapOffset >> 32),
|
|
(DWORD)MapOffset, Size);
|
|
if (CacheRec->MappedAddress == NULL)
|
|
{
|
|
free(CacheRec);
|
|
return NULL;
|
|
}
|
|
CacheRec->PageNumber = FileByteOffset / m_CacheGranularity;
|
|
|
|
//
|
|
// Insert new record in file order list
|
|
//
|
|
|
|
Next = m_InFileOrderList.Flink;
|
|
while (Next != &m_InFileOrderList)
|
|
{
|
|
NextCacheRec = CONTAINING_RECORD(Next, CacheRecord, InFileOrderList);
|
|
if (CacheRec->PageNumber < NextCacheRec->PageNumber)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
InsertTailList(Next, &CacheRec->InFileOrderList);
|
|
|
|
//
|
|
// Insert new record at tail of LRU list
|
|
//
|
|
|
|
InsertTailList(&m_InLRUOrderList,
|
|
&CacheRec->InLRUOrderList);
|
|
|
|
return CacheRec;
|
|
}
|
|
|
|
PUCHAR
|
|
ViewMappedFile::FileOffsetToMappedAddress(ULONG64 FileOffset,
|
|
BOOL LockCacheRec,
|
|
PULONG Avail)
|
|
{
|
|
CacheRecord* CacheRec;
|
|
ULONG64 FileByteOffset;
|
|
|
|
if (FileOffset == 0 || FileOffset >= m_FileSize)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// The base view covers the beginning of the file.
|
|
if (FileOffset < m_MapSize)
|
|
{
|
|
*Avail = (ULONG)(m_MapSize - FileOffset);
|
|
return (PUCHAR)m_MapBase + FileOffset;
|
|
}
|
|
|
|
FileByteOffset = FileOffset;
|
|
CacheRec = FindCacheRecordForFileByteOffset(FileByteOffset);
|
|
|
|
if (CacheRec == NULL)
|
|
{
|
|
if (m_ActiveCleanPages < MAX_CLEAN_PAGE_RECORD)
|
|
{
|
|
CacheRec = CreateNewFileCacheRecord(FileByteOffset);
|
|
if (CacheRec)
|
|
{
|
|
m_ActiveCleanPages++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// too many pages cached in
|
|
// overwrite existing cache
|
|
//
|
|
CacheRec = ReuseOldestCacheRecord(FileByteOffset);
|
|
}
|
|
}
|
|
|
|
if (CacheRec == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (LockCacheRec && !CacheRec->Locked)
|
|
{
|
|
RemoveEntryList(&CacheRec->InLRUOrderList);
|
|
CacheRec->Locked = TRUE;
|
|
m_ActiveCleanPages--;
|
|
}
|
|
|
|
ULONG PageRemainder =
|
|
(ULONG)(FileByteOffset & (m_CacheGranularity - 1));
|
|
*Avail = m_CacheGranularity - PageRemainder;
|
|
return ((PUCHAR)CacheRec->MappedAddress) + PageRemainder;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
ViewMappedFile::ReadFileOffset(ULONG64 Offset, PVOID Buffer, ULONG BufferSize)
|
|
{
|
|
ULONG Done = 0;
|
|
ULONG Avail;
|
|
PUCHAR Mapping;
|
|
|
|
if (m_File == NULL)
|
|
{
|
|
// Information for this kind of file wasn't provided.
|
|
return 0;
|
|
}
|
|
|
|
__try
|
|
{
|
|
while (BufferSize > 0)
|
|
{
|
|
Mapping = FileOffsetToMappedAddress(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
|
|
ViewMappedFile::WriteFileOffset(ULONG64 Offset, PVOID Buffer, ULONG BufferSize)
|
|
{
|
|
ULONG Done = 0;
|
|
ULONG Avail;
|
|
PUCHAR Mapping;
|
|
ULONG Protect;
|
|
|
|
if (m_File == NULL)
|
|
{
|
|
// Information for this kind of file wasn't provided.
|
|
return 0;
|
|
}
|
|
|
|
__try
|
|
{
|
|
while (BufferSize > 0)
|
|
{
|
|
Mapping = FileOffsetToMappedAddress(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
|
|
ViewMappedFile::Open(PCWSTR FileName, ULONG64 FileHandle, ULONG InitialView)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// Pick up default cache size if necessary.
|
|
if (!m_CacheGranularity)
|
|
{
|
|
m_CacheGranularity = g_SystemAllocGranularity;
|
|
}
|
|
|
|
if (((m_CacheGranularity - 1) & InitialView) ||
|
|
m_File != NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!FileName)
|
|
{
|
|
if (!FileHandle)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
FileName = L"<HandleOnly>";
|
|
}
|
|
|
|
m_FileNameW = _wcsdup(FileName);
|
|
if (!m_FileNameW)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if ((Status = WideToAnsi(FileName, &m_FileNameA)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (FileHandle)
|
|
{
|
|
m_File = OS_HANDLE(FileHandle);
|
|
}
|
|
else
|
|
{
|
|
// 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.
|
|
m_File = CreateFileW(FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
if ((!m_File || m_File == INVALID_HANDLE_VALUE) &&
|
|
GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
{
|
|
//
|
|
// ANSI-only platform. Try the ANSI name.
|
|
//
|
|
|
|
m_File = CreateFileA(m_FileNameA,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
}
|
|
if (!m_File || m_File == INVALID_HANDLE_VALUE)
|
|
{
|
|
goto LastStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get file size and map initial view.
|
|
//
|
|
|
|
ULONG SizeLow, SizeHigh;
|
|
|
|
SizeLow = GetFileSize(m_File, &SizeHigh);
|
|
m_FileSize = ((ULONG64)SizeHigh << 32) | SizeLow;
|
|
m_Map = CreateFileMapping(m_File, NULL, PAGE_READONLY,
|
|
0, 0, NULL);
|
|
if (m_Map == NULL)
|
|
{
|
|
goto LastStatus;
|
|
}
|
|
|
|
if (m_FileSize < InitialView)
|
|
{
|
|
InitialView = (ULONG)m_FileSize;
|
|
}
|
|
|
|
m_MapBase = MapViewOfFile(m_Map, FILE_MAP_READ, 0, 0,
|
|
InitialView);
|
|
if (m_MapBase == NULL)
|
|
{
|
|
goto LastStatus;
|
|
}
|
|
|
|
m_MapSize = InitialView;
|
|
|
|
return S_OK;
|
|
|
|
LastStatus:
|
|
Status = WIN32_LAST_STATUS();
|
|
Close();
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
ViewMappedFile::Close(void)
|
|
{
|
|
EmptyCache();
|
|
|
|
if (m_MapBase != NULL)
|
|
{
|
|
UnmapViewOfFile(m_MapBase);
|
|
m_MapBase = NULL;
|
|
}
|
|
if (m_Map != NULL)
|
|
{
|
|
CloseHandle(m_Map);
|
|
m_Map = NULL;
|
|
}
|
|
free(m_FileNameW);
|
|
m_FileNameW = NULL;
|
|
FreeAnsi(m_FileNameA);
|
|
m_FileNameA = NULL;
|
|
if (m_File != NULL && m_File != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(m_File);
|
|
}
|
|
m_File = NULL;
|
|
m_FileSize = 0;
|
|
m_MapSize = 0;
|
|
}
|
|
|
|
HRESULT
|
|
ViewMappedFile::RemapInitial(ULONG MapSize)
|
|
{
|
|
if (MapSize > m_FileSize)
|
|
{
|
|
MapSize = (ULONG)m_FileSize;
|
|
}
|
|
UnmapViewOfFile(m_MapBase);
|
|
m_MapBase = MapViewOfFile(m_Map, FILE_MAP_READ,
|
|
0, 0, MapSize);
|
|
if (m_MapBase == NULL)
|
|
{
|
|
m_MapSize = 0;
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
m_MapSize = MapSize;
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
ViewMappedFile::Transfer(ViewMappedFile* To)
|
|
{
|
|
To->m_FileNameW = m_FileNameW;
|
|
To->m_FileNameA = m_FileNameA;
|
|
To->m_File = m_File;
|
|
To->m_FileSize = m_FileSize;
|
|
To->m_Map = m_Map;
|
|
To->m_MapBase = m_MapBase;
|
|
To->m_MapSize = m_MapSize;
|
|
|
|
To->m_ActiveCleanPages = m_ActiveCleanPages;
|
|
if (!IsListEmpty(&m_InFileOrderList))
|
|
{
|
|
To->m_InFileOrderList = m_InFileOrderList;
|
|
}
|
|
if (!IsListEmpty(&m_InLRUOrderList))
|
|
{
|
|
To->m_InLRUOrderList = m_InLRUOrderList;
|
|
}
|
|
|
|
ResetCache();
|
|
ResetFile();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Initialization functions.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
AddDumpInfoFile(PCWSTR FileName, ULONG64 FileHandle,
|
|
ULONG Index, ULONG InitialView)
|
|
{
|
|
HRESULT Status;
|
|
ViewMappedFile* File = &g_PendingDumpInfoFiles[Index];
|
|
|
|
if ((Status = File->Open(FileName, FileHandle, InitialView)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
switch(Index)
|
|
{
|
|
case DUMP_INFO_PAGE_FILE:
|
|
if (memcmp(File->m_MapBase, DMPPF_IDENTIFIER,
|
|
sizeof(DMPPF_IDENTIFIER) - 1) != 0)
|
|
{
|
|
Status = HR_DATA_CORRUPT;
|
|
File->Close();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
DumpTargetInfo*
|
|
NewDumpTargetInfo(ULONG DumpType)
|
|
{
|
|
switch(DumpType)
|
|
{
|
|
case DTYPE_KERNEL_SUMMARY32:
|
|
return new KernelSummary32DumpTargetInfo;
|
|
case DTYPE_KERNEL_SUMMARY64:
|
|
return new KernelSummary64DumpTargetInfo;
|
|
case DTYPE_KERNEL_TRIAGE32:
|
|
return new KernelTriage32DumpTargetInfo;
|
|
case DTYPE_KERNEL_TRIAGE64:
|
|
return new KernelTriage64DumpTargetInfo;
|
|
case DTYPE_KERNEL_FULL32:
|
|
return new KernelFull32DumpTargetInfo;
|
|
case DTYPE_KERNEL_FULL64:
|
|
return new KernelFull64DumpTargetInfo;
|
|
case DTYPE_USER_FULL32:
|
|
return new UserFull32DumpTargetInfo;
|
|
case DTYPE_USER_FULL64:
|
|
return new UserFull64DumpTargetInfo;
|
|
case DTYPE_USER_MINI_PARTIAL:
|
|
return new UserMiniPartialDumpTargetInfo;
|
|
case DTYPE_USER_MINI_FULL:
|
|
return new UserMiniFullDumpTargetInfo;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT
|
|
IdentifyDump(PCWSTR FileName, ULONG64 FileHandle,
|
|
DumpTargetInfo** TargetRet)
|
|
{
|
|
HRESULT Status;
|
|
|
|
dprintf("\nLoading Dump File [%ws]\n", FileName);
|
|
|
|
if ((Status = AddDumpInfoFile(FileName, FileHandle,
|
|
DUMP_INFO_DUMP,
|
|
DUMP_INITIAL_VIEW_SIZE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
ViewMappedFile* File = &g_PendingDumpInfoFiles[DUMP_INFO_DUMP];
|
|
ULONG i, FileIdx;
|
|
ULONG64 BaseMapSize;
|
|
DumpTargetInfo* Target = NULL;
|
|
|
|
for (i = 0; i < DTYPE_COUNT; i++)
|
|
{
|
|
Target = NewDumpTargetInfo(i);
|
|
if (Target == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
Target->m_DumpBase = File->m_MapBase;
|
|
BaseMapSize = File->m_MapSize;
|
|
|
|
// The target owns the dump info files while it's active.
|
|
for (FileIdx = 0; FileIdx < DUMP_INFO_COUNT; FileIdx++)
|
|
{
|
|
g_PendingDumpInfoFiles[FileIdx].
|
|
Transfer(&Target->m_InfoFiles[FileIdx]);
|
|
}
|
|
|
|
Status = Target->IdentifyDump(&BaseMapSize);
|
|
|
|
// Remove the info files to handle potential errors and loops.
|
|
for (FileIdx = 0; FileIdx < DUMP_INFO_COUNT; FileIdx++)
|
|
{
|
|
Target->m_InfoFiles[FileIdx].
|
|
Transfer(&g_PendingDumpInfoFiles[FileIdx]);
|
|
}
|
|
|
|
if (Status != E_NOINTERFACE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
delete Target;
|
|
Target = NULL;
|
|
}
|
|
|
|
if (Status == E_NOINTERFACE)
|
|
{
|
|
ErrOut("Could not match Dump File signature - "
|
|
"invalid file format\n");
|
|
}
|
|
else if (Status == S_OK &&
|
|
BaseMapSize > File->m_MapSize)
|
|
{
|
|
if (BaseMapSize > File->m_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);
|
|
Status = File->RemapInitial((ULONG)BaseMapSize);
|
|
}
|
|
}
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
Target->m_DumpBase = File->m_MapBase;
|
|
|
|
// Target now owns the info files permanently.
|
|
for (FileIdx = 0; FileIdx < DUMP_INFO_COUNT; FileIdx++)
|
|
{
|
|
g_PendingDumpInfoFiles[FileIdx].
|
|
Transfer(&Target->m_InfoFiles[FileIdx]);
|
|
}
|
|
|
|
*TargetRet = Target;
|
|
}
|
|
else
|
|
{
|
|
delete Target;
|
|
File->Close();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// DumpTargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
DumpIdentifyStatus(ULONG ExcepCode)
|
|
{
|
|
//
|
|
// If we got an access violation it means that the
|
|
// dump content we wanted wasn't present. Treat
|
|
// this as an identification failure and not a
|
|
// critical failure.
|
|
//
|
|
// Any other code is just passed back as a critical failure.
|
|
//
|
|
|
|
if (ExcepCode == STATUS_ACCESS_VIOLATION)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
else
|
|
{
|
|
return HRESULT_FROM_NT(ExcepCode);
|
|
}
|
|
}
|
|
|
|
DumpTargetInfo::DumpTargetInfo(ULONG Class, ULONG Qual, BOOL MappedImages)
|
|
: TargetInfo(Class, Qual, FALSE)
|
|
{
|
|
m_DumpBase = NULL;
|
|
m_FormatFlags = 0;
|
|
m_MappedImages = MappedImages;
|
|
ZeroMemory(&m_ExceptionRecord, sizeof(m_ExceptionRecord));
|
|
m_ExceptionFirstChance = FALSE;
|
|
m_WriterStatus = DUMP_WRITER_STATUS_UNINITIALIZED;
|
|
}
|
|
|
|
DumpTargetInfo::~DumpTargetInfo(void)
|
|
{
|
|
// Force processes and threads to get cleaned up while
|
|
// the memory maps are still available.
|
|
DeleteSystemInfo();
|
|
}
|
|
|
|
HRESULT
|
|
DumpTargetInfo::InitSystemInfo(ULONG BuildNumber, ULONG CheckedBuild,
|
|
ULONG MachineType, ULONG PlatformId,
|
|
ULONG MajorVersion, ULONG MinorVersion)
|
|
{
|
|
HRESULT Status;
|
|
|
|
SetSystemVersionAndBuild(BuildNumber, PlatformId);
|
|
m_CheckedBuild = CheckedBuild;
|
|
m_KdApi64 = (m_SystemVersion > NT_SVER_NT4);
|
|
m_PlatformId = PlatformId;
|
|
|
|
// We can call InitializeForProcessor right away as it
|
|
// doesn't do anything interesting for dumps.
|
|
if ((Status = InitializeMachines(MachineType)) != S_OK ||
|
|
(Status = InitializeForProcessor()) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (m_Machine == NULL)
|
|
{
|
|
ErrOut("Dump has an unknown processor type, 0x%X\n", MachineType);
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
m_KdVersion.MachineType = (USHORT) MachineType;
|
|
m_KdVersion.MajorVersion = (USHORT) MajorVersion;
|
|
m_KdVersion.MinorVersion = (USHORT) MinorVersion;
|
|
m_KdVersion.Flags = DBGKD_VERS_FLAG_DATA |
|
|
(m_Machine->m_Ptr64 ? DBGKD_VERS_FLAG_PTR64 : 0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DumpTargetInfo::ReadVirtual(
|
|
IN ProcessInfo* Process,
|
|
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 = m_InfoFiles[FileIndex].
|
|
ReadFileOffset(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(
|
|
IN ProcessInfo* Process,
|
|
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 = m_InfoFiles[FileIndex].
|
|
WriteFileOffset(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::MapReadVirtual(ProcessInfo* Process,
|
|
ULONG64 Offset,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
PULONG BytesRead)
|
|
{
|
|
//
|
|
// There are two mapped memory lists kept, one
|
|
// for dump data and one for images mapped from disk.
|
|
// Dump data always takes precedence over mapped image
|
|
// data as on-disk images may not reflect the true state
|
|
// of memory at the time of the dump. Read from the dump
|
|
// data map whenever possible, only going to the image map
|
|
// when no dump data is available.
|
|
//
|
|
|
|
*BytesRead = 0;
|
|
while (BufferSize > 0)
|
|
{
|
|
ULONG64 NextAddr;
|
|
ULONG Req;
|
|
ULONG Read;
|
|
|
|
//
|
|
// Check the dump data map.
|
|
//
|
|
|
|
if (m_DataMemMap.ReadMemory(Offset, Buffer, BufferSize, &Read))
|
|
{
|
|
Offset += Read;
|
|
Buffer = (PVOID)((PUCHAR)Buffer + Read);
|
|
BufferSize -= Read;
|
|
*BytesRead += Read;
|
|
|
|
// If we got everything we're done.
|
|
if (BufferSize == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We still have memory to read so check the image map.
|
|
//
|
|
|
|
// Find out where the next data block is so we can limit
|
|
// the image map read.
|
|
Req = BufferSize;
|
|
if (m_DataMemMap.GetNextRegion(Offset, &NextAddr))
|
|
{
|
|
NextAddr -= Offset;
|
|
if (NextAddr < Req)
|
|
{
|
|
Req = (ULONG)NextAddr;
|
|
}
|
|
}
|
|
|
|
// Now demand-load any deferred image memory.
|
|
DemandLoadReferencedImageMemory(Process, Offset, Req);
|
|
|
|
// Try to read.
|
|
if (m_ImageMemMap.ReadMemory(Offset, Buffer, Req, &Read))
|
|
{
|
|
Offset += Read;
|
|
Buffer = (PVOID)((PUCHAR)Buffer + Read);
|
|
BufferSize -= Read;
|
|
*BytesRead += Read;
|
|
|
|
// This read was limited to an area that wouldn't overlap
|
|
// any data memory so if we didn't read the full request
|
|
// we know there isn't data memory available either and
|
|
// we can quit.
|
|
if (Read < Req)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No memory in either map.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return *BytesRead > 0 ? S_OK : E_FAIL;
|
|
}
|
|
|
|
void
|
|
DumpTargetInfo::MapNearestDifferentlyValidOffsets(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.
|
|
//
|
|
|
|
if (NextOffset != NULL)
|
|
{
|
|
ULONG64 Next;
|
|
|
|
*NextOffset = (ULONG64)-1;
|
|
if (m_DataMemMap.GetNextRegion(Offset, &Next))
|
|
{
|
|
*NextOffset = Next;
|
|
}
|
|
if (m_DataMemMap.GetNextRegion(Offset, &Next) &&
|
|
Next < *NextOffset)
|
|
{
|
|
*NextOffset = Next;
|
|
}
|
|
if (*NextOffset == (ULONG64)-1)
|
|
{
|
|
*NextOffset = Offset + 1;
|
|
}
|
|
}
|
|
if (NextPage != NULL)
|
|
{
|
|
*NextPage = (Offset + m_Machine->m_PageSize) &
|
|
~((ULONG64)m_Machine->m_PageSize - 1);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
DumpTargetInfo::SwitchProcessors(ULONG Processor)
|
|
{
|
|
SetCurrentProcessorThread(this, Processor, FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags,
|
|
PCSTR CommentA, PCWSTR CommentW)
|
|
{
|
|
ErrOut("Dump file type does not support writing\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
PVOID
|
|
DumpTargetInfo::IndexRva(PVOID Base, RVA Rva, ULONG Size, PCSTR Title)
|
|
{
|
|
if (Rva >= m_InfoFiles[DUMP_INFO_DUMP].m_MapSize)
|
|
{
|
|
ErrOut("ERROR: %s not present in dump (RVA 0x%X)\n",
|
|
Title, Rva);
|
|
FlushCallbacks();
|
|
return NULL;
|
|
}
|
|
else if (Rva + Size > m_InfoFiles[DUMP_INFO_DUMP].m_MapSize)
|
|
{
|
|
ErrOut("ERROR: %s only partially present in dump "
|
|
"(RVA 0x%X, size 0x%X)\n",
|
|
Title, Rva, Size);
|
|
FlushCallbacks();
|
|
return NULL;
|
|
}
|
|
|
|
return IndexByByte(Base, Rva);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// KernelDumpTargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
OutputHeaderString(PCSTR Format, PSTR Str)
|
|
{
|
|
if (*(PULONG)Str == DUMP_SIGNATURE32 ||
|
|
Str[0] == 0)
|
|
{
|
|
// String not present.
|
|
return;
|
|
}
|
|
|
|
dprintf(Format, Str);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::ReadControlSpaceIa64(
|
|
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 = m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbPcrPage,
|
|
Status = ReadVirtual(m_ProcessHead,
|
|
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 = m_KiProcessors[Processor];
|
|
Read = sizeof(ULONG64);
|
|
Status = S_OK;
|
|
break;
|
|
|
|
case IA64_DEBUG_CONTROL_SPACE_KSPECIAL:
|
|
StartAddr = m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbProcStateSpecialReg;
|
|
Status = ReadVirtual(m_ProcessHead,
|
|
StartAddr, Buffer, BufferSize, &Read);
|
|
break;
|
|
|
|
case IA64_DEBUG_CONTROL_SPACE_THREAD:
|
|
StartAddr = m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbCurrentThread;
|
|
Status = ReadVirtual(m_ProcessHead,
|
|
StartAddr, Buffer,
|
|
sizeof(ULONG64), &Read);
|
|
break;
|
|
}
|
|
|
|
*BytesRead = Read;
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::ReadControlSpaceAmd64(
|
|
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 = m_KiProcessors[Processor] -
|
|
m_KdDebuggerData.OffsetPcrContainedPrcb;
|
|
Read = sizeof(ULONG64);
|
|
Status = S_OK;
|
|
break;
|
|
|
|
case AMD64_DEBUG_CONTROL_SPACE_PRCB:
|
|
*(PULONG64)Buffer = m_KiProcessors[Processor];
|
|
Read = sizeof(ULONG64);
|
|
Status = S_OK;
|
|
break;
|
|
|
|
case AMD64_DEBUG_CONTROL_SPACE_KSPECIAL:
|
|
StartAddr = m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbProcStateSpecialReg;
|
|
Status = ReadVirtual(m_ProcessHead,
|
|
StartAddr, Buffer, BufferSize, &Read);
|
|
break;
|
|
|
|
case AMD64_DEBUG_CONTROL_SPACE_THREAD:
|
|
StartAddr = m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbCurrentThread;
|
|
Status = ReadVirtual(m_ProcessHead,
|
|
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(this) &&
|
|
m_KdDebuggerData.KiProcessorBlock == 0)
|
|
{
|
|
ErrOut("ReadControl failed - ntoskrnl symbols must be loaded first\n");
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (m_KiProcessors[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(m_MachineType)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
// X86 control space is just a view of the PRCB's
|
|
// processor state. That starts with the context.
|
|
StartAddr = Offset +
|
|
m_KiProcessors[Processor] +
|
|
m_KdDebuggerData.OffsetPrcbProcStateContext;
|
|
return ReadVirtual(m_ProcessHead,
|
|
StartAddr, Buffer, BufferSize, BytesRead);
|
|
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
return ReadControlSpaceIa64(Processor, Offset, Buffer,
|
|
BufferSize, BytesRead);
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
return ReadControlSpaceAmd64(Processor, Offset, Buffer,
|
|
BufferSize, BytesRead);
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetTaggedBaseOffset(PULONG64 Offset)
|
|
{
|
|
// The tagged offset can never be zero as that's
|
|
// always the dump header, so if the tagged offset
|
|
// is zero it means there's no tagged data.
|
|
*Offset = m_TaggedOffset;
|
|
return m_TaggedOffset ? S_OK : E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::ReadTagged(ULONG64 Offset, PVOID Buffer,
|
|
ULONG BufferSize)
|
|
{
|
|
ULONG Attempt = m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(Offset, Buffer, BufferSize);
|
|
return Attempt == BufferSize ?
|
|
S_OK : HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetThreadIdByProcessor(
|
|
IN ULONG Processor,
|
|
OUT PULONG Id
|
|
)
|
|
{
|
|
*Id = VIRTUAL_THREAD_ID(Processor);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetThreadInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetThreadInfoDataOffset(Thread, ThreadHandle, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetProcessInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetProcessInfoDataOffset(Thread, Processor, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetThreadInfoTeb(ThreadInfo* Thread,
|
|
ULONG ThreadIndex,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetThreadInfoTeb(Thread, ThreadIndex, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetProcessInfoPeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return KdGetProcessInfoPeb(Thread, Processor, ThreadData, Offset);
|
|
}
|
|
|
|
ULONG64
|
|
KernelDumpTargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
if (m_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 m_Machine->m_Ptr64 ?
|
|
((PDUMP_HEADER64)m_DumpBase)->SystemTime.QuadPart :
|
|
((PDUMP_HEADER32)m_DumpBase)->SystemTime.QuadPart;
|
|
}
|
|
|
|
ULONG64
|
|
KernelDumpTargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
ULONG64 Page = DUMP_SIGNATURE32;
|
|
ULONG64 Page64 = Page | (Page << 32);
|
|
ULONG64 SystemUpTime = m_Machine->m_Ptr64 ?
|
|
((PDUMP_HEADER64)m_DumpBase)->SystemUpTime.QuadPart :
|
|
((PDUMP_HEADER32)m_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::GetProductInfo(PULONG ProductType, PULONG SuiteMask)
|
|
{
|
|
PULONG HdrType, HdrMask;
|
|
|
|
// Try and get the information from the header. If that's
|
|
// not available try and read it from the shared user data.
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
HdrType = &((PDUMP_HEADER64)m_DumpBase)->ProductType;
|
|
HdrMask = &((PDUMP_HEADER64)m_DumpBase)->SuiteMask;
|
|
}
|
|
else
|
|
{
|
|
HdrType = &((PDUMP_HEADER32)m_DumpBase)->ProductType;
|
|
HdrMask = &((PDUMP_HEADER32)m_DumpBase)->SuiteMask;
|
|
}
|
|
if (*HdrType == DUMP_SIGNATURE32)
|
|
{
|
|
// Not available in header.
|
|
return ReadSharedUserProductInfo(ProductType, SuiteMask);
|
|
}
|
|
else
|
|
{
|
|
*ProductType = *HdrType;
|
|
*SuiteMask = *HdrMask;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetProcessorId
|
|
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
|
|
{
|
|
return m_Machine->ReadKernelProcessorId(Processor, Id);
|
|
}
|
|
|
|
HRESULT
|
|
KernelDumpTargetInfo::GetProcessorSpeed
|
|
(ULONG Processor, PULONG Speed)
|
|
{
|
|
HRESULT Status;
|
|
ULONG64 Prcb;
|
|
|
|
if ((Status =
|
|
GetProcessorSystemDataOffset(Processor, DEBUG_DATA_KPRCB_OFFSET,
|
|
&Prcb)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
return
|
|
ReadAllVirtual(m_ProcessHead,
|
|
Prcb + m_KdDebuggerData.OffsetPrcbMhz, Speed,
|
|
sizeof(ULONG));
|
|
}
|
|
|
|
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_DATA_CORRUPT;
|
|
}
|
|
|
|
if (Dump->Header.MinorVersion > 1381 &&
|
|
Dump->Header.PaeEnabled == TRUE )
|
|
{
|
|
m_KdDebuggerData.PaeEnabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_KdDebuggerData.PaeEnabled = FALSE;
|
|
}
|
|
|
|
m_KdDebuggerData.PsLoadedModuleList =
|
|
EXTEND64(Dump->Header.PsLoadedModuleList);
|
|
m_KdVersion.PsLoadedModuleList =
|
|
m_KdDebuggerData.PsLoadedModuleList;
|
|
m_NumProcessors = Dump->Header.NumberProcessors;
|
|
ExceptionRecord32To64(&Dump->Header.Exception, &m_ExceptionRecord);
|
|
m_ExceptionFirstChance = FALSE;
|
|
m_HeaderContext = Dump->Header.ContextRecord;
|
|
|
|
// New field in XP, Win2k SP1 and above
|
|
if ((Dump->Header.KdDebuggerDataBlock) &&
|
|
(Dump->Header.KdDebuggerDataBlock != DUMP_SIGNATURE32))
|
|
{
|
|
m_KdDebuggerDataOffset =
|
|
EXTEND64(Dump->Header.KdDebuggerDataBlock);
|
|
}
|
|
|
|
m_WriterStatus = Dump->Header.WriterStatus;
|
|
|
|
OutputHeaderString("Comment: '%s'\n", Dump->Header.Comment);
|
|
|
|
HRESULT Status =
|
|
InitSystemInfo(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;
|
|
}
|
|
|
|
if (IS_KERNEL_TRIAGE_DUMP(this))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG NextIdx;
|
|
|
|
// We're expecting that the header value will be non-zero
|
|
// so that we don't need a thread to indicate the current processor.
|
|
// If it turns out to be zero the implicit read will fail
|
|
// due to the NULL.
|
|
return m_Machine->
|
|
SetPageDirectory(NULL, 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_DATA_CORRUPT;
|
|
}
|
|
|
|
m_KdDebuggerData.PaeEnabled = FALSE;
|
|
m_KdDebuggerData.PsLoadedModuleList =
|
|
Dump->Header.PsLoadedModuleList;
|
|
m_KdVersion.PsLoadedModuleList =
|
|
m_KdDebuggerData.PsLoadedModuleList;
|
|
m_NumProcessors = Dump->Header.NumberProcessors;
|
|
m_ExceptionRecord = Dump->Header.Exception;
|
|
m_ExceptionFirstChance = FALSE;
|
|
m_HeaderContext = Dump->Header.ContextRecord;
|
|
|
|
// New field in XP, Win2k SP1 and above
|
|
if ((Dump->Header.KdDebuggerDataBlock) &&
|
|
(Dump->Header.KdDebuggerDataBlock != DUMP_SIGNATURE32))
|
|
{
|
|
m_KdDebuggerDataOffset =
|
|
Dump->Header.KdDebuggerDataBlock;
|
|
}
|
|
|
|
m_WriterStatus = Dump->Header.WriterStatus;
|
|
|
|
OutputHeaderString("Comment: '%s'\n", Dump->Header.Comment);
|
|
|
|
HRESULT Status =
|
|
InitSystemInfo(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;
|
|
}
|
|
|
|
if (IS_KERNEL_TRIAGE_DUMP(this))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG NextIdx;
|
|
|
|
// We're expecting that the header value will be non-zero
|
|
// so that we don't need a thread to indicate the current processor.
|
|
// If it turns out to be zero the implicit read will fail
|
|
// due to the NULL.
|
|
return m_Machine->
|
|
SetPageDirectory(NULL, 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);
|
|
if (Header->SecondaryDataState != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("SecondaryDataState %08lx\n", Header->SecondaryDataState);
|
|
}
|
|
if (Header->ProductType != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("ProductType %08lx\n", Header->ProductType);
|
|
dprintf("SuiteMask %08lx\n", Header->SuiteMask);
|
|
}
|
|
if (Header->WriterStatus != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("WriterStatus %08lx\n", Header->WriterStatus);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (Header->SecondaryDataState != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("SecondaryDataState %08lx\n", Header->SecondaryDataState);
|
|
}
|
|
if (Header->ProductType != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("ProductType %08lx\n", Header->ProductType);
|
|
dprintf("SuiteMask %08lx\n", Header->SuiteMask);
|
|
}
|
|
if (Header->WriterStatus != DUMP_SIGNATURE32)
|
|
{
|
|
dprintf("WriterStatus %08lx\n", Header->WriterStatus);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// KernelFullSumDumpTargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
KernelFullSumDumpTargetInfo::PageFileOffset(ULONG PfIndex, ULONG64 PfOffset,
|
|
PULONG64 FileOffset)
|
|
{
|
|
ViewMappedFile* File = &m_InfoFiles[DUMP_INFO_PAGE_FILE];
|
|
if (File->m_File == NULL)
|
|
{
|
|
return HR_PAGE_NOT_AVAILABLE;
|
|
}
|
|
if (PfIndex > MAX_PAGING_FILE_MASK)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
//
|
|
// We can safely assume the header information is present
|
|
// in the base mapping.
|
|
//
|
|
|
|
DMPPF_FILE_HEADER* Hdr = (DMPPF_FILE_HEADER*)File->m_MapBase;
|
|
DMPPF_PAGE_FILE_INFO* FileInfo = &Hdr->PageFiles[PfIndex];
|
|
ULONG64 PfPage = PfOffset >> m_Machine->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 (m_InfoFiles[DUMP_INFO_PAGE_FILE].
|
|
ReadFileOffset(PageDirOffs, &PageDirEnt, sizeof(PageDirEnt)) !=
|
|
sizeof(PageDirEnt))
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
if (PageDirEnt == DMPPF_PAGE_NOT_PRESENT)
|
|
{
|
|
return HR_PAGE_NOT_AVAILABLE;
|
|
}
|
|
|
|
*FileOffset = Hdr->PageData +
|
|
(PageDirEnt << m_Machine->m_PageShift) +
|
|
(PfOffset & (m_Machine->m_PageSize - 1));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
KernelFullSumDumpTargetInfo::ReadPhysical(
|
|
ULONG64 Offset,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
ULONG Flags,
|
|
PULONG BytesRead
|
|
)
|
|
{
|
|
ULONG Done = 0;
|
|
ULONG Avail;
|
|
ULONG Attempt;
|
|
ULONG64 FileOffset;
|
|
|
|
if (Flags != PHYS_FLAG_DEFAULT)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if (BufferSize == 0)
|
|
{
|
|
*BytesRead = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
while (BufferSize > 0)
|
|
{
|
|
// Don't produce error messages on a direct
|
|
// physical access as the behavior of all data access
|
|
// functions is to just return an error when
|
|
// data is not present.
|
|
FileOffset = PhysicalToOffset(Offset, FALSE, &Avail);
|
|
if (FileOffset == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Avail > BufferSize)
|
|
{
|
|
Avail = BufferSize;
|
|
}
|
|
|
|
Attempt = m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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,
|
|
ULONG Flags,
|
|
PULONG BytesWritten
|
|
)
|
|
{
|
|
ULONG Done = 0;
|
|
ULONG Avail;
|
|
ULONG Attempt;
|
|
ULONG64 FileOffset;
|
|
|
|
if (Flags != PHYS_FLAG_DEFAULT)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if (BufferSize == 0)
|
|
{
|
|
*BytesWritten = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
while (BufferSize > 0)
|
|
{
|
|
// Don't produce error messages on a direct
|
|
// physical access as the behavior of all data access
|
|
// functions is to just return an error when
|
|
// data is not present.
|
|
FileOffset = PhysicalToOffset(Offset, FALSE, &Avail);
|
|
if (FileOffset == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Avail > BufferSize)
|
|
{
|
|
Avail = BufferSize;
|
|
}
|
|
|
|
Attempt = m_InfoFiles[DUMP_INFO_DUMP].
|
|
WriteFileOffset(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::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 (m_InfoFiles[DUMP_INFO_PAGE_FILE].
|
|
ReadFileOffset(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(m_ProcessHead,
|
|
m_KiProcessors[VIRTUAL_THREAD_INDEX(Thread)] +
|
|
m_KdDebuggerData.OffsetPrcbProcStateContext,
|
|
Context, m_TypeInfo.SizeTargetContext,
|
|
&Read);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
return Read == m_TypeInfo.SizeTargetContext ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT
|
|
KernelFullSumDumpTargetInfo::GetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
return KdGetSelDescriptor(Thread, Machine, Selector, Desc);
|
|
}
|
|
|
|
void
|
|
KernelFullSumDumpTargetInfo::DumpDebug(void)
|
|
{
|
|
ULONG i;
|
|
|
|
dprintf("\nKiProcessorBlock at %s\n",
|
|
FormatAddr64(m_KdDebuggerData.KiProcessorBlock));
|
|
dprintf(" %d KiProcessorBlock entries:\n ",
|
|
m_NumProcessors);
|
|
for (i = 0; i < m_NumProcessors; i++)
|
|
{
|
|
dprintf(" %s", FormatAddr64(m_KiProcessors[i]));
|
|
}
|
|
dprintf("\n");
|
|
|
|
ViewMappedFile* PageDump = &m_InfoFiles[DUMP_INFO_PAGE_FILE];
|
|
if (PageDump->m_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 = m_Machine->
|
|
GetVirtualTranslationPhysicalOffsets(m_ProcessHead->
|
|
m_CurrentThread,
|
|
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 & (m_Machine->m_PageSize - 1);
|
|
*Avail = m_Machine->m_PageSize - PageIndex;
|
|
return Phys;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 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.
|
|
return PhysicalToOffset(Phys, Virt >= m_SystemRangeStart, Avail);
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
KernelFullSumDumpTargetInfo::GetCurrentProcessor(void)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < m_NumProcessors; i++)
|
|
{
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
|
|
if (GetContext(VIRTUAL_THREAD_HANDLE(i), &Context) == S_OK)
|
|
{
|
|
switch(m_MachineType)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
if (Context.X86Nt5Context.Esp ==
|
|
((PX86_NT5_CONTEXT)m_HeaderContext)->Esp)
|
|
{
|
|
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::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,
|
|
BOOL Verbose,
|
|
PULONG Avail)
|
|
{
|
|
ULONG Offset, j;
|
|
ULONG64 Page = Phys >> m_Machine->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;
|
|
}
|
|
|
|
BOOL Present = TRUE;
|
|
|
|
__try
|
|
{
|
|
if (!RtlCheckBit(&m_PageBitmap, Page))
|
|
{
|
|
if (Verbose)
|
|
{
|
|
ErrOut("Page %x not present in the dump file. "
|
|
"Type \".hh dbgerr004\" for details\n",
|
|
Page);
|
|
}
|
|
Present = FALSE;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!Present)
|
|
{
|
|
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;
|
|
|
|
__try
|
|
{
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
j = m_PageBitmapSize;
|
|
}
|
|
|
|
//
|
|
// 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 & (m_Machine->m_PageSize - 1);
|
|
*Avail = m_Machine->m_PageSize - PageIndex;
|
|
return HeaderSize + (Offset * m_Machine->m_PageSize) +
|
|
PageIndex;
|
|
}
|
|
|
|
HRESULT
|
|
KernelSummary32DumpTargetInfo::Initialize(void)
|
|
{
|
|
// Pick up any potentially modified base mapping pointer.
|
|
m_Dump = (PMEMORY_DUMP32)m_DumpBase;
|
|
|
|
dprintf("Kernel Summary Dump File: "
|
|
"Only kernel address space is available\n\n");
|
|
|
|
ConstructLocationCache(m_Dump->Summary.BitmapSize,
|
|
m_Dump->Summary.Bitmap.SizeOfBitMap,
|
|
m_Dump->Summary.Bitmap.Buffer);
|
|
|
|
HRESULT Status = InitGlobals32(m_Dump);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset =
|
|
m_Dump->Summary.HeaderSize +
|
|
m_Dump->Summary.Pages * m_Machine->m_PageSize;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].m_FileSize < m_TaggedOffset)
|
|
{
|
|
WarnOut("************************************************************\n");
|
|
WarnOut("WARNING: Dump file has been truncated. "
|
|
"Data may be missing.\n");
|
|
WarnOut("************************************************************\n\n");
|
|
}
|
|
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileSize =
|
|
m_Dump->Header.RequiredDumpSpace.QuadPart;
|
|
|
|
return KernelFullSumDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
KernelSummary32DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "32-bit Kernel summary dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE32 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP32 ||
|
|
Dump->Header.DumpType != DUMP_TYPE_SUMMARY)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
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_DATA_CORRUPT;
|
|
}
|
|
else
|
|
{
|
|
PPHYSICAL_MEMORY_RUN32 LastRun;
|
|
ULONG HighestPage;
|
|
|
|
// We rely on all of the header information being
|
|
// directly mapped. Unfortunately the header information
|
|
// can be off due to some size computation bugs, so
|
|
// attempt to get the right size regardless.
|
|
*BaseMapSize = Dump->Summary.HeaderSize;
|
|
LastRun = Dump->Header.PhysicalMemoryBlock.Run +
|
|
(Dump->Header.PhysicalMemoryBlock.NumberOfRuns - 1);
|
|
HighestPage = LastRun->BasePage + LastRun->PageCount;
|
|
if (HighestPage > Dump->Header.PhysicalMemoryBlock.NumberOfPages)
|
|
{
|
|
(*BaseMapSize) +=
|
|
(HighestPage -
|
|
Dump->Header.PhysicalMemoryBlock.NumberOfPages + 7) / 8;
|
|
}
|
|
|
|
Status = S_OK;
|
|
m_Dump = Dump;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG64
|
|
KernelSummary32DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, BOOL Verbose,
|
|
PULONG Avail)
|
|
{
|
|
return SumPhysicalToOffset(m_Dump->Summary.HeaderSize, Phys, Verbose,
|
|
Avail);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
HRESULT
|
|
KernelSummary64DumpTargetInfo::Initialize(void)
|
|
{
|
|
// Pick up any potentially modified base mapping pointer.
|
|
m_Dump = (PMEMORY_DUMP64)m_DumpBase;
|
|
|
|
dprintf("Kernel Summary Dump File: "
|
|
"Only kernel address space is available\n\n");
|
|
|
|
ConstructLocationCache(m_Dump->Summary.BitmapSize,
|
|
m_Dump->Summary.Bitmap.SizeOfBitMap,
|
|
m_Dump->Summary.Bitmap.Buffer);
|
|
|
|
HRESULT Status = InitGlobals64(m_Dump);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset =
|
|
m_Dump->Summary.HeaderSize +
|
|
m_Dump->Summary.Pages * m_Machine->m_PageSize;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].m_FileSize < m_TaggedOffset)
|
|
{
|
|
WarnOut("************************************************************\n");
|
|
WarnOut("WARNING: Dump file has been truncated. "
|
|
"Data may be missing.\n");
|
|
WarnOut("************************************************************\n\n");
|
|
}
|
|
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileSize =
|
|
m_Dump->Header.RequiredDumpSpace.QuadPart;
|
|
|
|
return KernelFullSumDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
KernelSummary64DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "64-bit Kernel summary dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE64 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP64 ||
|
|
Dump->Header.DumpType != DUMP_TYPE_SUMMARY)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
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_DATA_CORRUPT;
|
|
}
|
|
else
|
|
{
|
|
PPHYSICAL_MEMORY_RUN64 LastRun;
|
|
ULONG64 HighestPage;
|
|
|
|
// We rely on all of the header information being
|
|
// directly mapped. Unfortunately the header information
|
|
// can be off due to some size computation bugs, so
|
|
// attempt to get the right size regardless.
|
|
*BaseMapSize = Dump->Summary.HeaderSize;
|
|
LastRun = Dump->Header.PhysicalMemoryBlock.Run +
|
|
(Dump->Header.PhysicalMemoryBlock.NumberOfRuns - 1);
|
|
HighestPage = LastRun->BasePage + LastRun->PageCount;
|
|
if (HighestPage > Dump->Header.PhysicalMemoryBlock.NumberOfPages)
|
|
{
|
|
(*BaseMapSize) +=
|
|
(HighestPage -
|
|
Dump->Header.PhysicalMemoryBlock.NumberOfPages + 7) / 8;
|
|
}
|
|
|
|
Status = S_OK;
|
|
m_Dump = Dump;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG64
|
|
KernelSummary64DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, BOOL Verbose,
|
|
PULONG Avail)
|
|
{
|
|
return SumPhysicalToOffset(m_Dump->Summary.HeaderSize, Phys, Verbose,
|
|
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::NearestDifferentlyValidOffsets(ULONG64 Offset,
|
|
PULONG64 NextOffset,
|
|
PULONG64 NextPage)
|
|
{
|
|
return MapNearestDifferentlyValidOffsets(Offset, NextOffset, NextPage);
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriageDumpTargetInfo::ReadVirtual(
|
|
IN ProcessInfo* Process,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
)
|
|
{
|
|
return MapReadVirtual(Process, Offset, Buffer, BufferSize, BytesRead);
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriageDumpTargetInfo::GetProcessorSystemDataOffset(
|
|
IN ULONG Processor,
|
|
IN ULONG Index,
|
|
OUT PULONG64 Offset
|
|
)
|
|
{
|
|
if (Processor != GetCurrentProcessor())
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ULONG64 Prcb = m_KiProcessors[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
|
|
(m_ProcessHead, m_Machine,
|
|
Prcb + m_KdDebuggerData.OffsetPrcbCurrentThread,
|
|
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,
|
|
m_TypeInfo.SizeTargetContext);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriageDumpTargetInfo::GetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
return EmulateNtSelDescriptor(Thread, Machine, Selector, Desc);
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriageDumpTargetInfo::SwitchProcessors(ULONG Processor)
|
|
{
|
|
ErrOut("Can't switch processors on a kernel triage dump\n");
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
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 data memory regions.
|
|
if (m_DataMemMap.GetRegionInfo(Virt, &Base, &Size, &Mapping, &Param))
|
|
{
|
|
ULONG Delta = (ULONG)(Virt - Base);
|
|
*Avail = Size - Delta;
|
|
return ((PUCHAR)Mapping - (PUCHAR)m_DumpBase) + Delta;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
KernelTriageDumpTargetInfo::GetCurrentProcessor(void)
|
|
{
|
|
// Extract the processor number from the
|
|
// PRCB in the dump.
|
|
PUCHAR PrcbNumber = (PUCHAR)
|
|
IndexRva(m_DumpBase, m_PrcbOffset +
|
|
m_KdDebuggerData.OffsetPrcbNumber, sizeof(UCHAR),
|
|
"PRCB.Number");
|
|
return PrcbNumber ? *PrcbNumber : 0;
|
|
}
|
|
|
|
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;
|
|
|
|
// Map any debugger data.
|
|
// We have to do this first to get the size of the various NT
|
|
// data structures will will map after this.
|
|
|
|
if (DebuggerDataAddress)
|
|
{
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(DebuggerDataAddress,
|
|
DebuggerDataSize,
|
|
IndexRva(m_DumpBase,
|
|
DebuggerDataOffset,
|
|
DebuggerDataSize,
|
|
"Debugger data block"),
|
|
NULL, TRUE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
m_HasDebuggerData = 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 UNALIGNED* DbgDataPtr;
|
|
ULONG64 DbgData;
|
|
ULONG MmData;
|
|
ULONG Size;
|
|
|
|
DbgDataPtr = (ULONG64 UNALIGNED*)
|
|
IndexRva(m_DumpBase, DebuggerDataOffset +
|
|
Trans->DebuggerDataOffset,
|
|
sizeof(ULONG64), "Debugger data block");
|
|
if (!DbgDataPtr)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
DbgData = *DbgDataPtr;
|
|
Size = sizeof(ULONG);
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
MmData = MmDataOffset + Trans->Triage64Offset;
|
|
if (Trans->PtrSize)
|
|
{
|
|
Size = sizeof(ULONG64);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MmData = MmDataOffset + Trans->Triage32Offset;
|
|
DbgData = EXTEND64(DbgData);
|
|
}
|
|
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(DbgData, Size,
|
|
IndexRva(m_DumpBase, MmData, Size,
|
|
"MMTRIAGE data"),
|
|
NULL, TRUE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Trans++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load KdDebuggerDataBlock data right now so
|
|
// that the type constants in it are available immediately.
|
|
//
|
|
|
|
ReadKdDataBlock(m_ProcessHead);
|
|
|
|
// 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();
|
|
if (Processor >= MAXIMUM_PROCS)
|
|
{
|
|
ErrOut("Dump does not contain valid processor number\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
// 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.
|
|
m_KiProcessors[Processor] = m_TypeInfo.TriagePrcbOffset;
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(m_KiProcessors[Processor],
|
|
m_KdDebuggerData.SizePrcb,
|
|
IndexRva(m_DumpBase, PrcbOffset,
|
|
m_KdDebuggerData.SizePrcb, "PRCB"),
|
|
NULL, FALSE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Add ETHREAD and EPROCESS memory regions if available.
|
|
//
|
|
|
|
if (ThreadOffset != 0)
|
|
{
|
|
PVOID CurThread =
|
|
IndexRva(m_DumpBase, PrcbOffset +
|
|
m_KdDebuggerData.OffsetPrcbCurrentThread,
|
|
m_Machine->m_Ptr64 ? 8 : 4,
|
|
"PRCB.CurrentThread");
|
|
ULONG64 ThreadAddr, ProcAddr;
|
|
PVOID DataPtr;
|
|
|
|
if (!CurThread)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
ThreadAddr = *(PULONG64)CurThread;
|
|
DataPtr = IndexRva(m_DumpBase, ThreadOffset +
|
|
m_KdDebuggerData.OffsetKThreadApcProcess,
|
|
sizeof(ULONG64), "PRCB.ApcState.Process");
|
|
if (!DataPtr)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
ProcAddr = *(PULONG64)DataPtr;
|
|
}
|
|
else
|
|
{
|
|
ThreadAddr = EXTEND64(*(PULONG)CurThread);
|
|
DataPtr = IndexRva(m_DumpBase, ThreadOffset +
|
|
m_KdDebuggerData.OffsetKThreadApcProcess,
|
|
sizeof(ULONG), "PRCB.ApcState.Process");
|
|
if (!DataPtr)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
ProcAddr = EXTEND64(*(PULONG)DataPtr);
|
|
}
|
|
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(ThreadAddr,
|
|
m_KdDebuggerData.SizeEThread,
|
|
IndexRva(m_DumpBase, ThreadOffset,
|
|
m_KdDebuggerData.SizeEThread,
|
|
"ETHREAD"),
|
|
NULL, TRUE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (ProcessOffset != 0)
|
|
{
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(ProcAddr,
|
|
m_KdDebuggerData.SizeEProcess,
|
|
IndexRva(m_DumpBase, ProcessOffset,
|
|
m_KdDebuggerData.SizeEProcess,
|
|
"EPROCESS"),
|
|
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 (m_MachineType == IMAGE_FILE_MACHINE_IA64)
|
|
{
|
|
if (BStoreOffset != 0)
|
|
{
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(BStoreLimit - SizeOfBStore, SizeOfBStore,
|
|
IndexRva(m_DumpBase, BStoreOffset, SizeOfBStore,
|
|
"Backing store"),
|
|
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 = m_DataMemMap.
|
|
AddRegion(DataPageAddress, DataPageSize,
|
|
IndexRva(m_DumpBase, DataPageOffset, DataPageSize,
|
|
"Data page"),
|
|
NULL, TRUE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Map arbitrary data blocks.
|
|
if (DataBlocksCount > 0)
|
|
{
|
|
PTRIAGE_DATA_BLOCK Block;
|
|
|
|
Block = (PTRIAGE_DATA_BLOCK)
|
|
IndexRva(m_DumpBase, DataBlocksOffset, sizeof(Block),
|
|
"Data block descriptor");
|
|
if (!Block)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
while (DataBlocksCount-- > 0)
|
|
{
|
|
if ((Status = m_DataMemMap.
|
|
AddRegion(Block->Address, Block->Size,
|
|
IndexRva(m_DumpBase, Block->Offset, Block->Size,
|
|
"Data block"),
|
|
NULL, TRUE)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Block++;
|
|
}
|
|
}
|
|
|
|
// Add the stack to the valid memory region.
|
|
return m_DataMemMap.
|
|
AddRegion(TopOfStack, SizeOfCallStack,
|
|
IndexRva(m_DumpBase, CallStackOffset,
|
|
SizeOfCallStack, "Call stack"),
|
|
NULL, TRUE);
|
|
}
|
|
|
|
void
|
|
KernelTriageDumpTargetInfo::DumpDataBlocks(ULONG Offset, ULONG Count)
|
|
{
|
|
PTRIAGE_DATA_BLOCK Block;
|
|
ULONG MaxOffset;
|
|
|
|
if (Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Block = (PTRIAGE_DATA_BLOCK)
|
|
IndexRva(m_DumpBase, Offset, sizeof(Block),
|
|
"Data block descriptor");
|
|
if (!Block)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MaxOffset = 0;
|
|
while (Count-- > 0)
|
|
{
|
|
dprintf(" %s - %s at offset %08x\n",
|
|
FormatAddr64(Block->Address),
|
|
FormatAddr64(Block->Address + Block->Size - 1),
|
|
Block->Offset);
|
|
|
|
if (Block->Offset + Block->Size > MaxOffset)
|
|
{
|
|
MaxOffset = Block->Offset + Block->Size;
|
|
}
|
|
|
|
Block++;
|
|
}
|
|
|
|
dprintf(" Max offset %x, %x from end of file\n",
|
|
MaxOffset, (ULONG)(m_InfoFiles[DUMP_INFO_DUMP].m_FileSize -
|
|
MaxOffset));
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriage32DumpTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = KernelDumpTargetInfo::Initialize()) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Pick up any potentially modified base mapping pointer.
|
|
m_Dump = (PMEMORY_DUMP32)m_DumpBase;
|
|
|
|
dprintf("Mini Kernel Dump File: "
|
|
"Only registers and stack trace are available\n\n");
|
|
|
|
PTRIAGE_DUMP32 Triage = &m_Dump->Triage;
|
|
|
|
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 service pack version in the header because we
|
|
// don't store the actual memory
|
|
//
|
|
|
|
SetNtCsdVersion(m_BuildNumber, m_Dump->Triage.ServicePackBuild);
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset = m_Dump->Triage.SizeOfDump;
|
|
|
|
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);
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriage32DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "32-bit Kernel triage dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE32 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP32 ||
|
|
Dump->Header.DumpType != DUMP_TYPE_TRIAGE)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
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_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
// 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");
|
|
Status = E_FAIL;
|
|
__leave;
|
|
}
|
|
|
|
// 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 = m_InfoFiles[DUMP_INFO_DUMP].m_FileSize;
|
|
|
|
m_Dump = Dump;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
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 %s\n",
|
|
Triage->TriageOptions,
|
|
(Triage->TriageOptions != 0xffffffff &&
|
|
(Triage->TriageOptions & TRIAGE_OPTION_OVERFLOWED)) ?
|
|
"OVERFLOWED" : "");
|
|
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 );
|
|
DumpDataBlocks(Triage->DataBlocksOffset,
|
|
Triage->DataBlocksCount);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriage64DumpTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = KernelDumpTargetInfo::Initialize()) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Pick up any potentially modified base mapping pointer.
|
|
m_Dump = (PMEMORY_DUMP64)m_DumpBase;
|
|
|
|
dprintf("Mini Kernel Dump File: "
|
|
"Only registers and stack trace are available\n\n");
|
|
|
|
PTRIAGE_DUMP64 Triage = &m_Dump->Triage;
|
|
|
|
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 service pack version in the header because we
|
|
// don't store the actual memory
|
|
//
|
|
|
|
SetNtCsdVersion(m_BuildNumber, m_Dump->Triage.ServicePackBuild);
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset = m_Dump->Triage.SizeOfDump;
|
|
|
|
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);
|
|
}
|
|
|
|
HRESULT
|
|
KernelTriage64DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "64-bit Kernel triage dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE64 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP64 ||
|
|
Dump->Header.DumpType != DUMP_TYPE_TRIAGE)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
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_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
// 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");
|
|
Status = E_FAIL;
|
|
__leave;
|
|
}
|
|
|
|
// 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 = m_InfoFiles[DUMP_INFO_DUMP].m_FileSize;
|
|
|
|
m_Dump = Dump;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
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 %s\n",
|
|
Triage->TriageOptions,
|
|
(Triage->TriageOptions != 0xffffffff &&
|
|
(Triage->TriageOptions & TRIAGE_OPTION_OVERFLOWED)) ?
|
|
"OVERFLOWED" : "");
|
|
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 );
|
|
DumpDataBlocks(Triage->DataBlocksOffset,
|
|
Triage->DataBlocksCount);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// KernelFullDumpTargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
KernelFull32DumpTargetInfo::Initialize(void)
|
|
{
|
|
// Pick up any potentially modified base mapping pointer.
|
|
m_Dump = (PMEMORY_DUMP32)m_DumpBase;
|
|
|
|
dprintf("Kernel Dump File: Full address space is available\n\n");
|
|
|
|
HRESULT Status = InitGlobals32(m_Dump);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset =
|
|
sizeof(m_Dump->Header) +
|
|
m_Dump->Header.PhysicalMemoryBlock.NumberOfPages *
|
|
m_Machine->m_PageSize;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].m_FileSize < m_TaggedOffset)
|
|
{
|
|
WarnOut("************************************************************\n");
|
|
WarnOut("WARNING: Dump file has been truncated. "
|
|
"Data may be missing.\n");
|
|
WarnOut("************************************************************\n\n");
|
|
}
|
|
|
|
return KernelFullSumDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
KernelFull32DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "32-bit Full kernel dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)
|
|
{
|
|
HRESULT Status = E_NOINTERFACE;
|
|
PMEMORY_DUMP32 Dump = (PMEMORY_DUMP32)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE32 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP32)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (Dump->Header.DumpType != DUMP_SIGNATURE32 &&
|
|
Dump->Header.DumpType != DUMP_TYPE_FULL)
|
|
{
|
|
// We've seen some cases where the end of a dump
|
|
// header is messed up, leaving the dump type wrong.
|
|
// If this is an older build let such corruption
|
|
// go by with a warning.
|
|
if (Dump->Header.MinorVersion < 2195)
|
|
{
|
|
WarnOut("***************************************************************\n");
|
|
WarnOut("WARNING: Full dump header type is invalid, "
|
|
"dump may be corrupt.\n");
|
|
WarnOut("***************************************************************\n");
|
|
}
|
|
else
|
|
{
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
// Summary and triage dumps must be checked before this
|
|
// so there's nothing left to check.
|
|
m_Dump = Dump;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG64
|
|
KernelFull32DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, BOOL Verbose,
|
|
PULONG Avail)
|
|
{
|
|
ULONG PageIndex =
|
|
(ULONG)Phys & (m_Machine->m_PageSize - 1);
|
|
|
|
*Avail = m_Machine->m_PageSize - PageIndex;
|
|
|
|
ULONG64 Offset = 0;
|
|
|
|
__try
|
|
{
|
|
PPHYSICAL_MEMORY_DESCRIPTOR32 PhysDesc =
|
|
&m_Dump->Header.PhysicalMemoryBlock;
|
|
ULONG64 Page = Phys >> m_Machine->m_PageShift;
|
|
|
|
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;
|
|
Offset = Offset * m_Machine->m_PageSize +
|
|
sizeof(m_Dump->Header) + PageIndex;
|
|
break;
|
|
}
|
|
|
|
Offset += PhysDesc->Run[j].PageCount;
|
|
j += 1;
|
|
}
|
|
|
|
if (j >= PhysDesc->NumberOfRuns)
|
|
{
|
|
KdOut("Physical Memory Address %s is "
|
|
"greater than MaxPhysicalAddress\n",
|
|
FormatDisp64(Phys));
|
|
Offset = 0;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
Offset = 0;
|
|
}
|
|
|
|
return Offset;
|
|
}
|
|
|
|
void
|
|
KernelFull32DumpTargetInfo::DumpDebug(void)
|
|
{
|
|
PPHYSICAL_MEMORY_DESCRIPTOR32 PhysDesc =
|
|
&m_Dump->Header.PhysicalMemoryBlock;
|
|
ULONG PageSize = m_Machine->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)m_DumpBase;
|
|
|
|
dprintf("Kernel Dump File: Full address space is available\n\n");
|
|
|
|
HRESULT Status = InitGlobals64(m_Dump);
|
|
if (Status != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Tagged data will follow all of the normal dump data.
|
|
m_TaggedOffset =
|
|
sizeof(m_Dump->Header) +
|
|
m_Dump->Header.PhysicalMemoryBlock.NumberOfPages *
|
|
m_Machine->m_PageSize;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].m_FileSize < m_TaggedOffset)
|
|
{
|
|
WarnOut("************************************************************\n");
|
|
WarnOut("WARNING: Dump file has been truncated. "
|
|
"Data may be missing.\n");
|
|
WarnOut("************************************************************\n\n");
|
|
}
|
|
|
|
return KernelFullSumDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
KernelFull64DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "64-bit Full kernel dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
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)
|
|
{
|
|
HRESULT Status = E_NOINTERFACE;
|
|
PMEMORY_DUMP64 Dump = (PMEMORY_DUMP64)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Dump->Header.Signature != DUMP_SIGNATURE64 ||
|
|
Dump->Header.ValidDump != DUMP_VALID_DUMP64 ||
|
|
(Dump->Header.DumpType != DUMP_SIGNATURE32 &&
|
|
Dump->Header.DumpType != DUMP_TYPE_FULL))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
// Summary and triage dumps must be checked before this
|
|
// so there's nothing left to check.
|
|
m_Dump = Dump;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG64
|
|
KernelFull64DumpTargetInfo::PhysicalToOffset(ULONG64 Phys, BOOL Verbose,
|
|
PULONG Avail)
|
|
{
|
|
ULONG PageIndex =
|
|
(ULONG)Phys & (m_Machine->m_PageSize - 1);
|
|
|
|
*Avail = m_Machine->m_PageSize - PageIndex;
|
|
|
|
ULONG64 Offset = 0;
|
|
|
|
__try
|
|
{
|
|
PPHYSICAL_MEMORY_DESCRIPTOR64 PhysDesc =
|
|
&m_Dump->Header.PhysicalMemoryBlock;
|
|
ULONG64 Page = Phys >> m_Machine->m_PageShift;
|
|
|
|
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;
|
|
Offset = Offset * m_Machine->m_PageSize +
|
|
sizeof(m_Dump->Header) + PageIndex;
|
|
break;
|
|
}
|
|
|
|
Offset += PhysDesc->Run[j].PageCount;
|
|
j += 1;
|
|
}
|
|
|
|
if (j >= PhysDesc->NumberOfRuns)
|
|
{
|
|
KdOut("Physical Memory Address %s is "
|
|
"greater than MaxPhysicalAddress\n",
|
|
FormatDisp64(Phys));
|
|
Offset = 0;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
Offset = 0;
|
|
}
|
|
|
|
return Offset;
|
|
}
|
|
|
|
void
|
|
KernelFull64DumpTargetInfo::DumpDebug(void)
|
|
{
|
|
PPHYSICAL_MEMORY_DESCRIPTOR64 PhysDesc =
|
|
&m_Dump->Header.PhysicalMemoryBlock;
|
|
ULONG PageSize = m_Machine->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.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
UserDumpTargetInfo::GetThreadInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset)
|
|
{
|
|
if (Thread != NULL && Thread->m_DataOffset != 0)
|
|
{
|
|
*Offset = Thread->m_DataOffset;
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL ContextThread = FALSE;
|
|
|
|
if (Thread != NULL)
|
|
{
|
|
ThreadHandle = Thread->m_Handle;
|
|
ContextThread = Thread == m_RegContextThread;
|
|
}
|
|
else if (ThreadHandle == 0)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
// Arbitrary thread handle provided.
|
|
ContextThread = FALSE;
|
|
}
|
|
|
|
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 (!m_Machine->m_Ptr64 &&
|
|
m_HighestMemoryRegion32 > 0x80000000)
|
|
{
|
|
TebAddr = 0xbffe0000;
|
|
}
|
|
else
|
|
{
|
|
TebAddr = 0x7ffe0000;
|
|
}
|
|
TebAddr -= 2 * m_Machine->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(m_ProcessHead,
|
|
m_Machine,
|
|
TebCheck +
|
|
6 * (m_Machine->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;
|
|
|
|
m_Machine->GetSP(&Sp);
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
StackBase = STACK_BASE_FROM_TEB64;
|
|
StackLimit = StackBase + 8;
|
|
}
|
|
else
|
|
{
|
|
StackBase = STACK_BASE_FROM_TEB32;
|
|
StackLimit = StackBase + 4;
|
|
}
|
|
if (ReadPointer(m_ProcessHead,
|
|
m_Machine,
|
|
TebCheck + StackBase,
|
|
&StackBase) == S_OK &&
|
|
ReadPointer(m_ProcessHead,
|
|
m_Machine,
|
|
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 -= m_Machine->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->m_DataOffset = TebAddr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserDumpTargetInfo::GetProcessInfoDataOffset(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
if (Thread != NULL && Thread->m_Process->m_DataOffset != 0)
|
|
{
|
|
*Offset = Thread->m_Process->m_DataOffset;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT Status;
|
|
|
|
if (Thread != NULL || ThreadData == 0)
|
|
{
|
|
if ((Status = GetThreadInfoDataOffset(Thread, 0,
|
|
&ThreadData)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
ThreadData += m_Machine->m_Ptr64 ?
|
|
PEB_FROM_TEB64 : PEB_FROM_TEB32;
|
|
if ((Status = ReadPointer(m_ProcessHead,
|
|
m_Machine, ThreadData,
|
|
Offset)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (Thread != NULL)
|
|
{
|
|
Thread->m_Process->m_DataOffset = *Offset;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserDumpTargetInfo::GetThreadInfoTeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
return GetThreadInfoDataOffset(Thread, ThreadData, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
UserDumpTargetInfo::GetProcessInfoPeb(ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset)
|
|
{
|
|
// Thread data is not useful.
|
|
return GetProcessInfoDataOffset(Thread, 0, 0, Offset);
|
|
}
|
|
|
|
HRESULT
|
|
UserDumpTargetInfo::GetSelDescriptor(ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc)
|
|
{
|
|
return EmulateNtSelDescriptor(Thread, Machine, Selector, Desc);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// UserFullDumpTargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
UserFullDumpTargetInfo::GetBuildAndPlatform(ULONG MachineType,
|
|
PULONG MajorVersion,
|
|
PULONG MinorVersion,
|
|
PULONG BuildNumber,
|
|
PULONG PlatformId)
|
|
{
|
|
//
|
|
// Allow a knowledgeable user to override the
|
|
// dump version in order to work around problems
|
|
// with userdump.exe always generating version 5
|
|
// dumps regardless of the actual OS version.
|
|
//
|
|
|
|
PSTR Override = getenv("DBGENG_FULL_DUMP_VERSION");
|
|
if (Override)
|
|
{
|
|
switch(sscanf(Override, "%d.%d:%d:%d",
|
|
MajorVersion, MinorVersion,
|
|
BuildNumber, PlatformId))
|
|
{
|
|
case 2:
|
|
case 3:
|
|
// Only major/minor given, so let the build
|
|
// and platform be guessed from them.
|
|
break;
|
|
case 4:
|
|
// Everything was given, we're done.
|
|
return S_OK;
|
|
default:
|
|
// Invalid format, this will produce an error below.
|
|
*MajorVersion = 0;
|
|
*MinorVersion = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The only way to distinguish user dump
|
|
// platforms is guessing from the Major/MinorVersion
|
|
// and the extra QFE/Hotfix data.
|
|
//
|
|
|
|
// If this is for a processor that only CE supports
|
|
// we can immediately select CE.
|
|
if (MachineType == IMAGE_FILE_MACHINE_ARM)
|
|
{
|
|
*BuildNumber = 1;
|
|
*PlatformId = VER_PLATFORM_WIN32_CE;
|
|
goto CheckBuildNumber;
|
|
}
|
|
|
|
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;
|
|
case 90:
|
|
// Win98 ME.
|
|
*BuildNumber = 3000;
|
|
*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 6:
|
|
// Has to be some form of NT. Longhorn is the only
|
|
// one we recognize.
|
|
*PlatformId = VER_PLATFORM_WIN32_NT;
|
|
// XXX drewb - Longhorn hasn't split their build numbers
|
|
// off yet so they're the same as .NET. Just pick
|
|
// a junk build.
|
|
*BuildNumber = 9999;
|
|
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 Windows 2000, ");
|
|
*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;
|
|
}
|
|
|
|
CheckBuildNumber:
|
|
// 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)m_DumpBase;
|
|
|
|
dprintf("User Dump File: Only application data is available\n\n");
|
|
|
|
ULONG MajorVersion, MinorVersion;
|
|
ULONG BuildNumber;
|
|
ULONG PlatformId;
|
|
HRESULT Status;
|
|
|
|
MajorVersion = m_Header->MajorVersion;
|
|
MinorVersion = m_Header->MinorVersion;
|
|
|
|
if ((Status = GetBuildAndPlatform(m_Header->MachineImageType,
|
|
&MajorVersion, &MinorVersion,
|
|
&BuildNumber, &PlatformId)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if ((Status = InitSystemInfo(BuildNumber, 0,
|
|
m_Header->MachineImageType, PlatformId,
|
|
MajorVersion,
|
|
MinorVersion & 0xffff)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Dump does not contain this information.
|
|
m_NumProcessors = 1;
|
|
|
|
DEBUG_EVENT32 Event;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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_EventProcessId = Event.dwProcessId;
|
|
m_EventProcessSymHandle =
|
|
GloballyUniqueProcessHandle(this, Event.dwProcessId);
|
|
m_EventThreadId = Event.dwThreadId;
|
|
|
|
if (Event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
|
|
{
|
|
ExceptionRecord32To64(&Event.u.Exception.ExceptionRecord,
|
|
&m_ExceptionRecord);
|
|
m_ExceptionFirstChance = Event.u.Exception.dwFirstChance;
|
|
}
|
|
else
|
|
{
|
|
// Fake an exception.
|
|
ZeroMemory(&m_ExceptionRecord, sizeof(m_ExceptionRecord));
|
|
m_ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT;
|
|
m_ExceptionFirstChance = 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 HR_DATA_CORRUPT;
|
|
}
|
|
}
|
|
|
|
return UserDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
UserFull32DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "32-bit Full user dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
UserFull32DumpTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
if (VIRTUAL_THREAD_INDEX(Thread) >= m_Header->ThreadCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Header->ThreadOffset +
|
|
VIRTUAL_THREAD_INDEX(Thread) *
|
|
m_TypeInfo.SizeTargetContext,
|
|
Context,
|
|
m_TypeInfo.SizeTargetContext) ==
|
|
m_TypeInfo.SizeTargetContext)
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
ModuleInfo*
|
|
UserFull32DumpTargetInfo::GetModuleInfo(BOOL UserMode)
|
|
{
|
|
DBG_ASSERT(UserMode);
|
|
// If this dump came from an NT system we'll just
|
|
// use the system's loaded module list. Otherwise
|
|
// we'll use the dump's module list.
|
|
return m_PlatformId == VER_PLATFORM_WIN32_NT ?
|
|
(ModuleInfo*)&g_NtTargetUserModuleIterator :
|
|
(ModuleInfo*)&g_UserFull32ModuleIterator;
|
|
}
|
|
|
|
HRESULT
|
|
UserFull32DumpTargetInfo::QueryMemoryRegion(ProcessInfo* Process,
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info)
|
|
{
|
|
ULONG Index;
|
|
|
|
if (HandleIsOffset)
|
|
{
|
|
ULONG BestIndex;
|
|
ULONG BestDiff;
|
|
|
|
//
|
|
// Emulate VirtualQueryEx and return the closest higher
|
|
// region if a containing region isn't found.
|
|
//
|
|
|
|
BestIndex = 0xffffffff;
|
|
BestDiff = 0xffffffff;
|
|
for (Index = 0; Index < m_Header->MemoryRegionCount; Index++)
|
|
{
|
|
if ((ULONG)*Handle >= m_Memory[Index].BaseAddress)
|
|
{
|
|
if ((ULONG)*Handle < m_Memory[Index].BaseAddress +
|
|
m_Memory[Index].RegionSize)
|
|
{
|
|
// Found a containing region, we're done.
|
|
BestIndex = Index;
|
|
break;
|
|
}
|
|
|
|
// Not containing and lower in memory, ignore.
|
|
}
|
|
else
|
|
{
|
|
// Check and see if this is a closer
|
|
// region than what we've seen already.
|
|
ULONG Diff = m_Memory[Index].BaseAddress -
|
|
(ULONG)*Handle;
|
|
if (Diff <= BestDiff)
|
|
{
|
|
BestIndex = Index;
|
|
BestDiff = Diff;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BestIndex >= m_Header->MemoryRegionCount)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
Index = BestIndex;
|
|
}
|
|
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)
|
|
{
|
|
HRESULT Status = E_NOINTERFACE;
|
|
PUSERMODE_CRASHDUMP_HEADER32 Header =
|
|
(PUSERMODE_CRASHDUMP_HEADER32)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Header->Signature != USERMODE_CRASHDUMP_SIGNATURE ||
|
|
Header->ValidDump != USERMODE_CRASHDUMP_VALID_DUMP32)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Check for the presence of some basic things.
|
|
//
|
|
|
|
if (Header->ThreadCount == 0 ||
|
|
Header->ModuleCount == 0 ||
|
|
Header->MemoryRegionCount == 0)
|
|
{
|
|
ErrOut("Thread, module or memory region count is zero.\n"
|
|
"The dump file is probably corrupt.\n");
|
|
Status = HR_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
if (Header->ThreadOffset == 0 ||
|
|
Header->ModuleOffset == 0 ||
|
|
Header->DataOffset == 0 ||
|
|
Header->MemoryRegionOffset == 0 ||
|
|
Header->DebugEventOffset == 0 ||
|
|
Header->ThreadStateOffset == 0)
|
|
{
|
|
ErrOut("A dump header data offset is zero.\n"
|
|
"The dump file is probably corrupt.\n");
|
|
Status = HR_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
// We don't want to have to call ReadFileOffset
|
|
// every time we check memory ranges so just require
|
|
// that the memory descriptors fit in the base view.
|
|
*BaseMapSize = Header->MemoryRegionOffset +
|
|
Header->MemoryRegionCount * sizeof(*m_Memory);
|
|
|
|
m_Header = Header;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
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("ThreadStateOffset: %08lx\n", m_Header->ThreadStateOffset);
|
|
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("MemoryRegionOffset: %08lx\n", m_Header->MemoryRegionOffset);
|
|
dprintf("Number of regions: %d\n", m_Header->MemoryRegionCount);
|
|
|
|
dprintf(" FileOffset Start Address Length\n");
|
|
|
|
ULONG j = 0;
|
|
ULONG64 Offset = m_Header->DataOffset;
|
|
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;
|
|
}
|
|
|
|
dprintf("Total memory: %12I64x\n", Offset - m_Header->DataOffset);
|
|
}
|
|
|
|
ULONG64
|
|
UserFull32DumpTargetInfo::VirtualToOffset(ULONG64 Virt,
|
|
PULONG File, PULONG Avail)
|
|
{
|
|
ULONG i;
|
|
ULONG Offset = 0;
|
|
ULONG64 RetOffset = 0;
|
|
|
|
*File = DUMP_INFO_DUMP;
|
|
|
|
// Ignore the upper 32 bits to avoid getting
|
|
// confused by sign extensions in pointer handling
|
|
Virt &= 0xffffffff;
|
|
|
|
__try
|
|
{
|
|
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);
|
|
}
|
|
|
|
RetOffset = m_Header->DataOffset + Offset + Frag;
|
|
break;
|
|
}
|
|
|
|
Offset += m_Memory[i].RegionSize;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
RetOffset = 0;
|
|
}
|
|
|
|
return RetOffset;
|
|
}
|
|
|
|
HRESULT
|
|
UserFull32DumpTargetInfo::GetThreadInfo(ULONG Index,
|
|
PULONG Id, PULONG Suspend,
|
|
PULONG64 Teb)
|
|
{
|
|
if (Index >= m_ThreadCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CRASH_THREAD32 Thread;
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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 0
|
|
|
|
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;
|
|
|
|
#if 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 (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(Offset, &CrashModule, sizeof(CrashModule)) !=
|
|
sizeof(CrashModule))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#if DBG_VERIFY_MOD
|
|
g_NtDllCalls.DbgPrint("Mod %d of %d offs %X, base %s, ",
|
|
i, m_Header->ModuleCount, Offset,
|
|
FormatAddr64(CrashModule.BaseOfImage));
|
|
if (ReadVirtual(m_ProcessHead, 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(NULL, 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;
|
|
}
|
|
|
|
#if 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)m_DumpBase;
|
|
|
|
dprintf("User Dump File: Only application data is available\n\n");
|
|
|
|
ULONG MajorVersion, MinorVersion;
|
|
ULONG BuildNumber;
|
|
ULONG PlatformId;
|
|
HRESULT Status;
|
|
|
|
MajorVersion = m_Header->MajorVersion;
|
|
MinorVersion = m_Header->MinorVersion;
|
|
|
|
if ((Status = GetBuildAndPlatform(m_Header->MachineImageType,
|
|
&MajorVersion, &MinorVersion,
|
|
&BuildNumber, &PlatformId)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if ((Status = InitSystemInfo(BuildNumber, 0,
|
|
m_Header->MachineImageType, PlatformId,
|
|
MajorVersion,
|
|
MinorVersion & 0xffff)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
// Dump does not contain this information.
|
|
m_NumProcessors = 1;
|
|
|
|
DEBUG_EVENT64 Event;
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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_EventProcessId = Event.dwProcessId;
|
|
m_EventProcessSymHandle =
|
|
GloballyUniqueProcessHandle(this, Event.dwProcessId);
|
|
m_EventThreadId = Event.dwThreadId;
|
|
|
|
if (Event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
|
|
{
|
|
m_ExceptionRecord = Event.u.Exception.ExceptionRecord;
|
|
m_ExceptionFirstChance = Event.u.Exception.dwFirstChance;
|
|
}
|
|
else
|
|
{
|
|
// Fake an exception.
|
|
ZeroMemory(&m_ExceptionRecord, sizeof(m_ExceptionRecord));
|
|
m_ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT;
|
|
m_ExceptionFirstChance = 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 UserDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
UserFull64DumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "64-bit Full user dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
UserFull64DumpTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
if (VIRTUAL_THREAD_INDEX(Thread) >= m_Header->ThreadCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Header->ThreadOffset +
|
|
VIRTUAL_THREAD_INDEX(Thread) *
|
|
m_TypeInfo.SizeTargetContext,
|
|
Context,
|
|
m_TypeInfo.SizeTargetContext) ==
|
|
m_TypeInfo.SizeTargetContext)
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
ModuleInfo*
|
|
UserFull64DumpTargetInfo::GetModuleInfo(BOOL UserMode)
|
|
{
|
|
DBG_ASSERT(UserMode);
|
|
// If this dump came from an NT system we'll just
|
|
// use the system's loaded module list. Otherwise
|
|
// we'll use the dump's module list.
|
|
return m_PlatformId == VER_PLATFORM_WIN32_NT ?
|
|
(ModuleInfo*)&g_NtTargetUserModuleIterator :
|
|
(ModuleInfo*)&g_UserFull64ModuleIterator;
|
|
}
|
|
|
|
HRESULT
|
|
UserFull64DumpTargetInfo::QueryMemoryRegion(ProcessInfo* Process,
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info)
|
|
{
|
|
ULONG Index;
|
|
|
|
if (HandleIsOffset)
|
|
{
|
|
ULONG BestIndex;
|
|
ULONG64 BestDiff;
|
|
|
|
//
|
|
// Emulate VirtualQueryEx and return the closest higher
|
|
// region if a containing region isn't found.
|
|
//
|
|
|
|
BestIndex = 0xffffffff;
|
|
BestDiff = (ULONG64)-1;
|
|
for (Index = 0; Index < m_Header->MemoryRegionCount; Index++)
|
|
{
|
|
if (*Handle >= m_Memory[Index].BaseAddress)
|
|
{
|
|
if (*Handle < m_Memory[Index].BaseAddress +
|
|
m_Memory[Index].RegionSize)
|
|
{
|
|
// Found a containing region, we're done.
|
|
BestIndex = Index;
|
|
break;
|
|
}
|
|
|
|
// Not containing and lower in memory, ignore.
|
|
}
|
|
else
|
|
{
|
|
// Check and see if this is a closer
|
|
// region than what we've seen already.
|
|
ULONG64 Diff = m_Memory[Index].BaseAddress - *Handle;
|
|
if (Diff <= BestDiff)
|
|
{
|
|
BestIndex = Index;
|
|
BestDiff = Diff;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BestIndex >= m_Header->MemoryRegionCount)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
Index = BestIndex;
|
|
}
|
|
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)
|
|
{
|
|
HRESULT Status = E_NOINTERFACE;
|
|
PUSERMODE_CRASHDUMP_HEADER64 Header =
|
|
(PUSERMODE_CRASHDUMP_HEADER64)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (Header->Signature != USERMODE_CRASHDUMP_SIGNATURE ||
|
|
Header->ValidDump != USERMODE_CRASHDUMP_VALID_DUMP64)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Check for the presence of some basic things.
|
|
//
|
|
|
|
if (Header->ThreadCount == 0 ||
|
|
Header->ModuleCount == 0 ||
|
|
Header->MemoryRegionCount == 0)
|
|
{
|
|
ErrOut("Thread, module or memory region count is zero.\n"
|
|
"The dump file is probably corrupt.\n");
|
|
Status = HR_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
if (Header->ThreadOffset == 0 ||
|
|
Header->ModuleOffset == 0 ||
|
|
Header->DataOffset == 0 ||
|
|
Header->MemoryRegionOffset == 0 ||
|
|
Header->DebugEventOffset == 0 ||
|
|
Header->ThreadStateOffset == 0)
|
|
{
|
|
ErrOut("A dump header data offset is zero.\n"
|
|
"The dump file is probably corrupt.\n");
|
|
Status = HR_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
// We don't want to have to call ReadFileOffset
|
|
// every time we check memory ranges so just require
|
|
// that the memory descriptors fit in the base view.
|
|
*BaseMapSize = Header->MemoryRegionOffset +
|
|
Header->MemoryRegionCount * sizeof(*m_Memory);
|
|
|
|
m_Header = Header;
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
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("ThreadStateOffset: %12I64lx\n", m_Header->ThreadStateOffset);
|
|
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("MemoryRegionOffset: %12I64lx\n", m_Header->MemoryRegionOffset);
|
|
dprintf("Number of regions: %d\n", m_Header->MemoryRegionCount);
|
|
|
|
dprintf(" FileOffset Start Address"
|
|
" Length\n");
|
|
|
|
ULONG j = 0;
|
|
ULONG64 Offset = m_Header->DataOffset;
|
|
|
|
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;
|
|
}
|
|
|
|
dprintf("Total memory: %12I64x\n", Offset - m_Header->DataOffset);
|
|
}
|
|
|
|
ULONG64
|
|
UserFull64DumpTargetInfo::VirtualToOffset(ULONG64 Virt,
|
|
PULONG File, PULONG Avail)
|
|
{
|
|
ULONG i;
|
|
ULONG64 Offset = 0;
|
|
ULONG64 RetOffset = 0;
|
|
|
|
*File = DUMP_INFO_DUMP;
|
|
|
|
__try
|
|
{
|
|
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;
|
|
}
|
|
RetOffset = m_Header->DataOffset + Offset + Frag;
|
|
break;
|
|
}
|
|
|
|
Offset += m_Memory[i].RegionSize;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
RetOffset = 0;
|
|
}
|
|
|
|
return RetOffset;
|
|
}
|
|
|
|
HRESULT
|
|
UserFull64DumpTargetInfo::GetThreadInfo(ULONG Index,
|
|
PULONG Id, PULONG Suspend,
|
|
PULONG64 Teb)
|
|
{
|
|
if (Index >= m_ThreadCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CRASH_THREAD64 Thread;
|
|
if (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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)m_DumpBase;
|
|
// Clear pointers that have already been set so
|
|
// that they get picked up again.
|
|
m_SysInfo = NULL;
|
|
|
|
if (m_Header->Flags & MiniDumpWithFullMemory)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_FULL_MEMORY;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithHandleData)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_HANDLE_DATA;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithUnloadedModules)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithIndirectlyReferencedMemory)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithDataSegs)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS;
|
|
}
|
|
if (m_Header->Flags & MiniDumpFilterMemory)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY;
|
|
}
|
|
if (m_Header->Flags & MiniDumpFilterModulePaths)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_FILTER_PATHS;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithProcessThreadData)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA;
|
|
}
|
|
if (m_Header->Flags & MiniDumpWithPrivateReadWriteMemory)
|
|
{
|
|
m_FormatFlags |= DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY;
|
|
}
|
|
|
|
MINIDUMP_DIRECTORY UNALIGNED *Dir;
|
|
MINIDUMP_MISC_INFO UNALIGNED *MiscPtr = NULL;
|
|
ULONG i;
|
|
ULONG Size;
|
|
|
|
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
|
|
IndexRva(m_Header, m_Header->StreamDirectoryRva,
|
|
m_Header->NumberOfStreams * sizeof(*Dir),
|
|
"Directory");
|
|
if (Dir == NULL)
|
|
{
|
|
return HR_DATA_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 UnloadedModuleListStream:
|
|
if (IndexDirectory(i, Dir, (PVOID*)&m_UnlModules) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Dir->Location.DataSize !=
|
|
m_UnlModules->SizeOfHeader +
|
|
m_UnlModules->SizeOfEntry * m_UnlModules->NumberOfEntries)
|
|
{
|
|
m_UnlModules = NULL;
|
|
}
|
|
break;
|
|
|
|
case MemoryListStream:
|
|
if (m_Header->Flags & MiniDumpWithFullMemory)
|
|
{
|
|
ErrOut("Full memory minidumps can't have MemoryListStreams\n");
|
|
return HR_DATA_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_DATA_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 MiscInfoStream:
|
|
if (IndexDirectory(i, Dir, (PVOID*)&MiscPtr) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Dir->Location.DataSize < 2 * sizeof(ULONG32))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// The dump keeps its own version of the struct
|
|
// as a data member to avoid having to check pointers
|
|
// and structure size. Later references just check
|
|
// flags, copy in what this dump has available.
|
|
Size = sizeof(m_MiscInfo);
|
|
if (Size > Dir->Location.DataSize)
|
|
{
|
|
Size = Dir->Location.DataSize;
|
|
}
|
|
CopyMemory(&m_MiscInfo, MiscPtr, Size);
|
|
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_DATA_CORRUPT;
|
|
}
|
|
|
|
HRESULT Status;
|
|
|
|
if ((Status = InitSystemInfo(m_SysInfo->BuildNumber, 0,
|
|
m_ImageType, m_SysInfo->PlatformId,
|
|
m_SysInfo->MajorVersion,
|
|
m_SysInfo->MinorVersion)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (m_SysInfo->NumberOfProcessors)
|
|
{
|
|
m_NumProcessors = m_SysInfo->NumberOfProcessors;
|
|
}
|
|
else
|
|
{
|
|
// Dump does not contain this information.
|
|
m_NumProcessors = 1;
|
|
}
|
|
|
|
if (m_SysInfo->CSDVersionRva != 0)
|
|
{
|
|
MINIDUMP_STRING UNALIGNED *CsdString = (MINIDUMP_STRING UNALIGNED *)
|
|
IndexRva(m_Header,
|
|
m_SysInfo->CSDVersionRva, sizeof(*CsdString),
|
|
"CSD string");
|
|
if (CsdString != NULL && CsdString->Length > 0)
|
|
{
|
|
WCHAR UNALIGNED *WideStr = CsdString->Buffer;
|
|
ULONG WideLen = wcslen((PWSTR)WideStr);
|
|
|
|
if (m_ActualSystemVersion > W9X_SVER_START &&
|
|
m_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(m_ServicePackString,
|
|
"%.*S", WideLen, WideStr);
|
|
}
|
|
}
|
|
|
|
if (m_MiscInfo.Flags1 & MINIDUMP_MISC1_PROCESS_ID)
|
|
{
|
|
m_EventProcessId = m_MiscInfo.ProcessId;
|
|
}
|
|
else
|
|
{
|
|
// Some minidumps don't store the process ID. Add the system ID
|
|
// to the fake process ID base to keep each system's
|
|
// fake processes separate from each other.
|
|
m_EventProcessId = VIRTUAL_PROCESS_ID_BASE + m_UserId;
|
|
}
|
|
m_EventProcessSymHandle = VIRTUAL_PROCESS_HANDLE(m_EventProcessId);
|
|
|
|
if (m_Exception != NULL)
|
|
{
|
|
m_EventThreadId = m_Exception->ThreadId;
|
|
|
|
C_ASSERT(sizeof(m_Exception->ExceptionRecord) ==
|
|
sizeof(EXCEPTION_RECORD64));
|
|
m_ExceptionRecord = *(EXCEPTION_RECORD64 UNALIGNED *)
|
|
&m_Exception->ExceptionRecord;
|
|
}
|
|
else
|
|
{
|
|
m_EventThreadId = VIRTUAL_THREAD_ID(0);
|
|
|
|
// Fake an exception.
|
|
ZeroMemory(&m_ExceptionRecord, sizeof(m_ExceptionRecord));
|
|
m_ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT;
|
|
}
|
|
m_ExceptionFirstChance = FALSE;
|
|
|
|
if (m_Threads != NULL)
|
|
{
|
|
m_ThreadCount = m_ActualThreadCount;
|
|
|
|
if (m_Exception == NULL)
|
|
{
|
|
m_EventThreadId = IndexThreads(0)->ThreadId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_ThreadCount = 1;
|
|
}
|
|
|
|
return UserDumpTargetInfo::Initialize();
|
|
}
|
|
|
|
void
|
|
UserMiniDumpTargetInfo::NearestDifferentlyValidOffsets(ULONG64 Offset,
|
|
PULONG64 NextOffset,
|
|
PULONG64 NextPage)
|
|
{
|
|
return MapNearestDifferentlyValidOffsets(Offset, NextOffset, NextPage);
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::ReadHandleData(
|
|
IN ProcessInfo* Process,
|
|
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;
|
|
BOOL WideStr = FALSE;
|
|
|
|
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_WIDE:
|
|
WideStr = TRUE;
|
|
case DEBUG_HANDLE_DATA_TYPE_TYPE_NAME:
|
|
StrRva = Desc->TypeNameRva;
|
|
break;
|
|
|
|
case DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME_WIDE:
|
|
WideStr = TRUE;
|
|
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_TYPE_NAME_WIDE ||
|
|
DataType == DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME ||
|
|
DataType == DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME_WIDE)
|
|
{
|
|
if (StrRva == 0)
|
|
{
|
|
Used = WideStr ? sizeof(WCHAR) : sizeof(CHAR);
|
|
if (Buffer)
|
|
{
|
|
if (BufferSize < Used)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (WideStr)
|
|
{
|
|
*(PWCHAR)Buffer = 0;
|
|
}
|
|
else
|
|
{
|
|
*(PCHAR)Buffer = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MINIDUMP_STRING UNALIGNED *Str = (MINIDUMP_STRING UNALIGNED *)
|
|
IndexRva(m_Header, StrRva, sizeof(*Str), "Handle name string");
|
|
if (Str == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
if (WideStr)
|
|
{
|
|
Used = Str->Length + sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
Used = Str->Length / sizeof(WCHAR) + 1;
|
|
}
|
|
|
|
if (Buffer)
|
|
{
|
|
if (WideStr)
|
|
{
|
|
if (BufferSize < sizeof(WCHAR))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
BufferSize /= sizeof(WCHAR);
|
|
|
|
// The string data may not be aligned, so
|
|
// check and handle both the aligned
|
|
// and unaligned cases.
|
|
if (!((ULONG_PTR)Str->Buffer & (sizeof(WCHAR) - 1)))
|
|
{
|
|
CopyStringW((PWSTR)Buffer, (PWSTR)Str->Buffer,
|
|
BufferSize);
|
|
}
|
|
else
|
|
{
|
|
PUCHAR RawStr = (PUCHAR)Str->Buffer;
|
|
while (--BufferSize > 0 &&
|
|
(RawStr[0] || RawStr[1]))
|
|
{
|
|
*(PWCHAR)Buffer =
|
|
((USHORT)RawStr[1] << 8) | RawStr[0];
|
|
Buffer = (PVOID)((PWCHAR)Buffer + 1);
|
|
}
|
|
*(PWCHAR)Buffer = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!WideCharToMultiByte(CP_ACP, 0,
|
|
(LPCWSTR)Str->Buffer, -1,
|
|
(LPSTR)Buffer, BufferSize,
|
|
NULL, NULL))
|
|
{
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DataSize != NULL)
|
|
{
|
|
*DataSize = Used;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetProcessorId
|
|
(ULONG Processor, PDEBUG_PROCESSOR_IDENTIFICATION_ALL Id)
|
|
{
|
|
PSTR Unavail = "<unavailable>";
|
|
|
|
// Allow any processor index as minidumps now
|
|
// remember the number of processors so requests
|
|
// may come in for processor indices other than zero.
|
|
|
|
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;
|
|
|
|
if (m_SysInfo->Cpu.X86CpuInfo.VendorId[0])
|
|
{
|
|
memcpy(Id->X86.VendorString,
|
|
m_SysInfo->Cpu.X86CpuInfo.VendorId,
|
|
sizeof(m_SysInfo->Cpu.X86CpuInfo.VendorId));
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT(strlen(Unavail) < DIMA(Id->X86.VendorString));
|
|
strcpy(&(Id->X86.VendorString[0]), Unavail);
|
|
}
|
|
break;
|
|
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
Id->Ia64.Model = m_SysInfo->ProcessorLevel;
|
|
Id->Ia64.Revision = m_SysInfo->ProcessorRevision;
|
|
DBG_ASSERT(strlen(Unavail) < DIMA(Id->Ia64.VendorString));
|
|
strcpy(&(Id->Ia64.VendorString[0]), Unavail);
|
|
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;
|
|
DBG_ASSERT(strlen(Unavail) < DIMA(Id->Amd64.VendorString));
|
|
strcpy(&(Id->Amd64.VendorString[0]), Unavail);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetProcessorSpeed
|
|
(ULONG Processor, PULONG Speed)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetGenericProcessorFeatures(
|
|
ULONG Processor,
|
|
PULONG64 Features,
|
|
ULONG FeaturesSize,
|
|
PULONG Used
|
|
)
|
|
{
|
|
// Allow any processor index as minidumps now
|
|
// remember the number of processors so requests
|
|
// may come in for processor indices other than zero.
|
|
|
|
if (m_SysInfo == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if (m_MachineType == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
// x86 stores only specific features.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
*Used = DIMA(m_SysInfo->Cpu.OtherCpuInfo.ProcessorFeatures);
|
|
if (FeaturesSize > *Used)
|
|
{
|
|
FeaturesSize = *Used;
|
|
}
|
|
memcpy(Features, m_SysInfo->Cpu.OtherCpuInfo.ProcessorFeatures,
|
|
FeaturesSize * sizeof(*Features));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetSpecificProcessorFeatures(
|
|
ULONG Processor,
|
|
PULONG64 Features,
|
|
ULONG FeaturesSize,
|
|
PULONG Used
|
|
)
|
|
{
|
|
// Allow any processor index as minidumps now
|
|
// remember the number of processors so requests
|
|
// may come in for processor indices other than zero.
|
|
|
|
if (m_SysInfo == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if (m_MachineType != IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
// Only x86 stores specific features.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
*Used = 2;
|
|
if (FeaturesSize > 0)
|
|
{
|
|
*Features++ = m_SysInfo->Cpu.X86CpuInfo.VersionInformation;
|
|
FeaturesSize--;
|
|
}
|
|
if (FeaturesSize > 0)
|
|
{
|
|
*Features++ = m_SysInfo->Cpu.X86CpuInfo.FeatureInformation;
|
|
FeaturesSize--;
|
|
}
|
|
|
|
if (m_SysInfo->Cpu.X86CpuInfo.VendorId[0] == AMD_VENDOR_ID_EBX &&
|
|
m_SysInfo->Cpu.X86CpuInfo.VendorId[1] == AMD_VENDOR_ID_EDX &&
|
|
m_SysInfo->Cpu.X86CpuInfo.VendorId[2] == AMD_VENDOR_ID_EBX)
|
|
{
|
|
if (FeaturesSize > 0)
|
|
{
|
|
(*Used)++;
|
|
*Features++ = m_SysInfo->Cpu.X86CpuInfo.AMDExtendedCpuFeatures;
|
|
FeaturesSize--;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
PVOID
|
|
UserMiniDumpTargetInfo::FindDynamicFunctionEntry(ProcessInfo* Process,
|
|
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 = m_Machine->FindDynamicFunctionEntry
|
|
(RawTable, Address, TableData,
|
|
Desc->EntryCount * m_FunctionTables->SizeOfFunctionEntry);
|
|
if (Entry)
|
|
{
|
|
return Entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ULONG64
|
|
UserMiniDumpTargetInfo::GetDynamicFunctionTableBase(ProcessInfo* Process,
|
|
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::EnumFunctionTables(IN ProcessInfo* Process,
|
|
IN OUT PULONG64 Start,
|
|
IN OUT PULONG64 Handle,
|
|
OUT PULONG64 MinAddress,
|
|
OUT PULONG64 MaxAddress,
|
|
OUT PULONG64 BaseAddress,
|
|
OUT PULONG EntryCount,
|
|
OUT PCROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE RawTable,
|
|
OUT PVOID* RawEntries)
|
|
{
|
|
PUCHAR StreamData;
|
|
|
|
if (!m_FunctionTables)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (*Start == 0)
|
|
{
|
|
StreamData = (PUCHAR)m_FunctionTables +
|
|
m_FunctionTables->SizeOfHeader +
|
|
m_FunctionTables->SizeOfAlignPad;
|
|
*Start = (LONG_PTR)StreamData;
|
|
*Handle = 0;
|
|
}
|
|
else
|
|
{
|
|
StreamData = (PUCHAR)(ULONG_PTR)*Start;
|
|
}
|
|
|
|
if (*Handle >= m_FunctionTables->NumberOfDescriptors)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
// Stream structure contents are guaranteed to be
|
|
// properly aligned.
|
|
PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR Desc =
|
|
(PMINIDUMP_FUNCTION_TABLE_DESCRIPTOR)StreamData;
|
|
*MinAddress = Desc->MinimumAddress;
|
|
*MaxAddress = Desc->MaximumAddress;
|
|
*BaseAddress = Desc->BaseAddress;
|
|
*EntryCount = Desc->EntryCount;
|
|
StreamData += m_FunctionTables->SizeOfDescriptor;
|
|
|
|
memcpy(RawTable, StreamData, m_FunctionTables->SizeOfNativeDescriptor);
|
|
StreamData += m_FunctionTables->SizeOfNativeDescriptor;
|
|
|
|
*RawEntries = malloc(Desc->EntryCount *
|
|
m_FunctionTables->SizeOfFunctionEntry);
|
|
if (!*RawEntries)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
memcpy(*RawEntries, StreamData, Desc->EntryCount *
|
|
m_FunctionTables->SizeOfFunctionEntry);
|
|
|
|
StreamData += Desc->EntryCount *
|
|
m_FunctionTables->SizeOfFunctionEntry +
|
|
Desc->SizeOfAlignPad;
|
|
*Start = (LONG_PTR)StreamData;
|
|
(*Handle)++;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetTargetContext(
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
)
|
|
{
|
|
if (m_Threads == NULL ||
|
|
VIRTUAL_THREAD_INDEX(Thread) >= m_ActualThreadCount)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
PVOID ContextData =
|
|
IndexRva(m_Header,
|
|
IndexThreads(VIRTUAL_THREAD_INDEX(Thread))->ThreadContext.Rva,
|
|
m_TypeInfo.SizeTargetContext,
|
|
"Thread context data");
|
|
if (ContextData == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
memcpy(Context, ContextData, m_TypeInfo.SizeTargetContext);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
|
|
{
|
|
HRESULT Status = E_NOINTERFACE;
|
|
|
|
// m_Header must be set as other methods rely on it.
|
|
m_Header = (PMINIDUMP_HEADER)m_DumpBase;
|
|
|
|
__try
|
|
{
|
|
if (m_Header->Signature != MINIDUMP_SIGNATURE ||
|
|
(m_Header->Version & 0xffff) != MINIDUMP_VERSION)
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
MINIDUMP_DIRECTORY UNALIGNED *Dir;
|
|
ULONG i;
|
|
|
|
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
|
|
IndexRva(m_Header, m_Header->StreamDirectoryRva,
|
|
m_Header->NumberOfStreams * sizeof(*Dir),
|
|
"Directory");
|
|
if (Dir == NULL)
|
|
{
|
|
Status = HR_DATA_CORRUPT;
|
|
__leave;
|
|
}
|
|
|
|
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 (m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(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.
|
|
m_InfoFiles[DUMP_INFO_DUMP].EmptyCache();
|
|
break;
|
|
}
|
|
|
|
Dir++;
|
|
}
|
|
|
|
if (m_SysInfo == NULL)
|
|
{
|
|
ErrOut("Minidump does not have system info\n");
|
|
Status = E_FAIL;
|
|
__leave;
|
|
}
|
|
|
|
m_ImageType = ProcArchToImageMachine(m_SysInfo->ProcessorArchitecture);
|
|
if (m_ImageType == IMAGE_FILE_MACHINE_UNKNOWN)
|
|
{
|
|
ErrOut("Minidump has unrecognized processor architecture 0x%x\n",
|
|
m_SysInfo->ProcessorArchitecture);
|
|
Status = E_FAIL;
|
|
__leave;
|
|
}
|
|
|
|
// 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 = m_InfoFiles[DUMP_INFO_DUMP].m_FileSize;
|
|
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
m_Header = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ModuleInfo*
|
|
UserMiniDumpTargetInfo::GetModuleInfo(BOOL UserMode)
|
|
{
|
|
DBG_ASSERT(UserMode);
|
|
return &g_UserMiniModuleIterator;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetImageVersionInformation(ProcessInfo* Process,
|
|
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 <
|
|
m_TypeInfo.SizeTargetContext ||
|
|
(ContextData = IndexRva(m_Header,
|
|
m_Exception->ThreadContext.Rva,
|
|
m_TypeInfo.SizeTargetContext,
|
|
"Exception context")) == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
memcpy(Context, ContextData,
|
|
m_TypeInfo.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);
|
|
}
|
|
|
|
ULONG64
|
|
UserMiniDumpTargetInfo::GetProcessUpTimeN(ProcessInfo* Process)
|
|
{
|
|
if (m_MiscInfo.Flags1 & MINIDUMP_MISC1_PROCESS_TIMES)
|
|
{
|
|
return TimeToFileTime(m_Header->TimeDateStamp -
|
|
m_MiscInfo.ProcessCreateTime);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetProcessTimes(ProcessInfo* Process,
|
|
PULONG64 Create,
|
|
PULONG64 Exit,
|
|
PULONG64 Kernel,
|
|
PULONG64 User)
|
|
{
|
|
if (m_MiscInfo.Flags1 & MINIDUMP_MISC1_PROCESS_TIMES)
|
|
{
|
|
*Create = TimeDateStampToFileTime(m_MiscInfo.ProcessCreateTime);
|
|
*Exit = 0;
|
|
*Kernel = TimeToFileTime(m_MiscInfo.ProcessKernelTime);
|
|
*User = TimeToFileTime(m_MiscInfo.ProcessUserTime);
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniDumpTargetInfo::GetProductInfo(PULONG ProductType, PULONG SuiteMask)
|
|
{
|
|
if (m_SysInfo->ProductType != INVALID_PRODUCT_TYPE)
|
|
{
|
|
*ProductType = m_SysInfo->ProductType;
|
|
*SuiteMask = m_SysInfo->SuiteMask;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return TargetInfo::GetProductInfo(ProductType, SuiteMask);
|
|
}
|
|
}
|
|
|
|
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", "UnloadedModuleListStream", "MiscInfoStream",
|
|
};
|
|
|
|
PSTR
|
|
MiniStreamTypeName(ULONG32 Type)
|
|
{
|
|
if (Type < sizeof(g_MiniStreamNames) / sizeof(g_MiniStreamNames[0]))
|
|
{
|
|
return g_MiniStreamNames[Type];
|
|
}
|
|
else
|
|
{
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
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(m_Header,
|
|
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);
|
|
if (m_Header->CheckSum)
|
|
{
|
|
dprintf("Failure flags: %X\n", m_Header->CheckSum);
|
|
}
|
|
dprintf("Flags %X\n", m_Header->Flags);
|
|
|
|
MINIDUMP_DIRECTORY UNALIGNED *Dir;
|
|
|
|
dprintf("\nStreams:\n");
|
|
Dir = (MINIDUMP_DIRECTORY UNALIGNED *)
|
|
IndexRva(m_Header, 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;
|
|
WCHAR StrBuf[MAX_PATH];
|
|
|
|
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(m_Header, Mod->ModuleNameRva,
|
|
sizeof(MINIDUMP_STRING),
|
|
"Module entry name");
|
|
|
|
// The Unicode string text may not be aligned,
|
|
// so copy it to an alignment-friendly buffer.
|
|
if (Str)
|
|
{
|
|
memcpy(StrBuf, ((MINIDUMP_STRING UNALIGNED *)Str)->Buffer,
|
|
sizeof(StrBuf));
|
|
StrBuf[DIMA(StrBuf) - 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(StrBuf, L"** Invalid **");
|
|
}
|
|
|
|
dprintf(" RVA %08X, %s - %s: '%S'\n",
|
|
Rva,
|
|
FormatAddr64(Mod->BaseOfImage),
|
|
FormatAddr64(Mod->BaseOfImage + Mod->SizeOfImage),
|
|
StrBuf);
|
|
Mod++;
|
|
Rva += sizeof(*Mod);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case UnloadedModuleListStream:
|
|
{
|
|
MINIDUMP_UNLOADED_MODULE_LIST UNALIGNED *UnlModList;
|
|
|
|
UnlModList = (MINIDUMP_UNLOADED_MODULE_LIST UNALIGNED *)Data;
|
|
dprintf(" %d unloaded modules\n", UnlModList->NumberOfEntries);
|
|
Rva += UnlModList->SizeOfHeader;
|
|
for (j = 0; j < UnlModList->NumberOfEntries; j++)
|
|
{
|
|
MINIDUMP_UNLOADED_MODULE UNALIGNED *UnlMod =
|
|
(MINIDUMP_UNLOADED_MODULE UNALIGNED *)
|
|
IndexRva(m_Header, Rva, sizeof(MINIDUMP_UNLOADED_MODULE),
|
|
"Unloaded module entry");
|
|
if (!UnlMod)
|
|
{
|
|
break;
|
|
}
|
|
PVOID Str = IndexRva(m_Header, UnlMod->ModuleNameRva,
|
|
sizeof(MINIDUMP_STRING),
|
|
"Unloaded module entry name");
|
|
|
|
// The Unicode string text may not be aligned,
|
|
// so copy it to an alignment-friendly buffer.
|
|
if (Str)
|
|
{
|
|
memcpy(StrBuf, ((MINIDUMP_STRING UNALIGNED *)Str)->Buffer,
|
|
sizeof(StrBuf));
|
|
StrBuf[DIMA(StrBuf) - 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(StrBuf, L"** Invalid **");
|
|
}
|
|
|
|
dprintf(" RVA %08X, %s - %s: '%S'\n",
|
|
Rva,
|
|
FormatAddr64(UnlMod->BaseOfImage),
|
|
FormatAddr64(UnlMod->BaseOfImage +
|
|
UnlMod->SizeOfImage),
|
|
StrBuf);
|
|
Rva += UnlModList->SizeOfEntry;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MemoryListStream:
|
|
{
|
|
MINIDUMP_MEMORY_LIST UNALIGNED *MemList;
|
|
ULONG64 Total = 0;
|
|
|
|
MemList = (MINIDUMP_MEMORY_LIST UNALIGNED *)Data;
|
|
dprintf(" %d memory ranges\n", MemList->NumberOfMemoryRanges);
|
|
dprintf(" range# Address %sSize\n",
|
|
m_Machine->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));
|
|
Total += MemList->MemoryRanges[j].Memory.DataSize;
|
|
}
|
|
dprintf(" Total memory: %I64x\n", Total);
|
|
break;
|
|
}
|
|
|
|
case Memory64ListStream:
|
|
{
|
|
MINIDUMP_MEMORY64_LIST UNALIGNED *MemList;
|
|
ULONG64 Total = 0;
|
|
|
|
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",
|
|
m_Machine->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));
|
|
Total += MemList->MemoryRanges[j].DataSize;
|
|
}
|
|
dprintf(" Total memory: %I64x\n", Total);
|
|
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, stack and portions of "
|
|
"memory 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.
|
|
//
|
|
|
|
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *Mem;
|
|
ULONG i;
|
|
ULONG64 TotalMemory;
|
|
|
|
Mem = m_Memory->MemoryRanges;
|
|
for (i = 0; i < m_Memory->NumberOfMemoryRanges; i++)
|
|
{
|
|
PVOID Data = IndexRva(m_Header,
|
|
Mem->Memory.Rva, Mem->Memory.DataSize,
|
|
"Memory range data");
|
|
if (Data == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
if ((Status = m_DataMemMap.
|
|
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::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "User mini dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniPartialDumpTargetInfo::ReadVirtual(
|
|
IN ProcessInfo* Process,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
)
|
|
{
|
|
return MapReadVirtual(Process, Offset, Buffer, BufferSize, BytesRead);
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniPartialDumpTargetInfo::QueryMemoryRegion
|
|
(ProcessInfo* Process,
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info)
|
|
{
|
|
ULONG Index;
|
|
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *Mem;
|
|
|
|
if (HandleIsOffset)
|
|
{
|
|
if (m_Memory == NULL)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
MINIDUMP_MEMORY_DESCRIPTOR UNALIGNED *BestMem;
|
|
ULONG64 BestDiff;
|
|
|
|
//
|
|
// Emulate VirtualQueryEx and return the closest higher
|
|
// region if a containing region isn't found.
|
|
//
|
|
|
|
BestMem = NULL;
|
|
BestDiff = (ULONG64)-1;
|
|
Mem = m_Memory->MemoryRanges;
|
|
for (Index = 0; Index < m_Memory->NumberOfMemoryRanges; Index++)
|
|
{
|
|
if (*Handle >= Mem->StartOfMemoryRange)
|
|
{
|
|
if (*Handle < Mem->StartOfMemoryRange + Mem->Memory.DataSize)
|
|
{
|
|
// Found a containing region, we're done.
|
|
BestMem = Mem;
|
|
break;
|
|
}
|
|
|
|
// Not containing and lower in memory, ignore.
|
|
}
|
|
else
|
|
{
|
|
// Check and see if this is a closer
|
|
// region than what we've seen already.
|
|
ULONG64 Diff = Mem->StartOfMemoryRange - *Handle;
|
|
if (Diff <= BestDiff)
|
|
{
|
|
BestMem = Mem;
|
|
BestDiff = Diff;
|
|
}
|
|
}
|
|
|
|
Mem++;
|
|
}
|
|
|
|
if (!BestMem)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
Mem = BestMem;
|
|
}
|
|
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;
|
|
}
|
|
|
|
UnloadedModuleInfo*
|
|
UserMiniPartialDumpTargetInfo::GetUnloadedModuleInfo(void)
|
|
{
|
|
return &g_UserMiniUnloadedModuleIterator;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniPartialDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = UserMiniDumpTargetInfo::IdentifyDump(BaseMapSize)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
__try
|
|
{
|
|
if (m_Header->Flags & MiniDumpWithFullMemory)
|
|
{
|
|
m_Header = NULL;
|
|
m_SysInfo = NULL;
|
|
Status = E_NOINTERFACE;
|
|
}
|
|
else
|
|
{
|
|
Status = S_OK;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
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;
|
|
ULONG64 RetOffset = 0;
|
|
|
|
__try
|
|
{
|
|
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;
|
|
RetOffset = Mem->Memory.Rva + Frag;
|
|
break;
|
|
}
|
|
|
|
Mem++;
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
RetOffset = 0;
|
|
}
|
|
|
|
return RetOffset;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// 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_Memory64 != 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));
|
|
|
|
if (TotalMemory + m_Memory64->BaseRva >
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileSize)
|
|
{
|
|
WarnOut("****************************************"
|
|
"********************\n");
|
|
WarnOut("WARNING: Dump file has been truncated. "
|
|
"Data may be missing.\n");
|
|
WarnOut("****************************************"
|
|
"********************\n\n");
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniFullDumpTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "Full memory user mini dump: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status,
|
|
m_InfoFiles[DUMP_INFO_DUMP].m_FileNameA,
|
|
FALSE, &Buffer, &BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniFullDumpTargetInfo::QueryMemoryRegion
|
|
(ProcessInfo* Process,
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info)
|
|
{
|
|
ULONG Index;
|
|
MINIDUMP_MEMORY_DESCRIPTOR64 UNALIGNED *Mem;
|
|
|
|
if (HandleIsOffset)
|
|
{
|
|
if (m_Memory64 == NULL)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
MINIDUMP_MEMORY_DESCRIPTOR64 UNALIGNED *BestMem;
|
|
ULONG64 BestDiff;
|
|
|
|
//
|
|
// Emulate VirtualQueryEx and return the closest higher
|
|
// region if a containing region isn't found.
|
|
//
|
|
|
|
BestMem = NULL;
|
|
BestDiff = (ULONG64)-1;
|
|
Mem = m_Memory64->MemoryRanges;
|
|
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
for (Index = 0; Index < m_Memory64->NumberOfMemoryRanges; Index++)
|
|
{
|
|
if (*Handle >= Mem->StartOfMemoryRange)
|
|
{
|
|
if (*Handle < Mem->StartOfMemoryRange + Mem->DataSize)
|
|
{
|
|
// Found a containing region, we're done.
|
|
BestMem = Mem;
|
|
break;
|
|
}
|
|
|
|
// Not containing and lower in memory, ignore.
|
|
}
|
|
else
|
|
{
|
|
// Check and see if this is a closer
|
|
// region than what we've seen already.
|
|
ULONG64 Diff = Mem->StartOfMemoryRange - *Handle;
|
|
if (Diff <= BestDiff)
|
|
{
|
|
BestMem = Mem;
|
|
BestDiff = Diff;
|
|
}
|
|
}
|
|
|
|
Mem++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG64 Check;
|
|
|
|
//
|
|
// Ignore any sign extension on a 32-bit dump.
|
|
//
|
|
|
|
Check = (ULONG)*Handle;
|
|
for (Index = 0; Index < m_Memory64->NumberOfMemoryRanges; Index++)
|
|
{
|
|
if (Check >= (ULONG)Mem->StartOfMemoryRange)
|
|
{
|
|
if (Check < (ULONG)
|
|
(Mem->StartOfMemoryRange + Mem->DataSize))
|
|
{
|
|
// Found a containing region, we're done.
|
|
BestMem = Mem;
|
|
break;
|
|
}
|
|
|
|
// Not containing and lower in memory, ignore.
|
|
}
|
|
else
|
|
{
|
|
// Check and see if this is a closer
|
|
// region than what we've seen already.
|
|
ULONG64 Diff = (ULONG)Mem->StartOfMemoryRange - Check;
|
|
if (Diff <= BestDiff)
|
|
{
|
|
BestMem = Mem;
|
|
BestDiff = Diff;
|
|
}
|
|
}
|
|
|
|
Mem++;
|
|
}
|
|
}
|
|
|
|
if (!BestMem)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
Mem = BestMem;
|
|
}
|
|
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;
|
|
}
|
|
|
|
UnloadedModuleInfo*
|
|
UserMiniFullDumpTargetInfo::GetUnloadedModuleInfo(void)
|
|
{
|
|
// As this is a full-memory dump we may have unloaded module
|
|
// information in the memory itself. If we don't have
|
|
// an official unloaded module stream, try to get it from memory.
|
|
if (m_UnlModules)
|
|
{
|
|
return &g_UserMiniUnloadedModuleIterator;
|
|
}
|
|
else if (m_PlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
return &g_NtUserUnloadedModuleIterator;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniFullDumpTargetInfo::IdentifyDump(PULONG64 BaseMapSize)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = UserMiniDumpTargetInfo::IdentifyDump(BaseMapSize)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
__try
|
|
{
|
|
if (!(m_Header->Flags & MiniDumpWithFullMemory))
|
|
{
|
|
m_Header = NULL;
|
|
m_SysInfo = NULL;
|
|
Status = E_NOINTERFACE;
|
|
__leave;
|
|
}
|
|
if (m_Memory64DataBase == 0)
|
|
{
|
|
ErrOut("Full-memory minidump must have a Memory64ListStream\n");
|
|
Status = E_FAIL;
|
|
__leave;
|
|
}
|
|
|
|
// 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;
|
|
|
|
Status = S_OK;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = DumpIdentifyStatus(GetExceptionCode());
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
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;
|
|
ULONG64 RetOffset = 0;
|
|
|
|
__try
|
|
{
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
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;
|
|
}
|
|
RetOffset = Offset + Frag;
|
|
break;
|
|
}
|
|
|
|
Offset += Mem->DataSize;
|
|
Mem++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Ignore any sign extension on a 32-bit dump.
|
|
//
|
|
|
|
Virt = (ULONG)Virt;
|
|
for (i = 0; i < m_Memory64->NumberOfMemoryRanges; i++)
|
|
{
|
|
if (Virt >= (ULONG)Mem->StartOfMemoryRange &&
|
|
Virt < (ULONG)(Mem->StartOfMemoryRange + Mem->DataSize))
|
|
{
|
|
ULONG64 Frag = Virt - (ULONG)Mem->StartOfMemoryRange;
|
|
ULONG64 Avail64 = Mem->DataSize - Frag;
|
|
if (Avail64 > 0xffffffff)
|
|
{
|
|
*Avail = 0xffffffff;
|
|
}
|
|
else
|
|
{
|
|
*Avail = (ULONG)Avail64;
|
|
}
|
|
RetOffset = Offset + Frag;
|
|
break;
|
|
}
|
|
|
|
Offset += Mem->DataSize;
|
|
Mem++;
|
|
}
|
|
}
|
|
}
|
|
__except(MappingExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
RetOffset = 0;
|
|
}
|
|
|
|
return RetOffset;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ModuleInfo implementations.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
KernelTriage32ModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (KernelTriage32DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Dump->Triage.DriverListOffset != 0)
|
|
{
|
|
m_Count = m_DumpTarget->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_Count)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
PDUMP_DRIVER_ENTRY32 DriverEntry;
|
|
PDUMP_STRING DriverName;
|
|
|
|
DBG_ASSERT(m_DumpTarget->m_Dump->Triage.DriverListOffset != 0);
|
|
|
|
DriverEntry = (PDUMP_DRIVER_ENTRY32)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Dump, (RVA)
|
|
(m_DumpTarget->m_Dump->Triage.DriverListOffset +
|
|
m_Cur * sizeof(*DriverEntry)),
|
|
sizeof(*DriverEntry), "Driver entry");
|
|
if (DriverEntry == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
DriverName = (PDUMP_STRING)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Dump,
|
|
DriverEntry->DriverNameOffset,
|
|
sizeof(*DriverName), "Driver entry name");
|
|
if (DriverName == NULL)
|
|
{
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
}
|
|
else
|
|
{
|
|
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(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (KernelTriage64DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Dump->Triage.DriverListOffset != 0)
|
|
{
|
|
m_Count = m_DumpTarget->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_Count)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
PDUMP_DRIVER_ENTRY64 DriverEntry;
|
|
PDUMP_STRING DriverName;
|
|
|
|
DBG_ASSERT(m_DumpTarget->m_Dump->Triage.DriverListOffset != 0);
|
|
|
|
DriverEntry = (PDUMP_DRIVER_ENTRY64)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Dump, (RVA)
|
|
(m_DumpTarget->m_Dump->Triage.DriverListOffset +
|
|
m_Cur * sizeof(*DriverEntry)),
|
|
sizeof(*DriverEntry), "Driver entry");
|
|
if (DriverEntry == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
DriverName = (PDUMP_STRING)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Dump,
|
|
DriverEntry->DriverNameOffset,
|
|
sizeof(*DriverName), "Driver entry name");
|
|
if (DriverName == NULL)
|
|
{
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
}
|
|
else
|
|
{
|
|
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
|
|
UserFull32ModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (UserFull32DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Header->ModuleOffset &&
|
|
m_DumpTarget->m_Header->ModuleCount)
|
|
{
|
|
m_Offset = m_DumpTarget->m_Header->ModuleOffset;
|
|
m_Count = m_DumpTarget->m_Header->ModuleCount;
|
|
m_Cur = 0;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
m_Offset = 0;
|
|
m_Count = 0;
|
|
m_Cur = 0;
|
|
dprintf("User Mode Full Dump does not have a module list\n");
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserFull32ModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
if (m_Cur == m_Count)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
CRASH_MODULE32 CrashModule;
|
|
|
|
if (m_DumpTarget->m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Offset, &CrashModule, sizeof(CrashModule)) !=
|
|
sizeof(CrashModule))
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
if (CrashModule.ImageNameLength >= MAX_IMAGE_PATH ||
|
|
m_DumpTarget->m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Offset + FIELD_OFFSET(CRASH_MODULE32, ImageName),
|
|
Entry->Buffer, CrashModule.ImageNameLength) !=
|
|
CrashModule.ImageNameLength)
|
|
{
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
}
|
|
else
|
|
{
|
|
Entry->NamePtr = (PSTR)Entry->Buffer;
|
|
Entry->NameLength = CrashModule.ImageNameLength - 1;
|
|
}
|
|
|
|
Entry->Base = EXTEND64(CrashModule.BaseOfImage);
|
|
Entry->Size = CrashModule.SizeOfImage;
|
|
|
|
m_Cur++;
|
|
m_Offset += sizeof(CrashModule) + CrashModule.ImageNameLength;
|
|
return S_OK;
|
|
}
|
|
|
|
UserFull32ModuleInfo g_UserFull32ModuleIterator;
|
|
|
|
HRESULT
|
|
UserFull64ModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (UserFull64DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Header->ModuleOffset &&
|
|
m_DumpTarget->m_Header->ModuleCount)
|
|
{
|
|
m_Offset = m_DumpTarget->m_Header->ModuleOffset;
|
|
m_Count = m_DumpTarget->m_Header->ModuleCount;
|
|
m_Cur = 0;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
m_Offset = 0;
|
|
m_Count = 0;
|
|
m_Cur = 0;
|
|
dprintf("User Mode Full Dump does not have a module list\n");
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserFull64ModuleInfo::GetEntry(PMODULE_INFO_ENTRY Entry)
|
|
{
|
|
if (m_Cur == m_Count)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
CRASH_MODULE64 CrashModule;
|
|
|
|
if (m_DumpTarget->m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Offset, &CrashModule, sizeof(CrashModule)) !=
|
|
sizeof(CrashModule))
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
if (CrashModule.ImageNameLength >= MAX_IMAGE_PATH ||
|
|
m_DumpTarget->m_InfoFiles[DUMP_INFO_DUMP].
|
|
ReadFileOffset(m_Offset + FIELD_OFFSET(CRASH_MODULE64, ImageName),
|
|
Entry->Buffer, CrashModule.ImageNameLength) !=
|
|
CrashModule.ImageNameLength)
|
|
{
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
}
|
|
else
|
|
{
|
|
Entry->NamePtr = (PSTR)Entry->Buffer;
|
|
Entry->NameLength = CrashModule.ImageNameLength - 1;
|
|
}
|
|
|
|
Entry->Base = CrashModule.BaseOfImage;
|
|
Entry->Size = CrashModule.SizeOfImage;
|
|
|
|
m_Cur++;
|
|
m_Offset += sizeof(CrashModule) + CrashModule.ImageNameLength;
|
|
return S_OK;
|
|
}
|
|
|
|
UserFull64ModuleInfo g_UserFull64ModuleIterator;
|
|
|
|
HRESULT
|
|
UserMiniModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (UserMiniDumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Modules != NULL)
|
|
{
|
|
m_Count = m_DumpTarget->m_Modules->NumberOfModules;
|
|
m_Cur = 0;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
m_Count = 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_Count)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
MINIDUMP_MODULE UNALIGNED *Mod;
|
|
MINIDUMP_STRING UNALIGNED *ModName;
|
|
|
|
DBG_ASSERT(m_DumpTarget->m_Modules != NULL);
|
|
|
|
Mod = m_DumpTarget->m_Modules->Modules + m_Cur;
|
|
ModName = (MINIDUMP_STRING UNALIGNED *)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Header,
|
|
Mod->ModuleNameRva, sizeof(*ModName),
|
|
"Module entry name");
|
|
if (ModName == NULL)
|
|
{
|
|
Entry->NamePtr = NULL;
|
|
Entry->NameLength = 0;
|
|
}
|
|
else
|
|
{
|
|
memcpy(Entry->Buffer, (VOID * UNALIGNED) ModName->Buffer, sizeof(Entry->Buffer));
|
|
Entry->Buffer[sizeof(Entry->Buffer)/sizeof(WCHAR)-1] = 0;
|
|
Entry->NamePtr = (PSTR)Entry->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 (!m_Machine->m_Ptr64)
|
|
{
|
|
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
|
|
UserMiniUnloadedModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (UserMiniDumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_UnlModules)
|
|
{
|
|
m_Index = 0;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Don't display a message as this is a common case.
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
UserMiniUnloadedModuleInfo::GetEntry(PSTR Name,
|
|
PDEBUG_MODULE_PARAMETERS Params)
|
|
{
|
|
if (m_Index >= m_DumpTarget->m_UnlModules->NumberOfEntries)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
MINIDUMP_UNLOADED_MODULE UNALIGNED * Mod =
|
|
(MINIDUMP_UNLOADED_MODULE UNALIGNED *)
|
|
m_DumpTarget->
|
|
IndexRva(m_DumpTarget->m_Header,
|
|
(RVA)((PUCHAR)m_DumpTarget->m_UnlModules -
|
|
(PUCHAR)m_DumpTarget->m_Header) +
|
|
m_DumpTarget->m_UnlModules->SizeOfHeader +
|
|
m_DumpTarget->m_UnlModules->SizeOfEntry * m_Index,
|
|
sizeof(*Mod), "Unloaded module entry");
|
|
if (Mod == NULL)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
|
|
ZeroMemory(Params, sizeof(*Params));
|
|
Params->Base = Mod->BaseOfImage;
|
|
Params->Size = Mod->SizeOfImage;
|
|
Params->TimeDateStamp = Mod->TimeDateStamp;
|
|
Params->Checksum = Mod->CheckSum;
|
|
Params->Flags = DEBUG_MODULE_UNLOADED;
|
|
|
|
if (Name != NULL)
|
|
{
|
|
MINIDUMP_STRING UNALIGNED * ModName = (MINIDUMP_STRING UNALIGNED *)
|
|
m_DumpTarget->IndexRva(m_DumpTarget->m_Header,
|
|
Mod->ModuleNameRva, sizeof(*ModName),
|
|
"Unloaded module entry name");
|
|
if (ModName == NULL)
|
|
{
|
|
UnknownImageName(Mod->BaseOfImage, Name, MAX_INFO_UNLOADED_NAME);
|
|
}
|
|
else
|
|
{
|
|
ConvertAndValidateImagePathW((PWSTR)ModName->Buffer,
|
|
wcslen((PWSTR)ModName->Buffer),
|
|
Mod->BaseOfImage,
|
|
Name,
|
|
MAX_INFO_UNLOADED_NAME);
|
|
}
|
|
}
|
|
|
|
m_Index++;
|
|
return S_OK;
|
|
}
|
|
|
|
UserMiniUnloadedModuleInfo g_UserMiniUnloadedModuleIterator;
|
|
|
|
HRESULT
|
|
KernelTriage32UnloadedModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (KernelTriage32DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Dump->Triage.UnloadedDriversOffset != 0)
|
|
{
|
|
PVOID Data = m_DumpTarget->IndexRva
|
|
(m_DumpTarget->m_Dump,
|
|
m_DumpTarget->m_Dump->Triage.UnloadedDriversOffset,
|
|
sizeof(ULONG), "Unloaded driver list");
|
|
if (!Data)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
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;
|
|
}
|
|
ConvertAndValidateImagePathW(m_Cur->DriverName,
|
|
NameLen / sizeof(WCHAR),
|
|
Params->Base,
|
|
Name,
|
|
MAX_INFO_UNLOADED_NAME);
|
|
}
|
|
|
|
m_Cur++;
|
|
return S_OK;
|
|
}
|
|
|
|
KernelTriage32UnloadedModuleInfo g_KernelTriage32UnloadedModuleIterator;
|
|
|
|
HRESULT
|
|
KernelTriage64UnloadedModuleInfo::Initialize(ThreadInfo* Thread)
|
|
{
|
|
InitSource(Thread);
|
|
|
|
m_DumpTarget = (KernelTriage64DumpTargetInfo*)m_Target;
|
|
|
|
if (m_DumpTarget->m_Dump->Triage.UnloadedDriversOffset != 0)
|
|
{
|
|
PVOID Data = m_DumpTarget->IndexRva
|
|
(m_DumpTarget->m_Dump,
|
|
m_DumpTarget->m_Dump->Triage.UnloadedDriversOffset,
|
|
sizeof(ULONG), "Unloaded driver list");
|
|
if (!Data)
|
|
{
|
|
return HR_DATA_CORRUPT;
|
|
}
|
|
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;
|
|
}
|
|
ConvertAndValidateImagePathW(m_Cur->DriverName,
|
|
NameLen / sizeof(WCHAR),
|
|
Params->Base,
|
|
Name,
|
|
MAX_INFO_UNLOADED_NAME);
|
|
}
|
|
|
|
m_Cur++;
|
|
return S_OK;
|
|
}
|
|
|
|
KernelTriage64UnloadedModuleInfo g_KernelTriage64UnloadedModuleIterator;
|