push 212f1dad91f15aefd8e676124180e0b86d7c9ee6
[wine/hacks.git] / dlls / dbghelp / minidump.c
blob0419bc87b960e0d6e54014d65fcf4d1a86f64e86
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 /* exception information */
63 /* output information */
64 MINIDUMP_TYPE type;
65 HANDLE hFile;
66 RVA rva;
67 struct dump_memory* mem;
68 unsigned num_mem;
69 /* callback information */
70 MINIDUMP_CALLBACK_INFORMATION* cb;
73 /******************************************************************
74 * fetch_processes_info
76 * reads system wide process information, and make spi point to the record
77 * for process of id 'pid'
79 static BOOL fetch_processes_info(struct dump_context* dc)
81 ULONG buf_size = 0x1000;
82 NTSTATUS nts;
84 dc->pcs_buffer = NULL;
85 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
86 for (;;)
88 nts = NtQuerySystemInformation(SystemProcessInformation,
89 dc->pcs_buffer, buf_size, NULL);
90 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
91 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
92 buf_size *= 2);
93 if (!dc->pcs_buffer) return FALSE;
96 if (nts == STATUS_SUCCESS)
98 dc->spi = dc->pcs_buffer;
99 for (;;)
101 if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
102 if (!dc->spi->NextEntryOffset) break;
103 dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
106 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
107 dc->pcs_buffer = NULL;
108 dc->spi = NULL;
109 return FALSE;
112 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
113 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
115 NT_TIB tib;
117 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
119 #ifdef __i386__
120 /* limiting the stack dumping to the size actually used */
121 if (ctx->Esp){
123 /* make sure ESP is within the established range of the stack. It could have
124 been clobbered by whatever caused the original exception. */
125 if (ctx->Esp - 4 < (ULONG_PTR)tib.StackLimit || ctx->Esp - 4 > (ULONG_PTR)tib.StackBase)
126 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
128 else
129 mmd->StartOfMemoryRange = (ctx->Esp - 4);
132 else
133 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
135 #elif defined(__powerpc__)
136 if (ctx->Iar){
138 /* make sure IAR is within the established range of the stack. It could have
139 been clobbered by whatever caused the original exception. */
140 if (ctx->Iar - 4 < (ULONG_PTR)tib.StackLimit || ctx->Iar - 4 > (ULONG_PTR)tib.StackBase)
141 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
143 else
144 mmd->StartOfMemoryRange = (ctx->Iar - 4);
147 else
148 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
150 #elif defined(__x86_64__)
151 if (ctx->Rsp){
153 /* make sure RSP is within the established range of the stack. It could have
154 been clobbered by whatever caused the original exception. */
155 if (ctx->Rsp - 8 < (ULONG_PTR)tib.StackLimit || ctx->Rsp - 8 > (ULONG_PTR)tib.StackBase)
156 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
158 else
159 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
162 else
163 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
165 #else
166 #error unsupported CPU
167 #endif
168 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
172 /******************************************************************
173 * fetch_thread_info
175 * fetches some information about thread of id 'tid'
177 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
178 const MINIDUMP_EXCEPTION_INFORMATION* except,
179 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
181 DWORD tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
182 HANDLE hThread;
183 THREAD_BASIC_INFORMATION tbi;
185 memset(ctx, 0, sizeof(*ctx));
187 mdThd->ThreadId = tid;
188 mdThd->SuspendCount = 0;
189 mdThd->Teb = 0;
190 mdThd->Stack.StartOfMemoryRange = 0;
191 mdThd->Stack.Memory.DataSize = 0;
192 mdThd->Stack.Memory.Rva = 0;
193 mdThd->ThreadContext.DataSize = 0;
194 mdThd->ThreadContext.Rva = 0;
195 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
196 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
198 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
200 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
201 return FALSE;
204 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
205 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
207 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
208 if (tbi.ExitStatus == STILL_ACTIVE)
210 if (tid != GetCurrentThreadId() &&
211 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
213 ctx->ContextFlags = CONTEXT_FULL;
214 if (!GetThreadContext(hThread, ctx))
215 memset(ctx, 0, sizeof(*ctx));
217 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
218 ResumeThread(hThread);
220 else if (tid == GetCurrentThreadId() && except)
222 CONTEXT lctx, *pctx;
223 mdThd->SuspendCount = 1;
224 if (except->ClientPointers)
226 EXCEPTION_POINTERS ep;
228 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
229 &ep, sizeof(ep), NULL);
230 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
231 &ctx, sizeof(ctx), NULL);
232 pctx = &lctx;
234 else pctx = except->ExceptionPointers->ContextRecord;
236 *ctx = *pctx;
237 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
239 else mdThd->SuspendCount = 0;
242 CloseHandle(hThread);
243 return TRUE;
246 /******************************************************************
247 * add_module
249 * Add a module to a dump context
251 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
252 DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
253 BOOL is_elf)
255 if (!dc->modules)
256 dc->modules = HeapAlloc(GetProcessHeap(), 0,
257 ++dc->num_modules * sizeof(*dc->modules));
258 else
259 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
260 ++dc->num_modules * sizeof(*dc->modules));
261 if (!dc->modules) return FALSE;
262 if (is_elf ||
263 !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
264 dc->modules[dc->num_modules - 1].name,
265 sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR)))
266 lstrcpynW(dc->modules[dc->num_modules - 1].name, name,
267 sizeof(dc->modules[dc->num_modules - 1].name) / sizeof(WCHAR));
268 dc->modules[dc->num_modules - 1].base = base;
269 dc->modules[dc->num_modules - 1].size = size;
270 dc->modules[dc->num_modules - 1].timestamp = timestamp;
271 dc->modules[dc->num_modules - 1].checksum = checksum;
272 dc->modules[dc->num_modules - 1].is_elf = is_elf;
274 return TRUE;
277 /******************************************************************
278 * fetch_pe_module_info_cb
280 * Callback for accumulating in dump_context a PE modules set
282 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
283 PVOID user)
285 struct dump_context* dc = (struct dump_context*)user;
286 IMAGE_NT_HEADERS nth;
288 if (!validate_addr64(base)) return FALSE;
290 if (pe_load_nt_header(dc->hProcess, base, &nth))
291 add_module((struct dump_context*)user, name, base, size,
292 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
293 FALSE);
294 return TRUE;
297 /******************************************************************
298 * fetch_elf_module_info_cb
300 * Callback for accumulating in dump_context an ELF modules set
302 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
303 void* user)
305 struct dump_context* dc = (struct dump_context*)user;
306 DWORD rbase, size, checksum;
308 /* FIXME: there's no relevant timestamp on ELF modules */
309 /* NB: if we have a non-null base from the live-target use it (whenever
310 * the ELF module is relocatable or not). If we have a null base (ELF
311 * module isn't relocatable) then grab its base address from ELF file
313 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
314 size = checksum = 0;
315 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
316 return TRUE;
319 static void fetch_modules_info(struct dump_context* dc)
321 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
322 /* Since we include ELF modules in a separate stream from the regular PE ones,
323 * we can always include those ELF modules (they don't eat lots of space)
324 * And it's always a good idea to have a trace of the loaded ELF modules for
325 * a given application in a post mortem debugging condition.
327 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
330 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
332 DWORD handle;
333 DWORD sz;
334 static const WCHAR backslashW[] = {'\\', '\0'};
336 memset(ffi, 0, sizeof(*ffi));
337 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
339 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
340 if (info && GetFileVersionInfoW(filename, handle, sz, info))
342 VS_FIXEDFILEINFO* ptr;
343 UINT len;
345 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
346 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
348 HeapFree(GetProcessHeap(), 0, info);
352 /******************************************************************
353 * add_memory_block
355 * Add a memory block to be dumped in a minidump
356 * If rva is non 0, it's the rva in the minidump where has to be stored
357 * also the rva of the memory block when written (this allows to reference
358 * a memory block from outside the list of memory blocks).
360 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
362 if (dc->mem)
363 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
364 ++dc->num_mem * sizeof(*dc->mem));
365 else
366 dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
367 if (dc->mem)
369 dc->mem[dc->num_mem - 1].base = base;
370 dc->mem[dc->num_mem - 1].size = size;
371 dc->mem[dc->num_mem - 1].rva = rva;
373 else dc->num_mem = 0;
376 /******************************************************************
377 * writeat
379 * Writes a chunk of data at a given position in the minidump
381 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
383 DWORD written;
385 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
386 WriteFile(dc->hFile, data, size, &written, NULL);
389 /******************************************************************
390 * append
392 * writes a new chunk of data to the minidump, increasing the current
393 * rva in dc
395 static void append(struct dump_context* dc, void* data, unsigned size)
397 writeat(dc, dc->rva, data, size);
398 dc->rva += size;
401 /******************************************************************
402 * dump_exception_info
404 * Write in File the exception information from pcs
406 static unsigned dump_exception_info(struct dump_context* dc,
407 const MINIDUMP_EXCEPTION_INFORMATION* except)
409 MINIDUMP_EXCEPTION_STREAM mdExcpt;
410 EXCEPTION_RECORD rec, *prec;
411 CONTEXT ctx, *pctx;
412 DWORD i;
414 mdExcpt.ThreadId = except->ThreadId;
415 mdExcpt.__alignment = 0;
416 if (except->ClientPointers)
418 EXCEPTION_POINTERS ep;
420 ReadProcessMemory(dc->hProcess,
421 except->ExceptionPointers, &ep, sizeof(ep), NULL);
422 ReadProcessMemory(dc->hProcess,
423 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
424 ReadProcessMemory(dc->hProcess,
425 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
426 prec = &rec;
427 pctx = &ctx;
429 else
431 prec = except->ExceptionPointers->ExceptionRecord;
432 pctx = except->ExceptionPointers->ContextRecord;
434 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
435 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
436 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
437 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
438 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
439 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
440 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
441 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
442 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
443 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
445 append(dc, &mdExcpt, sizeof(mdExcpt));
446 append(dc, pctx, sizeof(*pctx));
447 return sizeof(mdExcpt);
450 /******************************************************************
451 * dump_modules
453 * Write in File the modules from pcs
455 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
457 MINIDUMP_MODULE mdModule;
458 MINIDUMP_MODULE_LIST mdModuleList;
459 char tmp[1024];
460 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
461 ULONG i, nmod;
462 RVA rva_base;
463 DWORD flags_out;
464 unsigned sz;
466 for (i = nmod = 0; i < dc->num_modules; i++)
468 if ((dc->modules[i].is_elf && dump_elf) ||
469 (!dc->modules[i].is_elf && !dump_elf))
470 nmod++;
473 mdModuleList.NumberOfModules = 0;
474 /* reserve space for mdModuleList
475 * FIXME: since we don't support 0 length arrays, we cannot use the
476 * size of mdModuleList
477 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
480 /* the stream size is just the size of the module index. It does not include the data for the
481 names of each module. *Technically* the names are supposed to go into the common string table
482 in the minidump file. Since each string is referenced by RVA they can all safely be located
483 anywhere between streams in the file, so the end of this stream is sufficient. */
484 rva_base = dc->rva;
485 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
486 for (i = 0; i < dc->num_modules; i++)
488 if ((dc->modules[i].is_elf && !dump_elf) ||
489 (!dc->modules[i].is_elf && dump_elf))
490 continue;
492 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
493 if (dc->type & MiniDumpWithDataSegs)
494 flags_out |= ModuleWriteDataSeg;
495 if (dc->type & MiniDumpWithProcessThreadData)
496 flags_out |= ModuleWriteTlsData;
497 if (dc->type & MiniDumpWithCodeSegs)
498 flags_out |= ModuleWriteCodeSegs;
499 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
500 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
501 FIXME("Buffer overflow!!!\n");
502 lstrcpyW(ms->Buffer, dc->modules[i].name);
504 if (dc->cb)
506 MINIDUMP_CALLBACK_INPUT cbin;
507 MINIDUMP_CALLBACK_OUTPUT cbout;
509 cbin.ProcessId = dc->pid;
510 cbin.ProcessHandle = dc->hProcess;
511 cbin.CallbackType = ModuleCallback;
513 cbin.u.Module.FullPath = ms->Buffer;
514 cbin.u.Module.BaseOfImage = dc->modules[i].base;
515 cbin.u.Module.SizeOfImage = dc->modules[i].size;
516 cbin.u.Module.CheckSum = dc->modules[i].checksum;
517 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
518 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
519 cbin.u.Module.CvRecord = NULL;
520 cbin.u.Module.SizeOfCvRecord = 0;
521 cbin.u.Module.MiscRecord = NULL;
522 cbin.u.Module.SizeOfMiscRecord = 0;
524 cbout.u.ModuleWriteFlags = flags_out;
525 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
526 continue;
527 flags_out &= cbout.u.ModuleWriteFlags;
529 if (flags_out & ModuleWriteModule)
531 mdModule.BaseOfImage = dc->modules[i].base;
532 mdModule.SizeOfImage = dc->modules[i].size;
533 mdModule.CheckSum = dc->modules[i].checksum;
534 mdModule.TimeDateStamp = dc->modules[i].timestamp;
535 mdModule.ModuleNameRva = dc->rva;
536 ms->Length -= sizeof(WCHAR);
537 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
538 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
539 mdModule.CvRecord.DataSize = 0; /* FIXME */
540 mdModule.CvRecord.Rva = 0; /* FIXME */
541 mdModule.MiscRecord.DataSize = 0; /* FIXME */
542 mdModule.MiscRecord.Rva = 0; /* FIXME */
543 mdModule.Reserved0 = 0; /* FIXME */
544 mdModule.Reserved1 = 0; /* FIXME */
545 writeat(dc,
546 rva_base + sizeof(mdModuleList.NumberOfModules) +
547 mdModuleList.NumberOfModules++ * sizeof(mdModule),
548 &mdModule, sizeof(mdModule));
551 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
552 sizeof(mdModuleList.NumberOfModules));
554 return sz;
557 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
558 * We are compiled with -fPIC, so we can't clobber ebx.
560 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
562 #if defined(__GNUC__) && defined(__i386__)
563 __asm__("pushl %%ebx\n\t"
564 "cpuid\n\t"
565 "movl %%ebx, %%esi\n\t"
566 "popl %%ebx"
567 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
568 : "0" (ax));
569 #endif
572 /* From xf86info havecpuid.c 1.11 */
573 static inline int have_x86cpuid(void)
575 #if defined(__GNUC__) && defined(__i386__)
576 unsigned int f1, f2;
577 __asm__("pushfl\n\t"
578 "pushfl\n\t"
579 "popl %0\n\t"
580 "movl %0,%1\n\t"
581 "xorl %2,%0\n\t"
582 "pushl %0\n\t"
583 "popfl\n\t"
584 "pushfl\n\t"
585 "popl %0\n\t"
586 "popfl"
587 : "=&r" (f1), "=&r" (f2)
588 : "ir" (0x00200000));
589 return ((f1^f2) & 0x00200000) != 0;
590 #else
591 return 0;
592 #endif
595 /******************************************************************
596 * dump_system_info
598 * Dumps into File the information about the system
600 static unsigned dump_system_info(struct dump_context* dc)
602 MINIDUMP_SYSTEM_INFO mdSysInfo;
603 SYSTEM_INFO sysInfo;
604 OSVERSIONINFOW osInfo;
605 DWORD written;
606 ULONG slen;
608 GetSystemInfo(&sysInfo);
609 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
610 GetVersionExW(&osInfo);
612 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
613 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
614 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
615 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
616 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
617 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
618 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
619 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
620 mdSysInfo.PlatformId = osInfo.dwPlatformId;
622 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
623 mdSysInfo.u1.Reserved1 = 0;
624 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
626 if (have_x86cpuid())
628 unsigned regs0[4], regs1[4];
630 do_x86cpuid(0, regs0);
631 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
632 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
633 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
634 do_x86cpuid(1, regs1);
635 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
636 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
637 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
638 if (regs0[1] == 0x68747541 /* "Auth" */ &&
639 regs0[3] == 0x69746e65 /* "enti" */ &&
640 regs0[2] == 0x444d4163 /* "cAMD" */)
642 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
643 if (regs1[0] >= 0x80000001)
645 do_x86cpuid(0x80000001, regs1); /* get vendor features */
646 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
650 else
652 unsigned i;
653 ULONG64 one = 1;
655 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
656 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
658 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
659 if (IsProcessorFeaturePresent(i))
660 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
662 append(dc, &mdSysInfo, sizeof(mdSysInfo));
664 /* write the service pack version string after this stream. It is referenced within the
665 stream by its RVA in the file. */
666 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
667 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
668 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
669 dc->rva += sizeof(ULONG) + slen;
671 return sizeof(mdSysInfo);
674 /******************************************************************
675 * dump_threads
677 * Dumps into File the information about running threads
679 static unsigned dump_threads(struct dump_context* dc,
680 const MINIDUMP_EXCEPTION_INFORMATION* except)
682 MINIDUMP_THREAD mdThd;
683 MINIDUMP_THREAD_LIST mdThdList;
684 unsigned i;
685 RVA rva_base;
686 DWORD flags_out;
687 CONTEXT ctx;
689 mdThdList.NumberOfThreads = 0;
691 rva_base = dc->rva;
692 dc->rva += sizeof(mdThdList.NumberOfThreads) +
693 dc->spi->dwThreadCount * sizeof(mdThd);
695 for (i = 0; i < dc->spi->dwThreadCount; i++)
697 fetch_thread_info(dc, i, except, &mdThd, &ctx);
699 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
700 ThreadWriteInstructionWindow;
701 if (dc->type & MiniDumpWithProcessThreadData)
702 flags_out |= ThreadWriteThreadData;
703 if (dc->type & MiniDumpWithThreadInfo)
704 flags_out |= ThreadWriteThreadInfo;
706 if (dc->cb)
708 MINIDUMP_CALLBACK_INPUT cbin;
709 MINIDUMP_CALLBACK_OUTPUT cbout;
711 cbin.ProcessId = dc->pid;
712 cbin.ProcessHandle = dc->hProcess;
713 cbin.CallbackType = ThreadCallback;
714 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
715 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
716 cbin.u.Thread.Context = ctx;
717 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
718 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
719 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
720 mdThd.Stack.Memory.DataSize;
722 cbout.u.ThreadWriteFlags = flags_out;
723 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
724 continue;
725 flags_out &= cbout.u.ThreadWriteFlags;
727 if (flags_out & ThreadWriteThread)
729 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
731 mdThd.ThreadContext.Rva = dc->rva;
732 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
733 append(dc, &ctx, sizeof(CONTEXT));
735 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
737 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
738 mdThd.Stack.Memory.DataSize,
739 rva_base + sizeof(mdThdList.NumberOfThreads) +
740 mdThdList.NumberOfThreads * sizeof(mdThd) +
741 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
743 writeat(dc,
744 rva_base + sizeof(mdThdList.NumberOfThreads) +
745 mdThdList.NumberOfThreads * sizeof(mdThd),
746 &mdThd, sizeof(mdThd));
747 mdThdList.NumberOfThreads++;
749 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
751 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
752 * - also crop values across module boundaries,
753 * - and don't make it i386 dependent
755 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
758 writeat(dc, rva_base,
759 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
761 return dc->rva - rva_base;
764 /******************************************************************
765 * dump_memory_info
767 * dumps information about the memory of the process (stack of the threads)
769 static unsigned dump_memory_info(struct dump_context* dc)
771 MINIDUMP_MEMORY_LIST mdMemList;
772 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
773 DWORD written;
774 unsigned i, pos, len, sz;
775 RVA rva_base;
776 char tmp[1024];
778 mdMemList.NumberOfMemoryRanges = dc->num_mem;
779 append(dc, &mdMemList.NumberOfMemoryRanges,
780 sizeof(mdMemList.NumberOfMemoryRanges));
781 rva_base = dc->rva;
782 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
783 dc->rva += sz;
784 sz += sizeof(mdMemList.NumberOfMemoryRanges);
786 for (i = 0; i < dc->num_mem; i++)
788 mdMem.StartOfMemoryRange = dc->mem[i].base;
789 mdMem.Memory.Rva = dc->rva;
790 mdMem.Memory.DataSize = dc->mem[i].size;
791 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
792 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
794 len = min(dc->mem[i].size - pos, sizeof(tmp));
795 if (ReadProcessMemory(dc->hProcess,
796 (void*)(dc->mem[i].base + pos),
797 tmp, len, NULL))
798 WriteFile(dc->hFile, tmp, len, &written, NULL);
800 dc->rva += mdMem.Memory.DataSize;
801 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
802 if (dc->mem[i].rva)
804 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
808 return sz;
811 static unsigned dump_misc_info(struct dump_context* dc)
813 MINIDUMP_MISC_INFO mmi;
815 mmi.SizeOfInfo = sizeof(mmi);
816 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
817 mmi.ProcessId = dc->pid;
818 /* FIXME: create/user/kernel time */
819 mmi.ProcessCreateTime = 0;
820 mmi.ProcessKernelTime = 0;
821 mmi.ProcessUserTime = 0;
823 append(dc, &mmi, sizeof(mmi));
824 return sizeof(mmi);
827 /******************************************************************
828 * MiniDumpWriteDump (DEBUGHLP.@)
831 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
832 MINIDUMP_TYPE DumpType,
833 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
834 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
835 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
837 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
838 MINIDUMP_HEADER mdHead;
839 MINIDUMP_DIRECTORY mdDir;
840 DWORD i, nStreams, idx_stream;
841 struct dump_context dc;
843 dc.hProcess = hProcess;
844 dc.hFile = hFile;
845 dc.pid = pid;
846 dc.modules = NULL;
847 dc.num_modules = 0;
848 dc.cb = CallbackParam;
849 dc.type = DumpType;
850 dc.mem = NULL;
851 dc.num_mem = 0;
852 dc.rva = 0;
854 if (!fetch_processes_info(&dc)) return FALSE;
855 fetch_modules_info(&dc);
857 /* 1) init */
858 nStreams = 6 + (ExceptionParam ? 1 : 0) +
859 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
861 /* pad the directory size to a multiple of 4 for alignment purposes */
862 nStreams = (nStreams + 3) & ~3;
864 if (DumpType & MiniDumpWithDataSegs)
865 FIXME("NIY MiniDumpWithDataSegs\n");
866 if (DumpType & MiniDumpWithFullMemory)
867 FIXME("NIY MiniDumpWithFullMemory\n");
868 if (DumpType & MiniDumpWithHandleData)
869 FIXME("NIY MiniDumpWithHandleData\n");
870 if (DumpType & MiniDumpFilterMemory)
871 FIXME("NIY MiniDumpFilterMemory\n");
872 if (DumpType & MiniDumpScanMemory)
873 FIXME("NIY MiniDumpScanMemory\n");
875 /* 2) write header */
876 mdHead.Signature = MINIDUMP_SIGNATURE;
877 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
878 mdHead.NumberOfStreams = nStreams;
879 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
880 mdHead.StreamDirectoryRva = sizeof(mdHead);
881 mdHead.u.TimeDateStamp = time(NULL);
882 mdHead.Flags = DumpType;
883 append(&dc, &mdHead, sizeof(mdHead));
885 /* 3) write stream directories */
886 dc.rva += nStreams * sizeof(mdDir);
887 idx_stream = 0;
889 /* 3.1) write data stream directories */
891 /* must be first in minidump */
892 mdDir.StreamType = SystemInfoStream;
893 mdDir.Location.Rva = dc.rva;
894 mdDir.Location.DataSize = dump_system_info(&dc);
895 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
896 &mdDir, sizeof(mdDir));
898 mdDir.StreamType = ThreadListStream;
899 mdDir.Location.Rva = dc.rva;
900 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
901 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
902 &mdDir, sizeof(mdDir));
904 mdDir.StreamType = ModuleListStream;
905 mdDir.Location.Rva = dc.rva;
906 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
907 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
908 &mdDir, sizeof(mdDir));
910 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
911 mdDir.Location.Rva = dc.rva;
912 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
913 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
914 &mdDir, sizeof(mdDir));
916 mdDir.StreamType = MemoryListStream;
917 mdDir.Location.Rva = dc.rva;
918 mdDir.Location.DataSize = dump_memory_info(&dc);
919 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
920 &mdDir, sizeof(mdDir));
922 mdDir.StreamType = MiscInfoStream;
923 mdDir.Location.Rva = dc.rva;
924 mdDir.Location.DataSize = dump_misc_info(&dc);
925 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
926 &mdDir, sizeof(mdDir));
928 /* 3.2) write exception information (if any) */
929 if (ExceptionParam)
931 mdDir.StreamType = ExceptionStream;
932 mdDir.Location.Rva = dc.rva;
933 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
934 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
935 &mdDir, sizeof(mdDir));
938 /* 3.3) write user defined streams (if any) */
939 if (UserStreamParam)
941 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
943 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
944 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
945 mdDir.Location.Rva = dc.rva;
946 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
947 &mdDir, sizeof(mdDir));
948 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
949 UserStreamParam->UserStreamArray[i].BufferSize);
953 /* fill the remaining directory entries with 0's (unused stream types) */
954 /* NOTE: this should always come last in the dump! */
955 for (i = idx_stream; i < nStreams; i++)
956 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
958 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
959 HeapFree(GetProcessHeap(), 0, dc.mem);
960 HeapFree(GetProcessHeap(), 0, dc.modules);
962 return TRUE;
965 /******************************************************************
966 * MiniDumpReadDumpStream (DEBUGHLP.@)
970 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
971 PMINIDUMP_DIRECTORY* pdir,
972 PVOID* stream, ULONG* size)
974 MINIDUMP_HEADER* mdHead = (MINIDUMP_HEADER*)base;
976 if (mdHead->Signature == MINIDUMP_SIGNATURE)
978 MINIDUMP_DIRECTORY* dir;
979 DWORD i;
981 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
982 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
984 if (dir->StreamType == str_idx)
986 *pdir = dir;
987 *stream = (char*)base + dir->Location.Rva;
988 *size = dir->Location.DataSize;
989 return TRUE;
993 SetLastError(ERROR_INVALID_PARAMETER);
994 return FALSE;