Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4986 lines
155 KiB

//----------------------------------------------------------------------------
//
// 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));
}