dplayx: Code to forward player creation
[wine/gsoc_dplay.git] / dlls / dbghelp / minidump.c
bloba2f1633657b88e70e2167cfa7fc5831dc0277a3c
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 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));
269 if (!dc->modules)
271 dc->alloc_modules = dc->num_modules = 0;
272 return FALSE;
274 if (is_elf ||
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;
285 dc->num_modules++;
287 return TRUE;
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,
296 PVOID user)
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,
306 FALSE);
307 return TRUE;
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,
316 void* user)
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))
327 size = checksum = 0;
328 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
329 return 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,
338 void* user)
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))
348 size = checksum = 0;
349 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
350 return 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)
367 DWORD handle;
368 DWORD sz;
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;
378 UINT len;
380 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
381 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
383 HeapFree(GetProcessHeap(), 0, info);
387 /******************************************************************
388 * add_memory_block
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)
397 if (!dc->mem)
399 dc->alloc_mem = 32;
400 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
402 else if (dc->num_mem >= dc->alloc_mem)
404 dc->alloc_mem *= 2;
405 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
406 dc->alloc_mem * sizeof(*dc->mem));
408 if (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;
413 dc->num_mem++;
415 else dc->num_mem = dc->alloc_mem = 0;
418 /******************************************************************
419 * writeat
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)
425 DWORD written;
427 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
428 WriteFile(dc->hFile, data, size, &written, NULL);
431 /******************************************************************
432 * append
434 * writes a new chunk of data to the minidump, increasing the current
435 * rva in dc
437 static void append(struct dump_context* dc, const void* data, unsigned size)
439 writeat(dc, dc->rva, data, size);
440 dc->rva += 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;
453 CONTEXT ctx, *pctx;
454 DWORD i;
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);
468 prec = &rec;
469 pctx = &ctx;
471 else
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 /******************************************************************
493 * dump_modules
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;
501 char tmp[1024];
502 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
503 ULONG i, nmod;
504 RVA rva_base;
505 DWORD flags_out;
506 unsigned sz;
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))
512 nmod++;
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. */
526 rva_base = dc->rva;
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))
532 continue;
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);
546 if (dc->cb)
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))
568 continue;
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 */
587 writeat(dc,
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));
596 return sz;
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"
606 "cpuid\n\t"
607 "movl %%ebx, %%esi\n\t"
608 "popl %%ebx"
609 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
610 : "0" (ax));
611 #endif
614 /* From xf86info havecpuid.c 1.11 */
615 static inline int have_x86cpuid(void)
617 #if defined(__GNUC__) && defined(__i386__)
618 unsigned int f1, f2;
619 __asm__("pushfl\n\t"
620 "pushfl\n\t"
621 "popl %0\n\t"
622 "movl %0,%1\n\t"
623 "xorl %2,%0\n\t"
624 "pushl %0\n\t"
625 "popfl\n\t"
626 "pushfl\n\t"
627 "popl %0\n\t"
628 "popfl"
629 : "=&r" (f1), "=&r" (f2)
630 : "ir" (0x00200000));
631 return ((f1^f2) & 0x00200000) != 0;
632 #else
633 return 0;
634 #endif
637 /******************************************************************
638 * dump_system_info
640 * Dumps into File the information about the system
642 static unsigned dump_system_info(struct dump_context* dc)
644 MINIDUMP_SYSTEM_INFO mdSysInfo;
645 SYSTEM_INFO sysInfo;
646 OSVERSIONINFOW osInfo;
647 DWORD written;
648 ULONG slen;
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;
668 if (have_x86cpuid())
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];
692 else
694 unsigned i;
695 ULONG64 one = 1;
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 /******************************************************************
717 * dump_threads
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;
726 unsigned i;
727 RVA rva_base;
728 DWORD flags_out;
729 CONTEXT ctx;
731 mdThdList.NumberOfThreads = 0;
733 rva_base = dc->rva;
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;
748 if (dc->cb)
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))
766 continue;
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));
785 writeat(dc,
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 /******************************************************************
807 * dump_memory_info
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;
815 DWORD written;
816 unsigned i, pos, len, sz;
817 RVA rva_base;
818 char tmp[1024];
820 mdMemList.NumberOfMemoryRanges = dc->num_mem;
821 append(dc, &mdMemList.NumberOfMemoryRanges,
822 sizeof(mdMemList.NumberOfMemoryRanges));
823 rva_base = dc->rva;
824 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
825 dc->rva += sz;
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),
839 tmp, len, NULL))
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));
844 if (dc->mem[i].rva)
846 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
850 return sz;
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));
866 return 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;
886 dc.hFile = hFile;
887 dc.pid = pid;
888 dc.modules = NULL;
889 dc.num_modules = 0;
890 dc.alloc_modules = 0;
891 dc.cb = CallbackParam;
892 dc.type = DumpType;
893 dc.mem = NULL;
894 dc.num_mem = 0;
895 dc.alloc_mem = 0;
896 dc.rva = 0;
898 if (!fetch_processes_info(&dc)) return FALSE;
899 fetch_modules_info(&dc);
901 /* 1) init */
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);
931 idx_stream = 0;
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) */
973 if (ExceptionParam)
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) */
983 if (UserStreamParam)
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);
1006 return TRUE;
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;
1023 DWORD i;
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;
1033 return TRUE;
1037 SetLastError(ERROR_INVALID_PARAMETER);
1038 return FALSE;