|
|
/*++
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]; } }
|