2 * File minidump.c - management of dumps (read & write)
4 * Copyright (C) 2004-2005, Eric Pouech
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
27 #define WIN32_NO_STATUS
28 #include "dbghelp_private.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
54 /* process & thread information */
58 SYSTEM_PROCESS_INFORMATION* spi;
59 /* module information */
60 struct dump_module* modules;
62 unsigned alloc_modules;
63 /* exception information */
64 /* output information */
68 struct dump_memory* mem;
71 /* callback information */
72 MINIDUMP_CALLBACK_INFORMATION* cb;
75 /******************************************************************
76 * fetch_processes_info
78 * reads system wide process information, and make spi point to the record
79 * for process of id 'pid'
81 static BOOL fetch_processes_info(struct dump_context* dc)
83 ULONG buf_size = 0x1000;
86 dc->pcs_buffer = NULL;
87 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
90 nts = NtQuerySystemInformation(SystemProcessInformation,
91 dc->pcs_buffer, buf_size, NULL);
92 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
93 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
95 if (!dc->pcs_buffer) return FALSE;
98 if (nts == STATUS_SUCCESS)
100 dc->spi = dc->pcs_buffer;
103 if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
104 if (!dc->spi->NextEntryOffset) break;
105 dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
108 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
109 dc->pcs_buffer = NULL;
114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
115 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
119 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
122 /* limiting the stack dumping to the size actually used */
125 /* make sure ESP is within the established range of the stack. It could have
126 been clobbered by whatever caused the original exception. */
127 if (ctx->Esp - 4 < (ULONG_PTR)tib.StackLimit || ctx->Esp - 4 > (ULONG_PTR)tib.StackBase)
128 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
131 mmd->StartOfMemoryRange = (ctx->Esp - 4);
135 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
137 #elif defined(__powerpc__)
140 /* make sure IAR is within the established range of the stack. It could have
141 been clobbered by whatever caused the original exception. */
142 if (ctx->Iar - 4 < (ULONG_PTR)tib.StackLimit || ctx->Iar - 4 > (ULONG_PTR)tib.StackBase)
143 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
146 mmd->StartOfMemoryRange = (ctx->Iar - 4);
150 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
152 #elif defined(__x86_64__)
155 /* make sure RSP is within the established range of the stack. It could have
156 been clobbered by whatever caused the original exception. */
157 if (ctx->Rsp - 8 < (ULONG_PTR)tib.StackLimit || ctx->Rsp - 8 > (ULONG_PTR)tib.StackBase)
158 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
161 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
165 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
168 #error unsupported CPU
170 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
174 /******************************************************************
177 * fetches some information about thread of id 'tid'
179 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
180 const MINIDUMP_EXCEPTION_INFORMATION* except,
181 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
183 DWORD tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
185 THREAD_BASIC_INFORMATION tbi;
187 memset(ctx, 0, sizeof(*ctx));
189 mdThd->ThreadId = tid;
190 mdThd->SuspendCount = 0;
192 mdThd->Stack.StartOfMemoryRange = 0;
193 mdThd->Stack.Memory.DataSize = 0;
194 mdThd->Stack.Memory.Rva = 0;
195 mdThd->ThreadContext.DataSize = 0;
196 mdThd->ThreadContext.Rva = 0;
197 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
198 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
200 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
202 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
206 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
207 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
209 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
210 if (tbi.ExitStatus == STILL_ACTIVE)
212 if (tid != GetCurrentThreadId() &&
213 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
215 ctx->ContextFlags = CONTEXT_FULL;
216 if (!GetThreadContext(hThread, ctx))
217 memset(ctx, 0, sizeof(*ctx));
219 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
220 ResumeThread(hThread);
222 else if (tid == GetCurrentThreadId() && except)
225 mdThd->SuspendCount = 1;
226 if (except->ClientPointers)
228 EXCEPTION_POINTERS ep;
230 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
231 &ep, sizeof(ep), NULL);
232 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
233 &ctx, sizeof(ctx), NULL);
236 else pctx = except->ExceptionPointers->ContextRecord;
239 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
241 else mdThd->SuspendCount = 0;
244 CloseHandle(hThread);
248 /******************************************************************
251 * Add a module to a dump context
253 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
254 DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
259 dc->alloc_modules = 32;
260 dc->modules = HeapAlloc(GetProcessHeap(), 0,
261 dc->alloc_modules * sizeof(*dc->modules));
263 else if(dc->num_modules >= dc->alloc_modules)
265 dc->alloc_modules *= 2;
266 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
267 dc->alloc_modules * sizeof(*dc->modules));
271 dc->alloc_modules = dc->num_modules = 0;
275 !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
276 dc->modules[dc->num_modules].name,
277 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)))
278 lstrcpynW(dc->modules[dc->num_modules].name, name,
279 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR));
280 dc->modules[dc->num_modules].base = base;
281 dc->modules[dc->num_modules].size = size;
282 dc->modules[dc->num_modules].timestamp = timestamp;
283 dc->modules[dc->num_modules].checksum = checksum;
284 dc->modules[dc->num_modules].is_elf = is_elf;
290 /******************************************************************
291 * fetch_pe_module_info_cb
293 * Callback for accumulating in dump_context a PE modules set
295 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
298 struct dump_context* dc = user;
299 IMAGE_NT_HEADERS nth;
301 if (!validate_addr64(base)) return FALSE;
303 if (pe_load_nt_header(dc->hProcess, base, &nth))
304 add_module(user, name, base, size,
305 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
310 /******************************************************************
311 * fetch_elf_module_info_cb
313 * Callback for accumulating in dump_context an ELF modules set
315 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
318 struct dump_context* dc = user;
319 DWORD rbase, size, checksum;
321 /* FIXME: there's no relevant timestamp on ELF modules */
322 /* NB: if we have a non-null base from the live-target use it (whenever
323 * the ELF module is relocatable or not). If we have a null base (ELF
324 * module isn't relocatable) then grab its base address from ELF file
326 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
328 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
332 /******************************************************************
333 * fetch_macho_module_info_cb
335 * Callback for accumulating in dump_context a Mach-O modules set
337 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
340 struct dump_context* dc = (struct dump_context*)user;
341 DWORD rbase, size, checksum;
343 /* FIXME: there's no relevant timestamp on Mach-O modules */
344 /* NB: if we have a non-null base from the live-target use it. If we have
345 * a null base, then grab its base address from Mach-O file.
347 if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
349 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
353 static void fetch_modules_info(struct dump_context* dc)
355 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
356 /* Since we include ELF modules in a separate stream from the regular PE ones,
357 * we can always include those ELF modules (they don't eat lots of space)
358 * And it's always a good idea to have a trace of the loaded ELF modules for
359 * a given application in a post mortem debugging condition.
361 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
362 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
365 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
369 static const WCHAR backslashW[] = {'\\', '\0'};
371 memset(ffi, 0, sizeof(*ffi));
372 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
374 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
375 if (info && GetFileVersionInfoW(filename, handle, sz, info))
377 VS_FIXEDFILEINFO* ptr;
380 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
381 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
383 HeapFree(GetProcessHeap(), 0, info);
387 /******************************************************************
390 * Add a memory block to be dumped in a minidump
391 * If rva is non 0, it's the rva in the minidump where has to be stored
392 * also the rva of the memory block when written (this allows to reference
393 * a memory block from outside the list of memory blocks).
395 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
400 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
402 else if (dc->num_mem >= dc->alloc_mem)
405 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
406 dc->alloc_mem * sizeof(*dc->mem));
410 dc->mem[dc->num_mem].base = base;
411 dc->mem[dc->num_mem].size = size;
412 dc->mem[dc->num_mem].rva = rva;
415 else dc->num_mem = dc->alloc_mem = 0;
418 /******************************************************************
421 * Writes a chunk of data at a given position in the minidump
423 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
427 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
428 WriteFile(dc->hFile, data, size, &written, NULL);
431 /******************************************************************
434 * writes a new chunk of data to the minidump, increasing the current
437 static void append(struct dump_context* dc, const void* data, unsigned size)
439 writeat(dc, dc->rva, data, size);
443 /******************************************************************
444 * dump_exception_info
446 * Write in File the exception information from pcs
448 static unsigned dump_exception_info(struct dump_context* dc,
449 const MINIDUMP_EXCEPTION_INFORMATION* except)
451 MINIDUMP_EXCEPTION_STREAM mdExcpt;
452 EXCEPTION_RECORD rec, *prec;
456 mdExcpt.ThreadId = except->ThreadId;
457 mdExcpt.__alignment = 0;
458 if (except->ClientPointers)
460 EXCEPTION_POINTERS ep;
462 ReadProcessMemory(dc->hProcess,
463 except->ExceptionPointers, &ep, sizeof(ep), NULL);
464 ReadProcessMemory(dc->hProcess,
465 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
466 ReadProcessMemory(dc->hProcess,
467 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
473 prec = except->ExceptionPointers->ExceptionRecord;
474 pctx = except->ExceptionPointers->ContextRecord;
476 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
477 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
478 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
479 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
480 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
481 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
482 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
483 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
484 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
485 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
487 append(dc, &mdExcpt, sizeof(mdExcpt));
488 append(dc, pctx, sizeof(*pctx));
489 return sizeof(mdExcpt);
492 /******************************************************************
495 * Write in File the modules from pcs
497 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
499 MINIDUMP_MODULE mdModule;
500 MINIDUMP_MODULE_LIST mdModuleList;
502 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
508 for (i = nmod = 0; i < dc->num_modules; i++)
510 if ((dc->modules[i].is_elf && dump_elf) ||
511 (!dc->modules[i].is_elf && !dump_elf))
515 mdModuleList.NumberOfModules = 0;
516 /* reserve space for mdModuleList
517 * FIXME: since we don't support 0 length arrays, we cannot use the
518 * size of mdModuleList
519 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
522 /* the stream size is just the size of the module index. It does not include the data for the
523 names of each module. *Technically* the names are supposed to go into the common string table
524 in the minidump file. Since each string is referenced by RVA they can all safely be located
525 anywhere between streams in the file, so the end of this stream is sufficient. */
527 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
528 for (i = 0; i < dc->num_modules; i++)
530 if ((dc->modules[i].is_elf && !dump_elf) ||
531 (!dc->modules[i].is_elf && dump_elf))
534 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
535 if (dc->type & MiniDumpWithDataSegs)
536 flags_out |= ModuleWriteDataSeg;
537 if (dc->type & MiniDumpWithProcessThreadData)
538 flags_out |= ModuleWriteTlsData;
539 if (dc->type & MiniDumpWithCodeSegs)
540 flags_out |= ModuleWriteCodeSegs;
541 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
542 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
543 FIXME("Buffer overflow!!!\n");
544 lstrcpyW(ms->Buffer, dc->modules[i].name);
548 MINIDUMP_CALLBACK_INPUT cbin;
549 MINIDUMP_CALLBACK_OUTPUT cbout;
551 cbin.ProcessId = dc->pid;
552 cbin.ProcessHandle = dc->hProcess;
553 cbin.CallbackType = ModuleCallback;
555 cbin.u.Module.FullPath = ms->Buffer;
556 cbin.u.Module.BaseOfImage = dc->modules[i].base;
557 cbin.u.Module.SizeOfImage = dc->modules[i].size;
558 cbin.u.Module.CheckSum = dc->modules[i].checksum;
559 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
560 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
561 cbin.u.Module.CvRecord = NULL;
562 cbin.u.Module.SizeOfCvRecord = 0;
563 cbin.u.Module.MiscRecord = NULL;
564 cbin.u.Module.SizeOfMiscRecord = 0;
566 cbout.u.ModuleWriteFlags = flags_out;
567 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
569 flags_out &= cbout.u.ModuleWriteFlags;
571 if (flags_out & ModuleWriteModule)
573 mdModule.BaseOfImage = dc->modules[i].base;
574 mdModule.SizeOfImage = dc->modules[i].size;
575 mdModule.CheckSum = dc->modules[i].checksum;
576 mdModule.TimeDateStamp = dc->modules[i].timestamp;
577 mdModule.ModuleNameRva = dc->rva;
578 ms->Length -= sizeof(WCHAR);
579 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
580 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
581 mdModule.CvRecord.DataSize = 0; /* FIXME */
582 mdModule.CvRecord.Rva = 0; /* FIXME */
583 mdModule.MiscRecord.DataSize = 0; /* FIXME */
584 mdModule.MiscRecord.Rva = 0; /* FIXME */
585 mdModule.Reserved0 = 0; /* FIXME */
586 mdModule.Reserved1 = 0; /* FIXME */
588 rva_base + sizeof(mdModuleList.NumberOfModules) +
589 mdModuleList.NumberOfModules++ * sizeof(mdModule),
590 &mdModule, sizeof(mdModule));
593 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
594 sizeof(mdModuleList.NumberOfModules));
599 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
600 * We are compiled with -fPIC, so we can't clobber ebx.
602 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
604 #if defined(__GNUC__) && defined(__i386__)
605 __asm__("pushl %%ebx\n\t"
607 "movl %%ebx, %%esi\n\t"
609 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
614 /* From xf86info havecpuid.c 1.11 */
615 static inline int have_x86cpuid(void)
617 #if defined(__GNUC__) && defined(__i386__)
629 : "=&r" (f1), "=&r" (f2)
630 : "ir" (0x00200000));
631 return ((f1^f2) & 0x00200000) != 0;
637 /******************************************************************
640 * Dumps into File the information about the system
642 static unsigned dump_system_info(struct dump_context* dc)
644 MINIDUMP_SYSTEM_INFO mdSysInfo;
646 OSVERSIONINFOW osInfo;
650 GetSystemInfo(&sysInfo);
651 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
652 GetVersionExW(&osInfo);
654 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
655 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
656 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
657 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
658 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
659 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
660 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
661 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
662 mdSysInfo.PlatformId = osInfo.dwPlatformId;
664 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
665 mdSysInfo.u1.Reserved1 = 0;
666 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
670 unsigned regs0[4], regs1[4];
672 do_x86cpuid(0, regs0);
673 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
674 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
675 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
676 do_x86cpuid(1, regs1);
677 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
678 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
679 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
680 if (regs0[1] == 0x68747541 /* "Auth" */ &&
681 regs0[3] == 0x69746e65 /* "enti" */ &&
682 regs0[2] == 0x444d4163 /* "cAMD" */)
684 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
685 if (regs1[0] >= 0x80000001)
687 do_x86cpuid(0x80000001, regs1); /* get vendor features */
688 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
697 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
698 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
700 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
701 if (IsProcessorFeaturePresent(i))
702 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
704 append(dc, &mdSysInfo, sizeof(mdSysInfo));
706 /* write the service pack version string after this stream. It is referenced within the
707 stream by its RVA in the file. */
708 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
709 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
710 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
711 dc->rva += sizeof(ULONG) + slen;
713 return sizeof(mdSysInfo);
716 /******************************************************************
719 * Dumps into File the information about running threads
721 static unsigned dump_threads(struct dump_context* dc,
722 const MINIDUMP_EXCEPTION_INFORMATION* except)
724 MINIDUMP_THREAD mdThd;
725 MINIDUMP_THREAD_LIST mdThdList;
731 mdThdList.NumberOfThreads = 0;
734 dc->rva += sizeof(mdThdList.NumberOfThreads) +
735 dc->spi->dwThreadCount * sizeof(mdThd);
737 for (i = 0; i < dc->spi->dwThreadCount; i++)
739 fetch_thread_info(dc, i, except, &mdThd, &ctx);
741 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
742 ThreadWriteInstructionWindow;
743 if (dc->type & MiniDumpWithProcessThreadData)
744 flags_out |= ThreadWriteThreadData;
745 if (dc->type & MiniDumpWithThreadInfo)
746 flags_out |= ThreadWriteThreadInfo;
750 MINIDUMP_CALLBACK_INPUT cbin;
751 MINIDUMP_CALLBACK_OUTPUT cbout;
753 cbin.ProcessId = dc->pid;
754 cbin.ProcessHandle = dc->hProcess;
755 cbin.CallbackType = ThreadCallback;
756 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
757 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
758 cbin.u.Thread.Context = ctx;
759 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
760 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
761 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
762 mdThd.Stack.Memory.DataSize;
764 cbout.u.ThreadWriteFlags = flags_out;
765 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
767 flags_out &= cbout.u.ThreadWriteFlags;
769 if (flags_out & ThreadWriteThread)
771 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
773 mdThd.ThreadContext.Rva = dc->rva;
774 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
775 append(dc, &ctx, sizeof(CONTEXT));
777 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
779 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
780 mdThd.Stack.Memory.DataSize,
781 rva_base + sizeof(mdThdList.NumberOfThreads) +
782 mdThdList.NumberOfThreads * sizeof(mdThd) +
783 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
786 rva_base + sizeof(mdThdList.NumberOfThreads) +
787 mdThdList.NumberOfThreads * sizeof(mdThd),
788 &mdThd, sizeof(mdThd));
789 mdThdList.NumberOfThreads++;
791 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
793 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
794 * - also crop values across module boundaries,
795 * - and don't make it i386 dependent
797 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
800 writeat(dc, rva_base,
801 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
803 return dc->rva - rva_base;
806 /******************************************************************
809 * dumps information about the memory of the process (stack of the threads)
811 static unsigned dump_memory_info(struct dump_context* dc)
813 MINIDUMP_MEMORY_LIST mdMemList;
814 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
816 unsigned i, pos, len, sz;
820 mdMemList.NumberOfMemoryRanges = dc->num_mem;
821 append(dc, &mdMemList.NumberOfMemoryRanges,
822 sizeof(mdMemList.NumberOfMemoryRanges));
824 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
826 sz += sizeof(mdMemList.NumberOfMemoryRanges);
828 for (i = 0; i < dc->num_mem; i++)
830 mdMem.StartOfMemoryRange = dc->mem[i].base;
831 mdMem.Memory.Rva = dc->rva;
832 mdMem.Memory.DataSize = dc->mem[i].size;
833 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
834 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
836 len = min(dc->mem[i].size - pos, sizeof(tmp));
837 if (ReadProcessMemory(dc->hProcess,
838 (void*)(dc->mem[i].base + pos),
840 WriteFile(dc->hFile, tmp, len, &written, NULL);
842 dc->rva += mdMem.Memory.DataSize;
843 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
846 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
853 static unsigned dump_misc_info(struct dump_context* dc)
855 MINIDUMP_MISC_INFO mmi;
857 mmi.SizeOfInfo = sizeof(mmi);
858 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
859 mmi.ProcessId = dc->pid;
860 /* FIXME: create/user/kernel time */
861 mmi.ProcessCreateTime = 0;
862 mmi.ProcessKernelTime = 0;
863 mmi.ProcessUserTime = 0;
865 append(dc, &mmi, sizeof(mmi));
869 /******************************************************************
870 * MiniDumpWriteDump (DEBUGHLP.@)
873 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
874 MINIDUMP_TYPE DumpType,
875 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
876 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
877 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
879 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
880 MINIDUMP_HEADER mdHead;
881 MINIDUMP_DIRECTORY mdDir;
882 DWORD i, nStreams, idx_stream;
883 struct dump_context dc;
885 dc.hProcess = hProcess;
890 dc.alloc_modules = 0;
891 dc.cb = CallbackParam;
898 if (!fetch_processes_info(&dc)) return FALSE;
899 fetch_modules_info(&dc);
902 nStreams = 6 + (ExceptionParam ? 1 : 0) +
903 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
905 /* pad the directory size to a multiple of 4 for alignment purposes */
906 nStreams = (nStreams + 3) & ~3;
908 if (DumpType & MiniDumpWithDataSegs)
909 FIXME("NIY MiniDumpWithDataSegs\n");
910 if (DumpType & MiniDumpWithFullMemory)
911 FIXME("NIY MiniDumpWithFullMemory\n");
912 if (DumpType & MiniDumpWithHandleData)
913 FIXME("NIY MiniDumpWithHandleData\n");
914 if (DumpType & MiniDumpFilterMemory)
915 FIXME("NIY MiniDumpFilterMemory\n");
916 if (DumpType & MiniDumpScanMemory)
917 FIXME("NIY MiniDumpScanMemory\n");
919 /* 2) write header */
920 mdHead.Signature = MINIDUMP_SIGNATURE;
921 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
922 mdHead.NumberOfStreams = nStreams;
923 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
924 mdHead.StreamDirectoryRva = sizeof(mdHead);
925 mdHead.u.TimeDateStamp = time(NULL);
926 mdHead.Flags = DumpType;
927 append(&dc, &mdHead, sizeof(mdHead));
929 /* 3) write stream directories */
930 dc.rva += nStreams * sizeof(mdDir);
933 /* 3.1) write data stream directories */
935 /* must be first in minidump */
936 mdDir.StreamType = SystemInfoStream;
937 mdDir.Location.Rva = dc.rva;
938 mdDir.Location.DataSize = dump_system_info(&dc);
939 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
940 &mdDir, sizeof(mdDir));
942 mdDir.StreamType = ThreadListStream;
943 mdDir.Location.Rva = dc.rva;
944 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
945 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
946 &mdDir, sizeof(mdDir));
948 mdDir.StreamType = ModuleListStream;
949 mdDir.Location.Rva = dc.rva;
950 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
951 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
952 &mdDir, sizeof(mdDir));
954 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
955 mdDir.Location.Rva = dc.rva;
956 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
957 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
958 &mdDir, sizeof(mdDir));
960 mdDir.StreamType = MemoryListStream;
961 mdDir.Location.Rva = dc.rva;
962 mdDir.Location.DataSize = dump_memory_info(&dc);
963 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
964 &mdDir, sizeof(mdDir));
966 mdDir.StreamType = MiscInfoStream;
967 mdDir.Location.Rva = dc.rva;
968 mdDir.Location.DataSize = dump_misc_info(&dc);
969 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
970 &mdDir, sizeof(mdDir));
972 /* 3.2) write exception information (if any) */
975 mdDir.StreamType = ExceptionStream;
976 mdDir.Location.Rva = dc.rva;
977 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
978 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
979 &mdDir, sizeof(mdDir));
982 /* 3.3) write user defined streams (if any) */
985 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
987 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
988 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
989 mdDir.Location.Rva = dc.rva;
990 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
991 &mdDir, sizeof(mdDir));
992 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
993 UserStreamParam->UserStreamArray[i].BufferSize);
997 /* fill the remaining directory entries with 0's (unused stream types) */
998 /* NOTE: this should always come last in the dump! */
999 for (i = idx_stream; i < nStreams; i++)
1000 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1002 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
1003 HeapFree(GetProcessHeap(), 0, dc.mem);
1004 HeapFree(GetProcessHeap(), 0, dc.modules);
1009 /******************************************************************
1010 * MiniDumpReadDumpStream (DEBUGHLP.@)
1014 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1015 PMINIDUMP_DIRECTORY* pdir,
1016 PVOID* stream, ULONG* size)
1018 MINIDUMP_HEADER* mdHead = base;
1020 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1022 MINIDUMP_DIRECTORY* dir;
1025 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1026 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1028 if (dir->StreamType == str_idx)
1030 if (pdir) *pdir = dir;
1031 if (stream) *stream = (char*)base + dir->Location.Rva;
1032 if (size) *size = dir->Location.DataSize;
1037 SetLastError(ERROR_INVALID_PARAMETER);