mirror of https://github.com/tongzx/nt5src
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
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
|