Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3878 lines
96 KiB

/*++
Copyright(c) 1999-2002 Microsoft Corporation
Module Name:
minidump.c
Abstract:
Minidump user-mode crashdump support.
Author:
Matthew D Hendel (math) 20-Aug-1999
--*/
#include "pch.h"
#ifdef _WIN32_WCE
#include <time.h>
#endif
#include <limits.h>
#include <dbgver.h>
#include "mprivate.h"
#include "impl.h"
PINTERNAL_MODULE
ModuleContainingAddress(
IN PINTERNAL_PROCESS Process,
IN ULONG64 Address
)
{
PINTERNAL_MODULE Module;
PLIST_ENTRY ModuleEntry;
ModuleEntry = Process->ModuleList.Flink;
while ( ModuleEntry != &Process->ModuleList ) {
Module = CONTAINING_RECORD (ModuleEntry, INTERNAL_MODULE,
ModulesLink);
ModuleEntry = ModuleEntry->Flink;
if (Address >= Module->BaseOfImage &&
Address < Module->BaseOfImage + Module->SizeOfImage) {
return Module;
}
}
return NULL;
}
VOID
ScanMemoryForModuleRefs(
IN PINTERNAL_PROCESS Process,
IN HANDLE hProcess,
IN ULONG64 Base,
IN ULONG Size,
IN PVOID MemBuffer,
IN MEMBLOCK_TYPE TypeOfMemory,
IN BOOL FilterContent
)
{
PULONG_PTR CurMem;
SIZE_T Done;
// We only want to scan certain kinds of memory.
if (TypeOfMemory != MEMBLOCK_STACK &&
TypeOfMemory != MEMBLOCK_STORE &&
TypeOfMemory != MEMBLOCK_DATA_SEG &&
TypeOfMemory != MEMBLOCK_INDIRECT)
{
return;
}
// If the base address is not pointer-size aligned
// we can't easily assume that this is a meaningful
// area of memory to scan for references. Normal
// stack and store addresses will always be pointer
// size aligned so this should only reject invalid
// addresses.
if (!Base || !Size || (Base & (sizeof(PVOID) - 1))) {
return;
}
if (hProcess) {
if (!ReadProcessMemory(hProcess, (PVOID)(ULONG_PTR)Base,
MemBuffer, Size, &Done)) {
return;
}
} else {
Done = Size;
}
CurMem = (PULONG_PTR)MemBuffer;
Done /= sizeof(PVOID);
while (Done-- > 0) {
PINTERNAL_MODULE Module;
BOOL InAny;
#ifdef _IA64_
// An IA64 backing store can contain PFS values
// that must be preserved in order to allow stack walking.
// The high two bits of PFS are the privilege level, which
// should always be 0y11 for user-mode code so we use this
// as a marker to look for PFS entries.
// There is also a NAT collection flush at every 0x1F8
// offset. These values cannot be filtered.
if (TypeOfMemory == MEMBLOCK_STORE) {
if ((Base & 0x1f8) == 0x1f8 ||
(*CurMem & 0xc000000000000000UI64) == 0xc000000000000000UI64) {
goto Next;
}
}
#endif
InAny = FALSE;
if (Module = ModuleContainingAddress(Process, SIGN_EXTEND(*CurMem))) {
Module->WriteFlags |= ModuleReferencedByMemory;
InAny = TRUE;
}
// If the current pointer is not a module reference
// or an internal reference for a thread stack or store,
// filter it.
if (FilterContent && !InAny) {
PINTERNAL_THREAD Thread;
PLIST_ENTRY ThreadEntry;
ThreadEntry = Process->ThreadList.Flink;
while ( ThreadEntry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (ThreadEntry, INTERNAL_THREAD,
ThreadsLink);
ThreadEntry = ThreadEntry->Flink;
if ((*CurMem >= (ULONG_PTR)Thread->StackEnd &&
*CurMem < (ULONG_PTR)Thread->StackBase) ||
(*CurMem >= (ULONG_PTR)Thread->BackingStoreBase &&
*CurMem < (ULONG_PTR)Thread->BackingStoreBase +
Thread->BackingStoreSize)) {
InAny = TRUE;
break;
}
}
if (!InAny) {
*CurMem = 0;
}
}
#ifdef _IA64_
Next:
#endif
CurMem++;
Base += sizeof(ULONG_PTR);
}
}
BOOL
WriteAtOffset(
IN HANDLE hFile,
ULONG Offset,
PVOID Buffer,
ULONG BufferSize
)
{
BOOL Succ;
DWORD OffsetRet;
ULONG BytesWritten;
OffsetRet = SetFilePointer (
hFile,
Offset,
NULL,
FILE_BEGIN
);
if ( OffsetRet != Offset ) {
return FALSE;
}
Succ = WriteFile (hFile,
Buffer,
BufferSize,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != BufferSize ) {
return FALSE;
}
return TRUE;
}
BOOL
WriteOther(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PVOID Buffer,
IN ULONG SizeOfBuffer,
OUT ULONG * BufferRva
)
/*++
Routine Description:
Write the buffer to the Other stream of the file.
Arguments:
FileHandle - A file handle opened for writing.
StreamInfo - Minidump size information structure.
Buffer - The buffer to write.
SizeOfBuffer - The size of the buffer to write.
BufferRva - The RVA in the file that the buffer was written to.
Return Values:
TRUE - Success.
FALSE - Failure.
--*/
{
BOOL Succ;
ULONG Rva;
ULONG BytesWritten;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (Buffer != NULL);
ASSERT (SizeOfBuffer != 0);
//
// If it's larger than we've allocated space for, fail.
//
Rva = StreamInfo->RvaForCurOther;
if (Rva + SizeOfBuffer >
StreamInfo->RvaOfOther + StreamInfo->SizeOfOther) {
return FALSE;
}
//
// Set location to point at which we want to write and write.
//
Succ = SetFilePointer (
FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (FileHandle,
Buffer,
SizeOfBuffer,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfBuffer ) {
return FALSE;
}
if ( BufferRva ) {
*BufferRva = Rva;
}
StreamInfo->RvaForCurOther += SizeOfBuffer;
return TRUE;
}
BOOL
WriteMemory(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PVOID Buffer,
IN ULONG64 StartOfRegion,
IN ULONG SizeOfRegion,
OUT ULONG * MemoryDataRva OPTIONAL
)
/*++
Routine Description:
Write MEMORY_DATA and MEMORY_LIST entries to to the dump file for the
memory range described by (StartOfRange, MemoryData, SizeOfMemoryData).
Arguments:
FileHandle - Handle of the minidump file we will write to.
StreamInfo - Pre-computed minidump size information.
Buffer -
StartOfRegion -
SizeOfRegion -
MemoryDataRva - On success, the RVA in the file where the memory
data was written will be returned in this variable.
Return Values:
TRUE - Success.
FALSE - Failure.
--*/
{
BOOL Succ;
ULONG BytesWritten;
ULONG DataRva;
ULONG ListRva;
ULONG SizeOfMemoryDescriptor;
MINIDUMP_MEMORY_DESCRIPTOR Descriptor;
ASSERT ( FileHandle != NULL && FileHandle != INVALID_HANDLE_VALUE );
ASSERT ( StreamInfo != NULL );
ASSERT ( Buffer != NULL );
ASSERT ( StartOfRegion != 0 );
ASSERT ( SizeOfRegion != 0 );
//
// Writing a memory entry is a little different. When a memory entry
// is written we need a descriptor in the memory list describing the
// memory written AND a variable-sized entry in the MEMORY_DATA region
// with the actual data.
//
ListRva = StreamInfo->RvaForCurMemoryDescriptor;
DataRva = StreamInfo->RvaForCurMemoryData;
SizeOfMemoryDescriptor = sizeof (MINIDUMP_MEMORY_DESCRIPTOR);
//
// If we overflowed either the memory list or the memory data
// regions, fail.
//
if ( ( ListRva + SizeOfMemoryDescriptor >
StreamInfo->RvaOfMemoryDescriptors + StreamInfo->SizeOfMemoryDescriptors) ||
( DataRva + SizeOfRegion >
StreamInfo->RvaOfMemoryData + StreamInfo->SizeOfMemoryData ) ) {
return FALSE;
}
//
// First, write the data to the MEMORY_DATA region.
//
Succ = SetFilePointer (
FileHandle,
DataRva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if (!Succ) {
return FALSE;
}
Succ = WriteFile (FileHandle,
Buffer,
SizeOfRegion,
&BytesWritten,
NULL
);
if (!Succ || BytesWritten != SizeOfRegion) {
return FALSE;
}
//
// Then update the memory descriptor in the MEMORY_LIST region.
//
Descriptor.StartOfMemoryRange = StartOfRegion;
Descriptor.Memory.DataSize = SizeOfRegion;
Descriptor.Memory.Rva = DataRva;
Succ = SetFilePointer (
FileHandle,
ListRva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (
FileHandle,
&Descriptor,
SizeOfMemoryDescriptor,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfMemoryDescriptor) {
return FALSE;
}
//
// Update both the List Rva and the Data Rva and return the
// the Data Rva.
//
StreamInfo->RvaForCurMemoryDescriptor += SizeOfMemoryDescriptor;
StreamInfo->RvaForCurMemoryData += SizeOfRegion;
if ( MemoryDataRva ) {
*MemoryDataRva = DataRva;
}
return TRUE;
}
BOOL
WriteMemoryFromProcess(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process,
IN PVOID BaseOfRegion,
IN ULONG SizeOfRegion,
IN BOOL FilterContent,
IN MEMBLOCK_TYPE TypeOfMemory,
OUT ULONG * MemoryDataRva OPTIONAL
)
{
BOOL Ret = FALSE;
BOOL Succ;
PVOID Buffer;
SIZE_T BytesRead = 0;
Buffer = AllocMemory ( SizeOfRegion );
if (Buffer) {
Succ = ReadProcessMemory (
Process->ProcessHandle,
BaseOfRegion,
Buffer,
SizeOfRegion,
&BytesRead);
if (Succ && (BytesRead == SizeOfRegion)) {
if (FilterContent) {
ScanMemoryForModuleRefs(Process, NULL,
SIGN_EXTEND(BaseOfRegion),
SizeOfRegion, Buffer, TypeOfMemory,
TRUE);
}
Ret = WriteMemory (
FileHandle,
StreamInfo,
Buffer,
SIGN_EXTEND (BaseOfRegion),
SizeOfRegion,
MemoryDataRva);
}
FreeMemory(Buffer);
}
return Ret;
}
BOOL
WriteThread(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN LPVOID ThreadData,
IN ULONG SizeOfThreadData,
OUT ULONG * ThreadDataRva OPTIONAL
)
{
BOOL Succ;
ULONG Rva;
ULONG BytesWritten;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (StreamInfo);
ASSERT (ThreadData);
Rva = StreamInfo->RvaForCurThread;
if ( Rva + SizeOfThreadData >
StreamInfo->RvaOfThreadList + StreamInfo->SizeOfThreadList ) {
return FALSE;
}
Succ = SetFilePointer (
FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (
FileHandle,
ThreadData,
SizeOfThreadData,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfThreadData ) {
return FALSE;
}
if ( ThreadDataRva ) {
*ThreadDataRva = Rva;
}
StreamInfo->RvaForCurThread += SizeOfThreadData;
return TRUE;
}
BOOL
WriteStringToPool(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PWSTR String,
OUT ULONG * StringRva
)
{
BOOL Succ;
ULONG BytesWritten;
ULONG32 StringLen;
ULONG SizeOfString;
ULONG Rva;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (String);
ASSERT (sizeof (ULONG32) == sizeof (MINIDUMP_STRING));
StringLen = lstrlenW ( String ) * sizeof (WCHAR);
SizeOfString = sizeof (MINIDUMP_STRING) + StringLen + sizeof (WCHAR);
Rva = StreamInfo->RvaForCurString;
if ( Rva + SizeOfString >
StreamInfo->RvaOfStringPool + StreamInfo->SizeOfStringPool ) {
return FALSE;
}
Succ = SetFilePointer (
FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (
FileHandle,
&StringLen,
sizeof (StringLen),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (StringLen) ) {
return FALSE;
}
//
// Possible alignment problems on 64-bit machines??
//
//
// Include the trailing '\000'.
//
StringLen += sizeof (WCHAR);
Succ = WriteFile (
FileHandle,
String,
StringLen,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != StringLen ) {
return FALSE;
}
if ( StringRva ) {
*StringRva = Rva;
}
StreamInfo->RvaForCurString += SizeOfString;
return TRUE;
}
BOOL
WriteModule (
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PMINIDUMP_MODULE Module,
OUT ULONG * ModuleRva
)
{
BOOL Succ;
ULONG Rva;
ULONG BytesWritten;
ULONG SizeOfModule;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (StreamInfo);
ASSERT (Module);
SizeOfModule = sizeof (MINIDUMP_MODULE);
Rva = StreamInfo->RvaForCurModule;
if ( Rva + SizeOfModule >
StreamInfo->RvaOfModuleList + StreamInfo->SizeOfModuleList ) {
return FALSE;
}
Succ = SetFilePointer (FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (FileHandle,
Module,
SizeOfModule,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfModule ) {
return FALSE;
}
if ( ModuleRva ) {
*ModuleRva = Rva;
}
StreamInfo->RvaForCurModule += SizeOfModule;
return TRUE;
}
BOOL
WriteUnloadedModule (
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PMINIDUMP_UNLOADED_MODULE Module,
OUT ULONG * ModuleRva
)
{
BOOL Succ;
ULONG Rva;
ULONG BytesWritten;
ULONG SizeOfModule;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (StreamInfo);
ASSERT (Module);
SizeOfModule = sizeof (*Module);
Rva = StreamInfo->RvaForCurUnloadedModule;
if ( Rva + SizeOfModule >
StreamInfo->RvaOfUnloadedModuleList +
StreamInfo->SizeOfUnloadedModuleList ) {
return FALSE;
}
Succ = SetFilePointer (FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (FileHandle,
Module,
SizeOfModule,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfModule ) {
return FALSE;
}
if ( ModuleRva ) {
*ModuleRva = Rva;
}
StreamInfo->RvaForCurUnloadedModule += SizeOfModule;
return TRUE;
}
BOOL
WriteThreadList(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process,
IN ULONG DumpType
)
/*++
Routine Description:
Write the thread list to the dump file. This includes the thread, and
optionally the context and memory for the thread.
Return Values:
TRUE - The thread list was successfully written.
FALSE - There was an error writing the thread list.
--*/
{
BOOL Succ;
ULONG StackMemoryRva;
ULONG StoreMemoryRva;
ULONG ContextRva;
MINIDUMP_THREAD_EX DumpThread;
PINTERNAL_THREAD Thread;
ULONG NumberOfThreads;
ULONG BytesWritten;
PLIST_ENTRY Entry;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (Process);
ASSERT (StreamInfo);
//
// Write the thread count.
//
NumberOfThreads = Process->NumberOfThreadsToWrite;
Succ = SetFilePointer (
FileHandle,
StreamInfo->RvaOfThreadList,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile ( FileHandle,
&NumberOfThreads,
sizeof (NumberOfThreads),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (NumberOfThreads) ) {
return FALSE;
}
StreamInfo->RvaForCurThread += BytesWritten;
//
// Iterate over the thread list writing the description,
// context and memory for each thread.
//
Entry = Process->ThreadList.Flink;
while ( Entry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (Entry,
INTERNAL_THREAD,
ThreadsLink);
Entry = Entry->Flink;
//
// Only write the threads that have been flagged to be written.
//
if (IsFlagClear (Thread->WriteFlags, ThreadWriteThread)) {
continue;
}
//
// Write the context if it was flagged to be written.
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteContext)) {
//
// Write the thread context to the OTHER stream.
//
Succ = WriteOther (
FileHandle,
StreamInfo,
&Thread->Context,
Thread->SizeOfContext,
&ContextRva
);
if ( !Succ ) {
return FALSE;
}
} else {
ContextRva = 0;
}
//
// Write the stack if it was flagged to be written.
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteStack)) {
//
// Write the stack memory data; write it directly from the image.
//
Succ = WriteMemoryFromProcess(
FileHandle,
StreamInfo,
Process,
(PVOID) Thread->StackEnd,
(ULONG) (Thread->StackBase - Thread->StackEnd),
IsFlagSet(DumpType, MiniDumpFilterMemory),
MEMBLOCK_STACK,
&StackMemoryRva
);
if ( !Succ ) {
return FALSE;
}
} else {
StackMemoryRva = 0;
}
//
// Write the backing store if it was flagged to be written.
// A newly created thread's backing store may be empty
// so handle the case of zero size.
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteBackingStore) &&
Thread->BackingStoreSize) {
//
// Write the store memory data; write it directly from the image.
//
Succ = WriteMemoryFromProcess(
FileHandle,
StreamInfo,
Process,
(PVOID) Thread->BackingStoreBase,
Thread->BackingStoreSize,
IsFlagSet(DumpType, MiniDumpFilterMemory),
MEMBLOCK_STORE,
&StoreMemoryRva
);
if ( !Succ ) {
return FALSE;
}
} else {
StoreMemoryRva = 0;
}
//
// Build the dump thread.
//
DumpThread.ThreadId = Thread->ThreadId;
DumpThread.SuspendCount = Thread->SuspendCount;
DumpThread.PriorityClass = Thread->PriorityClass;
DumpThread.Priority = Thread->Priority;
DumpThread.Teb = Thread->Teb;
//
// Stack offset and size.
//
DumpThread.Stack.StartOfMemoryRange = Thread->StackEnd;
DumpThread.Stack.Memory.DataSize =
(ULONG) ( Thread->StackBase - Thread->StackEnd );
DumpThread.Stack.Memory.Rva = StackMemoryRva;
//
// Backing store offset and size.
//
DumpThread.BackingStore.StartOfMemoryRange = Thread->BackingStoreBase;
DumpThread.BackingStore.Memory.DataSize = Thread->BackingStoreSize;
DumpThread.BackingStore.Memory.Rva = StoreMemoryRva;
//
// Context offset and size.
//
DumpThread.ThreadContext.DataSize = Thread->SizeOfContext;
DumpThread.ThreadContext.Rva = ContextRva;
//
// Write the dump thread to the threads region.
//
Succ = WriteThread (
FileHandle,
StreamInfo,
&DumpThread,
StreamInfo->ThreadStructSize,
NULL
);
}
return TRUE;
}
BOOL
WriteModuleList(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process
)
{
BOOL Succ;
MINIDUMP_MODULE DumpModule;
ULONG StringRva;
ULONG CvRecordRva;
ULONG MiscRecordRva;
PLIST_ENTRY Entry;
PINTERNAL_MODULE Module;
ULONG32 NumberOfModules;
ULONG BytesWritten;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (Process);
ASSERT (StreamInfo);
NumberOfModules = Process->NumberOfModulesToWrite;
Succ = SetFilePointer (
FileHandle,
StreamInfo->RvaForCurModule,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile ( FileHandle,
&NumberOfModules,
sizeof (NumberOfModules),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (NumberOfModules) ) {
return FALSE;
}
StreamInfo->RvaForCurModule += sizeof (NumberOfModules);
//
// Iterate through the module list writing the module name, module entry
// and module debug info to the dump file.
//
Entry = Process->ModuleList.Flink;
while ( Entry != &Process->ModuleList ) {
Module = CONTAINING_RECORD (Entry,
INTERNAL_MODULE,
ModulesLink);
Entry = Entry->Flink;
//
// If we are not to write information for this module, just continue.
//
if (IsFlagClear (Module->WriteFlags, ModuleWriteModule)) {
continue;
}
//
// Write module name.
//
Succ = WriteStringToPool (
FileHandle,
StreamInfo,
Module->SavePath,
&StringRva
);
if ( !Succ ) {
return FALSE;
}
//
// Write CvRecord for a module into the OTHER region.
//
if ( IsFlagSet (Module->WriteFlags, ModuleWriteCvRecord) &&
Module->CvRecord != NULL && Module->SizeOfCvRecord != 0 ) {
Succ = WriteOther (
FileHandle,
StreamInfo,
Module->CvRecord,
Module->SizeOfCvRecord,
&CvRecordRva
);
if ( !Succ) {
return FALSE;
}
} else {
CvRecordRva = 0;
}
if ( IsFlagSet (Module->WriteFlags, ModuleWriteMiscRecord) &&
Module->MiscRecord != NULL && Module->SizeOfMiscRecord != 0 ) {
Succ = WriteOther (
FileHandle,
StreamInfo,
Module->MiscRecord,
Module->SizeOfMiscRecord,
&MiscRecordRva
);
if ( !Succ ) {
return FALSE;
}
} else {
MiscRecordRva = 0;
}
DumpModule.BaseOfImage = Module->BaseOfImage;
DumpModule.SizeOfImage = Module->SizeOfImage;
DumpModule.CheckSum = Module->CheckSum;
DumpModule.TimeDateStamp = Module->TimeDateStamp;
DumpModule.VersionInfo = Module->VersionInfo;
DumpModule.CvRecord.Rva = CvRecordRva;
DumpModule.CvRecord.DataSize = Module->SizeOfCvRecord;
DumpModule.MiscRecord.Rva = MiscRecordRva;
DumpModule.MiscRecord.DataSize = Module->SizeOfMiscRecord;
DumpModule.ModuleNameRva = StringRva;
DumpModule.Reserved0 = 0;
DumpModule.Reserved1 = 0;
//
// Write the module entry itself.
//
Succ = WriteModule (
FileHandle,
StreamInfo,
&DumpModule,
NULL
);
if ( !Succ ) {
return FALSE;
}
}
return TRUE;
}
BOOL
WriteUnloadedModuleList(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process
)
{
BOOL Succ;
MINIDUMP_UNLOADED_MODULE_LIST DumpModuleList;
MINIDUMP_UNLOADED_MODULE DumpModule;
ULONG StringRva;
PLIST_ENTRY Entry;
PINTERNAL_UNLOADED_MODULE Module;
ULONG32 NumberOfModules;
ULONG BytesWritten;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (Process);
ASSERT (StreamInfo);
if (IsListEmpty(&Process->UnloadedModuleList)) {
// Nothing to write.
return TRUE;
}
NumberOfModules = Process->NumberOfUnloadedModules;
Succ = SetFilePointer (
FileHandle,
StreamInfo->RvaForCurUnloadedModule,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
DumpModuleList.SizeOfHeader = sizeof(DumpModuleList);
DumpModuleList.SizeOfEntry = sizeof(DumpModule);
DumpModuleList.NumberOfEntries = NumberOfModules;
Succ = WriteFile ( FileHandle,
&DumpModuleList,
sizeof (DumpModuleList),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (DumpModuleList) ) {
return FALSE;
}
StreamInfo->RvaForCurUnloadedModule += sizeof (DumpModuleList);
//
// Iterate through the module list writing the module name, module entry
// and module debug info to the dump file.
//
Entry = Process->UnloadedModuleList.Flink;
while ( Entry != &Process->UnloadedModuleList ) {
Module = CONTAINING_RECORD (Entry,
INTERNAL_UNLOADED_MODULE,
ModulesLink);
Entry = Entry->Flink;
//
// Write module name.
//
Succ = WriteStringToPool (
FileHandle,
StreamInfo,
Module->Path,
&StringRva
);
if ( !Succ ) {
return FALSE;
}
DumpModule.BaseOfImage = Module->BaseOfImage;
DumpModule.SizeOfImage = Module->SizeOfImage;
DumpModule.CheckSum = Module->CheckSum;
DumpModule.TimeDateStamp = Module->TimeDateStamp;
DumpModule.ModuleNameRva = StringRva;
//
// Write the module entry itself.
//
Succ = WriteUnloadedModule(FileHandle,
StreamInfo,
&DumpModule,
NULL);
if ( !Succ ) {
return FALSE;
}
}
return TRUE;
}
#define FUNCTION_TABLE_ALIGNMENT 8
BOOL
WriteFunctionTableList(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process
)
{
BOOL Succ;
MINIDUMP_FUNCTION_TABLE_STREAM TableStream;
MINIDUMP_FUNCTION_TABLE_DESCRIPTOR DumpTable;
PLIST_ENTRY Entry;
PINTERNAL_FUNCTION_TABLE Table;
ULONG BytesWritten;
RVA PrevRva, Rva;
ASSERT (FileHandle && FileHandle != INVALID_HANDLE_VALUE);
ASSERT (Process);
ASSERT (StreamInfo);
if (IsListEmpty(&Process->FunctionTableList)) {
// Nothing to write.
return TRUE;
}
Rva = StreamInfo->RvaOfFunctionTableList;
Succ = SetFilePointer (
FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
TableStream.SizeOfHeader = sizeof(TableStream);
TableStream.SizeOfDescriptor = sizeof(DumpTable);
TableStream.SizeOfNativeDescriptor = sizeof(DYNAMIC_FUNCTION_TABLE);
TableStream.SizeOfFunctionEntry = sizeof(RUNTIME_FUNCTION);
TableStream.NumberOfDescriptors = Process->NumberOfFunctionTables;
// Ensure that the actual descriptors are 8-byte aligned in
// the overall file.
Rva += sizeof(TableStream);
PrevRva = Rva;
Rva = (Rva + FUNCTION_TABLE_ALIGNMENT - 1) &
~(FUNCTION_TABLE_ALIGNMENT - 1);
TableStream.SizeOfAlignPad = Rva - PrevRva;
Succ = WriteFile ( FileHandle,
&TableStream,
sizeof (TableStream),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (TableStream) ) {
return FALSE;
}
//
// Iterate through the function table list
// and write out the table data.
//
Entry = Process->FunctionTableList.Flink;
while ( Entry != &Process->FunctionTableList ) {
Table = CONTAINING_RECORD (Entry,
INTERNAL_FUNCTION_TABLE,
TableLink);
Entry = Entry->Flink;
// Move to aligned RVA.
Succ = SetFilePointer (FileHandle,
Rva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
DumpTable.MinimumAddress = Table->MinimumAddress;
DumpTable.MaximumAddress = Table->MaximumAddress;
DumpTable.BaseAddress = Table->BaseAddress;
DumpTable.EntryCount = Table->EntryCount;
Rva += sizeof(DumpTable) + sizeof(DYNAMIC_FUNCTION_TABLE) +
sizeof(RUNTIME_FUNCTION) * Table->EntryCount;
PrevRva = Rva;
Rva = (Rva + FUNCTION_TABLE_ALIGNMENT - 1) &
~(FUNCTION_TABLE_ALIGNMENT - 1);
DumpTable.SizeOfAlignPad = Rva - PrevRva;
Succ = WriteFile ( FileHandle,
&DumpTable,
sizeof (DumpTable),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (DumpTable) ) {
return FALSE;
}
Succ = WriteFile ( FileHandle,
&Table->RawTable,
sizeof (Table->RawTable),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (Table->RawTable) ) {
return FALSE;
}
Succ = WriteFile ( FileHandle,
Table->RawEntries,
sizeof (RUNTIME_FUNCTION) * Table->EntryCount,
&BytesWritten,
NULL
);
if ( !Succ ||
BytesWritten != sizeof (RUNTIME_FUNCTION) * Table->EntryCount ) {
return FALSE;
}
}
return TRUE;
}
BOOL
WriteMemoryBlocks(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process
)
{
PLIST_ENTRY ScanEntry;
PVA_RANGE Scan;
ScanEntry = Process->MemoryBlocks.Flink;
while (ScanEntry != &Process->MemoryBlocks) {
Scan = CONTAINING_RECORD(ScanEntry, VA_RANGE, NextLink);
ScanEntry = Scan->NextLink.Flink;
if (!WriteMemoryFromProcess(FileHandle,
StreamInfo,
Process,
(PVOID)(ULONG_PTR)Scan->Start,
Scan->Size,
FALSE,
Scan->Type,
NULL)) {
return FALSE;
}
}
return TRUE;
}
BOOL
CalculateSizeForThreads(
IN PINTERNAL_PROCESS Process,
IN OUT MINIDUMP_STREAM_INFO * StreamInfo
)
{
ULONG SizeOfContexts;
ULONG SizeOfMemRegions;
ULONG SizeOfThreads;
ULONG SizeOfMemoryDescriptors;
ULONG NumberOfThreads;
ULONG NumberOfMemRegions;
PINTERNAL_THREAD Thread;
PLIST_ENTRY Entry;
ASSERT (Process);
ASSERT (StreamInfo);
NumberOfThreads = 0;
NumberOfMemRegions = 0;
SizeOfContexts = 0;
SizeOfMemRegions = 0;
// If no backing store information is written a normal
// MINIDUMP_THREAD can be used, otherwise a MINIDUMP_THREAD_EX
// is required.
StreamInfo->ThreadStructSize = sizeof(MINIDUMP_THREAD);
Entry = Process->ThreadList.Flink;
while ( Entry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (Entry,
INTERNAL_THREAD,
ThreadsLink);
Entry = Entry->Flink;
//
// Do we need to write any information for this thread at all?
//
if (IsFlagClear (Thread->WriteFlags, ThreadWriteThread)) {
continue;
}
NumberOfThreads++;
//
// Write a context for this thread?
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteContext)) {
SizeOfContexts += Thread->SizeOfContext;
}
//
// Write a stack for this thread?
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteStack)) {
NumberOfMemRegions++;
SizeOfMemRegions += (ULONG) (Thread->StackBase - Thread->StackEnd);
}
//
// Write the backing store for this thread?
//
if (IsFlagSet (Thread->WriteFlags, ThreadWriteBackingStore)) {
// A newly created thread's backing store may be empty
// so handle the case of zero size.
if (Thread->BackingStoreSize) {
NumberOfMemRegions++;
SizeOfMemRegions += Thread->BackingStoreSize;
}
// We still need a THREAD_EX as this is a platform
// which supports backing store.
StreamInfo->ThreadStructSize = sizeof(MINIDUMP_THREAD_EX);
}
// Write an instruction window for this thread?
if (IsFlagSet (Thread->WriteFlags, ThreadWriteInstructionWindow)) {
GenGetThreadInstructionWindow(Process, Thread);
}
// Write thread data for this thread?
if (IsFlagSet (Thread->WriteFlags, ThreadWriteThreadData) &&
Thread->SizeOfTeb) {
GenAddMemoryBlock(Process, MEMBLOCK_TEB,
Thread->Teb, Thread->SizeOfTeb);
}
}
Process->NumberOfThreadsToWrite = NumberOfThreads;
//
// Nobody should have allocated memory from the thread list region yet.
//
ASSERT (StreamInfo->SizeOfThreadList == 0);
SizeOfThreads = NumberOfThreads * StreamInfo->ThreadStructSize;
SizeOfMemoryDescriptors = NumberOfMemRegions *
sizeof (MINIDUMP_MEMORY_DESCRIPTOR);
StreamInfo->SizeOfThreadList += sizeof (ULONG32);
StreamInfo->SizeOfThreadList += SizeOfThreads;
StreamInfo->SizeOfOther += SizeOfContexts;
StreamInfo->SizeOfMemoryData += SizeOfMemRegions;
StreamInfo->SizeOfMemoryDescriptors += SizeOfMemoryDescriptors;
return TRUE;
}
BOOL
CalculateSizeForModules(
IN PINTERNAL_PROCESS Process,
IN OUT MINIDUMP_STREAM_INFO * StreamInfo
)
/*++
Routine Description:
Calculate amount of space needed in the string pool, the memory table and
the module list table for module information.
Arguments:
Process - Minidump process information.
StreamInfo - The stream size information for this dump.
Return Values:
TRUE - Success.
FALSE - Failure.
--*/
{
ULONG NumberOfModules;
ULONG SizeOfDebugInfo;
ULONG SizeOfStringData;
PINTERNAL_MODULE Module;
PLIST_ENTRY Entry;
ASSERT (Process);
ASSERT (StreamInfo);
NumberOfModules = 0;
SizeOfDebugInfo = 0;
SizeOfStringData = 0;
Entry = Process->ModuleList.Flink;
while ( Entry != &Process->ModuleList ) {
Module = CONTAINING_RECORD (Entry, INTERNAL_MODULE, ModulesLink);
Entry = Entry->Flink;
if (IsFlagClear (Module->WriteFlags, ModuleWriteModule)) {
continue;
}
NumberOfModules++;
SizeOfStringData += ( lstrlenW ( Module->SavePath ) + 1 ) * sizeof (WCHAR);
SizeOfStringData += sizeof ( MINIDUMP_STRING );
//
// Add in the sizes of both the CV and MISC records.
//
if (IsFlagSet (Module->WriteFlags, ModuleWriteCvRecord)) {
SizeOfDebugInfo += Module->SizeOfCvRecord;
}
if (IsFlagSet (Module->WriteFlags, ModuleWriteMiscRecord)) {
SizeOfDebugInfo += Module->SizeOfMiscRecord;
}
//
// Add the module data sections if requested.
//
if (IsFlagSet (Module->WriteFlags, ModuleWriteDataSeg)) {
GenGetDataContributors(Process, Module);
}
}
Process->NumberOfModulesToWrite = NumberOfModules;
ASSERT (StreamInfo->SizeOfModuleList == 0);
StreamInfo->SizeOfModuleList += sizeof (MINIDUMP_MODULE_LIST);
StreamInfo->SizeOfModuleList += (NumberOfModules * sizeof (MINIDUMP_MODULE));
StreamInfo->SizeOfStringPool += SizeOfStringData;
StreamInfo->SizeOfOther += SizeOfDebugInfo;
return TRUE;
}
BOOL
CalculateSizeForUnloadedModules(
IN PINTERNAL_PROCESS Process,
IN OUT MINIDUMP_STREAM_INFO * StreamInfo
)
{
ULONG SizeOfStringData;
PINTERNAL_UNLOADED_MODULE Module;
PLIST_ENTRY Entry;
ASSERT (Process);
ASSERT (StreamInfo);
SizeOfStringData = 0;
Entry = Process->UnloadedModuleList.Flink;
while ( Entry != &Process->UnloadedModuleList ) {
Module = CONTAINING_RECORD (Entry, INTERNAL_UNLOADED_MODULE,
ModulesLink);
Entry = Entry->Flink;
SizeOfStringData += ( lstrlenW ( Module->Path ) + 1 ) * sizeof (WCHAR);
SizeOfStringData += sizeof ( MINIDUMP_STRING );
}
ASSERT (StreamInfo->SizeOfUnloadedModuleList == 0);
StreamInfo->SizeOfUnloadedModuleList +=
sizeof (MINIDUMP_UNLOADED_MODULE_LIST);
StreamInfo->SizeOfUnloadedModuleList +=
(Process->NumberOfUnloadedModules * sizeof (MINIDUMP_UNLOADED_MODULE));
StreamInfo->SizeOfStringPool += SizeOfStringData;
return TRUE;
}
BOOL
CalculateSizeForFunctionTables(
IN PINTERNAL_PROCESS Process,
IN OUT MINIDUMP_STREAM_INFO * StreamInfo
)
{
ULONG SizeOfTableData;
PINTERNAL_FUNCTION_TABLE Table;
PLIST_ENTRY Entry;
ASSERT (Process);
ASSERT (StreamInfo);
SizeOfTableData = 0;
Entry = Process->FunctionTableList.Flink;
while ( Entry != &Process->FunctionTableList ) {
Table = CONTAINING_RECORD (Entry, INTERNAL_FUNCTION_TABLE, TableLink);
Entry = Entry->Flink;
// Alignment space is required as the structures
// in the stream must be properly aligned.
SizeOfTableData += FUNCTION_TABLE_ALIGNMENT +
sizeof(MINIDUMP_FUNCTION_TABLE_DESCRIPTOR) +
sizeof(DYNAMIC_FUNCTION_TABLE) +
Table->EntryCount * sizeof(RUNTIME_FUNCTION);
}
ASSERT (StreamInfo->SizeOfFunctionTableList == 0);
StreamInfo->SizeOfFunctionTableList +=
sizeof (MINIDUMP_FUNCTION_TABLE_STREAM) + SizeOfTableData;
return TRUE;
}
BOOL
WriteDirectoryEntry(
IN HANDLE hFile,
IN ULONG StreamType,
IN ULONG RvaOfDir,
IN SIZE_T SizeOfDir
)
{
BOOL Succ;
ULONG BytesWritten;
MINIDUMP_DIRECTORY Dir;
//
// Do not write empty streams.
//
if (SizeOfDir == 0) {
return TRUE;
}
//
// The maximum size of a directory is a ULONG.
//
if (SizeOfDir > _UI32_MAX) {
return FALSE;
}
Dir.StreamType = StreamType;
Dir.Location.Rva = RvaOfDir;
Dir.Location.DataSize = (ULONG) SizeOfDir;
Succ = WriteFile ( hFile,
&Dir,
sizeof (Dir),
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != sizeof (Dir) ) {
return FALSE;
}
return TRUE;
}
VOID
ScanContextForModuleRefs(
IN PINTERNAL_PROCESS Process,
IN PINTERNAL_THREAD Thread
)
{
ULONG NumReg;
PULONG_PTR Reg;
PINTERNAL_MODULE Module;
#if defined(_X86_)
Reg = (PULONG_PTR)&Thread->Context.Edi;
NumReg = 11;
#elif defined(_IA64_)
Reg = (PULONG_PTR)&Thread->Context.IntGp;
NumReg = 41;
#elif defined(_AMD64_)
Reg = (PULONG_PTR)&Thread->Context.Rax;
NumReg = 17;
#elif defined(ARM)
Reg = (PULONG_PTR)&Thread->Context.R0;
NumReg = 16;
#else
#error "Unknown processor"
#endif
while (NumReg-- > 0) {
if (Module = ModuleContainingAddress(Process, SIGN_EXTEND(*Reg))) {
Module->WriteFlags |= ModuleReferencedByMemory;
}
Reg++;
}
}
BOOL
FilterOrScanMemory(
IN PINTERNAL_PROCESS Process,
IN PVOID MemBuffer
)
{
PINTERNAL_THREAD Thread;
PLIST_ENTRY ThreadEntry;
//
// Scan the stack and backing store
// memory for every thread.
//
ThreadEntry = Process->ThreadList.Flink;
while ( ThreadEntry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (ThreadEntry, INTERNAL_THREAD, ThreadsLink);
ThreadEntry = ThreadEntry->Flink;
ScanContextForModuleRefs(Process, Thread);
ScanMemoryForModuleRefs(Process, Process->ProcessHandle,
Thread->StackEnd,
(ULONG)(Thread->StackBase - Thread->StackEnd),
MemBuffer, MEMBLOCK_STACK, FALSE);
ScanMemoryForModuleRefs(Process, Process->ProcessHandle,
Thread->BackingStoreBase,
Thread->BackingStoreSize,
MemBuffer, MEMBLOCK_STORE, FALSE);
}
return TRUE;
}
#define IND_CAPTURE_SIZE (PAGE_SIZE / 4)
#define PRE_IND_CAPTURE_SIZE (IND_CAPTURE_SIZE / 4)
BOOL
AddIndirectMemory(
IN PINTERNAL_PROCESS Process,
IN ULONG64 Base,
IN ULONG Size,
IN PVOID MemBuffer
)
{
PULONG_PTR CurMem;
SIZE_T Done;
BOOL Succ = TRUE;
// If the base address is not pointer-size aligned
// we can't easily assume that this is a meaningful
// area of memory to scan for references. Normal
// stack and store addresses will always be pointer
// size aligned so this should only reject invalid
// addresses.
if (!Base || !Size || (Base & (sizeof(PVOID) - 1))) {
return TRUE;
}
if (!ReadProcessMemory(Process->ProcessHandle, (PVOID)(ULONG_PTR)Base,
MemBuffer, Size, &Done)) {
return FALSE;
}
CurMem = (PULONG_PTR)MemBuffer;
Done /= sizeof(PVOID);
while (Done-- > 0) {
ULONG64 Start;
//
// How much memory to save behind the pointer is an
// interesting question. The reference could be to
// an arbitrary amount of data, so we want to save
// a good chunk, but we don't want to end up saving
// full memory.
// Instead, pick an arbitrary size -- 1/4 of a page --
// and save some before and after the pointer.
//
Start = SIGN_EXTEND(*CurMem);
// If it's a pointer into an image assume doesn't
// need to be stored via this mechanism as it's either
// code, which will be mapped later; or data, which can
// be saved with MiniDumpWithDataSegs.
if (!ModuleContainingAddress(Process, Start)) {
if (Start < PRE_IND_CAPTURE_SIZE) {
Start = 0;
} else {
Start -= PRE_IND_CAPTURE_SIZE;
}
if (!GenAddMemoryBlock(Process, MEMBLOCK_INDIRECT,
Start, IND_CAPTURE_SIZE)) {
Succ = FALSE;
}
}
CurMem++;
}
return Succ;
}
BOOL
AddIndirectlyReferencedMemory(
IN PINTERNAL_PROCESS Process,
IN PVOID MemBuffer
)
{
PINTERNAL_THREAD Thread;
PLIST_ENTRY ThreadEntry;
//
// Scan the stack and backing store
// memory for every thread.
//
ThreadEntry = Process->ThreadList.Flink;
while ( ThreadEntry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (ThreadEntry, INTERNAL_THREAD, ThreadsLink);
ThreadEntry = ThreadEntry->Flink;
if (!AddIndirectMemory(Process,
Thread->StackEnd,
(ULONG)(Thread->StackBase - Thread->StackEnd),
MemBuffer)) {
return FALSE;
}
if (!AddIndirectMemory(Process,
Thread->BackingStoreBase,
Thread->BackingStoreSize,
MemBuffer)) {
return FALSE;
}
}
return TRUE;
}
BOOL
PostProcessInfo(
IN ULONG DumpType,
IN PINTERNAL_PROCESS Process
)
{
PVOID MemBuffer;
BOOL Succ = TRUE;
MemBuffer = AllocMemory(Process->MaxStackOrStoreSize);
if (!MemBuffer) {
return FALSE;
}
if (DumpType & (MiniDumpFilterMemory | MiniDumpScanMemory)) {
if (!FilterOrScanMemory(Process, MemBuffer)) {
Succ = FALSE;
}
}
if (Succ &&
(DumpType & MiniDumpWithIndirectlyReferencedMemory)) {
// Indirect memory is not crucial to the dump so
// ignore any failures.
AddIndirectlyReferencedMemory(Process, MemBuffer);
}
FreeMemory(MemBuffer);
return Succ;
}
BOOL
ExecuteCallbacks(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN PINTERNAL_PROCESS Process,
IN MINIDUMP_CALLBACK_ROUTINE CallbackRoutine,
IN PVOID CallbackParam
)
{
BOOL Succ;
PINTERNAL_MODULE Module;
PINTERNAL_THREAD Thread;
PLIST_ENTRY Entry;
MINIDUMP_CALLBACK_INPUT CallbackInput;
MINIDUMP_CALLBACK_OUTPUT CallbackOutput;
ASSERT ( hProcess != NULL );
ASSERT ( ProcessId != 0 );
ASSERT ( Process != NULL );
Thread = NULL;
Module = NULL;
//
// If there are no callbacks to call, then we are done.
//
if ( CallbackRoutine == NULL ) {
return TRUE;
}
CallbackInput.ProcessHandle = hProcess;
CallbackInput.ProcessId = ProcessId;
//
// Call callbacks for each module.
//
CallbackInput.CallbackType = ModuleCallback;
Entry = Process->ModuleList.Flink;
while ( Entry != &Process->ModuleList ) {
Module = CONTAINING_RECORD (Entry, INTERNAL_MODULE, ModulesLink);
Entry = Entry->Flink;
CallbackInput.Module.FullPath = Module->FullPath;
CallbackInput.Module.BaseOfImage = Module->BaseOfImage;
CallbackInput.Module.SizeOfImage = Module->SizeOfImage;
CallbackInput.Module.CheckSum = Module->CheckSum;
CallbackInput.Module.TimeDateStamp = Module->TimeDateStamp;
CopyMemory (&CallbackInput.Module.VersionInfo,
&Module->VersionInfo,
sizeof (CallbackInput.Module.VersionInfo)
);
CallbackInput.Module.CvRecord = Module->CvRecord;
CallbackInput.Module.SizeOfCvRecord = Module->SizeOfCvRecord;
CallbackInput.Module.MiscRecord = Module->MiscRecord;
CallbackInput.Module.SizeOfMiscRecord = Module->SizeOfMiscRecord;
CallbackOutput.ModuleWriteFlags = Module->WriteFlags;
Succ = CallbackRoutine (
CallbackParam,
&CallbackInput,
&CallbackOutput
);
//
// If the callback returned FALSE, quit now.
//
if ( !Succ ) {
return FALSE;
}
// Don't turn on any flags that weren't originally set.
Module->WriteFlags &= CallbackOutput.ModuleWriteFlags;
}
Module = NULL;
//
// Call callbacks for each thread.
//
#if !defined (DUMP_BACKING_STORE)
CallbackInput.CallbackType = ThreadCallback;
#else
CallbackInput.CallbackType = ThreadExCallback;
#endif
Entry = Process->ThreadList.Flink;
while ( Entry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (Entry, INTERNAL_THREAD, ThreadsLink);
Entry = Entry->Flink;
CallbackInput.ThreadEx.ThreadId = Thread->ThreadId;
CallbackInput.ThreadEx.ThreadHandle = Thread->ThreadHandle;
CallbackInput.ThreadEx.Context = Thread->Context;
CallbackInput.ThreadEx.SizeOfContext = Thread->SizeOfContext;
CallbackInput.ThreadEx.StackBase = Thread->StackBase;
CallbackInput.ThreadEx.StackEnd = Thread->StackEnd;
CallbackInput.ThreadEx.BackingStoreBase = Thread->BackingStoreBase;
CallbackInput.ThreadEx.BackingStoreEnd =
Thread->BackingStoreBase + Thread->BackingStoreSize;
CallbackOutput.ThreadWriteFlags = Thread->WriteFlags;
Succ = CallbackRoutine (
CallbackParam,
&CallbackInput,
&CallbackOutput
);
//
// If the callback returned FALSE, quit now.
//
if ( !Succ ) {
return FALSE;
}
// Don't turn on any flags that weren't originally set.
Thread->WriteFlags &= CallbackOutput.ThreadWriteFlags;
}
Thread = NULL;
return TRUE;
}
#if defined (i386)
BOOL
X86CpuId(
IN ULONG32 SubFunction,
OUT PULONG32 EaxRegister, OPTIONAL
OUT PULONG32 EbxRegister, OPTIONAL
OUT PULONG32 EcxRegister, OPTIONAL
OUT PULONG32 EdxRegister OPTIONAL
)
{
BOOL Succ;
ULONG32 _Eax;
ULONG32 _Ebx;
ULONG32 _Ecx;
ULONG32 _Edx;
_try {
_asm {
mov eax, SubFunction
_emit 0x0F
_emit 0xA2 ;; CPUID
mov _Eax, eax
mov _Ebx, ebx
mov _Ecx, ecx
mov _Edx, edx
}
if ( EaxRegister ) {
*EaxRegister = _Eax;
}
if ( EbxRegister ) {
*EbxRegister = _Ebx;
}
if ( EcxRegister ) {
*EcxRegister = _Ecx;
}
if ( EdxRegister ) {
*EdxRegister = _Edx;
}
Succ = TRUE;
}
_except ( EXCEPTION_EXECUTE_HANDLER ) {
Succ = FALSE;
}
return Succ;
}
VOID
GetCpuInformation(
PCPU_INFORMATION Cpu
)
/*++
Routine Description:
Get X86 specific CPU information using the CPUID opcode.
Arguments:
Cpu - A buffer where the CPU information will be copied. If CPUID is
not supported on this processor (pre pentium processors) we will
fill in all zeros.
Return Value:
None.
--*/
{
BOOL Succ;
//
// Get the VendorID
//
Succ = X86CpuId ( CPUID_VENDOR_ID,
NULL,
&Cpu->X86CpuInfo.VendorId [0],
&Cpu->X86CpuInfo.VendorId [2],
&Cpu->X86CpuInfo.VendorId [1]
);
if ( !Succ ) {
//
// CPUID is not supported on this processor.
//
ZeroMemory (&Cpu->X86CpuInfo, sizeof (Cpu->X86CpuInfo));
}
//
// Get the feature information.
//
Succ = X86CpuId ( CPUID_VERSION_FEATURES,
&Cpu->X86CpuInfo.VersionInformation,
NULL,
NULL,
&Cpu->X86CpuInfo.FeatureInformation
);
if ( !Succ ) {
Cpu->X86CpuInfo.VersionInformation = 0;
Cpu->X86CpuInfo.FeatureInformation = 0;
}
//
// Get the AMD specific information if this is an AMD processor.
//
if ( Cpu->X86CpuInfo.VendorId [0] == AMD_VENDOR_ID_0 &&
Cpu->X86CpuInfo.VendorId [1] == AMD_VENDOR_ID_1 &&
Cpu->X86CpuInfo.VendorId [2] == AMD_VENDOR_ID_2 ) {
Succ = X86CpuId ( CPUID_AMD_EXTENDED_FEATURES,
NULL,
NULL,
NULL,
&Cpu->X86CpuInfo.AMDExtendedCpuFeatures
);
if ( !Succ ) {
Cpu->X86CpuInfo.AMDExtendedCpuFeatures = 0;
}
}
}
#else
VOID
GetCpuInformation(
PCPU_INFORMATION Cpu
)
/*++
Routine Description:
Get CPU information for non-X86 platform using the
IsProcessorFeaturePresent() API call.
Arguments:
Cpu - A buffer where the processor feature information will be copied.
Note: we copy the processor features as a set of bits or'd together.
Also, we only allow for the first 128 processor feature flags.
Return Value:
None.
--*/
{
ULONG64 i;
DWORD j;
for (i = 0; i < ARRAY_COUNT (Cpu->OtherCpuInfo.ProcessorFeatures); i++) {
Cpu->OtherCpuInfo.ProcessorFeatures[i] = 0;
for (j = 0; j < 64; j++) {
if (IsProcessorFeaturePresent ( j )) {
Cpu->OtherCpuInfo.ProcessorFeatures[i] |= 1 << j;
}
}
}
}
#endif
BOOL
WriteSystemInfo(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo
)
{
BOOL Succ;
MINIDUMP_SYSTEM_INFO SystemInfo;
SYSTEM_INFO SysInfo;
OSVERSIONINFOEX Version;
WCHAR CSDVersionW [128];
RVA StringRva;
ULONG Length;
StringRva = 0;
//
// First, get the system information.
//
GetSystemInfo (&SysInfo);
SystemInfo.ProcessorArchitecture = SysInfo.wProcessorArchitecture;
SystemInfo.ProcessorLevel = SysInfo.wProcessorLevel;
SystemInfo.ProcessorRevision = SysInfo.wProcessorRevision;
SystemInfo.NumberOfProcessors = (UCHAR)SysInfo.dwNumberOfProcessors;
//
// Next get OS Information.
//
// Try first with the EX struct.
Version.dwOSVersionInfoSize = sizeof (Version);
Succ = GetVersionEx ( (LPOSVERSIONINFO)&Version );
if ( !Succ ) {
// EX struct didn't work, try with the basic struct.
ZeroMemory(&Version, sizeof(Version));
Version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (!GetVersionEx ( (LPOSVERSIONINFO)&Version )) {
return FALSE;
}
}
SystemInfo.ProductType = Version.wProductType;
SystemInfo.MajorVersion = Version.dwMajorVersion;
SystemInfo.MinorVersion = Version.dwMinorVersion;
SystemInfo.BuildNumber = Version.dwBuildNumber;
SystemInfo.PlatformId = Version.dwPlatformId;
SystemInfo.SuiteMask = Version.wSuiteMask;
SystemInfo.Reserved2 = 0;
if (!MultiByteToWideChar (CP_ACP,
0,
Version.szCSDVersion,
-1,
CSDVersionW,
sizeof (CSDVersionW) / sizeof(WCHAR)
)) {
return FALSE;
}
Length = ( lstrlenW (CSDVersionW) + 1 ) * sizeof (WCHAR);
if ( Length != StreamInfo->VersionStringLength ) {
//
// If this fails it means that since the OS lied to us about the
// size of the string. Very bad, we should investigate.
//
ASSERT ( FALSE );
return FALSE;
}
Succ = WriteStringToPool (
FileHandle,
StreamInfo,
CSDVersionW,
&StringRva
);
if ( !Succ ) {
return FALSE;
}
SystemInfo.CSDVersionRva = StringRva;
//
// Finally, get CPU information.
//
GetCpuInformation ( &SystemInfo.Cpu );
ASSERT ( sizeof (SystemInfo) == StreamInfo->SizeOfSystemInfo );
Succ = WriteAtOffset (
FileHandle,
StreamInfo->RvaOfSystemInfo,
&SystemInfo,
sizeof (SystemInfo)
);
return Succ;
}
BOOL
CalculateSizeForSystemInfo(
IN PINTERNAL_PROCESS Process,
IN OUT MINIDUMP_STREAM_INFO * StreamInfo
)
{
BOOL Succ;
OSVERSIONINFO Version;
WCHAR CSDVersionW [128];
ULONG Length;
Version.dwOSVersionInfoSize = sizeof (Version);
Succ = GetVersionEx ( &Version );
if ( !Succ ) {
return FALSE;
}
if (!MultiByteToWideChar (CP_ACP,
0,
Version.szCSDVersion,
-1,
CSDVersionW,
sizeof (CSDVersionW) / sizeof(WCHAR)
)) {
return FALSE;
}
Length = ( lstrlenW (CSDVersionW) + 1 ) * sizeof (WCHAR);
StreamInfo->SizeOfSystemInfo = sizeof (MINIDUMP_SYSTEM_INFO);
StreamInfo->SizeOfStringPool += Length;
StreamInfo->SizeOfStringPool += sizeof (MINIDUMP_STRING);
StreamInfo->VersionStringLength = Length;
return TRUE;
}
BOOL
WriteMiscInfo(
IN HANDLE FileHandle,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process
)
{
MINIDUMP_MISC_INFO MiscInfo;
ZeroMemory(&MiscInfo, sizeof(MiscInfo));
MiscInfo.SizeOfInfo = sizeof(MiscInfo);
MiscInfo.Flags1 |= MINIDUMP_MISC1_PROCESS_ID;
MiscInfo.ProcessId = Process->ProcessId;
if (Process->TimesValid) {
MiscInfo.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES;
MiscInfo.ProcessCreateTime = Process->CreateTime;
MiscInfo.ProcessUserTime = Process->UserTime;
MiscInfo.ProcessKernelTime = Process->KernelTime;
}
return WriteAtOffset(FileHandle,
StreamInfo->RvaOfMiscInfo,
&MiscInfo,
sizeof(MiscInfo));
}
void
PostProcessMemoryBlocks(
IN PINTERNAL_PROCESS Process
)
{
PINTERNAL_THREAD Thread;
PLIST_ENTRY ThreadEntry;
//
// Remove any overlap with thread stacks and backing stores.
//
ThreadEntry = Process->ThreadList.Flink;
while ( ThreadEntry != &Process->ThreadList ) {
Thread = CONTAINING_RECORD (ThreadEntry, INTERNAL_THREAD, ThreadsLink);
ThreadEntry = ThreadEntry->Flink;
GenRemoveMemoryRange(Process,
Thread->StackEnd,
(ULONG)(Thread->StackBase - Thread->StackEnd));
GenRemoveMemoryRange(Process,
Thread->BackingStoreBase,
Thread->BackingStoreSize);
}
}
BOOL
CalculateStreamInfo(
IN PINTERNAL_PROCESS Process,
IN ULONG DumpType,
OUT PMINIDUMP_STREAM_INFO StreamInfo,
IN BOOL ExceptionPresent,
IN PMINIDUMP_USER_STREAM UserStreamArray,
IN ULONG UserStreamCount
)
{
ULONG i;
BOOL Succ;
ULONG NumberOfStreams;
ULONG SizeOfDirectory;
ULONG SizeOfUserStreams;
ASSERT ( Process != NULL );
ASSERT ( StreamInfo != NULL );
ZeroMemory (StreamInfo, sizeof (*StreamInfo));
if ( ExceptionPresent ) {
NumberOfStreams = NUMBER_OF_STREAMS + UserStreamCount;
} else {
NumberOfStreams = NUMBER_OF_STREAMS + UserStreamCount - 1;
}
if (DumpType & MiniDumpWithHandleData) {
NumberOfStreams++;
}
if (!IsListEmpty(&Process->UnloadedModuleList)) {
NumberOfStreams++;
}
// Add a stream for dynamic function tables if some were found.
if (!IsListEmpty(&Process->FunctionTableList)) {
NumberOfStreams++;
}
SizeOfDirectory = sizeof (MINIDUMP_DIRECTORY) * NumberOfStreams;
StreamInfo->NumberOfStreams = NumberOfStreams;
StreamInfo->RvaOfHeader = 0;
StreamInfo->SizeOfHeader = sizeof (MINIDUMP_HEADER);
StreamInfo->RvaOfDirectory =
StreamInfo->RvaOfHeader + StreamInfo->SizeOfHeader;
StreamInfo->SizeOfDirectory = SizeOfDirectory;
StreamInfo->RvaOfSystemInfo =
StreamInfo->RvaOfDirectory + StreamInfo->SizeOfDirectory;
Succ = CalculateSizeForSystemInfo ( Process, StreamInfo );
if ( !Succ ) {
return FALSE;
}
StreamInfo->RvaOfMiscInfo =
StreamInfo->RvaOfSystemInfo + StreamInfo->SizeOfSystemInfo;
StreamInfo->RvaOfException =
StreamInfo->RvaOfMiscInfo + sizeof(MINIDUMP_MISC_INFO);
//
// If an exception is present, reserve enough space for the exception
// and for the excepting thread's context in the Other stream.
//
if ( ExceptionPresent ) {
StreamInfo->SizeOfException = sizeof (MINIDUMP_EXCEPTION_STREAM);
StreamInfo->SizeOfOther += sizeof (CONTEXT);
}
StreamInfo->RvaOfThreadList =
StreamInfo->RvaOfException + StreamInfo->SizeOfException;
StreamInfo->RvaForCurThread = StreamInfo->RvaOfThreadList;
Succ = CalculateSizeForThreads ( Process, StreamInfo );
if ( !Succ ) {
return FALSE;
}
Succ = CalculateSizeForModules ( Process, StreamInfo );
if ( !Succ ) {
return FALSE;
}
if (!IsListEmpty(&Process->UnloadedModuleList)) {
Succ = CalculateSizeForUnloadedModules ( Process, StreamInfo );
if ( !Succ ) {
return FALSE;
}
}
if (!IsListEmpty(&Process->FunctionTableList)) {
Succ = CalculateSizeForFunctionTables ( Process, StreamInfo );
}
if ((DumpType & MiniDumpWithProcessThreadData) &&
Process->SizeOfPeb) {
GenAddMemoryBlock(Process, MEMBLOCK_PEB,
Process->Peb, Process->SizeOfPeb);
}
PostProcessMemoryBlocks(Process);
// Add in any extra memory blocks.
StreamInfo->SizeOfMemoryData += Process->SizeOfMemoryBlocks;
StreamInfo->SizeOfMemoryDescriptors += Process->NumberOfMemoryBlocks *
sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
StreamInfo->RvaOfModuleList =
StreamInfo->RvaOfThreadList + StreamInfo->SizeOfThreadList;
StreamInfo->RvaForCurModule = StreamInfo->RvaOfModuleList;
StreamInfo->RvaOfUnloadedModuleList =
StreamInfo->RvaOfModuleList + StreamInfo->SizeOfModuleList;
StreamInfo->RvaForCurUnloadedModule = StreamInfo->RvaOfUnloadedModuleList;
// If there aren't any function tables the size will be zero
// and the RVA will just end up being the RVA after
// the module list.
StreamInfo->RvaOfFunctionTableList =
StreamInfo->RvaOfUnloadedModuleList +
StreamInfo->SizeOfUnloadedModuleList;
StreamInfo->RvaOfStringPool =
StreamInfo->RvaOfFunctionTableList +
StreamInfo->SizeOfFunctionTableList;
StreamInfo->RvaForCurString = StreamInfo->RvaOfStringPool;
StreamInfo->RvaOfOther =
StreamInfo->RvaOfStringPool + StreamInfo->SizeOfStringPool;
StreamInfo->RvaForCurOther = StreamInfo->RvaOfOther;
SizeOfUserStreams = 0;
for (i = 0; i < UserStreamCount; i++) {
SizeOfUserStreams += (ULONG) UserStreamArray[i].BufferSize;
}
StreamInfo->RvaOfUserStreams =
StreamInfo->RvaOfOther + StreamInfo->SizeOfOther;
StreamInfo->SizeOfUserStreams = SizeOfUserStreams;
//
// Minidumps with full memory must put the raw memory
// data at the end of the dump so that it's easy to
// avoid mapping it when the dump is mapped. There's
// no problem with putting the memory data at the end
// of the dump in all the other cases so just always
// put the memory data at the end of the dump.
//
// One other benefit of having the raw data at the end
// is that we can safely assume that everything except
// the raw memory data will fit in the first 4GB of
// the file so we don't need to use 64-bit file offsets
// for everything.
//
// In the full memory case no other memory should have
// been saved so far as stacks, data segs and so on
// will automatically be included in the full memory
// information. If something was saved it'll throw off
// the dump writing as full memory descriptors are generated
// on the fly at write time rather than being precached.
// If other descriptors and memory blocks have been written
// out everything will be wrong.
// Full-memory descriptors are also 64-bit and do not
// match the 32-bit descriptors written elsewhere.
//
if ((DumpType & MiniDumpWithFullMemory) &&
(StreamInfo->SizeOfMemoryDescriptors > 0 ||
StreamInfo->SizeOfMemoryData > 0)) {
return FALSE;
}
StreamInfo->SizeOfMemoryDescriptors +=
(DumpType & MiniDumpWithFullMemory) ?
sizeof (MINIDUMP_MEMORY64_LIST) : sizeof (MINIDUMP_MEMORY_LIST);
StreamInfo->RvaOfMemoryDescriptors =
StreamInfo->RvaOfUserStreams + StreamInfo->SizeOfUserStreams;
StreamInfo->RvaForCurMemoryDescriptor =
StreamInfo->RvaOfMemoryDescriptors;
StreamInfo->RvaOfMemoryData =
StreamInfo->RvaOfMemoryDescriptors +
StreamInfo->SizeOfMemoryDescriptors;
StreamInfo->RvaForCurMemoryData = StreamInfo->RvaOfMemoryData;
//
// Handle data cannot easily be sized beforehand so it's
// also streamed in at write time. In a partial dump
// it'll come after the memory data. In a full dump
// it'll come before it.
//
StreamInfo->RvaOfHandleData = StreamInfo->RvaOfMemoryData +
StreamInfo->SizeOfMemoryData;
return TRUE;
}
BOOL
WriteHeader(
IN HANDLE hFile,
IN ULONG DumpType,
IN PMINIDUMP_STREAM_INFO StreamInfo
)
{
BOOL Succ;
MINIDUMP_HEADER Header;
Header.Signature = MINIDUMP_SIGNATURE;
// Encode an implementation-specific version into the high word
// of the version to make it clear what version of the code
// was used to generate a dump.
Header.Version =
(MINIDUMP_VERSION & 0xffff) |
((VER_PRODUCTMAJORVERSION & 0xf) << 28) |
((VER_PRODUCTMINORVERSION & 0xf) << 24) |
((VER_PRODUCTBUILD & 0xff) << 16);
Header.NumberOfStreams = StreamInfo->NumberOfStreams;
Header.StreamDirectoryRva = StreamInfo->RvaOfDirectory;
// If there were any partial failures during the
// dump generation set the checksum to indicate that.
// The checksum field was never used before so
// we're stealing it for a somewhat related purpose.
Header.CheckSum = GenGetAccumulatedStatus();
Header.Flags = DumpType;
//
// Store the time of dump generation.
//
#ifdef _WIN32_WCE
Header.TimeDateStamp = time(NULL);
#else
{
FILETIME FileTime;
GetSystemTimeAsFileTime(&FileTime);
Header.TimeDateStamp = FileTimeToTimeDate(&FileTime);
}
#endif
ASSERT (sizeof (Header) == StreamInfo->SizeOfHeader);
Succ = WriteAtOffset (
hFile,
StreamInfo->RvaOfHeader,
&Header,
sizeof (Header)
);
return Succ;
}
BOOL
WriteDirectoryTable(
IN HANDLE hFile,
IN ULONG DumpType,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process,
IN PMINIDUMP_USER_STREAM UserStreamArray,
IN ULONG UserStreamCount
)
{
ULONG i;
BOOL Succ;
ULONG Offset;
Succ = WriteDirectoryEntry (
hFile,
StreamInfo->ThreadStructSize ==
sizeof(MINIDUMP_THREAD_EX) ?
ThreadExListStream : ThreadListStream,
StreamInfo->RvaOfThreadList,
StreamInfo->SizeOfThreadList
);
if ( !Succ ) {
return FALSE;
}
Succ = WriteDirectoryEntry (
hFile,
ModuleListStream,
StreamInfo->RvaOfModuleList,
StreamInfo->SizeOfModuleList
);
if ( !Succ ) {
return FALSE;
}
if (!IsListEmpty(&Process->UnloadedModuleList)) {
Succ = WriteDirectoryEntry (hFile,
UnloadedModuleListStream,
StreamInfo->RvaOfUnloadedModuleList,
StreamInfo->SizeOfUnloadedModuleList);
if ( !Succ ) {
return FALSE;
}
}
if (!IsListEmpty(&Process->FunctionTableList)) {
Succ = WriteDirectoryEntry (hFile,
FunctionTableStream,
StreamInfo->RvaOfFunctionTableList,
StreamInfo->SizeOfFunctionTableList);
if ( !Succ ) {
return FALSE;
}
}
Succ = WriteDirectoryEntry (
hFile,
(DumpType & MiniDumpWithFullMemory) ?
Memory64ListStream : MemoryListStream,
StreamInfo->RvaOfMemoryDescriptors,
StreamInfo->SizeOfMemoryDescriptors
);
if ( !Succ ) {
return FALSE;
}
//
// Write exception directory entry.
//
Succ = WriteDirectoryEntry (
hFile,
ExceptionStream,
StreamInfo->RvaOfException,
StreamInfo->SizeOfException
);
if ( !Succ ) {
return FALSE;
}
//
// Write system info entry.
//
Succ = WriteDirectoryEntry (
hFile,
SystemInfoStream,
StreamInfo->RvaOfSystemInfo,
StreamInfo->SizeOfSystemInfo
);
if ( !Succ ) {
return FALSE;
}
//
// Write misc info entry.
//
if (!WriteDirectoryEntry(hFile,
MiscInfoStream,
StreamInfo->RvaOfMiscInfo,
sizeof(MINIDUMP_MISC_INFO))) {
return FALSE;
}
if (DumpType & MiniDumpWithHandleData) {
//
// Write handle data entry.
//
Succ = WriteDirectoryEntry (hFile,
HandleDataStream,
StreamInfo->RvaOfHandleData,
StreamInfo->SizeOfHandleData);
if ( !Succ ) {
return FALSE;
}
}
Offset = StreamInfo->RvaOfUserStreams;
for (i = 0; i < UserStreamCount; i++) {
Succ = WriteDirectoryEntry (hFile,
UserStreamArray[i].Type,
Offset,
UserStreamArray [i].BufferSize
);
if ( !Succ ) {
return FALSE;
}
Offset += UserStreamArray[i].BufferSize;
}
return TRUE;
}
BOOL
WriteException(
IN HANDLE hFile,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN CONST PEXCEPTION_INFO ExceptionInfo
)
{
BOOL Succ;
ULONG i;
ULONG ContextRva;
PEXCEPTION_RECORD ExceptionRecord;
PMINIDUMP_EXCEPTION DumpExceptionRecord;
MINIDUMP_EXCEPTION_STREAM ExceptionStream;
if (ExceptionInfo == NULL ) {
return TRUE;
}
Succ = WriteOther (
hFile,
StreamInfo,
ExceptionInfo->ExceptionPointers.ContextRecord,
sizeof (CONTEXT),
&ContextRva
);
ZeroMemory (&ExceptionStream, sizeof (ExceptionStream));
ExceptionStream.ThreadId = ExceptionInfo->ThreadId;
ExceptionRecord = ExceptionInfo->ExceptionPointers.ExceptionRecord;
DumpExceptionRecord = &ExceptionStream.ExceptionRecord;
DumpExceptionRecord->ExceptionCode = ExceptionRecord->ExceptionCode;
DumpExceptionRecord->ExceptionFlags = ExceptionRecord->ExceptionFlags;
DumpExceptionRecord->ExceptionRecord =
SIGN_EXTEND (ExceptionRecord->ExceptionRecord);
DumpExceptionRecord->ExceptionAddress =
SIGN_EXTEND (ExceptionRecord->ExceptionAddress);
DumpExceptionRecord->NumberParameters =
ExceptionRecord->NumberParameters;
//
// We've seen some cases where the exception record has
// a bogus number of parameters, causing stack corruption here.
// We could fail such cases but in the spirit of try to
// allow dumps to generated as often as possible we just
// limit the number to the maximum.
//
if (DumpExceptionRecord->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS) {
DumpExceptionRecord->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
}
for (i = 0; i < DumpExceptionRecord->NumberParameters; i++) {
DumpExceptionRecord->ExceptionInformation [ i ] =
SIGN_EXTEND (ExceptionRecord->ExceptionInformation [ i ]);
}
ExceptionStream.ThreadContext.DataSize = sizeof (CONTEXT);
ExceptionStream.ThreadContext.Rva = ContextRva;
Succ = WriteAtOffset(
hFile,
StreamInfo->RvaOfException,
&ExceptionStream,
StreamInfo->SizeOfException
);
return Succ;
}
BOOL
WriteUserStreams(
IN HANDLE hFile,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PMINIDUMP_USER_STREAM UserStreamArray,
IN ULONG UserStreamCount
)
{
BOOL Succ;
ULONG i;
ULONG Offset;
Succ = TRUE;
Offset = StreamInfo->RvaOfUserStreams;
for (i = 0; i < UserStreamCount; i++) {
Succ = WriteAtOffset(
hFile,
Offset,
UserStreamArray[i].Buffer,
UserStreamArray[i].BufferSize
);
if ( !Succ ) {
break;
}
Offset += UserStreamArray[ i ].BufferSize;
}
return Succ;
}
BOOL
WriteMemoryListHeader(
IN HANDLE hFile,
IN PMINIDUMP_STREAM_INFO StreamInfo
)
{
BOOL Succ;
ULONG Size;
ULONG Count;
MINIDUMP_MEMORY_LIST MemoryList;
ASSERT ( StreamInfo->RvaOfMemoryDescriptors == StreamInfo->RvaForCurMemoryDescriptor );
Size = StreamInfo->SizeOfMemoryDescriptors;
Size -= sizeof (MINIDUMP_MEMORY_LIST);
ASSERT ( (Size % sizeof (MINIDUMP_MEMORY_DESCRIPTOR)) == 0);
Count = Size / sizeof (MINIDUMP_MEMORY_DESCRIPTOR);
MemoryList.NumberOfMemoryRanges = Count;
Succ = WriteAtOffset (
hFile,
StreamInfo->RvaOfMemoryDescriptors,
&MemoryList,
sizeof (MemoryList)
);
if (Succ) {
StreamInfo->RvaForCurMemoryDescriptor += sizeof (MemoryList);
}
return Succ;
}
#define FULL_MEMORY_BUFFER 65536
BOOL
WriteFullMemory(
IN HANDLE ProcessHandle,
IN HANDLE hFile,
IN PMINIDUMP_STREAM_INFO StreamInfo
)
{
PVOID Buffer;
BOOL Succ;
ULONG_PTR Offset;
MEMORY_BASIC_INFORMATION Info;
MINIDUMP_MEMORY64_LIST List;
MINIDUMP_MEMORY_DESCRIPTOR64 Desc;
ULONG Done;
//
// Pick up the current offset for the RVA as
// variable data may have been written in previously.
//
if ((Done = SetFilePointer(hFile, 0, NULL, FILE_CURRENT)) ==
INVALID_SET_FILE_POINTER) {
return FALSE;
}
StreamInfo->RvaOfMemoryDescriptors = Done;
Buffer = AllocMemory(FULL_MEMORY_BUFFER);
if (Buffer == NULL) {
return FALSE;
}
Succ = FALSE;
//
// First pass: count and write descriptors.
// Only accessible, available memory is saved.
//
// Write placeholder list header.
ZeroMemory(&List, sizeof(List));
if (!WriteFile(hFile, &List, sizeof(List), &Done, NULL) ||
Done != sizeof(List)) {
goto Exit;
}
Offset = 0;
for (;;) {
if (!VirtualQueryEx(ProcessHandle, (LPCVOID)Offset,
&Info, sizeof(Info))) {
break;
}
Offset = (ULONG_PTR)Info.BaseAddress + Info.RegionSize;
if (((Info.Protect & PAGE_GUARD) ||
(Info.Protect & PAGE_NOACCESS) ||
(Info.State & MEM_FREE) ||
(Info.State & MEM_RESERVE))) {
continue;
}
// The size of a stream is a ULONG32 so we can't store
// any more than that.
if (List.NumberOfMemoryRanges ==
(_UI32_MAX - sizeof(MINIDUMP_MEMORY64_LIST)) / sizeof(Desc)) {
goto Exit;
}
List.NumberOfMemoryRanges++;
Desc.StartOfMemoryRange = SIGN_EXTEND((ULONG_PTR)Info.BaseAddress);
Desc.DataSize = Info.RegionSize;
if (!WriteFile(hFile, &Desc, sizeof(Desc), &Done, NULL) ||
Done != sizeof(Desc)) {
goto Exit;
}
}
StreamInfo->SizeOfMemoryDescriptors +=
(ULONG)List.NumberOfMemoryRanges * sizeof(Desc);
List.BaseRva = (RVA64)StreamInfo->RvaOfMemoryDescriptors +
StreamInfo->SizeOfMemoryDescriptors;
//
// Second pass: write memory contents.
//
Offset = 0;
for (;;) {
ULONG_PTR ChunkOffset;
SIZE_T ChunkSize;
SIZE_T MemDone;
if (!VirtualQueryEx(ProcessHandle, (LPCVOID)Offset,
&Info, sizeof(Info))) {
break;
}
Offset = (ULONG_PTR)Info.BaseAddress + Info.RegionSize;
if (((Info.Protect & PAGE_GUARD) ||
(Info.Protect & PAGE_NOACCESS) ||
(Info.State & MEM_FREE) ||
(Info.State & MEM_RESERVE))) {
continue;
}
ChunkOffset = (ULONG_PTR)Info.BaseAddress;
while (Info.RegionSize > 0) {
if (Info.RegionSize > FULL_MEMORY_BUFFER) {
ChunkSize = FULL_MEMORY_BUFFER;
} else {
ChunkSize = Info.RegionSize;
}
if (!ReadProcessMemory(ProcessHandle, (LPVOID)ChunkOffset,
Buffer, ChunkSize, &MemDone) ||
MemDone != ChunkSize ||
!WriteFile(hFile, Buffer, (DWORD)ChunkSize, &Done, NULL) ||
Done != ChunkSize) {
goto Exit;
}
ChunkOffset += ChunkSize;
Info.RegionSize -= ChunkSize;
}
}
// Write correct list header.
if (!WriteAtOffset(hFile, StreamInfo->RvaOfMemoryDescriptors,
&List, sizeof(List))) {
goto Exit;
}
Succ = TRUE;
Exit:
FreeMemory(Buffer);
return Succ;
}
BOOL
WriteDumpData(
IN HANDLE hFile,
IN ULONG DumpType,
IN PMINIDUMP_STREAM_INFO StreamInfo,
IN PINTERNAL_PROCESS Process,
IN CONST PEXCEPTION_INFO ExceptionInfo,
IN CONST PMINIDUMP_USER_STREAM UserStreamArray,
IN ULONG UserStreamCount
)
{
BOOL Succ;
Succ = WriteHeader ( hFile, DumpType, StreamInfo );
if ( !Succ ) {
return FALSE;
}
Succ = WriteSystemInfo ( hFile, StreamInfo );
if ( !Succ ) {
return FALSE;
}
if (!WriteMiscInfo(hFile, StreamInfo, Process)) {
return FALSE;
}
//
// Optionally, write the exception to the file.
//
Succ = WriteException ( hFile, StreamInfo, ExceptionInfo );
if ( !Succ ) {
return FALSE;
}
if (!(DumpType & MiniDumpWithFullMemory)) {
//
// WriteMemoryList initializes the memory list header (count).
// The actual writing of the entries is done by WriteThreadList
// and WriteModuleList.
//
Succ = WriteMemoryListHeader ( hFile, StreamInfo );
if ( !Succ ) {
return FALSE;
}
if (!WriteMemoryBlocks(hFile, StreamInfo, Process)) {
return FALSE;
}
}
//
// Write the threads list. This will also write the contexts, and
// stacks for each thread.
//
Succ = WriteThreadList ( hFile, StreamInfo, Process, DumpType );
if ( !Succ ) {
return FALSE;
}
//
// Write the module list. This will also write the debug information and
// module name to the file.
//
Succ = WriteModuleList ( hFile, StreamInfo, Process );
if ( !Succ ) {
return FALSE;
}
//
// Write the unloaded module list.
//
Succ = WriteUnloadedModuleList ( hFile, StreamInfo, Process );
if ( !Succ ) {
return FALSE;
}
//
// Write the function table list.
//
Succ = WriteFunctionTableList ( hFile, StreamInfo, Process );
if ( !Succ ) {
return FALSE;
}
Succ = WriteUserStreams ( hFile,
StreamInfo,
UserStreamArray,
UserStreamCount
);
if ( !Succ ) {
return FALSE;
}
// Put the file pointer at the end of the dump so
// we can accumulate write-streamed data.
if (SetFilePointer(hFile, StreamInfo->RvaOfHandleData, NULL,
FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
return FALSE;
}
if (DumpType & MiniDumpWithHandleData) {
Succ = GenWriteHandleData(Process->ProcessHandle, hFile, StreamInfo);
if ( !Succ ) {
return FALSE;
}
}
if (DumpType & MiniDumpWithFullMemory) {
Succ = WriteFullMemory(Process->ProcessHandle, hFile, StreamInfo);
if ( !Succ ) {
return FALSE;
}
}
if (SetFilePointer(hFile, StreamInfo->RvaOfDirectory, NULL,
FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
return FALSE;
}
Succ = WriteDirectoryTable ( hFile,
DumpType,
StreamInfo,
Process,
UserStreamArray,
UserStreamCount
);
if ( !Succ ) {
return FALSE;
}
return TRUE;
}
BOOL
MarshalExceptionPointers(
IN HANDLE hProcess,
IN PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
IN OUT PEXCEPTION_POINTERS ExceptionPointers
)
{
BOOL Succ;
SIZE_T BytesRead;
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ExceptionContext;
EXCEPTION_POINTERS ExceptionPointersBuffer;
//
// Is there any marshaling work to be done.
//
if (ExceptionParam == NULL) {
return TRUE;
}
ExceptionRecord = (PEXCEPTION_RECORD) AllocMemory ( sizeof (EXCEPTION_RECORD) );
ExceptionContext = (PCONTEXT) AllocMemory ( sizeof (CONTEXT) );
if (ExceptionRecord == NULL ||
ExceptionContext == NULL) {
Succ = FALSE;
goto Exit;
}
Succ = ReadProcessMemory (
hProcess,
ExceptionParam->ExceptionPointers,
&ExceptionPointersBuffer,
sizeof (ExceptionPointersBuffer),
&BytesRead
);
if ( !Succ || BytesRead != sizeof (ExceptionPointersBuffer) ) {
Succ = FALSE;
goto Exit;
}
Succ = ReadProcessMemory (
hProcess,
ExceptionPointersBuffer.ExceptionRecord,
ExceptionRecord,
sizeof (*ExceptionRecord),
&BytesRead
);
if ( !Succ || BytesRead != sizeof (*ExceptionRecord) ) {
Succ = FALSE;
goto Exit;
}
#if defined (i386)
{
OSVERSIONINFO OSVersionInfo;
OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
GetVersionEx(&OSVersionInfo);
// If this is Win9x don't read the Extended Registers
if ( OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
Succ = ReadProcessMemory (hProcess,
ExceptionPointersBuffer.ContextRecord,
ExceptionContext,
FIELD_OFFSET( CONTEXT, ExtendedRegisters),
&BytesRead);
if ( !Succ || BytesRead != FIELD_OFFSET( CONTEXT, ExtendedRegisters) ) {
Succ = FALSE;
goto Exit;
}
} else {
Succ = ReadProcessMemory (
hProcess,
ExceptionPointersBuffer.ContextRecord,
ExceptionContext,
sizeof(CONTEXT),
&BytesRead
);
if ( !Succ || BytesRead != sizeof (CONTEXT) ) {
Succ = FALSE;
goto Exit;
}
}
}
#else
Succ = ReadProcessMemory (
hProcess,
ExceptionPointersBuffer.ContextRecord,
ExceptionContext,
sizeof(CONTEXT),
&BytesRead
);
if ( !Succ || BytesRead != sizeof (CONTEXT) ) {
Succ = FALSE;
goto Exit;
}
#endif
ExceptionPointers->ExceptionRecord = ExceptionRecord;
ExceptionPointers->ContextRecord = ExceptionContext;
Exit:
if ( !Succ ) {
FreeMemory ( ExceptionRecord );
ExceptionRecord = NULL;
FreeMemory ( ExceptionContext );
ExceptionContext = NULL;
}
return Succ;
}
VOID
FreeExceptionPointers(
IN PEXCEPTION_POINTERS ExceptionPointers
)
{
if ( ExceptionPointers ) {
FreeMemory ( ExceptionPointers->ExceptionRecord );
FreeMemory ( ExceptionPointers->ContextRecord );
}
}
BOOL
GetExceptionInfo(
IN HANDLE hProcess,
IN PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
OUT PEXCEPTION_INFO * ExceptionInfoBuffer
)
{
BOOL Succ;
PEXCEPTION_INFO ExceptionInfo;
if ( ExceptionParam == NULL ) {
*ExceptionInfoBuffer = NULL;
return TRUE;
}
ExceptionInfo = AllocMemory ( sizeof (EXCEPTION_INFO) );
if ( ExceptionInfo == NULL ) {
*ExceptionInfoBuffer = NULL;
return FALSE;
}
if ( !ExceptionParam->ClientPointers ) {
ExceptionInfo->ExceptionPointers.ExceptionRecord =
ExceptionParam->ExceptionPointers->ExceptionRecord;
ExceptionInfo->ExceptionPointers.ContextRecord =
ExceptionParam->ExceptionPointers->ContextRecord;
ExceptionInfo->FreeExceptionPointers = FALSE;
Succ = TRUE;
} else {
Succ = MarshalExceptionPointers (
hProcess,
ExceptionParam,
&ExceptionInfo->ExceptionPointers
);
ExceptionInfo->FreeExceptionPointers = TRUE;
}
ExceptionInfo->ThreadId = ExceptionParam->ThreadId;
if ( !Succ ) {
FreeMemory (ExceptionInfo);
ExceptionInfo = NULL;
*ExceptionInfoBuffer = NULL;
} else {
*ExceptionInfoBuffer = ExceptionInfo;
}
return Succ;
}
VOID
FreeExceptionInfo(
IN PEXCEPTION_INFO ExceptionInfo
)
{
if ( ExceptionInfo && ExceptionInfo->FreeExceptionPointers ) {
FreeExceptionPointers ( &ExceptionInfo->ExceptionPointers );
FreeMemory ( ExceptionInfo );
}
}
BOOL
WINAPI
MiniDumpWriteDump(
IN HANDLE hProcess,
IN DWORD ProcessId,
IN HANDLE hFile,
IN MINIDUMP_TYPE DumpType,
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
)
{
BOOL Succ;
PINTERNAL_PROCESS Process;
MINIDUMP_STREAM_INFO StreamInfo;
PEXCEPTION_INFO ExceptionInfo;
PMINIDUMP_USER_STREAM UserStreamArray;
ULONG UserStreamCount;
MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
PVOID CallbackVoidParam;
if ((DumpType & ~(MiniDumpNormal |
MiniDumpWithDataSegs |
MiniDumpWithFullMemory |
MiniDumpWithHandleData |
MiniDumpFilterMemory |
MiniDumpScanMemory |
MiniDumpWithUnloadedModules |
MiniDumpWithIndirectlyReferencedMemory |
MiniDumpFilterModulePaths |
MiniDumpWithProcessThreadData |
MiniDumpWithPrivateReadWriteMemory))) {
SetLastError (ERROR_INVALID_PARAMETER);
return FALSE;
}
// Full memory by definition includes data segments,
// so turn off data segments if full memory is requested.
if (DumpType & MiniDumpWithFullMemory) {
DumpType &= ~(MiniDumpWithDataSegs | MiniDumpFilterMemory |
MiniDumpScanMemory |
MiniDumpWithIndirectlyReferencedMemory |
MiniDumpWithProcessThreadData |
MiniDumpWithPrivateReadWriteMemory);
}
//
// Initialization
//
Process = NULL;
UserStreamArray = NULL;
UserStreamCount = 0;
CallbackRoutine = NULL;
CallbackVoidParam = NULL;
if (!MiniDumpSetup ()) {
return FALSE;
}
#if !defined (_DBGHELP_SOURCE_)
//
// Try to call dbghelp.dll do to the work.
// If that fails, then we use the code in this lib.
//
if (xxxWriteDump(hProcess, ProcessId, hFile, DumpType, ExceptionParam, UserStreamParam, CallbackParam)) {
return TRUE;
}
#endif
GenClearAccumulatedStatus();
//
// Marshal exception pointers into our process space if necessary.
//
Succ = GetExceptionInfo (
hProcess,
ExceptionParam,
&ExceptionInfo
);
if ( !Succ ) {
goto Exit;
}
if ( UserStreamParam ) {
UserStreamArray = UserStreamParam->UserStreamArray;
UserStreamCount = UserStreamParam->UserStreamCount;
}
if ( CallbackParam ) {
CallbackRoutine = CallbackParam->CallbackRoutine;
CallbackVoidParam = CallbackParam->CallbackParam;
}
//
// Gather information about the process we are dumping.
//
Succ = GenGetProcessInfo (hProcess, ProcessId, DumpType,
CallbackRoutine, CallbackVoidParam,
&Process);
if ( !Succ ) {
goto Exit;
}
//
// Process gathered information.
//
Succ = PostProcessInfo(DumpType, Process);
if (!Succ) {
goto Exit;
}
//
// Execute user callbacks to filter out unwanted data.
//
Succ = ExecuteCallbacks ( hProcess,
ProcessId,
Process,
CallbackRoutine,
CallbackVoidParam
);
if ( !Succ ) {
goto Exit;
}
//
// Pass 1: Fill in the StreamInfo structure.
//
Succ = CalculateStreamInfo ( Process,
DumpType,
&StreamInfo,
( ExceptionInfo != NULL ) ? TRUE : FALSE,
UserStreamArray,
UserStreamCount
);
if ( !Succ ) {
goto Exit;
}
//
// Pass 2: Write the minidump data to disk.
//
Succ = WriteDumpData ( hFile,
DumpType,
&StreamInfo,
Process,
ExceptionInfo,
UserStreamArray,
UserStreamCount
);
Exit:
//
// Free up any memory marshalled for the exception pointers.
//
FreeExceptionInfo ( ExceptionInfo );
//
// Free the process objects.
//
if ( Process ) {
GenFreeProcessObject ( Process );
Process = NULL;
}
MiniDumpFree ();
return Succ;
}
BOOL
WINAPI
MiniDumpReadDumpStream(
IN PVOID Base,
ULONG StreamNumber,
OUT PMINIDUMP_DIRECTORY * Dir, OPTIONAL
OUT PVOID * Stream, OPTIONAL
OUT ULONG * StreamSize OPTIONAL
)
{
ULONG i;
BOOL Found;
PMINIDUMP_DIRECTORY Dirs;
PMINIDUMP_HEADER Header;
if (!MiniDumpSetup ()) {
return FALSE;
}
#if !defined (_DBGHELP_SOURCE_)
//
// Try to call dbghelp.dll do to the work.
// If that fails, then we use the code in this lib.
//
if (xxxReadDumpStream(Base, StreamNumber, Dir, Stream, StreamSize)) {
return TRUE;
}
#endif
//
// Initialization
//
Found = FALSE;
Header = (PMINIDUMP_HEADER) Base;
if ( Header->Signature != MINIDUMP_SIGNATURE ||
(Header->Version & 0xffff) != MINIDUMP_VERSION ) {
//
// Invalid Minidump file.
//
return FALSE;
}
Dirs = (PMINIDUMP_DIRECTORY) RVA_TO_ADDR (Header, Header->StreamDirectoryRva);
for (i = 0; i < Header->NumberOfStreams; i++) {
if (Dirs [i].StreamType == StreamNumber) {
Found = TRUE;
break;
}
}
if ( !Found ) {
return FALSE;
}
if ( Dir ) {
*Dir = &Dirs [i];
}
if ( Stream ) {
*Stream = RVA_TO_ADDR (Base, Dirs [i].Location.Rva);
}
if ( StreamSize ) {
*StreamSize = Dirs[i].Location.DataSize;
}
return TRUE;
}
#if 0
if (!Succ || BytesWritten != SizeOfRegion) {
return FALSE;
}
//
// Then update the memory descriptor in the MEMORY_LIST region.
//
Descriptor.StartOfMemoryRange = StartOfRegion;
Descriptor.Memory.DataSize = SizeOfRegion;
Descriptor.Memory.Rva = DataRva;
Succ = SetFilePointer (
FileHandle,
ListRva,
NULL,
FILE_BEGIN
) != INVALID_SET_FILE_POINTER;
if ( !Succ ) {
return FALSE;
}
Succ = WriteFile (
FileHandle,
&Descriptor,
SizeOfMemoryDescriptor,
&BytesWritten,
NULL
);
if ( !Succ || BytesWritten != SizeOfMemoryDescriptor) {
return FALSE;
}
//
// Update both the List Rva and the Data Rva and return the
// the Data Rva.
//
StreamInfo->RvaForCurMemoryDescriptor += SizeOfMemoryDescriptor;
StreamInfo->RvaForCurMemoryData += SizeOfRegion;
*MemoryDataRva = DataRva;
return TRUE;
}
#endif