Add support of flag MinidumpWithFullMemory in function MinidumpWriteDump. A Memory64ListStream is added to the minidump streams and all memory regions of the process with MEM_COMMIT state are written to the last part of the minidump file.
Signed-off-by: Eric Bissonnette ebisso.dev@gmail.com --- dlls/dbghelp/dbghelp_private.h | 9 +++ dlls/dbghelp/minidump.c | 129 ++++++++++++++++++++++++++++++++++++++--- include/dbghelp.h | 13 +++++ 3 files changed, 144 insertions(+), 7 deletions(-)
diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index ae8bc50..8083408 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -471,6 +471,12 @@ struct dump_memory ULONG rva; };
+struct dump_memory64 +{ + ULONG64 base; + ULONG64 size; +}; + struct dump_module { unsigned is_elf; @@ -509,6 +515,9 @@ struct dump_context struct dump_memory* mem; unsigned num_mem; unsigned alloc_mem; + struct dump_memory64* mem64; + unsigned num_mem64; + unsigned alloc_mem64; /* callback information */ MINIDUMP_CALLBACK_INFORMATION* cb; }; diff --git a/dlls/dbghelp/minidump.c b/dlls/dbghelp/minidump.c index 04dc775..ad69ba5 100644 --- a/dlls/dbghelp/minidump.c +++ b/dlls/dbghelp/minidump.c @@ -294,6 +294,49 @@ static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base, return TRUE; }
+void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size) +{ + if (!dc->mem64) + { + dc->alloc_mem64 = 32; + dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64)); + } + else if (dc->num_mem64 >= dc->alloc_mem64) + { + dc->alloc_mem64 *= 2; + dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64, + dc->alloc_mem64 * sizeof(*dc->mem64)); + } + if (dc->mem64) + { + dc->mem64[dc->num_mem64].base = base; + dc->mem64[dc->num_mem64].size = size; + dc->num_mem64++; + } + else dc->num_mem64 = dc->alloc_mem64 = 0; +} + +static void fetch_memory64_info(struct dump_context* dc) +{ + ULONG_PTR addr; + MEMORY_BASIC_INFORMATION mbi; + + addr = 0; + while (VirtualQueryEx(dc->hProcess, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0) + { + /* Memory regions with state MEM_COMMIT will be added to the dump */ + if (mbi.State == MEM_COMMIT) + { + minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize); + } + + if ((addr + mbi.RegionSize) < addr) + break; + + addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; + } +} + static void fetch_modules_info(struct dump_context* dc) { EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc); @@ -831,6 +874,60 @@ static unsigned dump_memory_info(struct dump_context* dc) return sz; }
+/****************************************************************** + * dump_memory64_info + * + * dumps information about the memory of the process (virtual memory) + */ +static unsigned dump_memory64_info(struct dump_context* dc) +{ + MINIDUMP_MEMORY64_LIST mdMem64List; + MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64; + DWORD written; + unsigned i, len, sz; + RVA rva_base; + char tmp[1024]; + ULONG64 pos; + LARGE_INTEGER filepos; + + sz = sizeof(mdMem64List.NumberOfMemoryRanges) + + sizeof(mdMem64List.BaseRva) + + dc->num_mem64 * sizeof(mdMem64); + + mdMem64List.NumberOfMemoryRanges = dc->num_mem64; + mdMem64List.BaseRva = dc->rva + sz; + + append(dc, &mdMem64List.NumberOfMemoryRanges, + sizeof(mdMem64List.NumberOfMemoryRanges)); + append(dc, &mdMem64List.BaseRva, + sizeof(mdMem64List.BaseRva)); + + rva_base = dc->rva; + dc->rva += dc->num_mem64 * sizeof(mdMem64); + + /* dc->rva is not updated past this point. The end of the dump + * is just the full memory data. */ + filepos.QuadPart = dc->rva; + for (i = 0; i < dc->num_mem64; i++) + { + mdMem64.StartOfMemoryRange = dc->mem64[i].base; + mdMem64.DataSize = dc->mem64[i].size; + SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN); + for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp)) + { + len = min(dc->mem64[i].size - pos, sizeof(tmp)); + if (ReadProcessMemory(dc->hProcess, + (void*)(ULONG_PTR)(dc->mem64[i].base + pos), + tmp, len, NULL)) + WriteFile(dc->hFile, tmp, len, &written, NULL); + } + filepos.QuadPart += mdMem64.DataSize; + writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64)); + } + + return sz; +} + static unsigned dump_misc_info(struct dump_context* dc) { MINIDUMP_MISC_INFO mmi; @@ -876,6 +973,9 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, dc.mem = NULL; dc.num_mem = 0; dc.alloc_mem = 0; + dc.mem64 = NULL; + dc.num_mem64 = 0; + dc.alloc_mem64 = 0; dc.rva = 0;
if (!fetch_process_info(&dc)) return FALSE; @@ -890,8 +990,6 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
if (DumpType & MiniDumpWithDataSegs) FIXME("NIY MiniDumpWithDataSegs\n"); - if (DumpType & MiniDumpWithFullMemory) - FIXME("NIY MiniDumpWithFullMemory\n"); if (DumpType & MiniDumpWithHandleData) FIXME("NIY MiniDumpWithHandleData\n"); if (DumpType & MiniDumpFilterMemory) @@ -940,11 +1038,15 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir));
- mdDir.StreamType = MemoryListStream; - mdDir.Location.Rva = dc.rva; - mdDir.Location.DataSize = dump_memory_info(&dc); - writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), - &mdDir, sizeof(mdDir)); + + if (!(DumpType & MiniDumpWithFullMemory)) + { + mdDir.StreamType = MemoryListStream; + mdDir.Location.Rva = dc.rva; + mdDir.Location.DataSize = dump_memory_info(&dc); + writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), + &mdDir, sizeof(mdDir)); + }
mdDir.StreamType = MiscInfoStream; mdDir.Location.Rva = dc.rva; @@ -977,12 +1079,25 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, } }
+ /* 3.4) write full memory (if requested) */ + if (DumpType & MiniDumpWithFullMemory) + { + fetch_memory64_info(&dc); + + mdDir.StreamType = Memory64ListStream; + mdDir.Location.Rva = dc.rva; + mdDir.Location.DataSize = dump_memory64_info(&dc); + writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), + &mdDir, sizeof(mdDir)); + } + /* fill the remaining directory entries with 0's (unused stream types) */ /* NOTE: this should always come last in the dump! */ for (i = idx_stream; i < nStreams; i++) writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
HeapFree(GetProcessHeap(), 0, dc.mem); + HeapFree(GetProcessHeap(), 0, dc.mem64); HeapFree(GetProcessHeap(), 0, dc.modules); HeapFree(GetProcessHeap(), 0, dc.threads);
diff --git a/include/dbghelp.h b/include/dbghelp.h index 6e8d375..4210616 100644 --- a/include/dbghelp.h +++ b/include/dbghelp.h @@ -747,6 +747,19 @@ typedef struct _MINIDUMP_MEMORY_LIST MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[1]; /* FIXME: 0-sized array not supported */ } MINIDUMP_MEMORY_LIST, *PMINIDUMP_MEMORY_LIST;
+typedef struct _MINIDUMP_MEMORY_DESCRIPTOR64 +{ + ULONG64 StartOfMemoryRange; + ULONG64 DataSize; +} MINIDUMP_MEMORY_DESCRIPTOR64, *PMINIDUMP_MEMORY_DESCRIPTOR64; + +typedef struct _MINIDUMP_MEMORY64_LIST +{ + ULONG64 NumberOfMemoryRanges; + RVA64 BaseRva; + MINIDUMP_MEMORY_DESCRIPTOR64 MemoryRanges[1]; /* FIXME: 0-sized array not supported */ +} MINIDUMP_MEMORY64_LIST, *PMINIDUMP_MEMORY64_LIST; + #define MINIDUMP_MISC1_PROCESS_ID 0x00000001 #define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002