Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2465 lines
69 KiB

/*++
Copyright (c) 1999-2002 Microsoft Corporation
Module Name:
gen.c
Abstract:
Generic routines for minidump that work on both NT and Win9x.
Author:
Matthew D Hendel (math) 10-Sep-1999
Revision History:
--*/
#include "pch.cpp"
#include <limits.h>
//
// For FPO frames on x86 we access bytes outside of the ESP - StackBase range.
// This variable determines how many extra bytes we need to add for this
// case.
//
#define X86_STACK_FRAME_EXTRA_FPO_BYTES 4
#define REASONABLE_NB11_RECORD_SIZE (10 * KBYTE)
#define REASONABLE_MISC_RECORD_SIZE (10 * KBYTE)
class GenMiniDumpProviderCallbacks : public MiniDumpProviderCallbacks
{
public:
GenMiniDumpProviderCallbacks(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process
)
{
m_Dump = Dump;
m_Process = Process;
m_MemType = MEMBLOCK_OTHER;
m_SaveMemType = MEMBLOCK_OTHER;
}
virtual HRESULT EnumMemory(ULONG64 Offset, ULONG Size)
{
return GenAddMemoryBlock(m_Dump, m_Process, m_MemType, Offset, Size);
}
void PushMemType(MEMBLOCK_TYPE Type)
{
m_SaveMemType = m_MemType;
m_MemType = Type;
}
void PopMemType(void)
{
m_MemType = m_SaveMemType;
}
PMINIDUMP_STATE m_Dump;
PINTERNAL_PROCESS m_Process;
MEMBLOCK_TYPE m_MemType, m_SaveMemType;
};
LPVOID
AllocMemory(
IN PMINIDUMP_STATE Dump,
IN ULONG Size
)
{
LPVOID Mem = Dump->AllocProv->Alloc(Size);
if (!Mem) {
// Handle marking the no-memory state for all allocations.
GenAccumulateStatus(Dump, MDSTATUS_OUT_OF_MEMORY);
}
return Mem;
}
VOID
FreeMemory(
IN PMINIDUMP_STATE Dump,
IN LPVOID Memory
)
{
Dump->AllocProv->Free(Memory);
}
PVOID
ReAllocMemory(
IN PMINIDUMP_STATE Dump,
IN LPVOID Memory,
IN ULONG Size
)
{
LPVOID Mem = Dump->AllocProv->Realloc(Memory, Size);
if (!Mem) {
// Handle marking the no-memory state for all allocations.
GenAccumulateStatus(Dump, MDSTATUS_OUT_OF_MEMORY);
}
return Mem;
}
void
GenAccumulateStatus(
IN PMINIDUMP_STATE Dump,
IN ULONG Status
)
{
// This is a function to make it easy to debug failures
// by setting a breakpoint here.
Dump->AccumStatus |= Status;
}
VOID
GenGetDefaultWriteFlags(
IN PMINIDUMP_STATE Dump,
OUT PULONG ModuleWriteFlags,
OUT PULONG ThreadWriteFlags
)
{
*ModuleWriteFlags = ModuleWriteModule | ModuleWriteMiscRecord |
ModuleWriteCvRecord;
if (Dump->DumpType & MiniDumpWithDataSegs) {
*ModuleWriteFlags |= ModuleWriteDataSeg;
}
*ThreadWriteFlags = ThreadWriteThread | ThreadWriteContext;
if (!(Dump->DumpType & MiniDumpWithFullMemory)) {
*ThreadWriteFlags |= ThreadWriteStack | ThreadWriteInstructionWindow;
if (Dump->BackingStore) {
*ThreadWriteFlags |= ThreadWriteBackingStore;
}
}
if (Dump->DumpType & MiniDumpWithProcessThreadData) {
*ThreadWriteFlags |= ThreadWriteThreadData;
}
}
BOOL
GenExecuteIncludeThreadCallback(
IN PMINIDUMP_STATE Dump,
IN ULONG ThreadId,
OUT PULONG WriteFlags
)
{
BOOL Succ;
MINIDUMP_CALLBACK_INPUT CallbackInput;
MINIDUMP_CALLBACK_OUTPUT CallbackOutput;
// Initialize the default write flags.
GenGetDefaultWriteFlags(Dump, &CallbackOutput.ModuleWriteFlags,
WriteFlags);
//
// If there are no callbacks to call, then we are done.
//
if ( Dump->CallbackRoutine == NULL ) {
return TRUE;
}
CallbackInput.ProcessHandle = Dump->ProcessHandle;
CallbackInput.ProcessId = Dump->ProcessId;
CallbackInput.CallbackType = IncludeThreadCallback;
CallbackInput.IncludeThread.ThreadId = ThreadId;
CallbackOutput.ThreadWriteFlags = *WriteFlags;
Succ = Dump->CallbackRoutine (Dump->CallbackParam,
&CallbackInput,
&CallbackOutput);
//
// If the callback returned FALSE, quit now.
//
if ( !Succ ) {
return FALSE;
}
// Limit the flags that can be added.
*WriteFlags &= CallbackOutput.ThreadWriteFlags;
return TRUE;
}
BOOL
GenExecuteIncludeModuleCallback(
IN PMINIDUMP_STATE Dump,
IN ULONG64 BaseOfImage,
OUT PULONG WriteFlags
)
{
BOOL Succ;
MINIDUMP_CALLBACK_INPUT CallbackInput;
MINIDUMP_CALLBACK_OUTPUT CallbackOutput;
// Initialize the default write flags.
GenGetDefaultWriteFlags(Dump, WriteFlags,
&CallbackOutput.ThreadWriteFlags);
//
// If there are no callbacks to call, then we are done.
//
if ( Dump->CallbackRoutine == NULL ) {
return TRUE;
}
CallbackInput.ProcessHandle = Dump->ProcessHandle;
CallbackInput.ProcessId = Dump->ProcessId;
CallbackInput.CallbackType = IncludeModuleCallback;
CallbackInput.IncludeModule.BaseOfImage = BaseOfImage;
CallbackOutput.ModuleWriteFlags = *WriteFlags;
Succ = Dump->CallbackRoutine (Dump->CallbackParam,
&CallbackInput,
&CallbackOutput);
//
// If the callback returned FALSE, quit now.
//
if ( !Succ ) {
return FALSE;
}
// Limit the flags that can be added.
*WriteFlags = (*WriteFlags | ModuleReferencedByMemory) &
CallbackOutput.ModuleWriteFlags;
return TRUE;
}
HRESULT
GenGetDebugRecord(
IN PMINIDUMP_STATE Dump,
IN PVOID Base,
IN ULONG MappedSize,
IN ULONG DebugRecordType,
IN ULONG DebugRecordMaxSize,
OUT PVOID * DebugInfo,
OUT ULONG * SizeOfDebugInfo
)
{
ULONG i;
ULONG Size;
ULONG NumberOfDebugDirectories;
IMAGE_DEBUG_DIRECTORY UNALIGNED* DebugDirectories;
Size = 0;
//
// Find the debug directory and copy the memory into the buffer.
// Assumes the call to this function is wrapped in a try/except.
//
DebugDirectories = (IMAGE_DEBUG_DIRECTORY UNALIGNED *)
GenImageDirectoryEntryToData (Base,
FALSE,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&Size);
//
// Check that we got a valid record.
//
if (DebugDirectories &&
((Size % sizeof (IMAGE_DEBUG_DIRECTORY)) == 0) &&
(ULONG_PTR)DebugDirectories - (ULONG_PTR)Base + Size <= MappedSize)
{
NumberOfDebugDirectories = Size / sizeof (IMAGE_DEBUG_DIRECTORY);
for (i = 0 ; i < NumberOfDebugDirectories; i++)
{
//
// We should check if it's a NB10 or something record.
//
if ((DebugDirectories[ i ].Type == DebugRecordType) &&
(DebugDirectories[ i ].SizeOfData < DebugRecordMaxSize))
{
if (DebugDirectories[i].PointerToRawData +
DebugDirectories[i].SizeOfData > MappedSize)
{
return E_INVALIDARG;
}
Size = DebugDirectories [ i ].SizeOfData;
PVOID NewInfo = AllocMemory(Dump, Size);
if (!NewInfo)
{
return E_OUTOFMEMORY;
}
CopyMemory(NewInfo,
((PBYTE) Base) +
DebugDirectories [ i ].PointerToRawData,
Size);
*DebugInfo = NewInfo;
*SizeOfDebugInfo = Size;
return S_OK;
}
}
}
return E_INVALIDARG;
}
HRESULT
GenAddDataSection(IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PINTERNAL_MODULE Module,
IN PIMAGE_SECTION_HEADER Section)
{
HRESULT Status = S_OK;
if ( (Section->Characteristics & IMAGE_SCN_MEM_WRITE) &&
(Section->Characteristics & IMAGE_SCN_MEM_READ) &&
( (Section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) ||
(Section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) )) {
Status = GenAddMemoryBlock(Dump, Process, MEMBLOCK_DATA_SEG,
Section->VirtualAddress +
Module->BaseOfImage,
Section->Misc.VirtualSize);
#if 0
if (Status == S_OK) {
printf ("Section: %8.8s Addr: %0I64x Size: %08x Raw Size: %08x\n",
Section->Name,
Section->VirtualAddress + Module->BaseOfImage,
Section->Misc.VirtualSize,
Section->SizeOfRawData);
}
#endif
}
return Status;
}
HRESULT
GenGetDataContributors(
IN PMINIDUMP_STATE Dump,
IN OUT PINTERNAL_PROCESS Process,
IN PINTERNAL_MODULE Module
)
{
ULONG i;
PIMAGE_SECTION_HEADER NtSection;
HRESULT Status;
PVOID MappedBase;
PIMAGE_NT_HEADERS NtHeaders;
ULONG MappedSize;
UCHAR HeaderBuffer[512];
PVOID HeaderBase;
GenMiniDumpProviderCallbacks Callbacks(Dump, Process);
// See if the provider wants to handle this.
Callbacks.PushMemType(MEMBLOCK_DATA_SEG);
if (Dump->SysProv->
EnumImageDataSections(Process->ProcessHandle, Module->FullPath,
Module->BaseOfImage, &Callbacks) == S_OK) {
// Provider did everything.
return S_OK;
}
if ((Status = Dump->SysProv->
OpenMapping(Module->FullPath, &MappedSize, NULL, 0,
&MappedBase)) != S_OK) {
MappedBase = NULL;
// If we can't map the file try and read the image
// data from the process.
if ((Status = Dump->SysProv->
ReadAllVirtual(Dump->ProcessHandle,
Module->BaseOfImage,
HeaderBuffer,
sizeof(HeaderBuffer))) != S_OK) {
GenAccumulateStatus(Dump, MDSTATUS_UNABLE_TO_READ_MEMORY);
return Status;
}
HeaderBase = HeaderBuffer;
} else {
HeaderBase = MappedBase;
}
NtHeaders = GenImageNtHeader(HeaderBase, NULL);
if (!NtHeaders) {
Status = HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
} else {
HRESULT OneStatus;
Status = S_OK;
NtSection = IMAGE_FIRST_SECTION ( NtHeaders );
if (MappedBase) {
__try {
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
if ((OneStatus =
GenAddDataSection(Dump, Process, Module,
&NtSection[i])) != S_OK) {
Status = OneStatus;
}
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
Status = HRESULT_FROM_NT(GetExceptionCode());
}
} else {
ULONG64 SectionOffs;
IMAGE_SECTION_HEADER SectionBuffer;
SectionOffs = Module->BaseOfImage +
((ULONG_PTR)NtSection - (ULONG_PTR)HeaderBase);
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
if ((OneStatus = Dump->SysProv->
ReadAllVirtual(Dump->ProcessHandle,
SectionOffs,
&SectionBuffer,
sizeof(SectionBuffer))) != S_OK) {
Status = OneStatus;
} else if ((OneStatus =
GenAddDataSection(Dump, Process, Module,
&SectionBuffer)) != S_OK) {
Status = OneStatus;
}
SectionOffs += sizeof(SectionBuffer);
}
}
}
if (MappedBase) {
Dump->SysProv->CloseMapping(MappedBase);
}
return Status;
}
HRESULT
GenAllocateThreadObject(
IN PMINIDUMP_STATE Dump,
IN struct _INTERNAL_PROCESS* Process,
IN ULONG ThreadId,
IN ULONG WriteFlags,
PINTERNAL_THREAD* ThreadRet
)
/*++
Routine Description:
Allocate and initialize an INTERNAL_THREAD structure.
Return Values:
S_OK on success.
S_FALSE if the thread can't be opened.
Errors on failure.
--*/
{
HRESULT Status;
PINTERNAL_THREAD Thread;
ULONG64 StackEnd;
ULONG64 StackLimit;
ULONG64 StoreLimit;
ULONG64 StoreCurrent;
Thread = (PINTERNAL_THREAD)
AllocMemory ( Dump, sizeof (INTERNAL_THREAD) + Dump->ContextSize );
if (Thread == NULL) {
return E_OUTOFMEMORY;
}
*ThreadRet = Thread;
Thread->ThreadId = ThreadId;
Status = Dump->SysProv->
OpenThread(THREAD_ALL_ACCESS,
FALSE,
Thread->ThreadId,
&Thread->ThreadHandle);
if (Status != S_OK) {
// The thread may have exited before we got around
// to trying to open it. If the open fails with
// a not-found code return an alternate success to
// indicate that it's not a critical failure.
if (Status == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) ||
Status == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
Status == HRESULT_FROM_NT(STATUS_INVALID_CID)) {
Status = S_FALSE;
} else if (SUCCEEDED(Status)) {
Status = E_FAIL;
}
if (FAILED(Status)) {
GenAccumulateStatus(Dump, MDSTATUS_CALL_FAILED);
}
goto Exit;
}
// If the current thread is dumping itself we can't
// suspend. We can also assume the suspend count must
// be zero since the thread is running.
if (Thread->ThreadId == Dump->SysProv->GetCurrentThreadId()) {
Thread->SuspendCount = 0;
} else {
Thread->SuspendCount = Dump->SysProv->
SuspendThread ( Thread->ThreadHandle );
}
Thread->WriteFlags = WriteFlags;
//
// Add this if we ever need it
//
Thread->PriorityClass = 0;
Thread->Priority = 0;
//
// Initialize the thread context.
//
Thread->ContextBuffer = Thread + 1;
Status = Dump->SysProv->
GetThreadContext (Thread->ThreadHandle,
Thread->ContextBuffer,
Dump->ContextSize,
&Thread->CurrentPc,
&StackEnd,
&StoreCurrent);
if ( Status != S_OK ) {
GenAccumulateStatus(Dump, MDSTATUS_CALL_FAILED);
goto Exit;
}
if (Dump->CpuType == IMAGE_FILE_MACHINE_I386) {
//
// Note: for FPO frames on x86 we access bytes outside of the
// ESP - StackBase range. Add a couple of bytes extra here so we
// don't fail these cases.
//
StackEnd -= X86_STACK_FRAME_EXTRA_FPO_BYTES;
}
if ((Status = Dump->SysProv->
GetThreadInfo(Dump->ProcessHandle,
Thread->ThreadHandle,
&Thread->Teb,
&Thread->SizeOfTeb,
&Thread->StackBase,
&StackLimit,
&Thread->BackingStoreBase,
&StoreLimit)) != S_OK) {
goto Exit;
}
//
// If the stack pointer (SP) is within the range of the stack
// region (as allocated by the OS), only take memory from
// the stack region up to the SP. Otherwise, assume the program
// has blown it's SP -- purposefully, or not -- and copy
// the entire stack as known by the OS.
//
if (Dump->BackingStore) {
Thread->BackingStoreSize =
(ULONG)(StoreCurrent - Thread->BackingStoreBase);
} else {
Thread->BackingStoreSize = 0;
}
if (StackLimit <= StackEnd && StackEnd < Thread->StackBase) {
Thread->StackEnd = StackEnd;
} else {
Thread->StackEnd = StackLimit;
}
if ((ULONG)(Thread->StackBase - Thread->StackEnd) >
Process->MaxStackOrStoreSize) {
Process->MaxStackOrStoreSize =
(ULONG)(Thread->StackBase - Thread->StackEnd);
}
if (Thread->BackingStoreSize > Process->MaxStackOrStoreSize) {
Process->MaxStackOrStoreSize = Thread->BackingStoreSize;
}
Exit:
if ( Status != S_OK ) {
FreeMemory ( Dump, Thread );
}
return Status;
}
VOID
GenFreeThreadObject(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_THREAD Thread
)
{
if (Thread->SuspendCount != -1 &&
Thread->ThreadId != Dump->SysProv->GetCurrentThreadId()) {
Dump->SysProv->ResumeThread (Thread->ThreadHandle);
Thread->SuspendCount = -1;
}
Dump->SysProv->CloseThread(Thread->ThreadHandle);
Thread->ThreadHandle = NULL;
FreeMemory ( Dump, Thread );
Thread = NULL;
}
HRESULT
GenGetThreadInstructionWindow(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PINTERNAL_THREAD Thread
)
{
PVOID MemBuf;
ULONG64 InstrStart;
ULONG InstrSize;
ULONG BytesRead;
HRESULT Status = E_FAIL;
if (!Dump->InstructionWindowSize) {
return S_OK;
}
//
// Store a window of the instruction stream around
// the current program counter. This allows some
// instruction context to be given even when images
// can't be mapped. It also allows instruction
// context to be given for generated code where
// no image contains the necessary instructions.
//
InstrStart = Thread->CurrentPc - Dump->InstructionWindowSize / 2;
InstrSize = Dump->InstructionWindowSize;
MemBuf = AllocMemory(Dump, InstrSize);
if (!MemBuf) {
return E_OUTOFMEMORY;
}
for (;;) {
// If we can read the instructions through the
// current program counter we'll say that's
// good enough.
if (Dump->SysProv->
ReadVirtual(Dump->ProcessHandle,
InstrStart,
MemBuf,
InstrSize,
&BytesRead) == S_OK &&
InstrStart + BytesRead >
Thread->CurrentPc) {
Status = GenAddMemoryBlock(Dump, Process, MEMBLOCK_INSTR_WINDOW,
InstrStart, BytesRead);
break;
}
// We couldn't read up to the program counter.
// If the start address is on the previous page
// move it up to the same page.
if ((InstrStart & ~((ULONG64)Dump->PageSize - 1)) !=
(Thread->CurrentPc & ~((ULONG64)Dump->PageSize - 1))) {
ULONG Fraction = Dump->PageSize -
(ULONG)InstrStart & (Dump->PageSize - 1);
InstrSize -= Fraction;
InstrStart += Fraction;
} else {
// The start and PC were on the same page so
// we just can't read memory. There may have been
// a jump to a bad address or something, so this
// doesn't constitute an unexpected failure.
break;
}
}
FreeMemory(Dump, MemBuf);
return Status;
}
PWSTR
GenGetPathTail(
IN PWSTR Path
)
{
PWSTR Scan = Path + GenStrLengthW(Path);
while (Scan > Path) {
Scan--;
if (*Scan == '\\' ||
*Scan == '/' ||
*Scan == ':') {
return Scan + 1;
}
}
return Path;
}
HRESULT
GenAllocateModuleObject(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PWSTR FullPathW,
IN ULONG64 BaseOfModule,
IN ULONG WriteFlags,
OUT PINTERNAL_MODULE* ModuleRet
)
/*++
Routine Description:
Given the full-path to the module and the base of the module, create and
initialize an INTERNAL_MODULE object, and return it.
--*/
{
HRESULT Status;
PVOID MappedBase;
ULONG MappedSize;
PIMAGE_NT_HEADERS NtHeaders;
PINTERNAL_MODULE Module;
ULONG Chars;
ASSERT (FullPathW);
ASSERT (BaseOfModule);
Module = (PINTERNAL_MODULE)
AllocMemory ( Dump, sizeof (INTERNAL_MODULE) );
if (Module == NULL) {
return E_OUTOFMEMORY;
}
//
// Get the version information for the module.
//
if ((Status = Dump->SysProv->
GetImageVersionInfo(Dump->ProcessHandle, FullPathW, BaseOfModule,
&Module->VersionInfo)) != S_OK) {
ZeroMemory(&Module->VersionInfo, sizeof(Module->VersionInfo));
}
if ((Status = Dump->SysProv->
OpenMapping(FullPathW, &MappedSize,
Module->FullPath, ARRAY_COUNT(Module->FullPath),
&MappedBase)) != S_OK) {
MappedBase = NULL;
// Some providers can't map but still have image
// information. Try that.
if ((Status = Dump->SysProv->
GetImageHeaderInfo(Dump->ProcessHandle,
FullPathW,
BaseOfModule,
&Module->SizeOfImage,
&Module->CheckSum,
&Module->TimeDateStamp)) != S_OK) {
GenAccumulateStatus(Dump, MDSTATUS_CALL_FAILED);
FreeMemory(Dump, Module);
return Status;
}
// No long path name is available so just use
// the incoming path.
GenStrCopyNW(Module->FullPath, FullPathW,
ARRAY_COUNT(Module->FullPath));
}
if (IsFlagSet(Dump->DumpType, MiniDumpFilterModulePaths)) {
Module->SavePath = GenGetPathTail(Module->FullPath);
} else {
Module->SavePath = Module->FullPath;
}
Module->BaseOfImage = BaseOfModule;
Module->WriteFlags = WriteFlags;
Module->CvRecord = NULL;
Module->SizeOfCvRecord = 0;
Module->MiscRecord = NULL;
Module->SizeOfMiscRecord = 0;
if (MappedBase) {
IMAGE_NT_HEADERS64 Hdr64;
NtHeaders = GenImageNtHeader ( MappedBase, &Hdr64 );
if (!NtHeaders) {
GenAccumulateStatus(Dump, MDSTATUS_CALL_FAILED);
FreeMemory(Dump, Module);
return HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
}
__try {
//
// Cull information from the image header.
//
Module->SizeOfImage = Hdr64.OptionalHeader.SizeOfImage;
Module->CheckSum = Hdr64.OptionalHeader.CheckSum;
Module->TimeDateStamp = Hdr64.FileHeader.TimeDateStamp;
//
// Get the CV record from the debug directory.
//
if (IsFlagSet(Module->WriteFlags, ModuleWriteCvRecord)) {
GenGetDebugRecord(Dump,
MappedBase,
MappedSize,
IMAGE_DEBUG_TYPE_CODEVIEW,
REASONABLE_NB11_RECORD_SIZE,
&Module->CvRecord,
&Module->SizeOfCvRecord);
}
//
// Get the MISC record from the debug directory.
//
if (IsFlagSet(Module->WriteFlags, ModuleWriteMiscRecord)) {
GenGetDebugRecord(Dump,
MappedBase,
MappedSize,
IMAGE_DEBUG_TYPE_MISC,
REASONABLE_MISC_RECORD_SIZE,
&Module->MiscRecord,
&Module->SizeOfMiscRecord);
}
Status = S_OK;
} __except(EXCEPTION_EXECUTE_HANDLER) {
Status = HRESULT_FROM_NT(GetExceptionCode());
}
Dump->SysProv->CloseMapping(MappedBase);
if (Status != S_OK) {
GenAccumulateStatus(Dump, MDSTATUS_CALL_FAILED);
FreeMemory(Dump, Module);
return Status;
}
} else {
ULONG RecordLen;
//
// See if the provider can retrieve debug records.
//
RecordLen = 0;
if (IsFlagSet(Module->WriteFlags, ModuleWriteCvRecord) &&
Dump->SysProv->
GetImageDebugRecord(Process->ProcessHandle,
Module->FullPath,
Module->BaseOfImage,
IMAGE_DEBUG_TYPE_CODEVIEW,
NULL,
&RecordLen) == S_OK &&
RecordLen <= REASONABLE_NB11_RECORD_SIZE &&
(Module->CvRecord = AllocMemory(Dump, RecordLen))) {
Module->SizeOfCvRecord = RecordLen;
if (Dump->SysProv->
GetImageDebugRecord(Process->ProcessHandle,
Module->FullPath,
Module->BaseOfImage,
IMAGE_DEBUG_TYPE_CODEVIEW,
Module->CvRecord,
&Module->SizeOfCvRecord) != S_OK) {
FreeMemory(Dump, Module->CvRecord);
Module->CvRecord = NULL;
Module->SizeOfCvRecord = 0;
}
}
RecordLen = 0;
if (IsFlagSet(Module->WriteFlags, ModuleWriteMiscRecord) &&
Dump->SysProv->
GetImageDebugRecord(Process->ProcessHandle,
Module->FullPath,
Module->BaseOfImage,
IMAGE_DEBUG_TYPE_CODEVIEW,
NULL,
&RecordLen) == S_OK &&
RecordLen <= REASONABLE_MISC_RECORD_SIZE &&
(Module->MiscRecord = AllocMemory(Dump, RecordLen))) {
Module->SizeOfMiscRecord = RecordLen;
if (Dump->SysProv->
GetImageDebugRecord(Process->ProcessHandle,
Module->FullPath,
Module->BaseOfImage,
IMAGE_DEBUG_TYPE_MISC,
Module->MiscRecord,
&Module->SizeOfMiscRecord) != S_OK) {
FreeMemory(Dump, Module->MiscRecord);
Module->MiscRecord = NULL;
Module->SizeOfMiscRecord = 0;
}
}
}
*ModuleRet = Module;
return S_OK;
}
VOID
GenFreeModuleObject(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_MODULE Module
)
{
FreeMemory ( Dump, Module->CvRecord );
Module->CvRecord = NULL;
FreeMemory ( Dump, Module->MiscRecord );
Module->MiscRecord = NULL;
FreeMemory ( Dump, Module );
Module = NULL;
}
HRESULT
GenAllocateUnloadedModuleObject(
IN PMINIDUMP_STATE Dump,
IN PWSTR Path,
IN ULONG64 BaseOfModule,
IN ULONG SizeOfModule,
IN ULONG CheckSum,
IN ULONG TimeDateStamp,
OUT PINTERNAL_UNLOADED_MODULE* ModuleRet
)
{
PINTERNAL_UNLOADED_MODULE Module;
Module = (PINTERNAL_UNLOADED_MODULE)
AllocMemory ( Dump, sizeof (*Module) );
if (Module == NULL) {
return E_OUTOFMEMORY;
}
GenStrCopyNW(Module->Path, Path, ARRAY_COUNT(Module->Path));
Module->BaseOfImage = BaseOfModule;
Module->SizeOfImage = SizeOfModule;
Module->CheckSum = CheckSum;
Module->TimeDateStamp = TimeDateStamp;
*ModuleRet = Module;
return S_OK;
}
VOID
GenFreeUnloadedModuleObject(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_UNLOADED_MODULE Module
)
{
FreeMemory ( Dump, Module );
Module = NULL;
}
HRESULT
GenAllocateFunctionTableObject(
IN PMINIDUMP_STATE Dump,
IN ULONG64 MinAddress,
IN ULONG64 MaxAddress,
IN ULONG64 BaseAddress,
IN ULONG EntryCount,
IN PVOID RawTable,
OUT PINTERNAL_FUNCTION_TABLE* TableRet
)
{
PINTERNAL_FUNCTION_TABLE Table;
Table = (PINTERNAL_FUNCTION_TABLE)
AllocMemory(Dump, sizeof(INTERNAL_FUNCTION_TABLE) +
Dump->FuncTableSize);
if (!Table) {
return E_OUTOFMEMORY;
}
Table->RawEntries =
AllocMemory(Dump, Dump->FuncTableEntrySize * EntryCount);
if (!Table->RawEntries) {
FreeMemory(Dump, Table);
return E_OUTOFMEMORY;
}
Table->MinimumAddress = MinAddress;
Table->MaximumAddress = MaxAddress;
Table->BaseAddress = BaseAddress;
Table->EntryCount = EntryCount;
Table->RawTable = (Table + 1);
memcpy(Table->RawTable, RawTable, Dump->FuncTableSize);
*TableRet = Table;
return S_OK;
}
VOID
GenFreeFunctionTableObject(
IN PMINIDUMP_STATE Dump,
IN struct _INTERNAL_FUNCTION_TABLE* Table
)
{
if (Table->RawEntries) {
FreeMemory(Dump, Table->RawEntries);
}
FreeMemory(Dump, Table);
}
HRESULT
GenAllocateProcessObject(
IN PMINIDUMP_STATE Dump,
OUT PINTERNAL_PROCESS* ProcessRet
)
{
HRESULT Status;
PINTERNAL_PROCESS Process;
FILETIME Create, Exit, User, Kernel;
Process = (PINTERNAL_PROCESS)
AllocMemory ( Dump, sizeof (INTERNAL_PROCESS) );
if (!Process) {
return E_OUTOFMEMORY;
}
Process->ProcessId = Dump->ProcessId;
Process->ProcessHandle = Dump->ProcessHandle;
Process->NumberOfThreads = 0;
Process->NumberOfModules = 0;
Process->NumberOfFunctionTables = 0;
InitializeListHead (&Process->ThreadList);
InitializeListHead (&Process->ModuleList);
InitializeListHead (&Process->UnloadedModuleList);
InitializeListHead (&Process->FunctionTableList);
InitializeListHead (&Process->MemoryBlocks);
if ((Status = Dump->SysProv->
GetPeb(Dump->ProcessHandle,
&Process->Peb, &Process->SizeOfPeb)) != S_OK) {
// Failure is only critical if the dump needs
// to include PEB memory.
if (Dump->DumpType & MiniDumpWithProcessThreadData) {
FreeMemory(Dump, Process);
return Status;
} else {
Process->Peb = 0;
Process->SizeOfPeb = 0;
}
}
// Win9x doesn't support GetProcessTimes so failures
// here are possible.
if (Dump->SysProv->
GetProcessTimes(Dump->ProcessHandle,
&Create, &User, &Kernel) == S_OK) {
Process->TimesValid = TRUE;
Process->CreateTime = FileTimeToTimeDate(&Create);
Process->UserTime = FileTimeToSeconds(&User);
Process->KernelTime = FileTimeToSeconds(&Kernel);
}
*ProcessRet = Process;
return S_OK;
}
VOID
GenFreeProcessObject(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process
)
{
PINTERNAL_MODULE Module;
PINTERNAL_UNLOADED_MODULE UnlModule;
PINTERNAL_THREAD Thread;
PINTERNAL_FUNCTION_TABLE Table;
PVA_RANGE Range;
PLIST_ENTRY Entry;
Thread = NULL;
Module = NULL;
Entry = Process->ModuleList.Flink;
while ( Entry != &Process->ModuleList ) {
Module = CONTAINING_RECORD (Entry, INTERNAL_MODULE, ModulesLink);
Entry = Entry->Flink;
GenFreeModuleObject ( Dump, Module );
}
Entry = Process->UnloadedModuleList.Flink;
while ( Entry != &Process->UnloadedModuleList ) {
UnlModule = CONTAINING_RECORD (Entry, INTERNAL_UNLOADED_MODULE,
ModulesLink);
Entry = Entry->Flink;
GenFreeUnloadedModuleObject ( Dump, UnlModule );
}
Entry = Process->ThreadList.Flink;
while ( Entry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (Entry, INTERNAL_THREAD, ThreadsLink);
Entry = Entry->Flink;
GenFreeThreadObject ( Dump, Thread );
}
Entry = Process->FunctionTableList.Flink;
while ( Entry != &Process->FunctionTableList ) {
Table = CONTAINING_RECORD (Entry, INTERNAL_FUNCTION_TABLE, TableLink);
Entry = Entry->Flink;
GenFreeFunctionTableObject ( Dump, Table );
}
Entry = Process->MemoryBlocks.Flink;
while (Entry != &Process->MemoryBlocks) {
Range = CONTAINING_RECORD(Entry, VA_RANGE, NextLink);
Entry = Entry->Flink;
FreeMemory(Dump, Range);
}
FreeMemory ( Dump, Process );
Process = NULL;
}
HRESULT
GenIncludeUnwindInfoMemory(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PINTERNAL_FUNCTION_TABLE Table
)
{
HRESULT Status;
ULONG i;
if (Dump->DumpType & MiniDumpWithFullMemory) {
// Memory will be included by default.
return S_OK;
}
for (i = 0; i < Table->EntryCount; i++) {
ULONG64 Start;
ULONG Size;
if ((Status = Dump->SysProv->
EnumFunctionTableEntryMemory(Table->BaseAddress,
Table->RawEntries,
i,
&Start,
&Size)) != S_OK) {
return Status;
}
if ((Status = GenAddMemoryBlock(Dump, Process, MEMBLOCK_UNWIND_INFO,
Start, Size)) != S_OK) {
return Status;
}
}
return S_OK;
}
void
GenRemoveMemoryBlock(
IN PINTERNAL_PROCESS Process,
IN PVA_RANGE Block
)
{
RemoveEntryList(&Block->NextLink);
Process->NumberOfMemoryBlocks--;
Process->SizeOfMemoryBlocks -= Block->Size;
}
HRESULT
GenAddMemoryBlock(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN MEMBLOCK_TYPE Type,
IN ULONG64 Start,
IN ULONG Size
)
{
ULONG64 End;
PLIST_ENTRY ScanEntry;
PVA_RANGE Scan;
ULONG64 ScanEnd;
PVA_RANGE New = NULL;
SIZE_T Done;
UCHAR Byte;
// Do not use Size after this to avoid ULONG overflows.
End = Start + Size;
if (End < Start) {
End = (ULONG64)-1;
}
if (Start == End) {
// Nothing to add.
return S_OK;
}
if ((End - Start) > ULONG_MAX - Process->SizeOfMemoryBlocks) {
// Overflow.
GenAccumulateStatus(Dump, MDSTATUS_INTERNAL_ERROR);
return E_INVALIDARG;
}
//
// First trim the range down to memory that can actually
// be accessed.
//
while (Start < End) {
if (Dump->SysProv->
ReadAllVirtual(Dump->ProcessHandle,
Start, &Byte, sizeof(Byte)) == S_OK) {
break;
}
// Move up to the next page.
Start = (Start + Dump->PageSize) & ~((ULONG64)Dump->PageSize - 1);
if (!Start) {
// Wrapped around.
return S_OK;
}
}
if (Start >= End) {
// No valid memory.
return S_OK;
}
ScanEnd = (Start + Dump->PageSize) & ~((ULONG64)Dump->PageSize - 1);
for (;;) {
if (ScanEnd >= End) {
break;
}
if (Dump->SysProv->
ReadAllVirtual(Dump->ProcessHandle,
ScanEnd, &Byte, sizeof(Byte)) != S_OK) {
End = ScanEnd;
break;
}
// Move up to the next page.
ScanEnd = (ScanEnd + Dump->PageSize) & ~((ULONG64)Dump->PageSize - 1);
if (!ScanEnd) {
ScanEnd--;
break;
}
}
//
// When adding memory to the list of memory to be saved
// we want to avoid overlaps and also coalesce adjacent regions
// so that the list has the largest possible non-adjacent
// blocks. In order to accomplish this we make a pass over
// the list and merge all listed blocks that overlap or abut the
// incoming range with the incoming range, then remove the
// merged entries from the list. After this pass we have
// a region which is guaranteed not to overlap or abut anything in
// the list.
//
ScanEntry = Process->MemoryBlocks.Flink;
while (ScanEntry != &Process->MemoryBlocks) {
Scan = CONTAINING_RECORD(ScanEntry, VA_RANGE, NextLink);
ScanEnd = Scan->Start + Scan->Size;
ScanEntry = Scan->NextLink.Flink;
if (Scan->Start > End || ScanEnd < Start) {
// No overlap or adjacency.
continue;
}
//
// Compute the union of the incoming range and
// the scan block, then remove the scan block.
//
if (Scan->Start < Start) {
Start = Scan->Start;
}
if (ScanEnd > End) {
End = ScanEnd;
}
// We've lost the specific type. This is not a problem
// right now but if specific types must be preserved
// all the way through in the future it will be necessary
// to avoid merging.
Type = MEMBLOCK_MERGED;
GenRemoveMemoryBlock(Process, Scan);
if (!New) {
// Save memory for reuse.
New = Scan;
} else {
FreeMemory(Dump, Scan);
}
}
if (!New) {
New = (PVA_RANGE)AllocMemory(Dump, sizeof(*New));
if (!New) {
return E_OUTOFMEMORY;
}
}
New->Start = Start;
// Overflow is extremely unlikely, so don't do anything
// fancy to handle it.
if (End - Start > ULONG_MAX) {
New->Size = ULONG_MAX;
} else {
New->Size = (ULONG)(End - Start);
}
New->Type = Type;
InsertTailList(&Process->MemoryBlocks, &New->NextLink);
Process->NumberOfMemoryBlocks++;
Process->SizeOfMemoryBlocks += New->Size;
return S_OK;
}
void
GenRemoveMemoryRange(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN ULONG64 Start,
IN ULONG Size
)
{
ULONG64 End = Start + Size;
PLIST_ENTRY ScanEntry;
PVA_RANGE Scan;
ULONG64 ScanEnd;
Restart:
ScanEntry = Process->MemoryBlocks.Flink;
while (ScanEntry != &Process->MemoryBlocks) {
Scan = CONTAINING_RECORD(ScanEntry, VA_RANGE, NextLink);
ScanEnd = Scan->Start + Scan->Size;
ScanEntry = Scan->NextLink.Flink;
if (Scan->Start >= End || ScanEnd <= Start) {
// No overlap.
continue;
}
if (Scan->Start < Start) {
// Trim block to non-overlapping pre-Start section.
Scan->Size = (ULONG)(Start - Scan->Start);
if (ScanEnd > End) {
// There's also a non-overlapping section post-End.
// We need to add a new block.
GenAddMemoryBlock(Dump, Process, Scan->Type,
End, (ULONG)(ScanEnd - End));
// The list has changed so restart.
goto Restart;
}
} else if (ScanEnd > End) {
// Trim block to non-overlapping post-End section.
Scan->Start = End;
Scan->Size = (ULONG)(ScanEnd - End);
} else {
// Scan is completely contained.
GenRemoveMemoryBlock(Process, Scan);
FreeMemory(Dump, Scan);
}
}
}
HRESULT
GenAddPebMemory(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process
)
{
HRESULT Status = S_OK, Check;
GenMiniDumpProviderCallbacks Callbacks(Dump, Process);
Callbacks.PushMemType(MEMBLOCK_PEB);
// Accumulate error status but do not stop processing
// for errors.
if ((Check = GenAddMemoryBlock(Dump, Process, MEMBLOCK_PEB,
Process->Peb,
Process->SizeOfPeb)) != S_OK) {
Status = Check;
}
if (!(Dump->DumpType & MiniDumpWithoutOptionalData) &&
(Check = Dump->SysProv->
EnumPebMemory(Process->ProcessHandle,
Process->Peb, Process->SizeOfPeb,
&Callbacks)) != S_OK) {
Status = Check;
}
Callbacks.PopMemType();
return Status;
}
HRESULT
GenAddTebMemory(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PINTERNAL_THREAD Thread
)
{
HRESULT Status = S_OK, Check;
GenMiniDumpProviderCallbacks Callbacks(Dump, Process);
Callbacks.PushMemType(MEMBLOCK_TEB);
// Accumulate error status but do not stop processing
// for errors.
if ((Check = GenAddMemoryBlock(Dump, Process, MEMBLOCK_TEB,
Thread->Teb, Thread->SizeOfTeb)) != S_OK) {
Status = Check;
}
if (!(Dump->DumpType & MiniDumpWithoutOptionalData) &&
(Check = Dump->SysProv->
EnumTebMemory(Process->ProcessHandle, Thread->ThreadHandle,
Thread->Teb, Thread->SizeOfTeb,
&Callbacks)) != S_OK) {
Status = Check;
}
Callbacks.PopMemType();
return Status;
}
HRESULT
GenScanAddressSpace(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process
)
{
HRESULT Status;
ULONG ProtectMask = 0, TypeMask = 0;
ULONG64 Offset, Size;
ULONG Protect, State, Type;
if (Dump->DumpType & MiniDumpWithPrivateReadWriteMemory) {
ProtectMask |= PAGE_READWRITE;
TypeMask |= MEM_PRIVATE;
}
if (!ProtectMask || !TypeMask) {
// Nothing to scan for.
return S_OK;
}
Status = S_OK;
Offset = 0;
for (;;) {
if (Dump->SysProv->
QueryVirtual(Dump->ProcessHandle, Offset, &Offset, &Size,
&Protect, &State, &Type) != S_OK) {
break;
}
ULONG64 ScanOffset = Offset;
Offset += Size;
if (State == MEM_COMMIT &&
(Protect & ProtectMask) &&
(Type & TypeMask)) {
while (Size > 0) {
ULONG BlockSize;
HRESULT OneStatus;
if (Size > ULONG_MAX / 2) {
BlockSize = ULONG_MAX / 2;
} else {
BlockSize = (ULONG)Size;
}
if ((OneStatus =
GenAddMemoryBlock(Dump,
Process,
MEMBLOCK_PRIVATE_RW,
ScanOffset,
BlockSize)) != S_OK) {
Status = OneStatus;
}
ScanOffset += BlockSize;
Size -= BlockSize;
}
}
}
return Status;
}
BOOL
GenAppendStrW(
IN OUT PWSTR* Str,
IN OUT PULONG Chars,
IN PCWSTR Append
)
{
if (!Append) {
return FALSE;
}
while (*Chars > 1 && *Append) {
**Str = *Append++;
(*Str)++;
(*Chars)--;
}
if (!*Chars) {
return FALSE;
}
**Str = 0;
return TRUE;
}
PWSTR
GenIToAW(
IN ULONG Val,
IN ULONG FieldChars,
IN WCHAR FillChar,
IN PWSTR Buf,
IN ULONG BufChars
)
{
PWSTR Store = Buf + (BufChars - 1);
*Store-- = 0;
if (Val == 0) {
*Store-- = L'0';
} else {
while (Val) {
if (Store < Buf) {
return NULL;
}
*Store-- = (WCHAR)(Val % 10) + L'0';
Val /= 10;
}
}
PWSTR FieldStart = Buf + (BufChars - 1) - FieldChars;
while (Store >= FieldStart) {
*Store-- = FillChar;
}
return Store + 1;
}
class GenCorDataAccessServices : public ICorDataAccessServices
{
public:
GenCorDataAccessServices(IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process)
{
m_Dump = Dump;
m_Process = Process;
}
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
*Interface = NULL;
return E_NOINTERFACE;
}
STDMETHOD_(ULONG, AddRef)(
THIS
)
{
return 1;
}
STDMETHOD_(ULONG, Release)(
THIS
)
{
return 0;
}
// ICorDataAccessServices.
virtual HRESULT STDMETHODCALLTYPE GetMachineType(
/* [out] */ ULONG32 *machine);
virtual HRESULT STDMETHODCALLTYPE GetPointerSize(
/* [out] */ ULONG32 *size);
virtual HRESULT STDMETHODCALLTYPE GetImageBase(
/* [string][in] */ LPCWSTR name,
/* [out] */ CORDATA_ADDRESS *base);
virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [length_is][size_is][out] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done);
virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [size_is][in] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done);
virtual HRESULT STDMETHODCALLTYPE GetTlsValue(
/* [in] */ ULONG32 index,
/* [out] */ CORDATA_ADDRESS* value);
virtual HRESULT STDMETHODCALLTYPE SetTlsValue(
/* [in] */ ULONG32 index,
/* [in] */ CORDATA_ADDRESS value);
virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadId(
/* [out] */ ULONG32* threadId);
virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextSize,
/* [out, size_is(contextSize)] */ PBYTE context);
virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextSize,
/* [in, size_is(contextSize)] */ PBYTE context);
PMINIDUMP_STATE m_Dump;
PINTERNAL_PROCESS m_Process;
};
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetMachineType(
/* [out] */ ULONG32 *machine
)
{
*machine = m_Dump->CpuType;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetPointerSize(
/* [out] */ ULONG32 *size
)
{
*size = m_Dump->PtrSize;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetImageBase(
/* [string][in] */ LPCWSTR name,
/* [out] */ CORDATA_ADDRESS *base
)
{
if ((!GenStrCompareW(name, L"mscoree.dll") &&
!GenStrCompareW(m_Process->CorDllType, L"ee")) ||
(!GenStrCompareW(name, L"mscorwks.dll") &&
!GenStrCompareW(m_Process->CorDllType, L"wks")) ||
(!GenStrCompareW(name, L"mscorsvr.dll") &&
!GenStrCompareW(m_Process->CorDllType, L"svr"))) {
*base = m_Process->CorDllBase;
return S_OK;
}
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::ReadVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [length_is][size_is][out] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done
)
{
return m_Dump->SysProv->
ReadVirtual(m_Process->ProcessHandle,
address, buffer, request, (PULONG)done);
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::WriteVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [size_is][in] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done)
{
// No modification supported.
return E_UNEXPECTED;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetTlsValue(
/* [in] */ ULONG32 index,
/* [out] */ CORDATA_ADDRESS* value
)
{
// Not needed for minidump.
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::SetTlsValue(
/* [in] */ ULONG32 index,
/* [in] */ CORDATA_ADDRESS value)
{
// No modification supported.
return E_UNEXPECTED;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetCurrentThreadId(
/* [out] */ ULONG32* threadId)
{
// Not needed for minidump.
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::GetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextSize,
/* [out, size_is(contextSize)] */ PBYTE context
)
{
PINTERNAL_THREAD Thread;
PLIST_ENTRY Entry;
Entry = m_Process->ThreadList.Flink;
while (Entry != &m_Process->ThreadList) {
Thread = CONTAINING_RECORD(Entry,
INTERNAL_THREAD,
ThreadsLink);
Entry = Entry->Flink;
if (Thread->ThreadId == threadId) {
ULONG64 Ignored;
return m_Dump->SysProv->
GetThreadContext(Thread->ThreadHandle,
context, contextSize,
&Ignored, &Ignored, &Ignored);
}
}
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE
GenCorDataAccessServices::SetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextSize,
/* [in, size_is(contextSize)] */ PBYTE context)
{
// No modification supported.
return E_UNEXPECTED;
}
class GenCorDataEnumMemoryRegions : public ICorDataEnumMemoryRegions
{
public:
GenCorDataEnumMemoryRegions(IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process)
{
m_Dump = Dump;
m_Process = Process;
}
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
*Interface = NULL;
return E_NOINTERFACE;
}
STDMETHOD_(ULONG, AddRef)(
THIS
)
{
return 1;
}
STDMETHOD_(ULONG, Release)(
THIS
)
{
return 0;
}
// ICorDataEnumMemoryRegions.
HRESULT STDMETHODCALLTYPE EnumMemoryRegion(
/* [in] */ CORDATA_ADDRESS address,
/* [in] */ ULONG32 size
)
{
return GenAddMemoryBlock(m_Dump, m_Process, MEMBLOCK_COR,
address, size);
}
private:
PMINIDUMP_STATE m_Dump;
PINTERNAL_PROCESS m_Process;
};
HRESULT
GenTryGetCorMemory(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process,
IN PWSTR CorDebugDllPath,
OUT PBOOL Loaded
)
{
HRESULT Status;
GenCorDataAccessServices Services(Dump, Process);
GenCorDataEnumMemoryRegions EnumMem(Dump, Process);
ICorDataAccess* Access;
*Loaded = FALSE;
if ((Status = Dump->SysProv->
GetCorDataAccess(CorDebugDllPath, &Services, &Access)) != S_OK) {
return Status;
}
*Loaded = TRUE;
Status = Access->EnumMemoryRegions(&EnumMem, DAC_ENUM_MEM_DEFAULT);
Dump->SysProv->ReleaseCorDataAccess(Access);
return Status;
}
HRESULT
GenGetCorMemory(
IN PMINIDUMP_STATE Dump,
IN PINTERNAL_PROCESS Process
)
{
HRESULT Status;
// Do not enable COR memory gathering for .NET Server
// as it's not stable yet.
#ifdef GET_COR_MEMORY
if (!Process->CorDllType) {
// COR is not loaded.
return S_OK;
}
if (Dump->DumpType & (MiniDumpWithFullMemory |
MiniDumpWithPrivateReadWriteMemory)) {
// All COR memory should already be included.
return S_OK;
}
WCHAR CorDebugDllPath[MAX_PATH + 1];
WCHAR NumStr[16];
PWSTR DllPathEnd, End;
ULONG Chars;
BOOL Loaded;
GenStrCopyNW(CorDebugDllPath, Process->CorDllPath,
ARRAY_COUNT(CorDebugDllPath));
DllPathEnd = GenGetPathTail(CorDebugDllPath);
//
// First try to load with the basic name.
//
End = DllPathEnd;
*End = 0;
Chars = (ULONG)(ARRAY_COUNT(CorDebugDllPath) - (End - CorDebugDllPath));
if (!GenAppendStrW(&End, &Chars, L"mscordacwks.dll")) {
return E_INVALIDARG;
}
if ((Status = GenTryGetCorMemory(Dump, Process, CorDebugDllPath,
&Loaded)) == S_OK ||
Loaded)
{
return Status;
}
//
// That didn't work, so try with the full name.
//
#if defined(_X86_)
PWSTR HostCpu = L"x86";
#elif defined(_IA64_)
PWSTR HostCpu = L"IA64";
#elif defined(_AMD64_)
PWSTR HostCpu = L"AMD64";
#elif defined(_ARM_)
PWSTR HostCpu = L"ARM";
#else
#error Unknown processor.
#endif
if (!GenAppendStrW(&End, &Chars, L"mscordac") ||
!GenAppendStrW(&End, &Chars, Process->CorDllType) ||
!GenAppendStrW(&End, &Chars, L"_") ||
!GenAppendStrW(&End, &Chars, HostCpu) ||
!GenAppendStrW(&End, &Chars, L"_") ||
!GenAppendStrW(&End, &Chars, Dump->CpuTypeName) ||
!GenAppendStrW(&End, &Chars, L"_") ||
!GenAppendStrW(&End, &Chars,
GenIToAW(Process->CorDllVer.dwFileVersionMS >> 16,
0, 0, NumStr, ARRAY_COUNT(NumStr))) ||
!GenAppendStrW(&End, &Chars, L".") ||
!GenAppendStrW(&End, &Chars,
GenIToAW(Process->CorDllVer.dwFileVersionMS & 0xffff,
0, 0, NumStr, ARRAY_COUNT(NumStr))) ||
!GenAppendStrW(&End, &Chars, L".") ||
!GenAppendStrW(&End, &Chars,
GenIToAW(Process->CorDllVer.dwFileVersionLS >> 16,
0, 0, NumStr, ARRAY_COUNT(NumStr))) ||
!GenAppendStrW(&End, &Chars, L".") ||
!GenAppendStrW(&End, &Chars,
GenIToAW(Process->CorDllVer.dwFileVersionLS & 0xffff,
2, L'0', NumStr, ARRAY_COUNT(NumStr))) ||
((Process->CorDllVer.dwFileFlags & VS_FF_DEBUG) &&
!GenAppendStrW(&End, &Chars,
(Process->CorDllVer.dwFileFlags & VS_FF_SPECIALBUILD) ?
L".dbg" : L".chk")) ||
!GenAppendStrW(&End, &Chars, L".dll")) {
return E_INVALIDARG;
}
return GenTryGetCorMemory(Dump, Process, CorDebugDllPath, &Loaded);
#else
return S_OK;
#endif
}
HRESULT
GenGetProcessInfo(
IN PMINIDUMP_STATE Dump,
OUT PINTERNAL_PROCESS * ProcessRet
)
{
HRESULT Status;
BOOL EnumStarted = FALSE;
PINTERNAL_PROCESS Process;
WCHAR UnicodePath[MAX_PATH + 10];
if ((Status = GenAllocateProcessObject(Dump, &Process)) != S_OK) {
return Status;
}
if ((Status = Dump->SysProv->StartProcessEnum(Dump->ProcessHandle,
Dump->ProcessId)) != S_OK) {
goto Exit;
}
EnumStarted = TRUE;
//
// Walk thread list, suspending all threads and getting thread info.
//
for (;;) {
PINTERNAL_THREAD Thread;
ULONG ThreadId;
Status = Dump->SysProv->EnumThreads(&ThreadId);
if (Status == S_FALSE) {
break;
} else if (Status != S_OK) {
goto Exit;
}
ULONG WriteFlags;
if (!GenExecuteIncludeThreadCallback(Dump,
ThreadId,
&WriteFlags) ||
IsFlagClear(WriteFlags, ThreadWriteThread)) {
continue;
}
Status = GenAllocateThreadObject(Dump,
Process,
ThreadId,
WriteFlags,
&Thread);
if (FAILED(Status)) {
goto Exit;
}
// If Status is S_FALSE it means that the thread
// couldn't be opened and probably exited before
// we got to it. Just continue on.
if (Status == S_OK) {
Process->NumberOfThreads++;
InsertTailList(&Process->ThreadList, &Thread->ThreadsLink);
}
}
//
// Walk module list, getting module information.
//
for (;;) {
PINTERNAL_MODULE Module;
ULONG64 ModuleBase;
Status = Dump->SysProv->EnumModules(&ModuleBase,
UnicodePath,
ARRAY_COUNT(UnicodePath));
if (Status == S_FALSE) {
break;
} else if (Status != S_OK) {
goto Exit;
}
PWSTR ModPathTail;
BOOL IsCor = FALSE;
ModPathTail = GenGetPathTail(UnicodePath);
if (!GenStrCompareW(ModPathTail, L"mscoree.dll") &&
!Process->CorDllType) {
IsCor = TRUE;
Process->CorDllType = L"ee";
} else if (!GenStrCompareW(ModPathTail, L"mscorwks.dll")) {
IsCor = TRUE;
Process->CorDllType = L"wks";
} else if (!GenStrCompareW(ModPathTail, L"mscorsvr.dll")) {
IsCor = TRUE;
Process->CorDllType = L"svr";
}
if (IsCor) {
Process->CorDllBase = ModuleBase;
GenStrCopyNW(Process->CorDllPath, UnicodePath,
ARRAY_COUNT(Process->CorDllPath));
}
ULONG WriteFlags;
if (!GenExecuteIncludeModuleCallback(Dump,
ModuleBase,
&WriteFlags) ||
IsFlagClear(WriteFlags, ModuleWriteModule)) {
// If this is the COR DLL module we need to get
// its version information for later use. The
// callback has dropped it from the enumeration
// so do it right now before the module is forgotten.
if (IsCor &&
(Status = Dump->SysProv->
GetImageVersionInfo(Dump->ProcessHandle,
UnicodePath,
ModuleBase,
&Process->CorDllVer)) != S_OK) {
// If we can't get the version just forget
// that this process has the COR loaded.
// The dump will probably be useless but
// there's a tiny chance it won't.
Process->CorDllType = NULL;
}
continue;
}
if ((Status =
GenAllocateModuleObject(Dump,
Process,
UnicodePath,
ModuleBase,
WriteFlags,
&Module)) != S_OK) {
goto Exit;
}
if (IsCor) {
Process->CorDllVer = Module->VersionInfo;
}
Process->NumberOfModules++;
InsertTailList (&Process->ModuleList, &Module->ModulesLink);
}
//
// Walk function table list. The function table list
// is important but not absolutely critical so failures
// here are not fatal.
//
for (;;) {
PINTERNAL_FUNCTION_TABLE Table;
ULONG64 MinAddr, MaxAddr, BaseAddr;
ULONG EntryCount;
ULONG64 RawTable[(MAX_DYNAMIC_FUNCTION_TABLE + sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
PVOID RawEntryHandle;
Status = Dump->SysProv->
EnumFunctionTables(&MinAddr,
&MaxAddr,
&BaseAddr,
&EntryCount,
RawTable,
Dump->FuncTableSize,
&RawEntryHandle);
if (Status != S_OK) {
break;
}
if (GenAllocateFunctionTableObject(Dump,
MinAddr,
MaxAddr,
BaseAddr,
EntryCount,
RawTable,
&Table) == S_OK) {
if (Dump->SysProv->
EnumFunctionTableEntries(RawTable,
Dump->FuncTableSize,
RawEntryHandle,
Table->RawEntries,
EntryCount *
Dump->FuncTableEntrySize) != S_OK) {
GenFreeFunctionTableObject(Dump, Table);
} else {
GenIncludeUnwindInfoMemory(Dump, Process, Table);
Process->NumberOfFunctionTables++;
InsertTailList(&Process->FunctionTableList,
&Table->TableLink);
}
}
}
//
// Walk unloaded module list. The unloaded module
// list is not critical information so failures here
// are not fatal.
//
if (Dump->DumpType & MiniDumpWithUnloadedModules) {
PINTERNAL_UNLOADED_MODULE UnlModule;
ULONG64 ModuleBase;
ULONG Size;
ULONG CheckSum;
ULONG TimeDateStamp;
while (Dump->SysProv->
EnumUnloadedModules(UnicodePath,
ARRAY_COUNT(UnicodePath),
&ModuleBase,
&Size,
&CheckSum,
&TimeDateStamp) == S_OK) {
if (GenAllocateUnloadedModuleObject(Dump,
UnicodePath,
ModuleBase,
Size,
CheckSum,
TimeDateStamp,
&UnlModule) == S_OK) {
Process->NumberOfUnloadedModules++;
InsertHeadList(&Process->UnloadedModuleList,
&UnlModule->ModulesLink);
} else {
break;
}
}
}
Status = S_OK;
Exit:
if (EnumStarted) {
Dump->SysProv->FinishProcessEnum();
}
if (Status == S_OK) {
// We don't consider a failure here to be a critical
// failure. The dump won't contain all of the
// requested information but it'll still have
// the basic thread information, which could be
// valuable on its own.
GenScanAddressSpace(Dump, Process);
GenGetCorMemory(Dump, Process);
} else {
GenFreeProcessObject(Dump, Process);
Process = NULL;
}
*ProcessRet = Process;
return Status;
}
HRESULT
GenWriteHandleData(
IN PMINIDUMP_STATE Dump,
IN PMINIDUMP_STREAM_INFO StreamInfo
)
{
HRESULT Status;
ULONG HandleCount;
ULONG Hits;
WCHAR TypeName[64];
WCHAR ObjectName[MAX_PATH];
PMINIDUMP_HANDLE_DESCRIPTOR Descs, Desc;
ULONG32 Len;
MINIDUMP_HANDLE_DATA_STREAM DataStream;
RVA Rva = StreamInfo->RvaOfHandleData;
if ((Status = Dump->SysProv->
StartHandleEnum(Dump->ProcessHandle,
Dump->ProcessId,
&HandleCount)) != S_OK) {
return Status;
}
if (!HandleCount) {
Dump->SysProv->FinishHandleEnum();
return S_OK;
}
Descs = (PMINIDUMP_HANDLE_DESCRIPTOR)
AllocMemory(Dump, HandleCount * sizeof(*Desc));
if (Descs == NULL) {
Dump->SysProv->FinishHandleEnum();
return E_OUTOFMEMORY;
}
Hits = 0;
Desc = Descs;
while (Hits < HandleCount &&
Dump->SysProv->
EnumHandles(&Desc->Handle,
(PULONG)&Desc->Attributes,
(PULONG)&Desc->GrantedAccess,
(PULONG)&Desc->HandleCount,
(PULONG)&Desc->PointerCount,
TypeName,
ARRAY_COUNT(TypeName),
ObjectName,
ARRAY_COUNT(ObjectName)) == S_OK) {
// Successfully got a handle, so consider this a hit.
Hits++;
Desc->TypeNameRva = Rva;
Len = GenStrLengthW(TypeName) * sizeof(WCHAR);
if ((Status = Dump->OutProv->
WriteAll(&Len, sizeof(Len))) != S_OK) {
goto Exit;
}
Len += sizeof(WCHAR);
if ((Status = Dump->OutProv->
WriteAll(TypeName, Len)) != S_OK) {
goto Exit;
}
Rva += Len + sizeof(Len);
if (ObjectName[0]) {
Desc->ObjectNameRva = Rva;
Len = GenStrLengthW(ObjectName) * sizeof(WCHAR);
if ((Status = Dump->OutProv->
WriteAll(&Len, sizeof(Len))) != S_OK) {
goto Exit;
}
Len += sizeof(WCHAR);
if ((Status = Dump->OutProv->
WriteAll(ObjectName, Len)) != S_OK) {
goto Exit;
}
Rva += Len + sizeof(Len);
} else {
Desc->ObjectNameRva = 0;
}
Desc++;
}
DataStream.SizeOfHeader = sizeof(DataStream);
DataStream.SizeOfDescriptor = sizeof(*Descs);
DataStream.NumberOfDescriptors = (ULONG)(Desc - Descs);
DataStream.Reserved = 0;
StreamInfo->RvaOfHandleData = Rva;
StreamInfo->SizeOfHandleData = sizeof(DataStream) +
DataStream.NumberOfDescriptors * sizeof(*Descs);
if ((Status = Dump->OutProv->
WriteAll(&DataStream, sizeof(DataStream))) == S_OK) {
Status = Dump->OutProv->
WriteAll(Descs, DataStream.NumberOfDescriptors * sizeof(*Descs));
}
Exit:
FreeMemory(Dump, Descs);
Dump->SysProv->FinishHandleEnum();
return Status;
}
ULONG
GenProcArchToImageMachine(ULONG ProcArch)
{
switch(ProcArch) {
case PROCESSOR_ARCHITECTURE_INTEL:
return IMAGE_FILE_MACHINE_I386;
case PROCESSOR_ARCHITECTURE_IA64:
return IMAGE_FILE_MACHINE_IA64;
case PROCESSOR_ARCHITECTURE_AMD64:
return IMAGE_FILE_MACHINE_AMD64;
case PROCESSOR_ARCHITECTURE_ARM:
return IMAGE_FILE_MACHINE_ARM;
case PROCESSOR_ARCHITECTURE_ALPHA:
return IMAGE_FILE_MACHINE_ALPHA;
case PROCESSOR_ARCHITECTURE_ALPHA64:
return IMAGE_FILE_MACHINE_AXP64;
default:
return IMAGE_FILE_MACHINE_UNKNOWN;
}
}
LPWSTR
GenStrCopyNW(
OUT LPWSTR lpString1,
IN LPCWSTR lpString2,
IN int iMaxLength
)
{
wchar_t * cp = lpString1;
if (iMaxLength > 0)
{
while( iMaxLength > 1 && (*cp++ = *lpString2++) )
iMaxLength--; /* Copy src over dst */
if (cp > lpString1 && cp[-1]) {
*cp = 0;
}
}
return( lpString1 );
}
size_t
GenStrLengthW(
const wchar_t * wcs
)
{
const wchar_t *eos = wcs;
while( *eos++ )
;
return( (size_t)(eos - wcs - 1) );
}
int
GenStrCompareW(
IN LPCWSTR String1,
IN LPCWSTR String2
)
{
while (*String1) {
if (*String1 < *String2) {
return -1;
} else if (*String1 > *String2) {
return 1;
}
String1++;
String2++;
}
return *String2 ? 1 : 0;
}
C_ASSERT(sizeof(EXCEPTION_RECORD64) == sizeof(MINIDUMP_EXCEPTION));
void
GenExRecord32ToMd(PEXCEPTION_RECORD32 Rec32,
PMINIDUMP_EXCEPTION RecMd)
{
ULONG i;
RecMd->ExceptionCode = Rec32->ExceptionCode;
RecMd->ExceptionFlags = Rec32->ExceptionFlags;
RecMd->ExceptionRecord = (LONG)Rec32->ExceptionRecord;
RecMd->ExceptionAddress = (LONG)Rec32->ExceptionAddress;
RecMd->NumberParameters = Rec32->NumberParameters;
for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
{
RecMd->ExceptionInformation[i] =
(LONG)Rec32->ExceptionInformation[i];
}
}