|
|
//----------------------------------------------------------------------------
//
// Dump file writing.
//
// Copyright (C) Microsoft Corporation, 2001-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
#include <uminiprov.hpp>
#include <dbgver.h>
#include <bugcodes.h>
#define GENERIC_FORMATS \
(DEBUG_FORMAT_WRITE_CAB | \ DEBUG_FORMAT_CAB_SECONDARY_FILES | \ DEBUG_FORMAT_NO_OVERWRITE) #define UMINI_FORMATS \
(DEBUG_FORMAT_USER_SMALL_FULL_MEMORY | \ DEBUG_FORMAT_USER_SMALL_HANDLE_DATA | \ DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES | \ DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY | \ DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS | \ DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY | \ DEBUG_FORMAT_USER_SMALL_FILTER_PATHS | \ DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA | \ DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY)
// Internal format flag for testing of microdumps. This
// will not be made into a public flag and must not conflict
// with them.
#define FORMAT_USER_MICRO 0x01000000
//----------------------------------------------------------------------------
//
// UserFullDumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
#define USER_DUMP_MEMORY_BUFFER 65536
struct CREATE_USER_DUMP_STATE { ThreadInfo* Thread; ImageInfo* Image; ULONG64 MemHandle; HANDLE DumpFileHandle; MEMORY_BASIC_INFORMATION64 MemInfo; MEMORY_BASIC_INFORMATION32 MemInfo32; ULONG64 MemBufDone; ULONG64 TotalMemQueried; ULONG64 TotalMemData; CROSS_PLATFORM_CONTEXT TargetContext; DEBUG_EVENT Event; CRASH_THREAD CrashThread; ULONG64 MemBuf[USER_DUMP_MEMORY_BUFFER / sizeof(ULONG64)]; };
BOOL WINAPI CreateUserDumpCallback( ULONG DataType, PVOID* Data, PULONG DataLength, PVOID UserData ) { CREATE_USER_DUMP_STATE* State = (CREATE_USER_DUMP_STATE*)UserData; ThreadInfo* Thread;
switch(DataType) { case DMP_DUMP_FILE_HANDLE: *Data = State->DumpFileHandle; *DataLength = sizeof(HANDLE); break;
case DMP_DEBUG_EVENT: ADDR PcAddr;
//
// Fake up an exception event for the current thread.
//
ZeroMemory(&State->Event, sizeof(State->Event));
g_Machine->GetPC(&PcAddr);
State->Event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT; State->Event.dwProcessId = g_Process->m_SystemId; State->Event.dwThreadId = g_Thread->m_SystemId; if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last exception.
ExceptionRecord64To(&g_LastEventInfo.Exception.ExceptionRecord, &State->Event.u.Exception.ExceptionRecord); State->Event.u.Exception.dwFirstChance = g_LastEventInfo.Exception.FirstChance; } else { // Fake a breakpoint exception.
State->Event.u.Exception.ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT; State->Event.u.Exception.ExceptionRecord.ExceptionAddress = (PVOID)(ULONG_PTR)Flat(PcAddr); State->Event.u.Exception.dwFirstChance = TRUE; }
*Data = &State->Event; *DataLength = sizeof(State->Event); break;
case DMP_THREAD_STATE: ULONG64 Teb64;
if (State->Thread == NULL) { Thread = g_Process->m_ThreadHead; } else { Thread = State->Thread->m_Next; } State->Thread = Thread; if (Thread == NULL) { return FALSE; }
ZeroMemory(&State->CrashThread, sizeof(State->CrashThread));
State->CrashThread.ThreadId = Thread->m_SystemId; State->CrashThread.SuspendCount = Thread->m_SuspendCount; if (IS_LIVE_USER_TARGET(g_Target)) { if (g_Target->m_ClassQualifier == DEBUG_USER_WINDOWS_PROCESS_SERVER) { // The priority information isn't important
// enough to warrant remoting.
State->CrashThread.PriorityClass = NORMAL_PRIORITY_CLASS; State->CrashThread.Priority = THREAD_PRIORITY_NORMAL; } else { State->CrashThread.PriorityClass = GetPriorityClass(OS_HANDLE(g_Process->m_SysHandle)); State->CrashThread.Priority = GetThreadPriority(OS_HANDLE(Thread->m_Handle)); } } else { State->CrashThread.PriorityClass = NORMAL_PRIORITY_CLASS; State->CrashThread.Priority = THREAD_PRIORITY_NORMAL; } if (g_Target->GetThreadInfoDataOffset(Thread, NULL, &Teb64) != S_OK) { Teb64 = 0; } State->CrashThread.Teb = (DWORD_PTR)Teb64;
*Data = &State->CrashThread; *DataLength = sizeof(State->CrashThread); break;
case DMP_MEMORY_BASIC_INFORMATION: if (g_Target->QueryMemoryRegion(g_Process, &State->MemHandle, FALSE, &State->MemInfo) != S_OK) { State->MemHandle = 0; State->MemInfo.RegionSize = 0; return FALSE; }
State->TotalMemQueried += State->MemInfo.RegionSize;
#ifdef _WIN64
*Data = &State->MemInfo; *DataLength = sizeof(State->MemInfo); #else
State->MemInfo32.BaseAddress = (ULONG)State->MemInfo.BaseAddress; State->MemInfo32.AllocationBase = (ULONG)State->MemInfo.AllocationBase; State->MemInfo32.AllocationProtect = State->MemInfo.AllocationProtect; State->MemInfo32.RegionSize = (ULONG)State->MemInfo.RegionSize; State->MemInfo32.State = State->MemInfo.State; State->MemInfo32.Protect = State->MemInfo.Protect; State->MemInfo32.Type = State->MemInfo.Type; *Data = &State->MemInfo32; *DataLength = sizeof(State->MemInfo32); #endif
break;
case DMP_THREAD_CONTEXT: if (State->Thread == NULL) { Thread = g_Process->m_ThreadHead; } else { Thread = State->Thread->m_Next; } State->Thread = Thread; if (Thread == NULL) { g_Target->ChangeRegContext(g_Thread); return FALSE; }
g_Target->ChangeRegContext(Thread); if (g_Machine->GetContextState(MCTX_CONTEXT) != S_OK || g_Machine->ConvertContextTo(&g_Machine->m_Context, g_Target->m_SystemVersion, g_Target->m_TypeInfo.SizeTargetContext, &State->TargetContext) != S_OK) { ErrOut("Unable to retrieve context for thread %d. " "Dump may be corrupt.", Thread->m_UserId); return FALSE; }
*Data = &State->TargetContext; *DataLength = g_Target->m_TypeInfo.SizeTargetContext; break;
case DMP_MODULE: ImageInfo* Image; PCRASH_MODULE Module;
if (State->Image == NULL) { Image = g_Process->m_ImageHead; } else { Image = State->Image->m_Next; } State->Image = Image; if (Image == NULL) { return FALSE; }
Module = (PCRASH_MODULE)State->MemBuf; Module->BaseOfImage = (DWORD_PTR)Image->m_BaseOfImage; Module->SizeOfImage = Image->m_SizeOfImage; Module->ImageNameLength = strlen(Image->m_ImagePath) + 1; CopyString(Module->ImageName, Image->m_ImagePath, USER_DUMP_MEMORY_BUFFER - sizeof(*Module));
*Data = Module; *DataLength = sizeof(*Module) + Module->ImageNameLength; break;
case DMP_MEMORY_DATA: ULONG64 Left;
Left = State->MemInfo.RegionSize - State->MemBufDone; if (Left == 0) { State->MemBufDone = 0; if (g_Target->QueryMemoryRegion(g_Process, &State->MemHandle, FALSE, &State->MemInfo) != S_OK) { State->MemHandle = 0; State->MemInfo.RegionSize = 0;
// Sanity check that we wrote out as much data
// as we stored in the MEMORY_BASIC phase.
if (State->TotalMemQueried != State->TotalMemData) { ErrOut("Queried %s bytes of memory but wrote %s " "bytes of memory data.\nDump may be corrupt.\n", FormatDisp64(State->TotalMemQueried), FormatDisp64(State->TotalMemData)); }
return FALSE; }
Left = State->MemInfo.RegionSize; State->TotalMemData += State->MemInfo.RegionSize; }
if (Left > USER_DUMP_MEMORY_BUFFER) { Left = USER_DUMP_MEMORY_BUFFER; } if (CurReadAllVirtual(State->MemInfo.BaseAddress + State->MemBufDone, State->MemBuf, (ULONG)Left) != S_OK) { ErrOut("ReadVirtual at %s failed. Dump may be corrupt.\n", FormatAddr64(State->MemInfo.BaseAddress + State->MemBufDone)); return FALSE; }
State->MemBufDone += Left;
*Data = State->MemBuf; *DataLength = (ULONG)Left; break; }
return TRUE; }
HRESULT UserFullDumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { dprintf("user full dump\n"); FlushCallbacks();
if (!IS_LIVE_USER_TARGET(g_Target)) { ErrOut("User full dumps can only be written in " "live user-mode sessions\n"); return E_UNEXPECTED; } if (CommentA != NULL || CommentW != NULL) { ErrOut("User full dumps do not support comments\n"); return E_INVALIDARG; }
CREATE_USER_DUMP_STATE* State;
State = (CREATE_USER_DUMP_STATE*)calloc(1, sizeof(*State)); if (State == NULL) { ErrOut("Unable to allocate memory for dump state\n"); return E_OUTOFMEMORY; }
State->DumpFileHandle = hFile;
HRESULT Status;
if (!DbgHelpCreateUserDump(NULL, CreateUserDumpCallback, State)) { Status = WIN32_LAST_STATUS(); ErrOut("Dump creation failed, %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatus(Status)); } else { Status = S_OK; }
free(State); return Status; }
//----------------------------------------------------------------------------
//
// UserMiniDumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
class DbgSystemProvider : public MiniDumpSystemProvider { public: DbgSystemProvider(void); ~DbgSystemProvider(void);
virtual void Release(void); virtual HRESULT GetCurrentTimeDate(OUT PULONG TimeDate); virtual HRESULT GetCpuType(OUT PULONG Type, OUT PBOOL BackingStore); virtual HRESULT GetCpuInfo(OUT PUSHORT Architecture, OUT PUSHORT Level, OUT PUSHORT Revision, OUT PUCHAR NumberOfProcessors, OUT PCPU_INFORMATION Info); virtual void GetContextSizes(OUT PULONG Size, OUT PULONG RegScanStart, OUT PULONG RegScanCount); virtual void GetPointerSize(OUT PULONG Size); virtual void GetPageSize(OUT PULONG Size); virtual void GetFunctionTableSizes(OUT PULONG TableSize, OUT PULONG EntrySize); virtual void GetInstructionWindowSize(OUT PULONG Size); virtual HRESULT GetOsInfo(OUT PULONG PlatformId, OUT PULONG Major, OUT PULONG Minor, OUT PULONG BuildNumber, OUT PUSHORT ProductType, OUT PUSHORT SuiteMask); virtual HRESULT GetOsCsdString(OUT PWSTR Buffer, IN ULONG BufferChars); virtual HRESULT OpenMapping(IN PCWSTR FilePath, OUT PULONG Size, OUT PWSTR LongPath, IN ULONG LongPathChars, OUT PVOID* Mapping); virtual void CloseMapping(PVOID Mapping); virtual HRESULT GetImageHeaderInfo(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, OUT PULONG Size, OUT PULONG CheckSum, OUT PULONG TimeDateStamp); virtual HRESULT GetImageVersionInfo(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, OUT VS_FIXEDFILEINFO* Info); virtual HRESULT GetImageDebugRecord(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, IN ULONG RecordType, OUT OPTIONAL PVOID Data, IN OUT PULONG DataLen); virtual HRESULT EnumImageDataSections(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, IN MiniDumpProviderCallbacks* Callback); virtual HRESULT OpenThread(IN ULONG DesiredAccess, IN BOOL InheritHandle, IN ULONG ThreadId, OUT PHANDLE Handle); virtual void CloseThread(IN HANDLE Handle); virtual ULONG GetCurrentThreadId(void); virtual ULONG SuspendThread(IN HANDLE Thread); virtual ULONG ResumeThread(IN HANDLE Thread); virtual HRESULT GetThreadContext(IN HANDLE Thread, OUT PVOID Context, IN ULONG ContextSize, OUT PULONG64 CurrentPc, OUT PULONG64 CurrentStack, OUT PULONG64 CurrentStore); virtual HRESULT GetTeb(IN HANDLE Thread, OUT PULONG64 Offset, OUT PULONG Size); virtual HRESULT GetThreadInfo(IN HANDLE Process, IN HANDLE Thread, OUT PULONG64 Teb, OUT PULONG SizeOfTeb, OUT PULONG64 StackBase, OUT PULONG64 StackLimit, OUT PULONG64 StoreBase, OUT PULONG64 StoreLimit); virtual HRESULT GetPeb(IN HANDLE Process, OUT PULONG64 Offset, OUT PULONG Size); virtual HRESULT GetProcessTimes(IN HANDLE Process, OUT LPFILETIME Create, OUT LPFILETIME User, OUT LPFILETIME Kernel); virtual HRESULT ReadVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG Request, OUT PULONG Done); virtual HRESULT ReadAllVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG Request); virtual HRESULT QueryVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PULONG64 Base, OUT PULONG64 Size, OUT PULONG Protect, OUT PULONG State, OUT PULONG Type); virtual HRESULT StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId); virtual HRESULT EnumThreads(OUT PULONG ThreadId); virtual HRESULT EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars); virtual HRESULT EnumFunctionTables(OUT PULONG64 MinAddress, OUT PULONG64 MaxAddress, OUT PULONG64 BaseAddress, OUT PULONG EntryCount, OUT PVOID RawTable, IN ULONG RawTableSize, OUT PVOID* RawEntryHandle); virtual HRESULT EnumFunctionTableEntries(IN PVOID RawTable, IN ULONG RawTableSize, IN PVOID RawEntryHandle, OUT PVOID RawEntries, IN ULONG RawEntriesSize); virtual HRESULT EnumFunctionTableEntryMemory(IN ULONG64 TableBase, IN PVOID RawEntries, IN ULONG Index, OUT PULONG64 Start, OUT PULONG Size); virtual HRESULT EnumUnloadedModules(OUT PWSTR Path, IN ULONG PathChars, OUT PULONG64 BaseOfModule, OUT PULONG SizeOfModule, OUT PULONG CheckSum, OUT PULONG TimeDateStamp); virtual void FinishProcessEnum(void); virtual HRESULT StartHandleEnum(IN HANDLE Process, IN ULONG ProcessId, OUT PULONG Count); virtual HRESULT EnumHandles(OUT PULONG64 Handle, OUT PULONG Attributes, OUT PULONG GrantedAccess, OUT PULONG HandleCount, OUT PULONG PointerCount, OUT PWSTR TypeName, IN ULONG TypeNameChars, OUT PWSTR ObjectName, IN ULONG ObjectNameChars); virtual void FinishHandleEnum(void);
virtual HRESULT EnumPebMemory(IN HANDLE Process, IN ULONG64 PebOffset, IN ULONG PebSize, IN MiniDumpProviderCallbacks* Callback); virtual HRESULT EnumTebMemory(IN HANDLE Process, IN HANDLE Thread, IN ULONG64 TebOffset, IN ULONG TebSize, IN MiniDumpProviderCallbacks* Callback);
virtual HRESULT GetCorDataAccess(IN PWSTR AccessDllName, IN struct ICorDataAccessServices* Services, OUT struct ICorDataAccess** Access); virtual void ReleaseCorDataAccess(IN struct ICorDataAccess* Access);
protected: ThreadInfo* m_Thread; ImageInfo* m_Image; UnloadedModuleInfo* m_UnlEnum; ULONG m_Handle; ULONG64 m_FuncTableStart; ULONG64 m_FuncTableHandle; };
DbgSystemProvider::DbgSystemProvider(void) { }
DbgSystemProvider::~DbgSystemProvider(void) { }
void DbgSystemProvider::Release(void) { delete this; }
HRESULT DbgSystemProvider::GetCurrentTimeDate(OUT PULONG TimeDate) { *TimeDate = FileTimeToTimeDateStamp(g_Target->GetCurrentTimeDateN()); return S_OK; }
HRESULT DbgSystemProvider::GetCpuType(OUT PULONG Type, OUT PBOOL BackingStore) { *Type = g_Target->m_MachineType; *BackingStore = *Type == IMAGE_FILE_MACHINE_IA64; return S_OK; }
HRESULT DbgSystemProvider::GetCpuInfo(OUT PUSHORT Architecture, OUT PUSHORT Level, OUT PUSHORT Revision, OUT PUCHAR NumberOfProcessors, OUT PCPU_INFORMATION Info) { DEBUG_PROCESSOR_IDENTIFICATION_ALL ProcId; ULONG64 ProcFeatures[4]; ULONG NumVals;
*Architecture = (USHORT)ImageMachineToProcArch(g_Target->m_MachineType); *NumberOfProcessors = (UCHAR)g_Target->m_NumProcessors;
//
// We've set the basic processor type so that the dump
// can be interpreted correctly. Any other failures should
// not be considered fatal.
//
*Level = 0; *Revision = 0; ZeroMemory(Info, sizeof(*Info));
if (g_Target->GetProcessorId(0, &ProcId) != S_OK) { return S_OK; }
switch(g_Target->m_MachineType) { case IMAGE_FILE_MACHINE_I386: *Level = (USHORT)ProcId.X86.Family; *Revision = ((USHORT)ProcId.X86.Model << 8) | (USHORT)ProcId.X86.Stepping;
memcpy(Info->X86CpuInfo.VendorId, ProcId.X86.VendorString, sizeof(Info->X86CpuInfo.VendorId)); if (SUCCEEDED(g_Target-> GetSpecificProcessorFeatures(0, ProcFeatures, DIMA(ProcFeatures), &NumVals)) && NumVals >= 2) { Info->X86CpuInfo.VersionInformation = (ULONG32)ProcFeatures[0]; Info->X86CpuInfo.FeatureInformation = (ULONG32)ProcFeatures[1];
if (NumVals >= 3) { Info->X86CpuInfo.AMDExtendedCpuFeatures = (ULONG32)ProcFeatures[2]; } } break;
case IMAGE_FILE_MACHINE_IA64: *Level = (USHORT)ProcId.Ia64.Model; *Revision = (USHORT)ProcId.Ia64.Revision; break;
case IMAGE_FILE_MACHINE_AMD64: *Level = (USHORT)ProcId.Amd64.Family; *Revision = ((USHORT)ProcId.Amd64.Model << 8) | (USHORT)ProcId.Amd64.Stepping; break; }
if (g_Target->m_MachineType != IMAGE_FILE_MACHINE_I386 && SUCCEEDED(g_Target-> GetGenericProcessorFeatures(0, ProcFeatures, DIMA(ProcFeatures), &NumVals))) { C_ASSERT(sizeof(Info->OtherCpuInfo.ProcessorFeatures) <= sizeof(ProcFeatures));
if (NumVals < DIMA(ProcFeatures)) { ZeroMemory(ProcFeatures + NumVals, (DIMA(ProcFeatures) - NumVals) * sizeof(ProcFeatures[0])); }
memcpy(Info->OtherCpuInfo.ProcessorFeatures, ProcFeatures, sizeof(Info->OtherCpuInfo.ProcessorFeatures)); }
return S_OK; }
void DbgSystemProvider::GetContextSizes(OUT PULONG Size, OUT PULONG RegScanOffset, OUT PULONG RegScanCount) { *Size = g_Target->m_TypeInfo.SizeTargetContext; // Default reg scan.
*RegScanOffset = -1; *RegScanCount = -1; }
void DbgSystemProvider::GetPointerSize(OUT PULONG Size) { *Size = g_Machine->m_Ptr64 ? 8 : 4; }
void DbgSystemProvider::GetPageSize(OUT PULONG Size) { *Size = g_Machine->m_PageSize; }
void DbgSystemProvider::GetFunctionTableSizes(OUT PULONG TableSize, OUT PULONG EntrySize) { *TableSize = g_Target->m_TypeInfo.SizeDynamicFunctionTable; *EntrySize = g_Target->m_TypeInfo.SizeRuntimeFunction; }
void DbgSystemProvider::GetInstructionWindowSize(OUT PULONG Size) { // Default window.
*Size = -1; }
HRESULT DbgSystemProvider::GetOsInfo(OUT PULONG PlatformId, OUT PULONG Major, OUT PULONG Minor, OUT PULONG BuildNumber, OUT PUSHORT ProductType, OUT PUSHORT SuiteMask) { *PlatformId = g_Target->m_PlatformId; *Major = g_Target->m_KdVersion.MajorVersion; *Minor = g_Target->m_KdVersion.MinorVersion; *BuildNumber = g_Target->m_BuildNumber; *ProductType = (USHORT)g_Target->m_ProductType; *SuiteMask = (USHORT)g_Target->m_SuiteMask; return S_OK; }
HRESULT DbgSystemProvider::GetOsCsdString(OUT PWSTR Buffer, IN ULONG BufferChars) { if (!MultiByteToWideChar(CP_ACP, 0, g_Target->m_ServicePackString, -1, Buffer, BufferChars)) { return WIN32_LAST_STATUS(); }
return S_OK; }
HRESULT DbgSystemProvider::OpenMapping(IN PCWSTR FilePath, OUT PULONG Size, OUT PWSTR LongPath, IN ULONG LongPathChars, OUT PVOID* ViewRet) { // We could potentially support this via image file
// location but the minidump code is deliberately
// written to not rely to mappings.
return E_NOTIMPL; }
void DbgSystemProvider::CloseMapping(PVOID Mapping) { // No mapping support.
DBG_ASSERT(!Mapping); }
HRESULT DbgSystemProvider::GetImageHeaderInfo(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, OUT PULONG Size, OUT PULONG CheckSum, OUT PULONG TimeDateStamp) { ImageInfo* Image = ((ProcessInfo*)Process)-> FindImageByOffset(ImageBase, FALSE); if (!Image) { return E_NOINTERFACE; }
*Size = Image->m_SizeOfImage; *CheckSum = Image->m_CheckSum; *TimeDateStamp = Image->m_TimeDateStamp;
return S_OK; }
HRESULT DbgSystemProvider::GetImageVersionInfo(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, OUT VS_FIXEDFILEINFO* Info) { HRESULT Status; PSTR Ansi;
if ((Status = WideToAnsi(FilePath, &Ansi)) != S_OK) { return Status; }
Status = g_Target-> GetImageVersionInformation((ProcessInfo*)Process, Ansi, ImageBase, "\\", Info, sizeof(*Info), NULL);
FreeAnsi(Ansi); return Status; }
HRESULT DbgSystemProvider::GetImageDebugRecord(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, IN ULONG RecordType, IN OUT OPTIONAL PVOID Data, OUT PULONG DataLen) { // We can rely on the default processing.
return E_NOINTERFACE; }
HRESULT DbgSystemProvider::EnumImageDataSections(IN HANDLE Process, IN PCWSTR FilePath, IN ULONG64 ImageBase, IN MiniDumpProviderCallbacks* Callback) { // We can rely on the default processing.
return E_NOINTERFACE; }
HRESULT DbgSystemProvider::OpenThread(IN ULONG DesiredAccess, IN BOOL InheritHandle, IN ULONG ThreadId, OUT PHANDLE Handle) { // Just use the thread pointer as the "handle".
*Handle = g_Process->FindThreadBySystemId(ThreadId); return *Handle ? S_OK : E_NOINTERFACE; }
void DbgSystemProvider::CloseThread(IN HANDLE Handle) { // "Handle" is just a pointer so nothing to do.
}
ULONG DbgSystemProvider::GetCurrentThreadId(void) { // The minidump code uses the current thread ID
// to avoid suspending the thread running the dump
// code. That's not a problem for the debugger,
// so return an ID that will never match.
// SuspendThread will always be called so all
// suspend counts will be set properly.
return 0; }
ULONG DbgSystemProvider::SuspendThread(IN HANDLE Thread) { return ((ThreadInfo*)Thread)->m_SuspendCount; }
ULONG DbgSystemProvider::ResumeThread(IN HANDLE Thread) { return ((ThreadInfo*)Thread)->m_SuspendCount; }
HRESULT DbgSystemProvider::GetThreadContext(IN HANDLE Thread, OUT PVOID Context, IN ULONG ContextSize, OUT PULONG64 CurrentPc, OUT PULONG64 CurrentStack, OUT PULONG64 CurrentStore) { HRESULT Status; ADDR Addr;
g_Target->ChangeRegContext((ThreadInfo*)Thread); if ((Status = g_Machine-> GetContextState(MCTX_CONTEXT)) != S_OK || (Status = g_Machine-> ConvertContextTo(&g_Machine->m_Context, g_Target->m_SystemVersion, g_Target->m_TypeInfo.SizeTargetContext, Context)) != S_OK) { return Status; }
g_Machine->GetPC(&Addr); *CurrentPc = Flat(Addr); g_Machine->GetSP(&Addr); *CurrentStack = Flat(Addr); if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_IA64) { *CurrentStore = g_Machine->m_Context.IA64Context.RsBSP; } else { *CurrentStore = 0; }
return S_OK; }
HRESULT DbgSystemProvider::GetTeb(IN HANDLE Thread, OUT PULONG64 Offset, OUT PULONG Size) { // Always save a whole page for the TEB.
*Size = g_Machine->m_PageSize; return g_Target-> GetThreadInfoTeb((ThreadInfo*)Thread, 0, 0, Offset); }
HRESULT DbgSystemProvider::GetThreadInfo(IN HANDLE Process, IN HANDLE Thread, OUT PULONG64 Teb, OUT PULONG SizeOfTeb, OUT PULONG64 StackBase, OUT PULONG64 StackLimit, OUT PULONG64 StoreBase, OUT PULONG64 StoreLimit) { HRESULT Status; MEMORY_BASIC_INFORMATION64 MemInfo; ULONG64 MemHandle;
if ((Status = g_Target-> GetThreadInfoTeb((ThreadInfo*)Thread, 0, 0, Teb)) != S_OK) { return Status; }
//
// Try and save a whole page for the TEB. If that's
// not possible, save as much as is there.
//
MemHandle = *Teb; if ((Status = g_Target-> QueryMemoryRegion((ProcessInfo*)Process, &MemHandle, TRUE, &MemInfo)) != S_OK) { return Status; }
*SizeOfTeb = g_Machine->m_PageSize; if (*Teb + *SizeOfTeb > MemInfo.BaseAddress + MemInfo.RegionSize) { *SizeOfTeb = (ULONG) ((MemInfo.BaseAddress + MemInfo.RegionSize) - *Teb); }
//
// Read the TIB for stack and store limit information.
//
ULONG PtrSize = g_Machine->m_Ptr64 ? 8 : 4;
if ((Status = g_Target-> ReadPointer((ProcessInfo*)Process, g_Machine, *Teb + PtrSize, StackBase)) != S_OK || (Status = g_Target-> ReadPointer((ProcessInfo*)Process, g_Machine, *Teb + 2 * PtrSize, StackLimit)) != S_OK) { return Status; }
*StoreBase = 0; *StoreLimit = 0;
switch(g_Target->m_PlatformId) { case VER_PLATFORM_WIN32_WINDOWS: // Can't have a backing store.
break;
case VER_PLATFORM_WIN32_NT: if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_IA64) { #if 1
// XXX drewb - The TEB bstore values don't seem to point to
// the actual base of the backing store. Just
// assume it's contiguous with the stack.
*StoreBase = *StackBase; #else
if ((Status = g_Target-> ReadPointer((ProcessInfo*)Process, g_Machine, *Teb + IA64_TEB_BSTORE_BASE, StoreBase)) != S_OK) { return Status; } #endif
if ((Status = g_Target-> ReadPointer((ProcessInfo*)Process, g_Machine, *Teb + IA64_TEB_BSTORE_BASE + PtrSize, StoreLimit)) != S_OK) { return Status; } } break;
default: return E_INVALIDARG; }
return S_OK; }
HRESULT DbgSystemProvider::GetPeb(IN HANDLE Process, OUT PULONG64 Offset, OUT PULONG Size) { HRESULT Status; MEMORY_BASIC_INFORMATION64 MemInfo; ULONG64 MemHandle;
// The passed in process isn't very useful but we know
// that we're dumping the current state so always
// retrieve the PEB for the current thread.
if ((Status = g_Target-> GetProcessInfoPeb(g_Thread, 0, 0, Offset)) != S_OK) { return Status; }
//
// Try and save a whole page for the PEB. If that's
// not possible, save as much as is there.
//
MemHandle = *Offset; if ((Status = g_Target-> QueryMemoryRegion((ProcessInfo*)Process, &MemHandle, TRUE, &MemInfo)) != S_OK) { return Status; }
*Size = g_Machine->m_PageSize; if (*Offset + *Size > MemInfo.BaseAddress + MemInfo.RegionSize) { *Size = (ULONG) ((MemInfo.BaseAddress + MemInfo.RegionSize) - *Offset); }
return S_OK; }
HRESULT DbgSystemProvider::GetProcessTimes(IN HANDLE Process, OUT LPFILETIME Create, OUT LPFILETIME User, OUT LPFILETIME Kernel) { HRESULT Status; ULONG64 Create64, Exit64, User64, Kernel64;
if ((Status = g_Target->GetProcessTimes((ProcessInfo*)Process, &Create64, &Exit64, &Kernel64, &User64)) != S_OK) { return Status; }
Create->dwHighDateTime = (ULONG)(Create64 >> 32); Create->dwLowDateTime = (ULONG)Create64; User->dwHighDateTime = (ULONG)(User64 >> 32); User->dwLowDateTime = (ULONG)User64; Kernel->dwHighDateTime = (ULONG)(Kernel64 >> 32); Kernel->dwLowDateTime = (ULONG)Kernel64;
return S_OK; }
HRESULT DbgSystemProvider::ReadVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG Request, OUT PULONG Done) { return g_Target->ReadVirtual((ProcessInfo*)Process, Offset, Buffer, Request, Done); }
HRESULT DbgSystemProvider::ReadAllVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG Request) { return g_Target->ReadAllVirtual((ProcessInfo*)Process, Offset, Buffer, Request); }
HRESULT DbgSystemProvider::QueryVirtual(IN HANDLE Process, IN ULONG64 Offset, OUT PULONG64 Base, OUT PULONG64 Size, OUT PULONG Protect, OUT PULONG State, OUT PULONG Type) { HRESULT Status; MEMORY_BASIC_INFORMATION64 Info;
if ((Status = g_Target-> QueryMemoryRegion((ProcessInfo*)Process, &Offset, TRUE, &Info)) != S_OK) { return Status; }
*Base = Info.BaseAddress; *Size = Info.RegionSize; *Protect = Info.Protect; *State = Info.State; *Type = Info.Type;
return S_OK; }
HRESULT DbgSystemProvider::StartProcessEnum(IN HANDLE Process, IN ULONG ProcessId) { m_Thread = ((ProcessInfo*)Process)->m_ThreadHead; m_Image = ((ProcessInfo*)Process)->m_ImageHead;
// Unloaded modules aren't critical, so just
// ignore them if the enumerator fails.
m_UnlEnum = ((ProcessInfo*)Process)->m_Target-> GetUnloadedModuleInfo(); if (m_UnlEnum && m_UnlEnum->Initialize(g_Thread) != S_OK) { m_UnlEnum = NULL; }
m_FuncTableStart = 0; m_FuncTableHandle = 0;
return S_OK; }
HRESULT DbgSystemProvider::EnumThreads(OUT PULONG ThreadId) { if (!m_Thread) { return S_FALSE; }
*ThreadId = m_Thread->m_SystemId; m_Thread = m_Thread->m_Next; return S_OK; }
HRESULT DbgSystemProvider::EnumModules(OUT PULONG64 Base, OUT PWSTR Path, IN ULONG PathChars) { if (!m_Image) { return S_FALSE; }
*Base = m_Image->m_BaseOfImage;
if (!MultiByteToWideChar(CP_ACP, 0, m_Image->m_ImagePath, -1, Path, PathChars)) { return WIN32_LAST_STATUS(); }
m_Image = m_Image->m_Next; return S_OK; }
HRESULT DbgSystemProvider::EnumFunctionTables(OUT PULONG64 MinAddress, OUT PULONG64 MaxAddress, OUT PULONG64 BaseAddress, OUT PULONG EntryCount, OUT PVOID RawTable, IN ULONG RawTableSize, OUT PVOID* RawEntryHandle) { HRESULT Status; CROSS_PLATFORM_DYNAMIC_FUNCTION_TABLE CpTable;
if ((Status = g_Target-> EnumFunctionTables(g_Process, &m_FuncTableStart, &m_FuncTableHandle, MinAddress, MaxAddress, BaseAddress, EntryCount, &CpTable, RawEntryHandle)) != S_OK) { return Status; }
memcpy(RawTable, &CpTable, RawTableSize); return S_OK; }
HRESULT DbgSystemProvider::EnumFunctionTableEntries(IN PVOID RawTable, IN ULONG RawTableSize, IN PVOID RawEntryHandle, OUT PVOID RawEntries, IN ULONG RawEntriesSize) { memcpy(RawEntries, RawEntryHandle, RawEntriesSize); free(RawEntryHandle); return S_OK; }
HRESULT DbgSystemProvider::EnumFunctionTableEntryMemory(IN ULONG64 TableBase, IN PVOID RawEntries, IN ULONG Index, OUT PULONG64 Start, OUT PULONG Size) { return g_Machine->GetUnwindInfoBounds(g_Process, TableBase, RawEntries, Index, Start, Size); }
HRESULT DbgSystemProvider::EnumUnloadedModules(OUT PWSTR Path, IN ULONG PathChars, OUT PULONG64 BaseOfModule, OUT PULONG SizeOfModule, OUT PULONG CheckSum, OUT PULONG TimeDateStamp) { char UnlName[MAX_INFO_UNLOADED_NAME]; DEBUG_MODULE_PARAMETERS Params;
if (!m_UnlEnum || m_UnlEnum->GetEntry(UnlName, &Params) != S_OK) { return S_FALSE; }
if (!MultiByteToWideChar(CP_ACP, 0, UnlName, -1, Path, PathChars)) { return WIN32_LAST_STATUS(); }
*BaseOfModule = Params.Base; *SizeOfModule = Params.Size; *CheckSum = Params.Checksum; *TimeDateStamp = Params.TimeDateStamp;
return S_OK; }
void DbgSystemProvider::FinishProcessEnum(void) { // Nothing to do.
}
HRESULT DbgSystemProvider::StartHandleEnum(IN HANDLE Process, IN ULONG ProcessId, OUT PULONG Count) { m_Handle = 4;
// If the target doesn't have handle data don't make
// it a fatal error, just don't enumerate anything.
if (g_Target-> ReadHandleData((ProcessInfo*)Process, 0, DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT, Count, sizeof(*Count), NULL) != S_OK) { *Count = 0; }
return S_OK; }
HRESULT DbgSystemProvider::EnumHandles(OUT PULONG64 Handle, OUT PULONG Attributes, OUT PULONG GrantedAccess, OUT PULONG HandleCount, OUT PULONG PointerCount, OUT PWSTR TypeName, IN ULONG TypeNameChars, OUT PWSTR ObjectName, IN ULONG ObjectNameChars) { DEBUG_HANDLE_DATA_BASIC BasicInfo;
for (;;) { if (m_Handle >= (1 << 24)) { return S_FALSE; }
// If we can't get the basic info and type there isn't much
// point in writing anything out so skip the handle.
if (g_Target-> ReadHandleData(g_Process, m_Handle, DEBUG_HANDLE_DATA_TYPE_BASIC, &BasicInfo, sizeof(BasicInfo), NULL) == S_OK && SUCCEEDED(g_Target-> ReadHandleData(g_Process, m_Handle, DEBUG_HANDLE_DATA_TYPE_TYPE_NAME_WIDE, TypeName, TypeNameChars * sizeof(*TypeName), NULL))) { break; }
m_Handle += 4; }
// Try and get the object name.
if (FAILED(g_Target-> ReadHandleData(g_Process, m_Handle, DEBUG_HANDLE_DATA_TYPE_OBJECT_NAME_WIDE, ObjectName, ObjectNameChars * sizeof(*ObjectName), NULL))) { *ObjectName = 0; }
*Handle = m_Handle; *Attributes = BasicInfo.Attributes; *GrantedAccess = BasicInfo.GrantedAccess; *HandleCount = BasicInfo.HandleCount; *PointerCount = BasicInfo.PointerCount;
m_Handle += 4; return S_OK; }
void DbgSystemProvider::FinishHandleEnum(void) { // Nothing to do.
}
HRESULT DbgSystemProvider::EnumPebMemory(IN HANDLE Process, IN ULONG64 PebOffset, IN ULONG PebSize, IN MiniDumpProviderCallbacks* Callback) { if (g_Target->m_SystemVersion <= NT_SVER_START || g_Target->m_SystemVersion >= NT_SVER_END) { // Basic Win32 doesn't have a defined PEB.
return S_OK; }
// XXX drewb - This requires a whole set of constants
// to abstract data structure locations. Leave it
// for when we really need it.
return S_OK; }
HRESULT DbgSystemProvider::EnumTebMemory(IN HANDLE Process, IN HANDLE Thread, IN ULONG64 TebOffset, IN ULONG TebSize, IN MiniDumpProviderCallbacks* Callback) { if (g_Target->m_SystemVersion <= NT_SVER_START || g_Target->m_SystemVersion >= NT_SVER_END) { // Basic Win32 doesn't have a defined TEB beyond
// the TIB. The TIB can reference fiber data but
// that's NT-specific.
return S_OK; }
// XXX drewb - This requires a whole set of constants
// to abstract data structure locations. Leave it
// for when we really need it.
return S_OK; }
HRESULT DbgSystemProvider::GetCorDataAccess(IN PWSTR AccessDllName, IN struct ICorDataAccessServices* Services, OUT struct ICorDataAccess** Access) { HRESULT Status;
// We're providing all of the system services to
// the minidump code so we know that its state
// matches what's available directly from the debugger's
// state. Just ignore the given DLL name and
// service interface in favor of the one the
// debugger already has.
if ((Status = g_Process->LoadCorDebugDll()) != S_OK) { return Status; }
*Access = g_Process->m_CorAccess; return S_OK; }
void DbgSystemProvider::ReleaseCorDataAccess(IN struct ICorDataAccess* Access) { // No work necessary.
}
PMINIDUMP_EXCEPTION_INFORMATION64 CreateMiniExceptionInformation(PMINIDUMP_EXCEPTION_INFORMATION64 ExInfo, PEXCEPTION_RECORD ExRecord, PCROSS_PLATFORM_CONTEXT Context) { // If the last event was an exception put together
// exception information in minidump format.
if (g_LastEventType != DEBUG_EVENT_EXCEPTION || g_Process != g_EventProcess) { return NULL; }
// Get the full context for the event thread.
g_Target->ChangeRegContext(g_EventThread); if (g_Machine->GetContextState(MCTX_CONTEXT) != S_OK) { return NULL; } *Context = g_Machine->m_Context;
ExInfo->ThreadId = g_EventThreadSysId; ExInfo->ClientPointers = FALSE; ExInfo->ExceptionRecord = (LONG_PTR)ExRecord; ExceptionRecord64To(&g_LastEventInfo.Exception.ExceptionRecord, ExRecord); ExInfo->ContextRecord = (LONG_PTR)Context;
return ExInfo; }
BOOL WINAPI MicroDumpCallback( IN PVOID CallbackParam, IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput, IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput ) { switch(CallbackInput->CallbackType) { case IncludeModuleCallback: // Mask off all flags other than the basic write flag.
CallbackOutput->ModuleWriteFlags &= ModuleWriteModule; break; case ModuleCallback: // Eliminate all unreferenced modules.
if (!(CallbackOutput->ModuleWriteFlags & ModuleReferencedByMemory)) { CallbackOutput->ModuleWriteFlags = 0; } break; case IncludeThreadCallback: if (CallbackInput->IncludeThread.ThreadId != g_EventThreadSysId) { return FALSE; }
// Reduce write to the minimum of information
// necessary for a stack walk.
CallbackOutput->ThreadWriteFlags &= ~ThreadWriteInstructionWindow; break; }
return TRUE; }
HRESULT UserMiniDumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { if (!IS_USER_TARGET(g_Target)) { ErrOut("User minidumps can only be written in user-mode sessions\n"); return E_UNEXPECTED; }
dprintf("mini user dump\n"); FlushCallbacks();
HRESULT Status; MINIDUMP_EXCEPTION_INFORMATION64 ExInfoBuf, *ExInfo; EXCEPTION_RECORD ExRecord; CROSS_PLATFORM_CONTEXT Context; ULONG MiniType; MINIDUMP_USER_STREAM UserStreams[2]; MINIDUMP_USER_STREAM_INFORMATION UserStreamInfo; MINIDUMP_CALLBACK_INFORMATION CallbackBuffer; PMINIDUMP_CALLBACK_INFORMATION Callback;
MiniType = MiniDumpNormal; if (FormatFlags & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) { MiniType |= MiniDumpWithFullMemory; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_HANDLE_DATA) { MiniType |= MiniDumpWithHandleData; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES) { MiniType |= MiniDumpWithUnloadedModules; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY) { MiniType |= MiniDumpWithIndirectlyReferencedMemory; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS) { MiniType |= MiniDumpWithDataSegs; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY) { MiniType |= MiniDumpFilterMemory; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_FILTER_PATHS) { MiniType |= MiniDumpFilterModulePaths; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA) { MiniType |= MiniDumpWithProcessThreadData; } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY) { MiniType |= MiniDumpWithPrivateReadWriteMemory; }
UserStreamInfo.UserStreamCount = 0; UserStreamInfo.UserStreamArray = UserStreams; if (CommentA != NULL) { UserStreams[UserStreamInfo.UserStreamCount].Type = CommentStreamA; UserStreams[UserStreamInfo.UserStreamCount].BufferSize = strlen(CommentA) + 1; UserStreams[UserStreamInfo.UserStreamCount].Buffer = (PVOID)CommentA; UserStreamInfo.UserStreamCount++; } if (CommentW != NULL) { UserStreams[UserStreamInfo.UserStreamCount].Type = CommentStreamW; UserStreams[UserStreamInfo.UserStreamCount].BufferSize = (wcslen(CommentW) + 1) * sizeof(WCHAR); UserStreams[UserStreamInfo.UserStreamCount].Buffer = (PVOID)CommentW; UserStreamInfo.UserStreamCount++; }
ExInfo = CreateMiniExceptionInformation(&ExInfoBuf, &ExRecord, &Context);
if (FormatFlags & FORMAT_USER_MICRO) { // This case isn't expected to be used by users,
// it's for testing of the microdump support.
Callback = &CallbackBuffer; Callback->CallbackRoutine = MicroDumpCallback; Callback->CallbackParam = NULL; ExInfo = NULL; MiniType |= MiniDumpScanMemory; } else { Callback = NULL; }
HANDLE ProcHandle; MiniDumpSystemProvider* SysProv = NULL; MiniDumpOutputProvider* OutProv = NULL; MiniDumpAllocationProvider* AllocProv = NULL;
if ((Status = MiniDumpCreateFileOutputProvider(hFile, &OutProv)) != S_OK || (Status = MiniDumpCreateLiveAllocationProvider(&AllocProv)) != S_OK) { goto Exit; }
//
// If we're live we can let the official minidump
// code do all the work. If not, hook up a provider
// that uses debugger information. This provider
// could always be used but the default live-system
// provider offers slightly more information so
// check and use that if possible.
//
if (IS_LIVE_USER_TARGET(g_Target) && ((LiveUserTargetInfo*)g_Target)->m_Local) { if ((Status = MiniDumpCreateLiveSystemProvider(&SysProv)) != S_OK) { goto Exit; }
ProcHandle = OS_HANDLE(g_Process->m_SysHandle); } else { DbgSystemProvider* DbgSysProv = new DbgSystemProvider; if (!DbgSysProv) { Status = E_OUTOFMEMORY; goto Exit; }
SysProv = DbgSysProv; ProcHandle = (HANDLE)g_Process; }
Status = MiniDumpProvideDump(ProcHandle, g_Process->m_SystemId, SysProv, OutProv, AllocProv, MiniType, ExInfo, &UserStreamInfo, Callback);
Exit:
if (Status != S_OK) { ErrOut("Dump creation failed, %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatus(Status)); }
if (SysProv) { SysProv->Release(); } if (OutProv) { OutProv->Release(); } if (AllocProv) { AllocProv->Release(); }
// Reset the current register context in case
// it was changed at some point.
g_Target->ChangeRegContext(g_Thread);
return Status; }
//-------------------------------------------------------------------
// initialize the dump headers
//
#define MINIDUMP_BUGCHECK 0x10000000
void KernelDumpTargetInfo::InitDumpHeader32( PDUMP_HEADER32 pdh, PCSTR CommentA, PCWSTR CommentW, ULONG BugCheckCodeModifier ) { ULONG64 Data[4]; PULONG FillPtr = (PULONG)pdh;
while (FillPtr < (PULONG)(pdh + 1)) { *FillPtr++ = DUMP_SIGNATURE32; }
pdh->Signature = DUMP_SIGNATURE32; pdh->ValidDump = DUMP_VALID_DUMP32; pdh->MajorVersion = g_Target->m_KdVersion.MajorVersion; pdh->MinorVersion = g_Target->m_KdVersion.MinorVersion;
g_Target->ReadDirectoryTableBase(Data); pdh->DirectoryTableBase = (ULONG)Data[0];
pdh->PfnDataBase = (ULONG)g_Target->m_KdDebuggerData.MmPfnDatabase; pdh->PsLoadedModuleList = (ULONG)g_Target->m_KdDebuggerData.PsLoadedModuleList; pdh->PsActiveProcessHead = (ULONG)g_Target->m_KdDebuggerData.PsActiveProcessHead; pdh->MachineImageType = g_Target->m_KdVersion.MachineType; pdh->NumberProcessors = g_Target->m_NumProcessors;
g_Target->ReadBugCheckData(&(pdh->BugCheckCode), Data); pdh->BugCheckCode |= BugCheckCodeModifier; pdh->BugCheckParameter1 = (ULONG)Data[0]; pdh->BugCheckParameter2 = (ULONG)Data[1]; pdh->BugCheckParameter3 = (ULONG)Data[2]; pdh->BugCheckParameter4 = (ULONG)Data[3];
//pdh->VersionUser = 0;
pdh->PaeEnabled = g_Target->m_KdDebuggerData.PaeEnabled; pdh->KdDebuggerDataBlock = (ULONG)g_Target->m_KdDebuggerDataOffset;
// pdh->PhysicalMemoryBlock =;
g_Machine->GetContextState(MCTX_CONTEXT); g_Machine->ConvertContextTo(&g_Machine->m_Context, g_Target->m_SystemVersion, sizeof(pdh->ContextRecord), pdh->ContextRecord);
if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last event.
ExceptionRecord64To32(&g_LastEventInfo.Exception.ExceptionRecord, &pdh->Exception); } else { ADDR PcAddr;
// Fake a breakpoint exception.
ZeroMemory(&pdh->Exception, sizeof(pdh->Exception)); pdh->Exception.ExceptionCode = STATUS_BREAKPOINT; g_Machine->GetPC(&PcAddr); pdh->Exception.ExceptionAddress = (ULONG)Flat(PcAddr); }
pdh->RequiredDumpSpace.QuadPart = TRIAGE_DUMP_SIZE32;
pdh->SystemTime.QuadPart = g_Target->GetCurrentTimeDateN(); pdh->SystemUpTime.QuadPart = g_Target->GetCurrentSystemUpTimeN();
if (g_Target->m_ProductType != INVALID_PRODUCT_TYPE) { pdh->ProductType = g_Target->m_ProductType; pdh->SuiteMask = g_Target->m_SuiteMask; }
PSTR ConvComment = NULL;
if (!CommentA && CommentW) { if (WideToAnsi(CommentW, &ConvComment) != S_OK) { ConvComment = NULL; } else { CommentA = ConvComment; } } if (CommentA != NULL && CommentA[0]) { CopyString(pdh->Comment, CommentA, DIMA(pdh->Comment)); } FreeAnsi(ConvComment); }
void KernelDumpTargetInfo::InitDumpHeader64( PDUMP_HEADER64 pdh, PCSTR CommentA, PCWSTR CommentW, ULONG BugCheckCodeModifier ) { ULONG64 Data[4]; PULONG FillPtr = (PULONG)pdh;
while (FillPtr < (PULONG)(pdh + 1)) { *FillPtr++ = DUMP_SIGNATURE32; }
pdh->Signature = DUMP_SIGNATURE64; pdh->ValidDump = DUMP_VALID_DUMP64; pdh->MajorVersion = g_Target->m_KdVersion.MajorVersion; pdh->MinorVersion = g_Target->m_KdVersion.MinorVersion;
// IA64 has several page directories. The defined
// behavior is to put the kernel page directory
// in the dump header as that's the one that can
// be most useful when first initializing the dump.
if (g_Target->m_EffMachineType == IMAGE_FILE_MACHINE_IA64) { ULONG Next;
if (g_Machine-> SetPageDirectory(g_Thread, PAGE_DIR_KERNEL, 0, &Next) != S_OK) { ErrOut("Unable to update the kernel dirbase\n"); Data[0] = 0; } else { Data[0] = g_Machine->m_PageDirectories[PAGE_DIR_KERNEL]; } } else { g_Target->ReadDirectoryTableBase(Data); } pdh->DirectoryTableBase = Data[0];
pdh->PfnDataBase = g_Target->m_KdDebuggerData.MmPfnDatabase; pdh->PsLoadedModuleList = g_Target->m_KdDebuggerData.PsLoadedModuleList; pdh->PsActiveProcessHead = g_Target->m_KdDebuggerData.PsActiveProcessHead; pdh->MachineImageType = g_Target->m_KdVersion.MachineType; pdh->NumberProcessors = g_Target->m_NumProcessors;
g_Target->ReadBugCheckData(&(pdh->BugCheckCode), Data); pdh->BugCheckCode |= BugCheckCodeModifier; pdh->BugCheckParameter1 = Data[0]; pdh->BugCheckParameter2 = Data[1]; pdh->BugCheckParameter3 = Data[2]; pdh->BugCheckParameter4 = Data[3];
//pdh->VersionUser = 0;
pdh->KdDebuggerDataBlock = g_Target->m_KdDebuggerDataOffset;
// pdh->PhysicalMemoryBlock =;
g_Machine->GetContextState(MCTX_CONTEXT); g_Machine->ConvertContextTo(&g_Machine->m_Context, g_Target->m_SystemVersion, sizeof(pdh->ContextRecord), pdh->ContextRecord);
if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last event.
pdh->Exception = g_LastEventInfo.Exception.ExceptionRecord; } else { ADDR PcAddr;
// Fake a breakpoint exception.
ZeroMemory(&pdh->Exception, sizeof(pdh->Exception)); pdh->Exception.ExceptionCode = STATUS_BREAKPOINT; g_Machine->GetPC(&PcAddr); pdh->Exception.ExceptionAddress = Flat(PcAddr); }
pdh->RequiredDumpSpace.QuadPart = TRIAGE_DUMP_SIZE64;
pdh->SystemTime.QuadPart = g_Target->GetCurrentTimeDateN(); pdh->SystemUpTime.QuadPart = g_Target->GetCurrentSystemUpTimeN();
if (g_Target->m_ProductType != INVALID_PRODUCT_TYPE) { pdh->ProductType = g_Target->m_ProductType; pdh->SuiteMask = g_Target->m_SuiteMask; }
PSTR ConvComment = NULL;
if (!CommentA && CommentW) { if (WideToAnsi(CommentW, &ConvComment) != S_OK) { ConvComment = NULL; } else { CommentA = ConvComment; } } if (CommentA != NULL && CommentA[0]) { CopyString(pdh->Comment, CommentA, DIMA(pdh->Comment)); } FreeAnsi(ConvComment); }
//----------------------------------------------------------------------------
//
// KernelFull64DumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
HRESULT KernelFull64DumpTargetInfo::Write(HANDLE File, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { PDUMP_HEADER64 DumpHeader64; HRESULT Status; ULONG64 Offset; PPHYSICAL_MEMORY_DESCRIPTOR64 PhysMem = NULL; DWORD i, j; PUCHAR PageBuffer = NULL; DWORD BytesRead; DWORD BytesWritten; DWORD Percent; ULONG64 CurrentPagesWritten; DbgKdTransport* KdTrans;
DumpHeader64 = (PDUMP_HEADER64) LocalAlloc(LPTR, sizeof(DUMP_HEADER64)); if (DumpHeader64 == NULL) { ErrOut("Failed to allocate dump header buffer\n"); return E_OUTOFMEMORY; }
if (!IS_REMOTE_KERNEL_TARGET(g_Target) && !IS_KERNEL_FULL_DUMP(g_Target)) { ErrOut("\nKernel full dumps can only be written when all of physical " "memory is accessible - aborting now\n"); return E_INVALIDARG; }
if (IS_CONN_KERNEL_TARGET(g_Target)) { KdTrans = ((ConnLiveKernelTargetInfo*)g_Target)->m_Transport; } else { KdTrans = NULL; }
dprintf("Full kernel dump\n"); FlushCallbacks();
KernelDumpTargetInfo::InitDumpHeader64(DumpHeader64, CommentA, CommentW, 0); DumpHeader64->DumpType = DUMP_TYPE_FULL; DumpHeader64->WriterStatus = DUMP_DBGENG_SUCCESS;
//
// now copy the memory descriptor list to our header..
// first get the pointer va
//
Status = g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData. MmPhysicalMemoryBlock, &Offset); if (Status != S_OK || (Offset == 0)) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); Status = Status != S_OK ? Status : E_INVALIDARG; } else { //
// first read the memory descriptor size...
//
PhysMem = &DumpHeader64->PhysicalMemoryBlock; Status = g_Target-> ReadVirtual(g_Process, Offset, PhysMem, DMP_PHYSICAL_MEMORY_BLOCK_SIZE_64, &BytesRead); if (Status != S_OK || BytesRead < sizeof(*PhysMem) + (sizeof(PhysMem->Run[0]) * (PhysMem->NumberOfRuns - 1))) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); Status = Status != S_OK ? Status : E_INVALIDARG; } else { //
// calculate total dump file size
//
DumpHeader64->RequiredDumpSpace.QuadPart = DumpHeader64->PhysicalMemoryBlock.NumberOfPages * g_Machine->m_PageSize;
//
// write dump header to crash dump file
//
if (!WriteFile(File, DumpHeader64, sizeof(DUMP_HEADER64), &BytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } } }
if (Status == S_OK) { PageBuffer = (PUCHAR) LocalAlloc(LPTR, g_Machine->m_PageSize); if (PageBuffer == NULL) { ErrOut("Failed to allocate double buffer\n"); } else { //
// now write the physical memory out to disk.
// we use the dump header to retrieve the physical memory base and
// run count then ask the transport to gecth these pages form the
// target. On 1394, the virtual debugger driver will do physical
// address reads on the remote host since there is a onoe-to-one
// relationships between physical 1394 and physical mem addresses.
//
CurrentPagesWritten = 0; Percent = 0;
for (i = 0; i < PhysMem->NumberOfRuns; i++) { Offset = 0; Offset = PhysMem->Run[i].BasePage*g_Machine->m_PageSize;
if (CheckUserInterrupt()) { ErrOut("Creation of crashdump file interrupted\n"); Status = E_ABORT; break; }
for (j = 0; j< PhysMem->Run[i].PageCount; j++) { //
// printout the percent done every 5% increments
//
if ((CurrentPagesWritten*100) / PhysMem->NumberOfPages == Percent) { dprintf("Percent written %d \n", Percent); FlushCallbacks(); if (KdTrans && KdTrans->m_DirectPhysicalMemory) { Percent += 5; } else { Percent += 1; } }
if (KdTrans && KdTrans->m_DirectPhysicalMemory) { Status = KdTrans-> ReadTargetPhysicalMemory(Offset, PageBuffer, g_Machine->m_PageSize, &BytesWritten); } else { Status = g_Target->ReadPhysical(Offset, PageBuffer, g_Machine->m_PageSize, PHYS_FLAG_DEFAULT, &BytesWritten); }
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT) { break; } else if (Status != S_OK) { ErrOut("Failed Reading page for crashdump file\n"); break; } else { //
// now write the page to the local crashdump file
//
if (!WriteFile(File, PageBuffer, g_Machine->m_PageSize, &BytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing header to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); break; }
Offset += g_Machine->m_PageSize; CurrentPagesWritten++; } } }
if (Status == S_OK) { dprintf("CrashDump write complete\n"); } LocalFree(PageBuffer); } }
LocalFree(DumpHeader64);
return Status; }
//----------------------------------------------------------------------------
//
// KernelFull32DumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
HRESULT KernelFull32DumpTargetInfo::Write(HANDLE File, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { PDUMP_HEADER32 DumpHeader32 = NULL; HRESULT Status; ULONG64 Offset; PPHYSICAL_MEMORY_DESCRIPTOR32 PhysMem = NULL; DWORD i, j; PUCHAR PageBuffer = NULL; DWORD BytesRead; DWORD BytesWritten; DWORD Percent; ULONG CurrentPagesWritten; DbgKdTransport* KdTrans;
DumpHeader32 = (PDUMP_HEADER32) LocalAlloc(LPTR, sizeof(DUMP_HEADER32)); if (DumpHeader32 == NULL) { ErrOut("Failed to allocate dump header buffer\n"); return E_OUTOFMEMORY; }
if (!IS_REMOTE_KERNEL_TARGET(g_Target) && !IS_KERNEL_FULL_DUMP(g_Target)) { ErrOut("\nKernel full dumps can only be written when all of physical " "memory is accessible - aborting now\n"); return E_INVALIDARG; }
if (IS_CONN_KERNEL_TARGET(g_Target)) { KdTrans = ((ConnLiveKernelTargetInfo*)g_Target)->m_Transport; } else { KdTrans = NULL; }
dprintf("Full kernel dump\n"); FlushCallbacks();
//
// Build the header
//
KernelDumpTargetInfo::InitDumpHeader32(DumpHeader32, CommentA, CommentW, 0); DumpHeader32->DumpType = DUMP_TYPE_FULL; DumpHeader32->WriterStatus = DUMP_DBGENG_SUCCESS;
//
// now copy the memory descriptor list to our header..
// first get the pointer va
//
Status = g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData. MmPhysicalMemoryBlock, &Offset); if (Status != S_OK || (Offset == 0)) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); Status = Status != S_OK ? Status : E_INVALIDARG; } else { //
// first read the memory descriptor size...
//
PhysMem = &DumpHeader32->PhysicalMemoryBlock; Status = g_Target-> ReadVirtual(g_Process, Offset, PhysMem, DMP_PHYSICAL_MEMORY_BLOCK_SIZE_32, &BytesRead); if (Status != S_OK || BytesRead < sizeof(*PhysMem) + (sizeof(PhysMem->Run[0]) * (PhysMem->NumberOfRuns - 1))) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); Status = Status != S_OK ? Status : E_INVALIDARG; } else { //
// calculate total dump file size
//
DumpHeader32->RequiredDumpSpace.QuadPart = DumpHeader32->PhysicalMemoryBlock.NumberOfPages * g_Machine->m_PageSize;
//
// write dump header to crash dump file
//
if (!WriteFile(File, DumpHeader32, sizeof(DUMP_HEADER32), &BytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } } }
if (Status == S_OK) { PageBuffer = (PUCHAR) LocalAlloc(LPTR, g_Machine->m_PageSize); if (PageBuffer == NULL) { ErrOut("Failed to allocate double buffer\n"); } else { //
// now write the physical memory out to disk.
// we use the dump header to retrieve the physical memory base and
// run count then ask the transport to gecth these pages form the
// target. On 1394, the virtual debugger driver will do physical
// address reads on the remote host since there is a onoe-to-one
// relationships between physical 1394 and physical mem addresses.
//
CurrentPagesWritten = 0; Percent = 0;
for (i = 0; i < PhysMem->NumberOfRuns; i++) { Offset = 0; Offset = PhysMem->Run[i].BasePage*g_Machine->m_PageSize;
if (CheckUserInterrupt()) { ErrOut("Creation of crashdump file interrupted\n"); Status = E_ABORT; break; }
for (j = 0; j< PhysMem->Run[i].PageCount; j++) { //
// printout the percent done every 5% increments
//
if ((CurrentPagesWritten*100) / PhysMem->NumberOfPages == Percent) { dprintf("Percent written %d \n", Percent); FlushCallbacks(); if (KdTrans && KdTrans->m_DirectPhysicalMemory) { Percent += 5; } else { Percent += 1; } }
if (KdTrans && KdTrans->m_DirectPhysicalMemory) { Status = KdTrans-> ReadTargetPhysicalMemory(Offset, PageBuffer, g_Machine->m_PageSize, &BytesWritten); } else { Status = g_Target->ReadPhysical(Offset, PageBuffer, g_Machine->m_PageSize, PHYS_FLAG_DEFAULT, &BytesWritten); }
if (g_EngStatus & ENG_STATUS_USER_INTERRUPT) { break; } else if (Status != S_OK) { ErrOut("Failed Reading page for crashdump file\n"); break; } else { //
// now write the page to the local crashdump file
//
if (!WriteFile(File, PageBuffer, g_Machine->m_PageSize, &BytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing header to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); break; }
Offset += g_Machine->m_PageSize; CurrentPagesWritten++; } } }
if (Status == S_OK) { dprintf("CrashDump write complete\n"); } LocalFree(PageBuffer); } }
LocalFree(DumpHeader32);
return Status; }
enum { GNME_ENTRY, GNME_DONE, GNME_NO_NAME, GNME_CORRUPT, };
DWORD GetNextModuleEntry( ModuleInfo *ModIter, MODULE_INFO_ENTRY *ModEntry ) { HRESULT Status; ZeroMemory(ModEntry, sizeof(MODULE_INFO_ENTRY));
if ((Status = ModIter->GetEntry(ModEntry)) != S_OK) { return Status == S_FALSE ? GNME_DONE : GNME_CORRUPT; }
if (ModEntry->NameLength > (MAX_IMAGE_PATH - 1) * (ModEntry->UnicodeNamePtr ? sizeof(WCHAR) : sizeof(CHAR))) { ErrOut("Module list is corrupt."); if (IS_KERNEL_TARGET(g_Target)) { ErrOut(" Check your kernel symbols.\n"); } else { ErrOut(" Loader list may be invalid\n"); } return GNME_CORRUPT; }
// If this entry has no name just skip it.
if (!ModEntry->NamePtr || !ModEntry->NameLength) { ErrOut(" Module List has empty entry in it - skipping\n"); return GNME_NO_NAME; }
// If the image header information couldn't be read
// we end up with placeholder values for certain entries.
// The kernel writes out zeroes in this case so copy
// its behavior so that there's one consistent value
// for unknown.
if (ModEntry->CheckSum == UNKNOWN_CHECKSUM) { ModEntry->CheckSum = 0; } if (ModEntry->TimeDateStamp == UNKNOWN_TIMESTAMP) { ModEntry->TimeDateStamp = 0; }
return GNME_ENTRY; }
//----------------------------------------------------------------------------
//
// Shared triage writing things.
//
//----------------------------------------------------------------------------
#define ExtractValue(NAME, val) { \
if (!g_Target->m_KdDebuggerData.NAME) { \ val = 0; \ ErrOut("KdDebuggerData." #NAME " is NULL\n"); \ } else { \ g_Target->ReadAllVirtual(g_Process, \ g_Target->m_KdDebuggerData.NAME, &(val), \ sizeof(val)); \ } \ }
inline ALIGN_8(unsigned Offset) { return (Offset + 7) & 0xfffffff8; }
const unsigned MAX_TRIAGE_STACK_SIZE32 = 16 * 1024; const unsigned MAX_TRIAGE_STACK_SIZE64 = 32 * 1024; const unsigned MAX_TRIAGE_BSTORE_SIZE = 16 * 4096; // as defined in ntia64.h
const ULONG TRIAGE_DRIVER_NAME_SIZE_GUESS = 0x40;
typedef struct _TRIAGE_PTR_DATA_BLOCK { ULONG64 MinAddress; ULONG64 MaxAddress; } TRIAGE_PTR_DATA_BLOCK, *PTRIAGE_PTR_DATA_BLOCK;
// A triage dump is sixteen pages long. Some of that is
// header information and at least a few other pages will
// be used for basic dump information so limit the number
// of extra data blocks to something less than sixteen
// to save array space.
#define IO_MAX_TRIAGE_DUMP_DATA_BLOCKS 8
ULONG IopNumTriageDumpDataBlocks; TRIAGE_PTR_DATA_BLOCK IopTriageDumpDataBlocks[IO_MAX_TRIAGE_DUMP_DATA_BLOCKS];
//
// If space is available in a triage dump it's possible
// to add "interesting" data pages referenced by runtime
// information such as context registers. The following
// lists are offsets into the CONTEXT structure of pointers
// which usually point to interesting data. They are
// in priority order.
//
#define IOP_LAST_CONTEXT_OFFSET 0xffff
USHORT IopRunTimeContextOffsetsX86[] = { FIELD_OFFSET(X86_NT5_CONTEXT, Ebx), FIELD_OFFSET(X86_NT5_CONTEXT, Esi), FIELD_OFFSET(X86_NT5_CONTEXT, Edi), FIELD_OFFSET(X86_NT5_CONTEXT, Ecx), FIELD_OFFSET(X86_NT5_CONTEXT, Edx), FIELD_OFFSET(X86_NT5_CONTEXT, Eax), FIELD_OFFSET(X86_NT5_CONTEXT, Eip), IOP_LAST_CONTEXT_OFFSET };
USHORT IopRunTimeContextOffsetsIa64[] = { FIELD_OFFSET(IA64_CONTEXT, IntS0), FIELD_OFFSET(IA64_CONTEXT, IntS1), FIELD_OFFSET(IA64_CONTEXT, IntS2), FIELD_OFFSET(IA64_CONTEXT, IntS3), FIELD_OFFSET(IA64_CONTEXT, StIIP), IOP_LAST_CONTEXT_OFFSET };
USHORT IopRunTimeContextOffsetsAmd64[] = { FIELD_OFFSET(AMD64_CONTEXT, Rbx), FIELD_OFFSET(AMD64_CONTEXT, Rsi), FIELD_OFFSET(AMD64_CONTEXT, Rdi), FIELD_OFFSET(AMD64_CONTEXT, Rcx), FIELD_OFFSET(AMD64_CONTEXT, Rdx), FIELD_OFFSET(AMD64_CONTEXT, Rax), FIELD_OFFSET(AMD64_CONTEXT, Rip), IOP_LAST_CONTEXT_OFFSET };
USHORT IopRunTimeContextOffsetsEmpty[] = { IOP_LAST_CONTEXT_OFFSET };
BOOLEAN IopIsAddressRangeValid( IN ULONG64 VirtualAddress, IN ULONG Length ) { VirtualAddress = PAGE_ALIGN(g_Machine, VirtualAddress); Length = (Length + g_Machine->m_PageSize - 1) >> g_Machine->m_PageShift; while (Length > 0) { UCHAR Data;
if (CurReadAllVirtual(VirtualAddress, &Data, sizeof(Data)) != S_OK) { return FALSE; }
VirtualAddress += g_Machine->m_PageSize; Length--; }
return TRUE; }
BOOLEAN IoAddTriageDumpDataBlock( IN ULONG64 Address, IN ULONG Length ) { ULONG i; PTRIAGE_PTR_DATA_BLOCK Block; ULONG64 MinAddress, MaxAddress;
// Check against SIZE32 for both 32 and 64-bit dumps
// as no data block needs to be larger than that.
if (Length >= TRIAGE_DUMP_SIZE32 || !IopIsAddressRangeValid(Address, Length)) { return FALSE; }
MinAddress = Address; MaxAddress = MinAddress + Length;
//
// Minimize overlap between the new block and existing blocks.
// Blocks cannot simply be merged as blocks are inserted in
// priority order for storage in the dump. Combining a low-priority
// block with a high-priority block could lead to a medium-
// priority block being bumped improperly from the dump.
//
Block = IopTriageDumpDataBlocks; for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) { if (MinAddress >= Block->MaxAddress || MaxAddress <= Block->MinAddress) { // No overlap.
continue; }
//
// Trim overlap out of the new block. If this
// would split the new block into pieces don't
// trim to keep things simple. Content may then
// be duplicated in the dump.
//
if (MinAddress >= Block->MinAddress) { if (MaxAddress <= Block->MaxAddress) { // New block is completely contained.
return TRUE; }
// New block extends above the current block
// so trim off the low-range overlap.
MinAddress = Block->MaxAddress; } else if (MaxAddress <= Block->MaxAddress) { // New block extends below the current block
// so trim off the high-range overlap.
MaxAddress = Block->MinAddress; } }
if (IopNumTriageDumpDataBlocks >= IO_MAX_TRIAGE_DUMP_DATA_BLOCKS) { return FALSE; }
Block = IopTriageDumpDataBlocks + IopNumTriageDumpDataBlocks++; Block->MinAddress = MinAddress; Block->MaxAddress = MaxAddress;
return TRUE; }
VOID IopAddRunTimeTriageDataBlocks( IN PCROSS_PLATFORM_CONTEXT Context, IN ULONG64 StackMin, IN ULONG64 StackMax, IN ULONG64 StoreMin, IN ULONG64 StoreMax ) { PUSHORT ContextOffset;
switch(g_Target->m_MachineType) { case IMAGE_FILE_MACHINE_I386: ContextOffset = IopRunTimeContextOffsetsX86; break; case IMAGE_FILE_MACHINE_IA64: ContextOffset = IopRunTimeContextOffsetsIa64; break; case IMAGE_FILE_MACHINE_AMD64: ContextOffset = IopRunTimeContextOffsetsAmd64; break; default: ContextOffset = IopRunTimeContextOffsetsEmpty; break; }
while (*ContextOffset < IOP_LAST_CONTEXT_OFFSET) { ULONG64 Ptr;
//
// Retrieve possible pointers from the context
// registers.
//
if (g_Machine->m_Ptr64) { Ptr = *(PULONG64)((PUCHAR)Context + *ContextOffset); } else { Ptr = EXTEND64(*(PULONG)((PUCHAR)Context + *ContextOffset)); }
// Stack and backing store memory is already saved
// so ignore any pointers that fall into those ranges.
if ((Ptr < StackMin || Ptr >= StackMax) && (Ptr < StoreMin || Ptr >= StoreMax)) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, Ptr), g_Machine->m_PageSize); }
ContextOffset++; } }
void AddInMemoryTriageDataBlocks(void) { //
// Look at the global data for nt!IopTriageDumpDataBlocks
// and include the same data blocks so that dump conversion
// preserves data blocks.
//
// If we don't know where IopTriageDumpDataBlocks is then
// we don't have anything to do.
if (!g_Target->m_KdDebuggerData.IopNumTriageDumpDataBlocks || !g_Target->m_KdDebuggerData.IopTriageDumpDataBlocks) { return; }
ULONG NumBlocks;
if (g_Target-> ReadAllVirtual(g_Process, g_Target-> m_KdDebuggerData.IopNumTriageDumpDataBlocks, &NumBlocks, sizeof(NumBlocks)) != S_OK) { return; }
if (NumBlocks > IO_MAX_TRIAGE_DUMP_DATA_BLOCKS) { NumBlocks = IO_MAX_TRIAGE_DUMP_DATA_BLOCKS; } ULONG64 BlockDescOffs = g_Target->m_KdDebuggerData.IopTriageDumpDataBlocks; TRIAGE_PTR_DATA_BLOCK BlockDesc; ULONG i; ULONG PtrSize = g_Machine->m_Ptr64 ? 8 : 4;
for (i = 0; i < NumBlocks; i++) { if (g_Target->ReadPointer(g_Process, g_Machine, BlockDescOffs, &BlockDesc.MinAddress) != S_OK || g_Target->ReadPointer(g_Process, g_Machine, BlockDescOffs + PtrSize, &BlockDesc.MaxAddress) != S_OK) { return; }
BlockDescOffs += 2 * PtrSize;
IoAddTriageDumpDataBlock(BlockDesc.MinAddress, (LONG)(BlockDesc.MaxAddress - BlockDesc.MinAddress)); } }
ULONG IopSizeTriageDumpDataBlocks( ULONG Offset, ULONG BufferSize, PULONG StartOffset, PULONG Count ) { ULONG i; ULONG Size; PTRIAGE_PTR_DATA_BLOCK Block;
*Count = 0;
Block = IopTriageDumpDataBlocks; for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) { Size = ALIGN_8(sizeof(TRIAGE_DATA_BLOCK)) + ALIGN_8((ULONG)(Block->MaxAddress - Block->MinAddress)); if (Offset + Size >= BufferSize) { break; }
if (i == 0) { *StartOffset = Offset; }
Offset += Size; (*Count)++; }
return Offset; }
VOID IopWriteTriageDumpDataBlocks( ULONG StartOffset, ULONG Count, PUCHAR BufferAddress ) { ULONG i; PTRIAGE_PTR_DATA_BLOCK Block; PUCHAR DataBuffer; PTRIAGE_DATA_BLOCK DumpBlock;
DumpBlock = (PTRIAGE_DATA_BLOCK) (BufferAddress + StartOffset); DataBuffer = (PUCHAR)(DumpBlock + Count);
Block = IopTriageDumpDataBlocks; for (i = 0; i < Count; i++, Block++) { DumpBlock->Address = Block->MinAddress; DumpBlock->Offset = (ULONG)(DataBuffer - BufferAddress); DumpBlock->Size = (ULONG)(Block->MaxAddress - Block->MinAddress);
CurReadAllVirtual(Block->MinAddress, DataBuffer, DumpBlock->Size);
DataBuffer += DumpBlock->Size; DumpBlock++; } }
//----------------------------------------------------------------------------
//
// KernelTriage32DumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
HRESULT KernelTriage32DumpTargetInfo::Write(HANDLE File, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { HRESULT Status; PMEMORY_DUMP32 NewHeader; ULONG64 ThreadAddr; ULONG CodeMod; ULONG BugCheckCode; ULONG64 BugCheckData[4]; ULONG64 SaveDataPage = 0; ULONG64 PrcbAddr; ContextSave* PushedContext;
if (!IS_KERNEL_TARGET(g_Target)) { ErrOut("kernel minidumps can only be written " "in kernel-mode sessions\n"); return E_UNEXPECTED; }
dprintf("mini kernel dump\n"); FlushCallbacks();
NewHeader = (PMEMORY_DUMP32) malloc(TRIAGE_DUMP_SIZE32); if (NewHeader == NULL) { return E_OUTOFMEMORY; }
//
// Get the current thread address, used to extract various blocks of data.
// For some bugchecks the interesting thread is a different
// thread than the current thread, so make the following code
// generic so it handles any thread.
//
if ((Status = g_Target-> ReadBugCheckData(&BugCheckCode, BugCheckData)) != S_OK) { goto NewHeader; }
// Set a special marker to indicate there is no pushed context.
PushedContext = (ContextSave*)&PushedContext;
if (BugCheckCode == THREAD_STUCK_IN_DEVICE_DRIVER) { CROSS_PLATFORM_CONTEXT Context;
// Modify the bugcheck code to indicate this
// minidump represents a special state.
CodeMod = MINIDUMP_BUGCHECK;
// The interesting thread is the first bugcheck parameter.
ThreadAddr = BugCheckData[0];
// We need to make the thread's context the current
// machine context for the duration of dump generation.
if ((Status = g_Target-> GetContextFromThreadStack(ThreadAddr, &Context, FALSE)) != S_OK) { goto NewHeader; }
PushedContext = g_Machine->PushContext(&Context); } else if (BugCheckCode == SYSTEM_THREAD_EXCEPTION_NOT_HANDLED) { //
// System thread stores a context record as the 4th parameter.
// use that.
// Also save the context record in case someone needs to look
// at it.
//
if (BugCheckData[3]) { CROSS_PLATFORM_CONTEXT TargetContext, Context;
if (CurReadAllVirtual(BugCheckData[3], &TargetContext, g_Target->m_TypeInfo. SizeTargetContext) == S_OK && g_Machine->ConvertContextFrom(&Context, g_Target->m_SystemVersion, g_Target-> m_TypeInfo.SizeTargetContext, &TargetContext) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; PushedContext = g_Machine->PushContext(&Context); SaveDataPage = BugCheckData[3]; } } } else if (BugCheckCode == KERNEL_MODE_EXCEPTION_NOT_HANDLED) { CROSS_PLATFORM_CONTEXT Context;
//
// 3rd parameter is a trap frame.
//
// Build a context record out of that only if it's a kernel mode
// failure because esp may be wrong in that case ???.
//
if (BugCheckData[2] && g_Machine->GetContextFromTrapFrame(BugCheckData[2], &Context, FALSE) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; PushedContext = g_Machine->PushContext(&Context); SaveDataPage = BugCheckData[2]; } } else if (BugCheckCode == UNEXPECTED_KERNEL_MODE_TRAP) { CROSS_PLATFORM_CONTEXT Context;
//
// Double fault
//
// The thread is correct in this case.
// Second parameter is the TSS. If we have a TSS, convert
// the context and mark the bugcheck as converted.
//
if (BugCheckData[0] == 8 && BugCheckData[1] && g_Machine->GetContextFromTaskSegment(BugCheckData[1], &Context, FALSE) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; PushedContext = g_Machine->PushContext(&Context); } } else { CodeMod = 0;
if ((Status = g_Process-> GetImplicitThreadData(g_Thread, &ThreadAddr)) != S_OK) { goto NewHeader; } }
CCrashDumpWrapper32 Wrapper;
//
// setup the main header
//
KernelDumpTargetInfo::InitDumpHeader32(&NewHeader->Header, CommentA, CommentW, CodeMod); NewHeader->Header.DumpType = DUMP_TYPE_TRIAGE; NewHeader->Header.MiniDumpFields = TRIAGE_DUMP_BASIC_INFO; NewHeader->Header.WriterStatus = DUMP_DBGENG_SUCCESS;
//
// triage dump header begins on second page
//
TRIAGE_DUMP32 *ptdh = &NewHeader->Triage;
ULONG i;
ptdh->ServicePackBuild = g_Target->m_ServicePackNumber; ptdh->SizeOfDump = TRIAGE_DUMP_SIZE32;
ptdh->ContextOffset = FIELD_OFFSET (DUMP_HEADER32, ContextRecord); ptdh->ExceptionOffset = FIELD_OFFSET (DUMP_HEADER32, Exception);
//
// starting offset in triage dump follows the triage dump header
//
unsigned Offset = ALIGN_8(sizeof(DUMP_HEADER32) + sizeof(TRIAGE_DUMP32));
//
// write mm information for Win2K and above only
//
if (g_Target->m_SystemVersion >= NT_SVER_W2K) { ptdh->MmOffset = Offset; Wrapper.WriteMmTriageInformation((PBYTE)NewHeader + ptdh->MmOffset); Offset += ALIGN_8(sizeof(DUMP_MM_STORAGE32)); }
//
// write unloaded drivers
//
ptdh->UnloadedDriversOffset = Offset; Wrapper.WriteUnloadedDrivers((PBYTE)NewHeader + ptdh->UnloadedDriversOffset); Offset += ALIGN_8(sizeof(ULONG) + MI_UNLOADED_DRIVERS * sizeof(DUMP_UNLOADED_DRIVERS32));
//
// write processor control block (KPRCB)
//
if (S_OK == g_Target->GetProcessorSystemDataOffset(CURRENT_PROC, DEBUG_DATA_KPRCB_OFFSET, &PrcbAddr)) { ptdh->PrcbOffset = Offset; CurReadAllVirtual(PrcbAddr, ((PBYTE)NewHeader) + ptdh->PrcbOffset, g_Target->m_KdDebuggerData.SizePrcb); Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizePrcb); } else { PrcbAddr = 0; }
//
// Write the thread and process data structures.
//
ptdh->ProcessOffset = Offset; Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizeEProcess); ptdh->ThreadOffset = Offset; Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizeEThread);
CurReadAllVirtual(ThreadAddr + g_Target->m_KdDebuggerData.OffsetKThreadApcProcess, (PBYTE)NewHeader + ptdh->ProcessOffset, g_Target->m_KdDebuggerData.SizeEProcess); CurReadAllVirtual(ThreadAddr, (PBYTE)NewHeader + ptdh->ThreadOffset, g_Target->m_KdDebuggerData.SizeEThread);
//
// write the call stack
//
ADDR StackPtr; ULONG64 StackBase = 0;
g_Machine->GetSP(&StackPtr); ptdh->TopOfStack = (ULONG)(ULONG_PTR)Flat(StackPtr);
g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData.OffsetKThreadInitialStack + ThreadAddr, &StackBase);
// Take the Min in case something goes wrong getting the stack base.
ptdh->SizeOfCallStack = min((ULONG)(ULONG_PTR)(StackBase - Flat(StackPtr)), MAX_TRIAGE_STACK_SIZE32);
ptdh->CallStackOffset = Offset;
if (ptdh->SizeOfCallStack) { CurReadAllVirtual(EXTEND64(ptdh->TopOfStack), ((PBYTE)NewHeader) + ptdh->CallStackOffset, ptdh->SizeOfCallStack); } Offset += ALIGN_8(ptdh->SizeOfCallStack);
//
// write debugger data
//
if (g_Target->m_SystemVersion >= NT_SVER_XP && g_Target->m_KdDebuggerDataOffset && (!IS_KERNEL_TRIAGE_DUMP(g_Target) || ((KernelTriageDumpTargetInfo*)g_Target)->m_HasDebuggerData) && Offset + ALIGN_8(sizeof(g_Target->m_KdDebuggerData)) < TRIAGE_DUMP_SIZE32) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DEBUGGER_DATA; ptdh->DebuggerDataOffset = Offset; Offset += ALIGN_8(sizeof(g_Target->m_KdDebuggerData)); ptdh->DebuggerDataSize = sizeof(g_Target->m_KdDebuggerData); memcpy((PBYTE)NewHeader + ptdh->DebuggerDataOffset, &g_Target->m_KdDebuggerData, sizeof(g_Target->m_KdDebuggerData)); }
//
// write loaded driver list
//
ModuleInfo* ModIter; ULONG MaxEntries;
// Use a heuristic to guess how many entries we
// can pack into the remaining space.
MaxEntries = (TRIAGE_DUMP_SIZE32 - Offset) / (sizeof(DUMP_DRIVER_ENTRY32) + TRIAGE_DRIVER_NAME_SIZE_GUESS);
ptdh->DriverCount = 0; if (ModIter = g_Target->GetModuleInfo(FALSE)) { if ((Status = ModIter->Initialize(g_Thread)) == S_OK) { while (ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == GNME_CORRUPT || retval == GNME_DONE) { if (retval == GNME_CORRUPT) { NewHeader->Header.WriterStatus = DUMP_DBGENG_CORRUPT_MODULE_LIST; } break; } else if (retval == GNME_NO_NAME) { continue; }
ptdh->DriverCount++; } } else { NewHeader->Header.WriterStatus = DUMP_DBGENG_NO_MODULE_LIST; } }
ptdh->DriverListOffset = Offset; Offset += ALIGN_8(ptdh->DriverCount * sizeof(DUMP_DRIVER_ENTRY32)); ptdh->StringPoolOffset = Offset; ptdh->BrokenDriverOffset = 0;
Wrapper.WriteDriverList((PBYTE)NewHeader, ptdh);
Offset = ptdh->StringPoolOffset + ptdh->StringPoolSize; Offset = ALIGN_8(Offset);
//
// For XP and above add in any additional data pages and write out
// whatever fits.
//
if (g_Target->m_SystemVersion >= NT_SVER_XP) { if (SaveDataPage) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, SaveDataPage), g_Machine->m_PageSize); }
// If there are other interesting data pages, such as
// alternate stacks for DPCs and such, pick them up.
if (PrcbAddr) { ADDR_RANGE AltData[MAX_ALT_ADDR_RANGES];
ZeroMemory(AltData, sizeof(AltData)); if (g_Machine->GetAlternateTriageDumpDataRanges(PrcbAddr, ThreadAddr, AltData) == S_OK) { for (i = 0; i < MAX_ALT_ADDR_RANGES; i++) { if (AltData[i].Base) { IoAddTriageDumpDataBlock(AltData[i].Base, AltData[i].Size); } } } }
// Add any data blocks that were registered
// in the debuggee.
AddInMemoryTriageDataBlocks(); // Add data blocks which might be referred to by
// the context or other runtime state.
IopAddRunTimeTriageDataBlocks(&g_Machine->m_Context, EXTEND64(ptdh->TopOfStack), EXTEND64(ptdh->TopOfStack + ptdh->SizeOfCallStack), 0, 0);
// Check which data blocks fit and write them.
Offset = IopSizeTriageDumpDataBlocks(Offset, TRIAGE_DUMP_SIZE32, &ptdh->DataBlocksOffset, &ptdh->DataBlocksCount); Offset = ALIGN_8(Offset); if (ptdh->DataBlocksCount) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DATA_BLOCKS; IopWriteTriageDumpDataBlocks(ptdh->DataBlocksOffset, ptdh->DataBlocksCount, (PUCHAR)NewHeader); } }
//
// all options are enabled
//
ptdh->TriageOptions = 0xffffffff;
//
// end of triage dump validated
//
ptdh->ValidOffset = TRIAGE_DUMP_SIZE32 - sizeof(ULONG); *(PULONG)(((PBYTE) NewHeader) + ptdh->ValidOffset) = TRIAGE_DUMP_VALID;
//
// Write it out to the file.
//
ULONG cbWritten;
if (!WriteFile(File, NewHeader, TRIAGE_DUMP_SIZE32, &cbWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Write to minidump file failed for reason %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } else if (cbWritten != TRIAGE_DUMP_SIZE32) { ErrOut("Write to minidump failed because disk is full.\n"); Status = HRESULT_FROM_WIN32(ERROR_DISK_FULL); } else { Status = S_OK; }
if (PushedContext != (ContextSave*)&PushedContext) { g_Machine->PopContext(PushedContext); }
NewHeader: free(NewHeader); return Status; }
//----------------------------------------------------------------------------
//
// KernelTriage64DumpTargetInfo::Write.
//
//----------------------------------------------------------------------------
HRESULT KernelTriage64DumpTargetInfo::Write(HANDLE File, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { HRESULT Status; PMEMORY_DUMP64 NewHeader; ULONG64 ThreadAddr; ULONG CodeMod; ULONG BugCheckCode; ULONG64 BugCheckData[4]; ULONG64 SaveDataPage = 0; ULONG64 BStoreBase = 0; ULONG BStoreSize = 0; ULONG64 PrcbAddr; ContextSave* PushedContext;
if (!IS_KERNEL_TARGET(g_Target)) { ErrOut("kernel minidumps can only be written " "in kernel-mode sessions\n"); return E_UNEXPECTED; }
dprintf("mini kernel dump\n"); FlushCallbacks();
NewHeader = (PMEMORY_DUMP64) malloc(TRIAGE_DUMP_SIZE64); if (NewHeader == NULL) { return E_OUTOFMEMORY; }
//
// Get the current thread address, used to extract various blocks of data.
// For some bugchecks the interesting thread is a different
// thread than the current thread, so make the following code
// generic so it handles any thread.
//
if ((Status = g_Target-> ReadBugCheckData(&BugCheckCode, BugCheckData)) != S_OK) { goto NewHeader; }
// Set a special marker to indicate there is no pushed context.
PushedContext = (ContextSave*)&PushedContext;
if (BugCheckCode == THREAD_STUCK_IN_DEVICE_DRIVER) { CROSS_PLATFORM_CONTEXT Context;
// Modify the bugcheck code to indicate this
// minidump represents a special state.
CodeMod = MINIDUMP_BUGCHECK;
// The interesting thread is the first bugcheck parameter.
ThreadAddr = BugCheckData[0];
// We need to make the thread's context the current
// machine context for the duration of dump generation.
if ((Status = g_Target-> GetContextFromThreadStack(ThreadAddr, &Context, FALSE)) != S_OK) { goto NewHeader; }
PushedContext = g_Machine->PushContext(&Context); } else if (BugCheckCode == SYSTEM_THREAD_EXCEPTION_NOT_HANDLED) { //
// System thread stores a context record as the 4th parameter.
// use that.
// Also save the context record in case someone needs to look
// at it.
//
if (BugCheckData[3]) { CROSS_PLATFORM_CONTEXT TargetContext, Context;
if (CurReadAllVirtual(BugCheckData[3], &TargetContext, g_Target-> m_TypeInfo.SizeTargetContext) == S_OK && g_Machine->ConvertContextFrom(&Context, g_Target->m_SystemVersion, g_Target-> m_TypeInfo.SizeTargetContext, &TargetContext) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; PushedContext = g_Machine->PushContext(&Context); SaveDataPage = BugCheckData[3]; } } } else if (BugCheckCode == KERNEL_MODE_EXCEPTION_NOT_HANDLED) { CROSS_PLATFORM_CONTEXT Context;
//
// 3rd parameter is a trap frame.
//
// Build a context record out of that only if it's a kernel mode
// failure because esp may be wrong in that case ???.
//
if (BugCheckData[2] && g_Machine->GetContextFromTrapFrame(BugCheckData[2], &Context, FALSE) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; PushedContext = g_Machine->PushContext(&Context); SaveDataPage = BugCheckData[2]; } } else { CodeMod = 0;
if ((Status = g_Process-> GetImplicitThreadData(g_Thread, &ThreadAddr)) != S_OK) { goto NewHeader; } }
CCrashDumpWrapper64 Wrapper;
//
// setup the main header
//
KernelDumpTargetInfo::InitDumpHeader64(&NewHeader->Header, CommentA, CommentW, CodeMod); NewHeader->Header.DumpType = DUMP_TYPE_TRIAGE; NewHeader->Header.MiniDumpFields = TRIAGE_DUMP_BASIC_INFO; NewHeader->Header.WriterStatus = DUMP_DBGENG_SUCCESS;
//
// triage dump header begins on second page
//
TRIAGE_DUMP64 *ptdh = &NewHeader->Triage;
ULONG i;
ptdh->ServicePackBuild = g_Target->m_ServicePackNumber; ptdh->SizeOfDump = TRIAGE_DUMP_SIZE64;
ptdh->ContextOffset = FIELD_OFFSET (DUMP_HEADER64, ContextRecord); ptdh->ExceptionOffset = FIELD_OFFSET (DUMP_HEADER64, Exception);
//
// starting Offset in triage dump follows the triage dump header
//
unsigned Offset = ALIGN_8(sizeof(DUMP_HEADER64) + sizeof(TRIAGE_DUMP64));
//
// write mm information
//
ptdh->MmOffset = Offset; Wrapper.WriteMmTriageInformation((PBYTE)NewHeader + ptdh->MmOffset); Offset += ALIGN_8(sizeof(DUMP_MM_STORAGE64));
//
// write unloaded drivers
//
ptdh->UnloadedDriversOffset = Offset; Wrapper.WriteUnloadedDrivers((PBYTE)NewHeader + ptdh->UnloadedDriversOffset); Offset += ALIGN_8(sizeof(ULONG64) + MI_UNLOADED_DRIVERS * sizeof(DUMP_UNLOADED_DRIVERS64));
//
// write processor control block (KPRCB)
//
if (S_OK == g_Target->GetProcessorSystemDataOffset(CURRENT_PROC, DEBUG_DATA_KPRCB_OFFSET, &PrcbAddr)) { ptdh->PrcbOffset = Offset; CurReadAllVirtual(PrcbAddr, ((PBYTE)NewHeader) + ptdh->PrcbOffset, g_Target->m_KdDebuggerData.SizePrcb); Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizePrcb); } else { PrcbAddr = 0; }
//
// Write the thread and process data structures.
//
ptdh->ProcessOffset = Offset; Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizeEProcess); ptdh->ThreadOffset = Offset; Offset += ALIGN_8(g_Target->m_KdDebuggerData.SizeEThread);
CurReadAllVirtual(ThreadAddr + g_Target->m_KdDebuggerData.OffsetKThreadApcProcess, (PBYTE)NewHeader + ptdh->ProcessOffset, g_Target->m_KdDebuggerData.SizeEProcess); CurReadAllVirtual(ThreadAddr, (PBYTE)NewHeader + ptdh->ThreadOffset, g_Target->m_KdDebuggerData.SizeEThread);
//
// write the call stack
//
ADDR StackPtr; ULONG64 StackBase = 0;
g_Machine->GetSP(&StackPtr); ptdh->TopOfStack = Flat(StackPtr);
g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData.OffsetKThreadInitialStack + ThreadAddr, &StackBase);
// Take the Min in case something goes wrong getting the stack base.
ptdh->SizeOfCallStack = min((ULONG)(ULONG_PTR)(StackBase - Flat(StackPtr)), MAX_TRIAGE_STACK_SIZE64);
ptdh->CallStackOffset = Offset;
if (ptdh->SizeOfCallStack) { CurReadAllVirtual(ptdh->TopOfStack, ((PBYTE)NewHeader) + ptdh->CallStackOffset, ptdh->SizeOfCallStack); } Offset += ALIGN_8(ptdh->SizeOfCallStack);
//
// The IA64 contains two callstacks. The first is the normal
// callstack, and the second is a scratch region where
// the processor can spill registers. It is this latter stack,
// the backing-store, that we now save.
//
if (g_Target->m_MachineType == IMAGE_FILE_MACHINE_IA64) { ULONG64 BStoreLimit;
g_Target->ReadPointer(g_Process, g_Target->m_Machine, ThreadAddr + g_Target->m_KdDebuggerData.OffsetKThreadBStore, &BStoreBase); g_Target->ReadPointer(g_Process, g_Target->m_Machine, ThreadAddr + g_Target->m_KdDebuggerData.OffsetKThreadBStoreLimit, &BStoreLimit);
ptdh->ArchitectureSpecific.Ia64.BStoreOffset = Offset; ptdh->ArchitectureSpecific.Ia64.LimitOfBStore = BStoreLimit; ptdh->ArchitectureSpecific.Ia64.SizeOfBStore = min((ULONG)(BStoreLimit - BStoreBase), MAX_TRIAGE_BSTORE_SIZE); BStoreSize = ptdh->ArchitectureSpecific.Ia64.SizeOfBStore;
if (ptdh->ArchitectureSpecific.Ia64.SizeOfBStore) { CurReadAllVirtual(BStoreBase, ((PBYTE)NewHeader) + ptdh->ArchitectureSpecific.Ia64.BStoreOffset, ptdh->ArchitectureSpecific.Ia64.SizeOfBStore); Offset += ALIGN_8(ptdh->ArchitectureSpecific.Ia64.SizeOfBStore); } }
//
// write debugger data
//
if (g_Target->m_SystemVersion >= NT_SVER_XP && g_Target->m_KdDebuggerDataOffset && (!IS_KERNEL_TRIAGE_DUMP(g_Target) || ((KernelTriageDumpTargetInfo*)g_Target)->m_HasDebuggerData) && Offset + ALIGN_8(sizeof(g_Target->m_KdDebuggerData)) < TRIAGE_DUMP_SIZE64) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DEBUGGER_DATA; ptdh->DebuggerDataOffset = Offset; Offset += ALIGN_8(sizeof(g_Target->m_KdDebuggerData)); ptdh->DebuggerDataSize = sizeof(g_Target->m_KdDebuggerData); memcpy((PBYTE)NewHeader + ptdh->DebuggerDataOffset, &g_Target->m_KdDebuggerData, sizeof(g_Target->m_KdDebuggerData)); }
//
// write loaded driver list
//
ModuleInfo* ModIter; ULONG MaxEntries;
// Use a heuristic to guess how many entries we
// can pack into the remaining space.
MaxEntries = (TRIAGE_DUMP_SIZE64 - Offset) / (sizeof(DUMP_DRIVER_ENTRY64) + TRIAGE_DRIVER_NAME_SIZE_GUESS);
ptdh->DriverCount = 0; if (ModIter = g_Target->GetModuleInfo(FALSE)) { if ((Status = ModIter->Initialize(g_Thread)) == S_OK) { while (ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == GNME_CORRUPT || retval == GNME_DONE) { if (retval == GNME_CORRUPT) { NewHeader->Header.WriterStatus = DUMP_DBGENG_CORRUPT_MODULE_LIST; } break; } else if (retval == GNME_NO_NAME) { continue; }
ptdh->DriverCount++; } } else { NewHeader->Header.WriterStatus = DUMP_DBGENG_NO_MODULE_LIST; } }
ptdh->DriverListOffset = Offset; Offset += ALIGN_8(ptdh->DriverCount * sizeof(DUMP_DRIVER_ENTRY64)); ptdh->StringPoolOffset = Offset; ptdh->BrokenDriverOffset = 0;
Wrapper.WriteDriverList((PBYTE)NewHeader, ptdh);
Offset = ptdh->StringPoolOffset + ptdh->StringPoolSize; Offset = ALIGN_8(Offset);
//
// For XP and above add in any additional data pages and write out
// whatever fits.
//
if (g_Target->m_SystemVersion >= NT_SVER_XP) { if (SaveDataPage) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, SaveDataPage), g_Machine->m_PageSize); }
// If there are other interesting data pages, such as
// alternate stacks for DPCs and such, pick them up.
if (PrcbAddr) { ADDR_RANGE AltData[MAX_ALT_ADDR_RANGES];
ZeroMemory(AltData, sizeof(AltData)); if (g_Machine->GetAlternateTriageDumpDataRanges(PrcbAddr, ThreadAddr, AltData) == S_OK) { for (i = 0; i < MAX_ALT_ADDR_RANGES; i++) { if (AltData[i].Base) { IoAddTriageDumpDataBlock(AltData[i].Base, AltData[i].Size); } } } }
// Add any data blocks that were registered
// in the debuggee.
AddInMemoryTriageDataBlocks();
// Add data blocks which might be referred to by
// the context or other runtime state.
IopAddRunTimeTriageDataBlocks(&g_Machine->m_Context, ptdh->TopOfStack, ptdh->TopOfStack + ptdh->SizeOfCallStack, BStoreBase, BStoreSize);
// Check which data blocks fit and write them.
Offset = IopSizeTriageDumpDataBlocks(Offset, TRIAGE_DUMP_SIZE64, &ptdh->DataBlocksOffset, &ptdh->DataBlocksCount); Offset = ALIGN_8(Offset); if (ptdh->DataBlocksCount) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DATA_BLOCKS; IopWriteTriageDumpDataBlocks(ptdh->DataBlocksOffset, ptdh->DataBlocksCount, (PUCHAR)NewHeader); } }
//
// all options are enabled
//
ptdh->TriageOptions = 0xffffffff;
//
// end of triage dump validated
//
ptdh->ValidOffset = TRIAGE_DUMP_SIZE64 - sizeof(ULONG); *(PULONG)(((PBYTE) NewHeader) + ptdh->ValidOffset) = TRIAGE_DUMP_VALID;
//
// Write it out to the file.
//
ULONG cbWritten;
if (!WriteFile(File, NewHeader, TRIAGE_DUMP_SIZE64, &cbWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Write to minidump file failed for reason %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } else if (cbWritten != TRIAGE_DUMP_SIZE64) { ErrOut("Write to minidump failed because disk is full.\n"); Status = HRESULT_FROM_WIN32(ERROR_DISK_FULL); } else { Status = S_OK; }
if (PushedContext != (ContextSave*)&PushedContext) { g_Machine->PopContext(PushedContext); }
NewHeader: free(NewHeader); return Status; }
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
HRESULT WriteDumpFile(PCWSTR FileName, ULONG64 FileHandle, ULONG Qualifier, ULONG FormatFlags, PCSTR CommentA, PCWSTR CommentW) { ULONG DumpType = DTYPE_COUNT; DumpTargetInfo* WriteTarget; HRESULT Status; ULONG OldMachine; WCHAR TempFile[2 * MAX_PATH]; PCWSTR DumpWriteFile; HANDLE DumpWriteHandle; PSTR AnsiFile = NULL; BOOL CreatedAnsi = FALSE;
if (!IS_CUR_MACHINE_ACCESSIBLE()) { return E_UNEXPECTED; }
if (IS_KERNEL_TARGET(g_Target)) { DbgKdTransport* KdTrans;
if (FormatFlags & ~GENERIC_FORMATS) { return E_INVALIDARG; }
//
// not much we can do without the processor block
// or at least the PRCB for the current process in a minidump.
//
if (!g_Target->m_KdDebuggerData.KiProcessorBlock && IS_DUMP_TARGET(g_Target) && !((KernelDumpTargetInfo*)g_Target)->m_KiProcessors[CURRENT_PROC]) { ErrOut("Cannot find KiProcessorBlock - " "can not create dump file\n");
return E_FAIL; }
if (IS_CONN_KERNEL_TARGET(g_Target)) { KdTrans = ((ConnLiveKernelTargetInfo*)g_Target)->m_Transport; } else { KdTrans = NULL; }
switch(Qualifier) { case DEBUG_KERNEL_SMALL_DUMP: DumpType = g_Target->m_Machine->m_Ptr64 ? DTYPE_KERNEL_TRIAGE64 : DTYPE_KERNEL_TRIAGE32; break; case DEBUG_KERNEL_FULL_DUMP: if (KdTrans != NULL && KdTrans->m_DirectPhysicalMemory == FALSE) { WarnOut("Creating a full kernel dump over the COM port is a " "VERY VERY slow operation.\n" "This command may take many HOURS to complete. " "Ctrl-C if you want to terminate the command.\n"); } DumpType = g_Target->m_Machine->m_Ptr64 ? DTYPE_KERNEL_FULL64 : DTYPE_KERNEL_FULL32; break; default: // Other formats are not supported.
return E_INVALIDARG; } } else { DBG_ASSERT(IS_USER_TARGET(g_Target));
switch(Qualifier) { case DEBUG_USER_WINDOWS_SMALL_DUMP: if (FormatFlags & ~(GENERIC_FORMATS | UMINI_FORMATS | FORMAT_USER_MICRO)) { return E_INVALIDARG; }
DumpType = (FormatFlags & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) ? DTYPE_USER_MINI_FULL : DTYPE_USER_MINI_PARTIAL; break;
case DEBUG_USER_WINDOWS_DUMP: if (FormatFlags & ~GENERIC_FORMATS) { return E_INVALIDARG; }
DumpType = g_Target->m_Machine->m_Ptr64 ? DTYPE_USER_FULL64 : DTYPE_USER_FULL32; break; default: // Other formats are not supported.
return E_INVALIDARG; } }
WriteTarget = NewDumpTargetInfo(DumpType); if (WriteTarget == NULL) { ErrOut("Unable to create dump write target\n"); return E_OUTOFMEMORY; }
// Ensure that the dump is always written according to the
// target machine type and not any emulated machine.
OldMachine = g_Target->m_EffMachineType; g_Target->SetEffMachine(g_Target->m_MachineType, FALSE);
// Flush context first so that the minidump reads the
// same register values the debugger has.
g_Target->FlushRegContext();
//
// If we're producing a CAB put the dump in a temp file.
//
if (FormatFlags & DEBUG_FORMAT_WRITE_CAB) { if (FileHandle) { Status = E_INVALIDARG; goto Exit; }
if (!GetTempPathW(DIMA(TempFile), TempFile)) { wcscpy(TempFile, L".\\"); } // Use the CAB name as the dump file name so the
// name in the CAB will match.
CatStringW(TempFile, PathTailW(FileName), DIMA(TempFile)); CatStringW(TempFile, L".dmp", DIMA(TempFile));
DumpWriteFile = TempFile; FormatFlags &= ~DEBUG_FORMAT_NO_OVERWRITE; } else { DumpWriteFile = FileName; if (!DumpWriteFile) { DumpWriteFile = L"<HandleOnly>"; } }
if (FileHandle) { DumpWriteHandle = OS_HANDLE(FileHandle); if (!DumpWriteHandle || DumpWriteHandle == INVALID_HANDLE_VALUE) { Status = E_INVALIDARG; } else { Status = S_OK; } } else if ((Status = WideToAnsi(DumpWriteFile, &AnsiFile)) == S_OK) { // Dumps are almost always written sequentially so
// add that hint to the file flags.
DumpWriteHandle = CreateFileW(DumpWriteFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, (FormatFlags & DEBUG_FORMAT_NO_OVERWRITE) ? CREATE_NEW : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if ((!DumpWriteHandle || DumpWriteHandle == INVALID_HANDLE_VALUE) && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { //
// ANSI-only system. It's Win9x so don't
// bother with sequential scan.
//
DumpWriteHandle = CreateFileA(AnsiFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, (FormatFlags & DEBUG_FORMAT_NO_OVERWRITE) ? CREATE_NEW : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); CreatedAnsi = TRUE; }
if (!DumpWriteHandle || DumpWriteHandle == INVALID_HANDLE_VALUE) { Status = WIN32_LAST_STATUS(); ErrOut("Unable to create file '%ws' - %s\n \"%s\"\n", DumpWriteFile, FormatStatusCode(Status), FormatStatus(Status)); } }
if (Status == S_OK) { dprintf("Creating %ws - ", DumpWriteFile); Status = WriteTarget->Write(DumpWriteHandle, FormatFlags, CommentA, CommentW);
if (!FileHandle) { CloseHandle(DumpWriteHandle); if (Status != S_OK) { if (CreatedAnsi) { DeleteFileA(AnsiFile); } else { DeleteFileW(DumpWriteFile); } } } }
if (Status == S_OK && (FormatFlags & DEBUG_FORMAT_WRITE_CAB)) { PSTR AnsiBaseFile;
if ((Status = WideToAnsi(FileName, &AnsiBaseFile)) == S_OK) { Status = CreateCabFromDump(AnsiFile, AnsiBaseFile, FormatFlags); FreeAnsi(AnsiBaseFile); } if (CreatedAnsi) { DeleteFileA(AnsiFile); } else { DeleteFileW(TempFile); } }
Exit: FreeAnsi(AnsiFile); g_Target->SetEffMachine(OldMachine, FALSE); delete WriteTarget; return Status; }
void DotDump(PDOT_COMMAND Cmd, DebugClient* Client) { BOOL Usage = FALSE; ULONG Qual; ULONG FormatFlags;
//
// Default to minidumps
//
if (IS_KERNEL_TARGET(g_Target)) { Qual = DEBUG_KERNEL_SMALL_DUMP;
if (IS_LOCAL_KERNEL_TARGET(g_Target)) { error(SESSIONNOTSUP); } } else { Qual = DEBUG_USER_WINDOWS_SMALL_DUMP; } FormatFlags = DEBUG_FORMAT_DEFAULT | DEBUG_FORMAT_NO_OVERWRITE;
//
// Scan for options.
//
CHAR Save; PSTR FileName; BOOL SubLoop; PCSTR Comment = NULL; PSTR CommentEnd = NULL; BOOL Unique = FALSE; ProcessInfo* DumpProcess = g_Process;
for (;;) { if (PeekChar() == '-' || *g_CurCmd == '/') { SubLoop = TRUE;
g_CurCmd++; switch(*g_CurCmd) { case 'a': DumpProcess = NULL; break;
case 'b': FormatFlags |= DEBUG_FORMAT_WRITE_CAB; g_CurCmd++; if (*g_CurCmd == 'a') { FormatFlags |= DEBUG_FORMAT_CAB_SECONDARY_FILES; g_CurCmd++; } break;
case 'c': g_CurCmd++; Comment = StringValue(STRV_SPACE_IS_SEPARATOR | STRV_TRIM_TRAILING_SPACE, &Save); *g_CurCmd = Save; CommentEnd = g_CurCmd; break;
case 'f': if (IS_KERNEL_TARGET(g_Target)) { Qual = DEBUG_KERNEL_FULL_DUMP; } else { Qual = DEBUG_USER_WINDOWS_DUMP; } break;
case 'm': if (IS_KERNEL_TARGET(g_Target)) { Qual = DEBUG_KERNEL_SMALL_DUMP; } else { Qual = DEBUG_USER_WINDOWS_SMALL_DUMP;
for (;;) { switch(*(g_CurCmd + 1)) { case 'a': // Synthetic flag meaning "save the
// maximum amount of data."
FormatFlags |= DEBUG_FORMAT_USER_SMALL_FULL_MEMORY | DEBUG_FORMAT_USER_SMALL_HANDLE_DATA | DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES; break; case 'C': // Flag to test microdump code.
FormatFlags |= FORMAT_USER_MICRO; break; case 'd': FormatFlags |= DEBUG_FORMAT_USER_SMALL_DATA_SEGMENTS; break; case 'f': FormatFlags |= DEBUG_FORMAT_USER_SMALL_FULL_MEMORY; break; case 'h': FormatFlags |= DEBUG_FORMAT_USER_SMALL_HANDLE_DATA; break; case 'i': FormatFlags |= DEBUG_FORMAT_USER_SMALL_INDIRECT_MEMORY; break; case 'p': FormatFlags |= DEBUG_FORMAT_USER_SMALL_PROCESS_THREAD_DATA; break; case 'r': FormatFlags |= DEBUG_FORMAT_USER_SMALL_FILTER_MEMORY; break; case 'R': FormatFlags |= DEBUG_FORMAT_USER_SMALL_FILTER_PATHS; break; case 'u': FormatFlags |= DEBUG_FORMAT_USER_SMALL_UNLOADED_MODULES; break; case 'w': FormatFlags |= DEBUG_FORMAT_USER_SMALL_PRIVATE_READ_WRITE_MEMORY; break; default: SubLoop = FALSE; break; }
if (SubLoop) { g_CurCmd++; } else { break; } } } break;
case 'o': FormatFlags &= ~DEBUG_FORMAT_NO_OVERWRITE; break;
case 'u': Unique = TRUE; break;
case '?': Usage = TRUE; break;
default: ErrOut("Unknown option '%c'\n", *g_CurCmd); Usage = TRUE; break; }
g_CurCmd++; } else { FileName = StringValue(STRV_TRIM_TRAILING_SPACE, &Save); if (*FileName) { break; } else { *g_CurCmd = Save; Usage = TRUE; } }
if (Usage) { break; } }
if (DumpProcess == NULL && !Unique) { Usage = TRUE; }
if (Usage) { ErrOut("Usage: .dump [options] filename\n"); ErrOut("Options are:\n"); ErrOut(" /a - Create dumps for all processes (requires -u)\n"); ErrOut(" /b[a] - Package dump in a CAB and delete dump\n"); ErrOut(" /c <comment> - Add a comment " "(not supported in all formats)\n"); ErrOut(" /f - Create a full dump\n"); if (IS_KERNEL_TARGET(g_Target)) { ErrOut(" /m - Create a minidump (default)\n"); } else { ErrOut(" /m[adfhiprRuw] - Create a minidump (default)\n"); } ErrOut(" /o - Overwrite any existing file\n"); ErrOut(" /u - Append unique identifier to dump name\n");
ErrOut("\nUse \".hh .dump\" or open debugger.chm in the " "debuggers directory to get\n" "detailed documentation on this command.\n\n");
return; }
if (CommentEnd != NULL) { *CommentEnd = 0; }
ThreadInfo* OldThread = g_Thread; TargetInfo* Target; ProcessInfo* Process;
ForAllLayersToProcess() { PSTR DumpFileName; char UniqueName[2 * MAX_PATH];
if (DumpProcess != NULL && Process != DumpProcess) { continue; }
if (Process != g_Process) { SetCurrentThread(Process->m_ThreadHead, TRUE); }
if (Unique) { MakeFileNameUnique(FileName, UniqueName, DIMA(UniqueName), TRUE, g_Process); DumpFileName = UniqueName; } else { DumpFileName = FileName; }
PWSTR WideName;
if (AnsiToWide(DumpFileName, &WideName) == S_OK) { WriteDumpFile(WideName, 0, Qual, FormatFlags, Comment, NULL); FreeWide(WideName); } else { ErrOut("Unable to convert dump filename\n"); } }
if (!OldThread || OldThread->m_Process != g_Process) { SetCurrentThread(OldThread, TRUE); }
*g_CurCmd = Save; }
BOOL DumpCabAdd(PCSTR File) { HRESULT Status;
dprintf(" Adding %s - ", File); FlushCallbacks(); if ((Status = AddToDumpCab(File)) != S_OK) { ErrOut("%s\n", FormatStatusCode(Status)); } else { dprintf("added\n"); }
if (CheckUserInterrupt()) { return FALSE; } FlushCallbacks(); return TRUE; }
HRESULT CreateCabFromDump(PCSTR DumpFile, PCSTR CabFile, ULONG Flags) { HRESULT Status;
if ((Status = CreateDumpCab(CabFile)) != S_OK) { ErrOut("Unable to create CAB, %s\n", FormatStatusCode(Status)); return Status; }
WarnOut("Creating a cab file can take a VERY VERY long time\n." "Ctrl-C can only interrupt the command after a file " "has been added to the cab.\n"); //
// First add all base dump files.
//
if (!DumpFile) { DumpTargetInfo* Dump = (DumpTargetInfo*)g_Target; ULONG i;
for (i = DUMP_INFO_DUMP; i < DUMP_INFO_COUNT; i++) { if (Dump->m_InfoFiles[i].m_File) { if (!DumpCabAdd(Dump->m_InfoFiles[i].m_FileNameA)) { Status = E_UNEXPECTED; goto Leave; } } } } else { if (!DumpCabAdd(DumpFile)) { Status = E_UNEXPECTED; goto Leave; } }
if (Flags & DEBUG_FORMAT_CAB_SECONDARY_FILES) { ImageInfo* Image;
//
// Add all symbols and images.
//
for (Image = g_Process->m_ImageHead; Image; Image = Image->m_Next) { if (Image->m_MappedImagePath[0]) { if (!DumpCabAdd(Image->m_MappedImagePath)) { Status = E_UNEXPECTED; break; } }
IMAGEHLP_MODULE64 ModInfo;
ModInfo.SizeOfStruct = sizeof(ModInfo); if (SymGetModuleInfo64(g_Process->m_SymHandle, Image->m_BaseOfImage, &ModInfo)) { ULONG Len;
// The loaded image name often refers directly to the
// image. Only save the loaded image file if it
// refers to a .dbg file.
if (ModInfo.LoadedImageName[0] && (Len = strlen(ModInfo.LoadedImageName)) > 4 && !_stricmp(ModInfo.LoadedImageName + (Len - 4), ".dbg")) { if (!DumpCabAdd(ModInfo.LoadedImageName)) { Status = E_UNEXPECTED; break; } }
// Save any PDB that was opened.
if (ModInfo.LoadedPdbName[0]) { if (!DumpCabAdd(ModInfo.LoadedPdbName)) { Status = E_UNEXPECTED; break; } } } } }
Leave: CloseDumpCab();
if (Status == S_OK) { dprintf("Wrote %s\n", CabFile); }
return Status; }
// extern PKDDEBUGGER_DATA64 blocks[];
#define ALIGN_DOWN_POINTER(address, type) \
((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)sizeof(type) - 1)))
#define ALIGN_UP_POINTER(address, type) \
(ALIGN_DOWN_POINTER(((ULONG_PTR)(address) + sizeof(type) - 1), type))
//----------------------------------------------------------------------------
//
// CCrashDumpWrapper32.
//
//----------------------------------------------------------------------------
void CCrashDumpWrapper32::WriteDriverList( BYTE *pb, TRIAGE_DUMP32 *ptdh ) { PDUMP_DRIVER_ENTRY32 pdde; PDUMP_STRING pds; ModuleInfo* ModIter; ULONG MaxEntries = ptdh->DriverCount;
ptdh->DriverCount = 0;
if (((ModIter = g_Target->GetModuleInfo(FALSE)) == NULL) || ((ModIter->Initialize(g_Thread)) != S_OK)) { return; }
// pointer to first driver entry to write out
pdde = (PDUMP_DRIVER_ENTRY32) (pb + ptdh->DriverListOffset);
// pointer to first module name to write out
pds = (PDUMP_STRING) (pb + ptdh->StringPoolOffset);
while ((PBYTE)(pds + 1) < pb + TRIAGE_DUMP_SIZE32 && ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry;
ULONG retval = GetNextModuleEntry(ModIter, &ModEntry);
if (retval == GNME_CORRUPT || retval == GNME_DONE) { break; } else if (retval == GNME_NO_NAME) { continue; }
pdde->LdrEntry.DllBase = (ULONG)(ULONG_PTR)ModEntry.Base; pdde->LdrEntry.SizeOfImage = ModEntry.Size; pdde->LdrEntry.CheckSum = ModEntry.CheckSum; pdde->LdrEntry.TimeDateStamp = ModEntry.TimeDateStamp;
if (ModEntry.UnicodeNamePtr) { // convert length from bytes to characters
pds->Length = ModEntry.NameLength / sizeof(WCHAR); if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE32) { break; }
CopyMemory(pds->Buffer, ModEntry.NamePtr, ModEntry.NameLength); } else { pds->Length = ModEntry.NameLength; if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE32) { break; }
MultiByteToWideChar(CP_ACP, 0, ModEntry.NamePtr, ModEntry.NameLength, pds->Buffer, ModEntry.NameLength); }
// null terminate string
pds->Buffer[pds->Length] = '\0';
pdde->DriverNameOffset = (ULONG)((ULONG_PTR) pds - (ULONG_PTR) pb);
// get pointer to next string
pds = (PDUMP_STRING) ALIGN_UP_POINTER(((LPBYTE) pds) + sizeof(DUMP_STRING) + sizeof(WCHAR) * (pds->Length + 1), ULONGLONG);
pdde = (PDUMP_DRIVER_ENTRY32)(((PUCHAR) pdde) + sizeof(*pdde));
ptdh->DriverCount++; }
ptdh->StringPoolSize = (ULONG) ((ULONG_PTR)pds - (ULONG_PTR)(pb + ptdh->StringPoolOffset)); }
void CCrashDumpWrapper32::WriteUnloadedDrivers(BYTE *pb) { ULONG i; ULONG Index; UNLOADED_DRIVERS32 ud; PDUMP_UNLOADED_DRIVERS32 pdud; ULONG64 pvMiUnloadedDrivers=0; ULONG ulMiLastUnloadedDriver=0;
*((PULONG) pb) = 0;
//
// find location of unloaded drivers
//
if (!g_Target->m_KdDebuggerData.MmUnloadedDrivers || !g_Target->m_KdDebuggerData.MmLastUnloadedDriver) { return; }
g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData.MmUnloadedDrivers, &pvMiUnloadedDrivers); CurReadAllVirtual(g_Target->m_KdDebuggerData.MmLastUnloadedDriver, &ulMiLastUnloadedDriver, sizeof(ULONG));
if (pvMiUnloadedDrivers == NULL || ulMiLastUnloadedDriver == 0) { return; }
// point to last unloaded drivers
pdud = (PDUMP_UNLOADED_DRIVERS32)(((PULONG) pb) + 1);
//
// Write the list with the most recently unloaded driver first to the
// least recently unloaded driver last.
//
Index = ulMiLastUnloadedDriver - 1;
for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) { if (Index >= MI_UNLOADED_DRIVERS) { Index = MI_UNLOADED_DRIVERS - 1; }
// read in unloaded driver
if (CurReadAllVirtual(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS32), &ud, sizeof(ud)) != S_OK) { ErrOut("Can't read memory from %s", FormatAddr64(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS32))); }
// copy name lengths
pdud->Name.MaximumLength = ud.Name.MaximumLength; pdud->Name.Length = ud.Name.Length; if (ud.Name.Buffer == NULL) { break; }
// copy start and end address
pdud->StartAddress = ud.StartAddress; pdud->EndAddress = ud.EndAddress;
// restrict name length and maximum name length to 12 characters
if (pdud->Name.Length > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.Length = MAX_UNLOADED_NAME_LENGTH; } if (pdud->Name.MaximumLength > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.MaximumLength = MAX_UNLOADED_NAME_LENGTH; } // Can't store pointers in the dump so just zero it.
pdud->Name.Buffer = 0; // Read in name.
if (CurReadAllVirtual(EXTEND64(ud.Name.Buffer), pdud->DriverName, pdud->Name.MaximumLength) != S_OK) { ErrOut("Can't read memory at address %08x", (ULONG)(ud.Name.Buffer)); }
// move to previous driver
pdud += 1; Index -= 1; }
// number of drivers in the list
*((PULONG) pb) = i; }
void CCrashDumpWrapper32::WriteMmTriageInformation(BYTE *pb) { DUMP_MM_STORAGE32 TriageInformation; ULONG64 pMmVerifierData; ULONG64 pvMmPagedPoolInfo; ULONG cbNonPagedPool; ULONG cbPagedPool;
// version information
TriageInformation.Version = 1;
// size information
TriageInformation.Size = sizeof(TriageInformation);
// get special pool tag
ExtractValue(MmSpecialPoolTag, TriageInformation.MmSpecialPoolTag);
// get triage action taken
ExtractValue(MmTriageActionTaken, TriageInformation.MiTriageActionTaken); pMmVerifierData = g_Target->m_KdDebuggerData.MmVerifierData;
// read in verifier level
// BUGBUG - should not read internal data structures in MM
//if (pMmVerifierData)
// DmpReadMemory(
// (ULONG64) &((MM_DRIVER_VERIFIER_DATA *) pMmVerifierData)->Level,
// &TriageInformation.MmVerifyDriverLevel,
// sizeof(TriageInformation.MmVerifyDriverLevel));
//else
TriageInformation.MmVerifyDriverLevel = 0;
// read in verifier
ExtractValue(KernelVerifier, TriageInformation.KernelVerifier);
// read non paged pool info
ExtractValue(MmMaximumNonPagedPoolInBytes, cbNonPagedPool); TriageInformation.MmMaximumNonPagedPool = cbNonPagedPool / g_Target->m_Machine->m_PageSize; ExtractValue(MmAllocatedNonPagedPool, TriageInformation.MmAllocatedNonPagedPool);
// read paged pool info
ExtractValue(MmSizeOfPagedPoolInBytes, cbPagedPool); TriageInformation.PagedPoolMaximum = cbPagedPool / g_Target->m_Machine->m_PageSize; pvMmPagedPoolInfo = g_Target->m_KdDebuggerData.MmPagedPoolInformation;
// BUGBUG - should not read internal data structures in MM
//if (pvMmPagedPoolInfo)
// DmpReadMemory(
// (ULONG64) &((MM_PAGED_POOL_INFO *) pvMmPagedPoolInfo)->AllocatedPagedPool,
// &TriageInformation.PagedPoolAllocated,
// sizeof(TriageInformation.PagedPoolAllocated));
//else
TriageInformation.PagedPoolAllocated = 0;
// read committed pages info
ExtractValue(MmTotalCommittedPages, TriageInformation.CommittedPages); ExtractValue(MmPeakCommitment, TriageInformation.CommittedPagesPeak); ExtractValue(MmTotalCommitLimitMaximum, TriageInformation.CommitLimitMaximum); memcpy(pb, &TriageInformation, sizeof(TriageInformation)); }
//----------------------------------------------------------------------------
//
// CCrashDumpWrapper64.
//
//----------------------------------------------------------------------------
void CCrashDumpWrapper64::WriteDriverList( BYTE *pb, TRIAGE_DUMP64 *ptdh ) { PDUMP_DRIVER_ENTRY64 pdde; PDUMP_STRING pds; ModuleInfo* ModIter; ULONG MaxEntries = ptdh->DriverCount;
ptdh->DriverCount = 0;
if (((ModIter = g_Target->GetModuleInfo(FALSE)) == NULL) || ((ModIter->Initialize(g_Thread)) != S_OK)) { return; }
// pointer to first driver entry to write out
pdde = (PDUMP_DRIVER_ENTRY64) (pb + ptdh->DriverListOffset);
// pointer to first module name to write out
pds = (PDUMP_STRING) (pb + ptdh->StringPoolOffset);
while ((PBYTE)(pds + 1) < pb + TRIAGE_DUMP_SIZE64 && ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry;
ULONG retval = GetNextModuleEntry(ModIter, &ModEntry);
if (retval == GNME_CORRUPT || retval == GNME_DONE) { break; } else if (retval == GNME_NO_NAME) { continue; }
pdde->LdrEntry.DllBase = ModEntry.Base; pdde->LdrEntry.SizeOfImage = ModEntry.Size; pdde->LdrEntry.CheckSum = ModEntry.CheckSum; pdde->LdrEntry.TimeDateStamp = ModEntry.TimeDateStamp;
if (ModEntry.UnicodeNamePtr) { // convert length from bytes to characters
pds->Length = ModEntry.NameLength / sizeof(WCHAR); if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE64) { break; }
CopyMemory(pds->Buffer, ModEntry.NamePtr, ModEntry.NameLength); } else { pds->Length = ModEntry.NameLength; if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE64) { break; }
MultiByteToWideChar(CP_ACP, 0, ModEntry.NamePtr, ModEntry.NameLength, pds->Buffer, ModEntry.NameLength); }
// null terminate string
pds->Buffer[pds->Length] = '\0';
pdde->DriverNameOffset = (ULONG)((ULONG_PTR) pds - (ULONG_PTR) pb);
// get pointer to next string
pds = (PDUMP_STRING) ALIGN_UP_POINTER(((LPBYTE) pds) + sizeof(DUMP_STRING) + sizeof(WCHAR) * (pds->Length + 1), ULONGLONG);
pdde = (PDUMP_DRIVER_ENTRY64)(((PUCHAR) pdde) + sizeof(*pdde));
ptdh->DriverCount++; }
ptdh->StringPoolSize = (ULONG) ((ULONG_PTR)pds - (ULONG_PTR)(pb + ptdh->StringPoolOffset)); }
void CCrashDumpWrapper64::WriteUnloadedDrivers(BYTE *pb) { ULONG i; ULONG Index; UNLOADED_DRIVERS64 ud; PDUMP_UNLOADED_DRIVERS64 pdud; ULONG64 pvMiUnloadedDrivers; ULONG ulMiLastUnloadedDriver;
*((PULONG) pb) = 0;
//
// find location of unloaded drivers
//
if (!g_Target->m_KdDebuggerData.MmUnloadedDrivers || !g_Target->m_KdDebuggerData.MmLastUnloadedDriver) { return; }
g_Target->ReadPointer(g_Process, g_Target->m_Machine, g_Target->m_KdDebuggerData.MmUnloadedDrivers, &pvMiUnloadedDrivers); CurReadAllVirtual(g_Target->m_KdDebuggerData.MmLastUnloadedDriver, &ulMiLastUnloadedDriver, sizeof(ULONG));
if (pvMiUnloadedDrivers == NULL || ulMiLastUnloadedDriver == 0) { return; }
// point to last unloaded drivers
pdud = (PDUMP_UNLOADED_DRIVERS64)(((PULONG64) pb) + 1);
//
// Write the list with the most recently unloaded driver first to the
// least recently unloaded driver last.
//
Index = ulMiLastUnloadedDriver - 1;
for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) { if (Index >= MI_UNLOADED_DRIVERS) { Index = MI_UNLOADED_DRIVERS - 1; }
// read in unloaded driver
if (CurReadAllVirtual(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS64), &ud, sizeof(ud)) != S_OK) { ErrOut("Can't read memory from %s", FormatAddr64(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS64))); }
// copy name lengths
pdud->Name.MaximumLength = ud.Name.MaximumLength; pdud->Name.Length = ud.Name.Length; if (ud.Name.Buffer == NULL) { break; }
// copy start and end address
pdud->StartAddress = ud.StartAddress; pdud->EndAddress = ud.EndAddress;
// restrict name length and maximum name length to 12 characters
if (pdud->Name.Length > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.Length = MAX_UNLOADED_NAME_LENGTH; } if (pdud->Name.MaximumLength > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.MaximumLength = MAX_UNLOADED_NAME_LENGTH; } // Can't store pointers in the dump so just zero it.
pdud->Name.Buffer = 0; // Read in name.
if (CurReadAllVirtual(ud.Name.Buffer, pdud->DriverName, pdud->Name.MaximumLength) != S_OK) { ErrOut("Can't read memory at address %s", FormatAddr64(ud.Name.Buffer)); }
// move to previous driver
pdud += 1; Index -= 1; }
// number of drivers in the list
*((PULONG) pb) = i; }
void CCrashDumpWrapper64::WriteMmTriageInformation(BYTE *pb) { DUMP_MM_STORAGE64 TriageInformation; ULONG64 pMmVerifierData; ULONG64 pvMmPagedPoolInfo; ULONG64 cbNonPagedPool; ULONG64 cbPagedPool;
// version information
TriageInformation.Version = 1;
// size information
TriageInformation.Size = sizeof(TriageInformation);
// get special pool tag
ExtractValue(MmSpecialPoolTag, TriageInformation.MmSpecialPoolTag);
// get triage action taken
ExtractValue(MmTriageActionTaken, TriageInformation.MiTriageActionTaken); pMmVerifierData = g_Target->m_KdDebuggerData.MmVerifierData;
// read in verifier level
// BUGBUG - should not read internal data structures in MM
//if (pMmVerifierData)
// DmpReadMemory(
// (ULONG64) &((MM_DRIVER_VERIFIER_DATA *) pMmVerifierData)->Level,
// &TriageInformation.MmVerifyDriverLevel,
// sizeof(TriageInformation.MmVerifyDriverLevel));
//else
TriageInformation.MmVerifyDriverLevel = 0;
// read in verifier
ExtractValue(KernelVerifier, TriageInformation.KernelVerifier);
// read non paged pool info
ExtractValue(MmMaximumNonPagedPoolInBytes, cbNonPagedPool); TriageInformation.MmMaximumNonPagedPool = cbNonPagedPool / g_Target->m_Machine->m_PageSize; ExtractValue(MmAllocatedNonPagedPool, TriageInformation.MmAllocatedNonPagedPool);
// read paged pool info
ExtractValue(MmSizeOfPagedPoolInBytes, cbPagedPool); TriageInformation.PagedPoolMaximum = cbPagedPool / g_Target->m_Machine->m_PageSize; pvMmPagedPoolInfo = g_Target->m_KdDebuggerData.MmPagedPoolInformation;
// BUGBUG - should not read internal data structures in MM
//if (pvMmPagedPoolInfo)
// DmpReadMemory(
// (ULONG64) &((MM_PAGED_POOL_INFO *) pvMmPagedPoolInfo)->AllocatedPagedPool,
// &TriageInformation.PagedPoolAllocated,
// sizeof(TriageInformation.PagedPoolAllocated));
//else
TriageInformation.PagedPoolAllocated = 0;
// read committed pages info
ExtractValue(MmTotalCommittedPages, TriageInformation.CommittedPages); ExtractValue(MmPeakCommitment, TriageInformation.CommittedPagesPeak); ExtractValue(MmTotalCommitLimitMaximum, TriageInformation.CommitLimitMaximum); memcpy(pb, &TriageInformation, sizeof(TriageInformation)); }
|