3879 lines
96 KiB
C
3879 lines
96 KiB
C
/*++
|
||
|
||
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
|