From c1e36adf4c7e422f7b0fbbbe8d78e1f961174031 Mon Sep 17 00:00:00 2001 From: Jason Green Date: Tue, 20 Nov 2007 15:52:51 -0500 Subject: [PATCH] Fixes for minidump files so that windbg can load them and display crash information. --- dlls/dbghelp/minidump.c | 133 +++++++++++++++++++++++++++++++++++++--------- 1 files changed, 107 insertions(+), 26 deletions(-) diff --git a/dlls/dbghelp/minidump.c b/dlls/dbghelp/minidump.c index e8daeda..964ce10 100644 --- a/dlls/dbghelp/minidump.c +++ b/dlls/dbghelp/minidump.c @@ -34,7 +34,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); struct dump_memory { - ULONG base; + ULONG64 base; ULONG size; ULONG rva; }; @@ -182,7 +182,6 @@ static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx, if (tid != GetCurrentThreadId() && (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1) { - mdThd->SuspendCount--; ctx->ContextFlags = CONTEXT_FULL; if (!GetThreadContext(hThread, ctx)) memset(ctx, 0, sizeof(*ctx)); @@ -203,7 +202,10 @@ static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx, &ctx, sizeof(ctx), NULL); pctx = &lctx; } - else pctx = except->ExceptionPointers->ContextRecord; + else + pctx = except->ExceptionPointers->ContextRecord; + + memcpy(ctx, pctx, sizeof(*ctx)); fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack); } } @@ -351,7 +353,8 @@ static void append(struct dump_context* dc, void* data, unsigned size) * Write in File the exception information from pcs */ static void dump_exception_info(struct dump_context* dc, - const MINIDUMP_EXCEPTION_INFORMATION* except) + const MINIDUMP_EXCEPTION_INFORMATION* except, + ULONG64 *streamSize) { MINIDUMP_EXCEPTION_STREAM mdExcpt; EXCEPTION_RECORD rec, *prec; @@ -389,8 +392,14 @@ static void dump_exception_info(struct dump_context* dc, mdExcpt.ThreadContext.DataSize = sizeof(*pctx); mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt); + /* the size of this stream should be reported as the size of just the struct. The thread context + block exists outside this stream and is referenced by just its RVA. */ + *streamSize = sizeof(mdExcpt); + append(dc, &mdExcpt, sizeof(mdExcpt)); append(dc, pctx, sizeof(*pctx)); + + TRACE("wrote out an exception info block {streamSize = %llu, extraSpace = %u}\n", *streamSize, sizeof(*pctx)); } /****************************************************************** @@ -398,7 +407,7 @@ static void dump_exception_info(struct dump_context* dc, * * Write in File the modules from pcs */ -static void dump_modules(struct dump_context* dc, BOOL dump_elf) +static void dump_modules(struct dump_context* dc, BOOL dump_elf, ULONG64 *streamSize) { MINIDUMP_MODULE mdModule; MINIDUMP_MODULE_LIST mdModuleList; @@ -423,6 +432,8 @@ static void dump_modules(struct dump_context* dc, BOOL dump_elf) */ rva_base = dc->rva; dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod; + + for (i = 0; i < dc->num_module; i++) { if ((dc->module[i].is_elf && !dump_elf) || @@ -490,6 +501,14 @@ static void dump_modules(struct dump_context* dc, BOOL dump_elf) } writeat(dc, rva_base, &mdModuleList.NumberOfModules, sizeof(mdModuleList.NumberOfModules)); + + /* the stream size is just the size of the module index. It does not include the data for the + names of each module. *Technically* the names are supposed to go into the common string table + in the minidump file. Since each string is referenced by RVA they can all safely be located + anywhere between streams in the file, so the end of this stream is sufficient. */ + *streamSize = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * mdModuleList.NumberOfModules; + + TRACE("wrote out %u module info blocks {streamSize = %llu, extraSpace = %u}\n", mdModuleList.NumberOfModules, *streamSize, dc->rva - rva_base - (ULONG)(*streamSize)); } /****************************************************************** @@ -497,7 +516,7 @@ static void dump_modules(struct dump_context* dc, BOOL dump_elf) * * Dumps into File the information about the system */ -static void dump_system_info(struct dump_context* dc) +static void dump_system_info(struct dump_context* dc, ULONG64 *streamSize) { MINIDUMP_SYSTEM_INFO mdSysInfo; SYSTEM_INFO sysInfo; @@ -521,15 +540,24 @@ static void dump_system_info(struct dump_context* dc) mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo); mdSysInfo.u1.Reserved1 = 0; + mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL; + FIXME("fill in CPU vendorID and feature set\n"); memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu)); append(dc, &mdSysInfo, sizeof(mdSysInfo)); + + *streamSize = sizeof(MINIDUMP_SYSTEM_INFO); + + /* write the service pack version string after this stream. It is referenced within the + stream by its RVA in the file. */ slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR); WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL); dc->rva += sizeof(ULONG) + slen; + + TRACE("wrote out a system information block {streamSize = %llu, extraSpace = %u}\n", *streamSize, sizeof(ULONG) + slen); } /****************************************************************** @@ -538,7 +566,8 @@ static void dump_system_info(struct dump_context* dc) * Dumps into File the information about running threads */ static void dump_threads(struct dump_context* dc, - const MINIDUMP_EXCEPTION_INFORMATION* except) + const MINIDUMP_EXCEPTION_INFORMATION* except, + ULONG64 *streamSize) { MINIDUMP_THREAD mdThd; MINIDUMP_THREAD_LIST mdThdList; @@ -618,6 +647,14 @@ static void dump_threads(struct dump_context* dc, } writeat(dc, rva_base, &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads)); + + + /* size of this stream is the total size of the thread index. The stack memory + data and the context records are not included in this count */ + *streamSize = sizeof(mdThdList.NumberOfThreads) + + mdThdList.NumberOfThreads * sizeof(mdThd); + + TRACE("wrote out %u thread info blocks {streamSize = %llu, extraSpace = %u}\n", mdThdList.NumberOfThreads, *streamSize, dc->rva - rva_base - (ULONG)(*streamSize)); } /****************************************************************** @@ -625,21 +662,29 @@ static void dump_threads(struct dump_context* dc, * * dumps information about the memory of the process (stack of the threads) */ -static void dump_memory_info(struct dump_context* dc) +static void dump_memory_info(struct dump_context* dc, ULONG64 *streamSize) { MINIDUMP_MEMORY_LIST mdMemList; MINIDUMP_MEMORY_DESCRIPTOR mdMem; DWORD written; + DWORD64 totalSize = 0; unsigned i, pos, len; RVA rva_base; char tmp[1024]; + mdMemList.NumberOfMemoryRanges = dc->num_mem; append(dc, &mdMemList.NumberOfMemoryRanges, sizeof(mdMemList.NumberOfMemoryRanges)); rva_base = dc->rva; dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem); + /* the size of this stream only includes the memory list index. It does not include + the size of each memory block. Memory blocks are referenced by RVA so they can + safely be stored starting at the end of this stream. */ + *streamSize = sizeof(mdMemList.NumberOfMemoryRanges) + mdMemList.NumberOfMemoryRanges * sizeof(mdMem); + + for (i = 0; i < dc->num_mem; i++) { mdMem.StartOfMemoryRange = dc->mem[i].base; @@ -654,24 +699,37 @@ static void dump_memory_info(struct dump_context* dc) tmp, len, NULL)) WriteFile(dc->hFile, tmp, len, &written, NULL); } + dc->rva += mdMem.Memory.DataSize; + totalSize += mdMem.Memory.DataSize; + writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem)); if (dc->mem[i].rva) { writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva)); } } + + TRACE("wrote out %u blocks of memory {streamSize = %llu, extraSpace = %llu}\n", dc->num_mem, *streamSize, totalSize); } -static void dump_misc_info(struct dump_context* dc) +static void dump_misc_info(struct dump_context* dc, ULONG64 *streamSize) { MINIDUMP_MISC_INFO mmi; mmi.SizeOfInfo = sizeof(mmi); mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID; mmi.ProcessId = dc->pid; + /* FIXME: create/user/kernel time */ + mmi.ProcessCreateTime = 0; + mmi.ProcessKernelTime = 0; + mmi.ProcessUserTime = 0; + append(dc, &mmi, sizeof(mmi)); + + *streamSize = sizeof(mmi); + TRACE("wrote out a misc info block {streamSize = %llu}\n", *streamSize); } /****************************************************************** @@ -686,9 +744,16 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, { MINIDUMP_HEADER mdHead; MINIDUMP_DIRECTORY mdDir; + MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}}; DWORD i, nStreams, idx_stream; + ULONG64 streamSize; struct dump_context dc; + + TRACE("creating a minidump (hProcess = %p, pid = %x, hFile = %p, DumpType = 0x%08x, exceptParam = %p, userStream = %p, callbackParam = %p)\n", + hProcess, pid, hFile, DumpType, ExceptionParam, UserStreamParam, CallbackParam); + + dc.hProcess = hProcess; dc.hFile = hFile; dc.pid = pid; @@ -700,13 +765,21 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, dc.num_mem = 0; dc.rva = 0; - if (!fetch_process_info(&dc)) return FALSE; + if (!fetch_process_info(&dc)){ + ERR("could not retrieve my process info! {pid = %d}\n", pid); + + return FALSE; + } + fetch_module_info(&dc); /* 1) init */ nStreams = 6 + (ExceptionParam ? 1 : 0) + (UserStreamParam ? UserStreamParam->UserStreamCount : 0); + /* pad the directory size to a multiple of 4 for alignment purposes */ + nStreams = (nStreams + 3) & ~3; + if (DumpType & MiniDumpWithDataSegs) FIXME("NIY MiniDumpWithDataSegs\n"); if (DumpType & MiniDumpWithFullMemory) @@ -720,10 +793,11 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, /* 2) write header */ mdHead.Signature = MINIDUMP_SIGNATURE; - mdHead.Version = MINIDUMP_VERSION; + mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */ mdHead.NumberOfStreams = nStreams; + mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */ mdHead.StreamDirectoryRva = sizeof(mdHead); - mdHead.u.TimeDateStamp = time(NULL); + mdHead.u.TimeDateStamp = (ULONG32)time(NULL); mdHead.Flags = DumpType; append(&dc, &mdHead, sizeof(mdHead)); @@ -735,43 +809,43 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, mdDir.StreamType = ThreadListStream; mdDir.Location.Rva = dc.rva; - dump_threads(&dc, ExceptionParam); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_threads(&dc, ExceptionParam, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = ModuleListStream; mdDir.Location.Rva = dc.rva; - dump_modules(&dc, FALSE); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_modules(&dc, FALSE, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */ mdDir.Location.Rva = dc.rva; - dump_modules(&dc, TRUE); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_modules(&dc, TRUE, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = MemoryListStream; mdDir.Location.Rva = dc.rva; - dump_memory_info(&dc); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_memory_info(&dc, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = SystemInfoStream; mdDir.Location.Rva = dc.rva; - dump_system_info(&dc); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_system_info(&dc, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = MiscInfoStream; mdDir.Location.Rva = dc.rva; - dump_misc_info(&dc); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_misc_info(&dc, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); @@ -780,8 +854,8 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, { mdDir.StreamType = ExceptionStream; mdDir.Location.Rva = dc.rva; - dump_exception_info(&dc, ExceptionParam); - mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; + dump_exception_info(&dc, ExceptionParam, &streamSize); + mdDir.Location.DataSize = (ULONG32)streamSize; /*dc.rva - mdDir.Location.Rva;*/ writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); } @@ -801,10 +875,17 @@ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, } } + + /* 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.pcs_buffer); HeapFree(GetProcessHeap(), 0, dc.mem); HeapFree(GetProcessHeap(), 0, dc.module); + TRACE("minidump performed successfully\n"); return TRUE; } -- 1.4.4.2