push c4c845830c6aff14e1b16bbb8b4a57a7e0d6213f
[wine/hacks.git] / dlls / dbghelp / minidump.c
blob7c624c9977303bd86ae4ca923dd3c94cbbc097bd
1 /*
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
21 #include <time.h>
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "dbghelp_private.h"
29 #include "winternl.h"
30 #include "psapi.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
35 struct dump_memory
37 ULONG base;
38 ULONG size;
39 ULONG rva;
42 struct dump_module
44 unsigned is_elf;
45 ULONG base;
46 ULONG size;
47 DWORD timestamp;
48 DWORD checksum;
49 WCHAR name[MAX_PATH];
52 struct dump_context
54 /* process & thread information */
55 HANDLE hProcess;
56 DWORD pid;
57 void* pcs_buffer;
58 SYSTEM_PROCESS_INFORMATION* spi;
59 /* module information */
60 struct dump_module* modules;
61 unsigned num_modules;
62 unsigned alloc_modules;
63 /* exception information */
64 /* output information */
65 MINIDUMP_TYPE type;
66 HANDLE hFile;
67 RVA rva;
68 struct dump_memory* mem;
69 unsigned num_mem;
70 unsigned alloc_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;
84 NTSTATUS nts;
86 dc->pcs_buffer = NULL;
87 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
88 for (;;)
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,
94 buf_size *= 2);
95 if (!dc->pcs_buffer) return FALSE;
98 if (nts == STATUS_SUCCESS)
100 dc->spi = dc->pcs_buffer;
101 for (;;)
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;
110 dc->spi = NULL;
111 return FALSE;
114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
115 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
117 NT_TIB tib;
119 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
121 #ifdef __i386__
122 /* limiting the stack dumping to the size actually used */
123 if (ctx->Esp){
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;
130 else
131 mmd->StartOfMemoryRange = (ctx->Esp - 4);
134 else
135 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
137 #elif defined(__powerpc__)
138 if (ctx->Iar){
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;
145 else
146 mmd->StartOfMemoryRange = (ctx->Iar - 4);
149 else
150 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
152 #elif defined(__x86_64__)
153 if (ctx->Rsp){
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;
160 else
161 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
164 else
165 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
167 #else
168 #error unsupported CPU
169 #endif
170 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
174 /******************************************************************
175 * fetch_thread_info
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);
184 HANDLE hThread;
185 THREAD_BASIC_INFORMATION tbi;
187 memset(ctx, 0, sizeof(*ctx));
189 mdThd->ThreadId = tid;
190 mdThd->SuspendCount = 0;
191 mdThd->Teb = 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());
203 return FALSE;
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)
224 CONTEXT lctx, *pctx;
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);
234 pctx = &lctx;
236 else pctx = except->ExceptionPointers->ContextRecord;
238 *ctx = *pctx;
239 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
241 else mdThd->SuspendCount = 0;
244 CloseHandle(hThread);
245 return TRUE;
248 /******************************************************************
249 * add_module
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,
255 BOOL is_elf)
257 if (!dc->modules)
259 dc->alloc_modules = 32;
260 dc->modules = HeapAlloc(GetProcessHeap(), 0,
261 dc->alloc_modules * sizeof(*dc->modules));
263 else
265 dc->alloc_modules *= 2;
266 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
267 dc->alloc_modules * sizeof(*dc->modules));
269 if (!dc->modules) return FALSE;
270 if (is_elf ||
271 !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
272 dc->modules[dc->num_modules - 1].name,
273 sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR)))
274 lstrcpynW(dc->modules[dc->num_modules - 1].name, name,
275 sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR));
276 dc->modules[dc->num_modules - 1].base = base;
277 dc->modules[dc->num_modules - 1].size = size;
278 dc->modules[dc->num_modules - 1].timestamp = timestamp;
279 dc->modules[dc->num_modules - 1].checksum = checksum;
280 dc->modules[dc->num_modules - 1].is_elf = is_elf;
282 return TRUE;
285 /******************************************************************
286 * fetch_pe_module_info_cb
288 * Callback for accumulating in dump_context a PE modules set
290 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
291 PVOID user)
293 struct dump_context* dc = user;
294 IMAGE_NT_HEADERS nth;
296 if (!validate_addr64(base)) return FALSE;
298 if (pe_load_nt_header(dc->hProcess, base, &nth))
299 add_module(user, name, base, size,
300 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
301 FALSE);
302 return TRUE;
305 /******************************************************************
306 * fetch_elf_module_info_cb
308 * Callback for accumulating in dump_context an ELF modules set
310 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
311 void* user)
313 struct dump_context* dc = user;
314 DWORD rbase, size, checksum;
316 /* FIXME: there's no relevant timestamp on ELF modules */
317 /* NB: if we have a non-null base from the live-target use it (whenever
318 * the ELF module is relocatable or not). If we have a null base (ELF
319 * module isn't relocatable) then grab its base address from ELF file
321 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
322 size = checksum = 0;
323 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
324 return TRUE;
327 /******************************************************************
328 * fetch_macho_module_info_cb
330 * Callback for accumulating in dump_context a Mach-O modules set
332 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
333 void* user)
335 struct dump_context* dc = (struct dump_context*)user;
336 DWORD rbase, size, checksum;
338 /* FIXME: there's no relevant timestamp on Mach-O modules */
339 /* NB: if we have a non-null base from the live-target use it. If we have
340 * a null base, then grab its base address from Mach-O file.
342 if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
343 size = checksum = 0;
344 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
345 return TRUE;
348 static void fetch_modules_info(struct dump_context* dc)
350 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
351 /* Since we include ELF modules in a separate stream from the regular PE ones,
352 * we can always include those ELF modules (they don't eat lots of space)
353 * And it's always a good idea to have a trace of the loaded ELF modules for
354 * a given application in a post mortem debugging condition.
356 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
357 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
360 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
362 DWORD handle;
363 DWORD sz;
364 static const WCHAR backslashW[] = {'\\', '\0'};
366 memset(ffi, 0, sizeof(*ffi));
367 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
369 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
370 if (info && GetFileVersionInfoW(filename, handle, sz, info))
372 VS_FIXEDFILEINFO* ptr;
373 UINT len;
375 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
376 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
378 HeapFree(GetProcessHeap(), 0, info);
382 /******************************************************************
383 * add_memory_block
385 * Add a memory block to be dumped in a minidump
386 * If rva is non 0, it's the rva in the minidump where has to be stored
387 * also the rva of the memory block when written (this allows to reference
388 * a memory block from outside the list of memory blocks).
390 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
392 if (dc->mem)
394 dc->alloc_mem *= 2;
395 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
396 dc->alloc_mem * sizeof(*dc->mem));
398 else
400 dc->alloc_mem = 32;
401 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
403 if (dc->mem)
405 dc->mem[dc->num_mem - 1].base = base;
406 dc->mem[dc->num_mem - 1].size = size;
407 dc->mem[dc->num_mem - 1].rva = rva;
409 else dc->num_mem = dc->alloc_mem = 0;
412 /******************************************************************
413 * writeat
415 * Writes a chunk of data at a given position in the minidump
417 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
419 DWORD written;
421 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
422 WriteFile(dc->hFile, data, size, &written, NULL);
425 /******************************************************************
426 * append
428 * writes a new chunk of data to the minidump, increasing the current
429 * rva in dc
431 static void append(struct dump_context* dc, void* data, unsigned size)
433 writeat(dc, dc->rva, data, size);
434 dc->rva += size;
437 /******************************************************************
438 * dump_exception_info
440 * Write in File the exception information from pcs
442 static unsigned dump_exception_info(struct dump_context* dc,
443 const MINIDUMP_EXCEPTION_INFORMATION* except)
445 MINIDUMP_EXCEPTION_STREAM mdExcpt;
446 EXCEPTION_RECORD rec, *prec;
447 CONTEXT ctx, *pctx;
448 DWORD i;
450 mdExcpt.ThreadId = except->ThreadId;
451 mdExcpt.__alignment = 0;
452 if (except->ClientPointers)
454 EXCEPTION_POINTERS ep;
456 ReadProcessMemory(dc->hProcess,
457 except->ExceptionPointers, &ep, sizeof(ep), NULL);
458 ReadProcessMemory(dc->hProcess,
459 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
460 ReadProcessMemory(dc->hProcess,
461 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
462 prec = &rec;
463 pctx = &ctx;
465 else
467 prec = except->ExceptionPointers->ExceptionRecord;
468 pctx = except->ExceptionPointers->ContextRecord;
470 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
471 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
472 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
473 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
474 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
475 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
476 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
477 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
478 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
479 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
481 append(dc, &mdExcpt, sizeof(mdExcpt));
482 append(dc, pctx, sizeof(*pctx));
483 return sizeof(mdExcpt);
486 /******************************************************************
487 * dump_modules
489 * Write in File the modules from pcs
491 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
493 MINIDUMP_MODULE mdModule;
494 MINIDUMP_MODULE_LIST mdModuleList;
495 char tmp[1024];
496 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
497 ULONG i, nmod;
498 RVA rva_base;
499 DWORD flags_out;
500 unsigned sz;
502 for (i = nmod = 0; i < dc->num_modules; i++)
504 if ((dc->modules[i].is_elf && dump_elf) ||
505 (!dc->modules[i].is_elf && !dump_elf))
506 nmod++;
509 mdModuleList.NumberOfModules = 0;
510 /* reserve space for mdModuleList
511 * FIXME: since we don't support 0 length arrays, we cannot use the
512 * size of mdModuleList
513 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
516 /* the stream size is just the size of the module index. It does not include the data for the
517 names of each module. *Technically* the names are supposed to go into the common string table
518 in the minidump file. Since each string is referenced by RVA they can all safely be located
519 anywhere between streams in the file, so the end of this stream is sufficient. */
520 rva_base = dc->rva;
521 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
522 for (i = 0; i < dc->num_modules; i++)
524 if ((dc->modules[i].is_elf && !dump_elf) ||
525 (!dc->modules[i].is_elf && dump_elf))
526 continue;
528 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
529 if (dc->type & MiniDumpWithDataSegs)
530 flags_out |= ModuleWriteDataSeg;
531 if (dc->type & MiniDumpWithProcessThreadData)
532 flags_out |= ModuleWriteTlsData;
533 if (dc->type & MiniDumpWithCodeSegs)
534 flags_out |= ModuleWriteCodeSegs;
535 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
536 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
537 FIXME("Buffer overflow!!!\n");
538 lstrcpyW(ms->Buffer, dc->modules[i].name);
540 if (dc->cb)
542 MINIDUMP_CALLBACK_INPUT cbin;
543 MINIDUMP_CALLBACK_OUTPUT cbout;
545 cbin.ProcessId = dc->pid;
546 cbin.ProcessHandle = dc->hProcess;
547 cbin.CallbackType = ModuleCallback;
549 cbin.u.Module.FullPath = ms->Buffer;
550 cbin.u.Module.BaseOfImage = dc->modules[i].base;
551 cbin.u.Module.SizeOfImage = dc->modules[i].size;
552 cbin.u.Module.CheckSum = dc->modules[i].checksum;
553 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
554 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
555 cbin.u.Module.CvRecord = NULL;
556 cbin.u.Module.SizeOfCvRecord = 0;
557 cbin.u.Module.MiscRecord = NULL;
558 cbin.u.Module.SizeOfMiscRecord = 0;
560 cbout.u.ModuleWriteFlags = flags_out;
561 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
562 continue;
563 flags_out &= cbout.u.ModuleWriteFlags;
565 if (flags_out & ModuleWriteModule)
567 mdModule.BaseOfImage = dc->modules[i].base;
568 mdModule.SizeOfImage = dc->modules[i].size;
569 mdModule.CheckSum = dc->modules[i].checksum;
570 mdModule.TimeDateStamp = dc->modules[i].timestamp;
571 mdModule.ModuleNameRva = dc->rva;
572 ms->Length -= sizeof(WCHAR);
573 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
574 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
575 mdModule.CvRecord.DataSize = 0; /* FIXME */
576 mdModule.CvRecord.Rva = 0; /* FIXME */
577 mdModule.MiscRecord.DataSize = 0; /* FIXME */
578 mdModule.MiscRecord.Rva = 0; /* FIXME */
579 mdModule.Reserved0 = 0; /* FIXME */
580 mdModule.Reserved1 = 0; /* FIXME */
581 writeat(dc,
582 rva_base + sizeof(mdModuleList.NumberOfModules) +
583 mdModuleList.NumberOfModules++ * sizeof(mdModule),
584 &mdModule, sizeof(mdModule));
587 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
588 sizeof(mdModuleList.NumberOfModules));
590 return sz;
593 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
594 * We are compiled with -fPIC, so we can't clobber ebx.
596 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
598 #if defined(__GNUC__) && defined(__i386__)
599 __asm__("pushl %%ebx\n\t"
600 "cpuid\n\t"
601 "movl %%ebx, %%esi\n\t"
602 "popl %%ebx"
603 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
604 : "0" (ax));
605 #endif
608 /* From xf86info havecpuid.c 1.11 */
609 static inline int have_x86cpuid(void)
611 #if defined(__GNUC__) && defined(__i386__)
612 unsigned int f1, f2;
613 __asm__("pushfl\n\t"
614 "pushfl\n\t"
615 "popl %0\n\t"
616 "movl %0,%1\n\t"
617 "xorl %2,%0\n\t"
618 "pushl %0\n\t"
619 "popfl\n\t"
620 "pushfl\n\t"
621 "popl %0\n\t"
622 "popfl"
623 : "=&r" (f1), "=&r" (f2)
624 : "ir" (0x00200000));
625 return ((f1^f2) & 0x00200000) != 0;
626 #else
627 return 0;
628 #endif
631 /******************************************************************
632 * dump_system_info
634 * Dumps into File the information about the system
636 static unsigned dump_system_info(struct dump_context* dc)
638 MINIDUMP_SYSTEM_INFO mdSysInfo;
639 SYSTEM_INFO sysInfo;
640 OSVERSIONINFOW osInfo;
641 DWORD written;
642 ULONG slen;
644 GetSystemInfo(&sysInfo);
645 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
646 GetVersionExW(&osInfo);
648 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
649 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
650 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
651 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
652 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
653 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
654 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
655 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
656 mdSysInfo.PlatformId = osInfo.dwPlatformId;
658 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
659 mdSysInfo.u1.Reserved1 = 0;
660 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
662 if (have_x86cpuid())
664 unsigned regs0[4], regs1[4];
666 do_x86cpuid(0, regs0);
667 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
668 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
669 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
670 do_x86cpuid(1, regs1);
671 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
672 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
673 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
674 if (regs0[1] == 0x68747541 /* "Auth" */ &&
675 regs0[3] == 0x69746e65 /* "enti" */ &&
676 regs0[2] == 0x444d4163 /* "cAMD" */)
678 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
679 if (regs1[0] >= 0x80000001)
681 do_x86cpuid(0x80000001, regs1); /* get vendor features */
682 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
686 else
688 unsigned i;
689 ULONG64 one = 1;
691 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
692 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
694 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
695 if (IsProcessorFeaturePresent(i))
696 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
698 append(dc, &mdSysInfo, sizeof(mdSysInfo));
700 /* write the service pack version string after this stream. It is referenced within the
701 stream by its RVA in the file. */
702 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
703 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
704 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
705 dc->rva += sizeof(ULONG) + slen;
707 return sizeof(mdSysInfo);
710 /******************************************************************
711 * dump_threads
713 * Dumps into File the information about running threads
715 static unsigned dump_threads(struct dump_context* dc,
716 const MINIDUMP_EXCEPTION_INFORMATION* except)
718 MINIDUMP_THREAD mdThd;
719 MINIDUMP_THREAD_LIST mdThdList;
720 unsigned i;
721 RVA rva_base;
722 DWORD flags_out;
723 CONTEXT ctx;
725 mdThdList.NumberOfThreads = 0;
727 rva_base = dc->rva;
728 dc->rva += sizeof(mdThdList.NumberOfThreads) +
729 dc->spi->dwThreadCount * sizeof(mdThd);
731 for (i = 0; i < dc->spi->dwThreadCount; i++)
733 fetch_thread_info(dc, i, except, &mdThd, &ctx);
735 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
736 ThreadWriteInstructionWindow;
737 if (dc->type & MiniDumpWithProcessThreadData)
738 flags_out |= ThreadWriteThreadData;
739 if (dc->type & MiniDumpWithThreadInfo)
740 flags_out |= ThreadWriteThreadInfo;
742 if (dc->cb)
744 MINIDUMP_CALLBACK_INPUT cbin;
745 MINIDUMP_CALLBACK_OUTPUT cbout;
747 cbin.ProcessId = dc->pid;
748 cbin.ProcessHandle = dc->hProcess;
749 cbin.CallbackType = ThreadCallback;
750 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
751 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
752 cbin.u.Thread.Context = ctx;
753 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
754 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
755 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
756 mdThd.Stack.Memory.DataSize;
758 cbout.u.ThreadWriteFlags = flags_out;
759 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
760 continue;
761 flags_out &= cbout.u.ThreadWriteFlags;
763 if (flags_out & ThreadWriteThread)
765 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
767 mdThd.ThreadContext.Rva = dc->rva;
768 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
769 append(dc, &ctx, sizeof(CONTEXT));
771 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
773 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
774 mdThd.Stack.Memory.DataSize,
775 rva_base + sizeof(mdThdList.NumberOfThreads) +
776 mdThdList.NumberOfThreads * sizeof(mdThd) +
777 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
779 writeat(dc,
780 rva_base + sizeof(mdThdList.NumberOfThreads) +
781 mdThdList.NumberOfThreads * sizeof(mdThd),
782 &mdThd, sizeof(mdThd));
783 mdThdList.NumberOfThreads++;
785 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
787 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
788 * - also crop values across module boundaries,
789 * - and don't make it i386 dependent
791 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
794 writeat(dc, rva_base,
795 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
797 return dc->rva - rva_base;
800 /******************************************************************
801 * dump_memory_info
803 * dumps information about the memory of the process (stack of the threads)
805 static unsigned dump_memory_info(struct dump_context* dc)
807 MINIDUMP_MEMORY_LIST mdMemList;
808 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
809 DWORD written;
810 unsigned i, pos, len, sz;
811 RVA rva_base;
812 char tmp[1024];
814 mdMemList.NumberOfMemoryRanges = dc->num_mem;
815 append(dc, &mdMemList.NumberOfMemoryRanges,
816 sizeof(mdMemList.NumberOfMemoryRanges));
817 rva_base = dc->rva;
818 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
819 dc->rva += sz;
820 sz += sizeof(mdMemList.NumberOfMemoryRanges);
822 for (i = 0; i < dc->num_mem; i++)
824 mdMem.StartOfMemoryRange = dc->mem[i].base;
825 mdMem.Memory.Rva = dc->rva;
826 mdMem.Memory.DataSize = dc->mem[i].size;
827 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
828 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
830 len = min(dc->mem[i].size - pos, sizeof(tmp));
831 if (ReadProcessMemory(dc->hProcess,
832 (void*)(dc->mem[i].base + pos),
833 tmp, len, NULL))
834 WriteFile(dc->hFile, tmp, len, &written, NULL);
836 dc->rva += mdMem.Memory.DataSize;
837 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
838 if (dc->mem[i].rva)
840 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
844 return sz;
847 static unsigned dump_misc_info(struct dump_context* dc)
849 MINIDUMP_MISC_INFO mmi;
851 mmi.SizeOfInfo = sizeof(mmi);
852 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
853 mmi.ProcessId = dc->pid;
854 /* FIXME: create/user/kernel time */
855 mmi.ProcessCreateTime = 0;
856 mmi.ProcessKernelTime = 0;
857 mmi.ProcessUserTime = 0;
859 append(dc, &mmi, sizeof(mmi));
860 return sizeof(mmi);
863 /******************************************************************
864 * MiniDumpWriteDump (DEBUGHLP.@)
867 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
868 MINIDUMP_TYPE DumpType,
869 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
870 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
871 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
873 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
874 MINIDUMP_HEADER mdHead;
875 MINIDUMP_DIRECTORY mdDir;
876 DWORD i, nStreams, idx_stream;
877 struct dump_context dc;
879 dc.hProcess = hProcess;
880 dc.hFile = hFile;
881 dc.pid = pid;
882 dc.modules = NULL;
883 dc.num_modules = 0;
884 dc.alloc_modules = 0;
885 dc.cb = CallbackParam;
886 dc.type = DumpType;
887 dc.mem = NULL;
888 dc.num_mem = 0;
889 dc.alloc_mem = 0;
890 dc.rva = 0;
892 if (!fetch_processes_info(&dc)) return FALSE;
893 fetch_modules_info(&dc);
895 /* 1) init */
896 nStreams = 6 + (ExceptionParam ? 1 : 0) +
897 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
899 /* pad the directory size to a multiple of 4 for alignment purposes */
900 nStreams = (nStreams + 3) & ~3;
902 if (DumpType & MiniDumpWithDataSegs)
903 FIXME("NIY MiniDumpWithDataSegs\n");
904 if (DumpType & MiniDumpWithFullMemory)
905 FIXME("NIY MiniDumpWithFullMemory\n");
906 if (DumpType & MiniDumpWithHandleData)
907 FIXME("NIY MiniDumpWithHandleData\n");
908 if (DumpType & MiniDumpFilterMemory)
909 FIXME("NIY MiniDumpFilterMemory\n");
910 if (DumpType & MiniDumpScanMemory)
911 FIXME("NIY MiniDumpScanMemory\n");
913 /* 2) write header */
914 mdHead.Signature = MINIDUMP_SIGNATURE;
915 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
916 mdHead.NumberOfStreams = nStreams;
917 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
918 mdHead.StreamDirectoryRva = sizeof(mdHead);
919 mdHead.u.TimeDateStamp = time(NULL);
920 mdHead.Flags = DumpType;
921 append(&dc, &mdHead, sizeof(mdHead));
923 /* 3) write stream directories */
924 dc.rva += nStreams * sizeof(mdDir);
925 idx_stream = 0;
927 /* 3.1) write data stream directories */
929 /* must be first in minidump */
930 mdDir.StreamType = SystemInfoStream;
931 mdDir.Location.Rva = dc.rva;
932 mdDir.Location.DataSize = dump_system_info(&dc);
933 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
934 &mdDir, sizeof(mdDir));
936 mdDir.StreamType = ThreadListStream;
937 mdDir.Location.Rva = dc.rva;
938 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
939 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
940 &mdDir, sizeof(mdDir));
942 mdDir.StreamType = ModuleListStream;
943 mdDir.Location.Rva = dc.rva;
944 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
945 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
946 &mdDir, sizeof(mdDir));
948 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
949 mdDir.Location.Rva = dc.rva;
950 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
951 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
952 &mdDir, sizeof(mdDir));
954 mdDir.StreamType = MemoryListStream;
955 mdDir.Location.Rva = dc.rva;
956 mdDir.Location.DataSize = dump_memory_info(&dc);
957 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
958 &mdDir, sizeof(mdDir));
960 mdDir.StreamType = MiscInfoStream;
961 mdDir.Location.Rva = dc.rva;
962 mdDir.Location.DataSize = dump_misc_info(&dc);
963 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
964 &mdDir, sizeof(mdDir));
966 /* 3.2) write exception information (if any) */
967 if (ExceptionParam)
969 mdDir.StreamType = ExceptionStream;
970 mdDir.Location.Rva = dc.rva;
971 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
972 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
973 &mdDir, sizeof(mdDir));
976 /* 3.3) write user defined streams (if any) */
977 if (UserStreamParam)
979 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
981 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
982 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
983 mdDir.Location.Rva = dc.rva;
984 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
985 &mdDir, sizeof(mdDir));
986 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
987 UserStreamParam->UserStreamArray[i].BufferSize);
991 /* fill the remaining directory entries with 0's (unused stream types) */
992 /* NOTE: this should always come last in the dump! */
993 for (i = idx_stream; i < nStreams; i++)
994 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
996 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
997 HeapFree(GetProcessHeap(), 0, dc.mem);
998 HeapFree(GetProcessHeap(), 0, dc.modules);
1000 return TRUE;
1003 /******************************************************************
1004 * MiniDumpReadDumpStream (DEBUGHLP.@)
1008 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1009 PMINIDUMP_DIRECTORY* pdir,
1010 PVOID* stream, ULONG* size)
1012 MINIDUMP_HEADER* mdHead = base;
1014 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1016 MINIDUMP_DIRECTORY* dir;
1017 DWORD i;
1019 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1020 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1022 if (dir->StreamType == str_idx)
1024 if (pdir) *pdir = dir;
1025 if (stream) *stream = (char*)base + dir->Location.Rva;
1026 if (size) *size = dir->Location.DataSize;
1027 return TRUE;
1031 SetLastError(ERROR_INVALID_PARAMETER);
1032 return FALSE;