include: Added missing Header_* defines.
[wine.git] / dlls / dbghelp / minidump.c
blob9922d0803256db24348bdc3ae0f5bfeb93efca7c
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 "config.h"
22 #include <time.h>
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "dbghelp_private.h"
30 #include "winternl.h"
31 #include "psapi.h"
32 #include "wine/asm.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
37 /******************************************************************
38 * fetch_process_info
40 * reads system wide process information, and gather from it the threads information
41 * for process of id 'pid'
43 static BOOL fetch_process_info(struct dump_context* dc)
45 ULONG buf_size = 0x1000;
46 NTSTATUS nts;
47 void* pcs_buffer = NULL;
49 if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
50 for (;;)
52 nts = NtQuerySystemInformation(SystemProcessInformation,
53 pcs_buffer, buf_size, NULL);
54 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
55 pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2);
56 if (!pcs_buffer) return FALSE;
59 if (nts == STATUS_SUCCESS)
61 SYSTEM_PROCESS_INFORMATION* spi = pcs_buffer;
62 unsigned i;
64 for (;;)
66 if (HandleToUlong(spi->UniqueProcessId) == dc->pid)
68 dc->num_threads = spi->dwThreadCount;
69 dc->threads = HeapAlloc(GetProcessHeap(), 0,
70 dc->num_threads * sizeof(dc->threads[0]));
71 if (!dc->threads) goto failed;
72 for (i = 0; i < dc->num_threads; i++)
74 dc->threads[i].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
75 dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */
76 dc->threads[i].curr_prio = spi->ti[i].dwCurrentPriority;
78 HeapFree(GetProcessHeap(), 0, pcs_buffer);
79 return TRUE;
81 if (!spi->NextEntryOffset) break;
82 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset);
85 failed:
86 HeapFree(GetProcessHeap(), 0, pcs_buffer);
87 return FALSE;
90 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
91 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
93 NT_TIB tib;
94 ADDRESS64 addr;
96 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL) &&
97 dbghelp_current_cpu &&
98 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
100 if (addr.Offset)
102 addr.Offset -= dbghelp_current_cpu->word_size;
103 /* make sure stack pointer is within the established range of the stack. It could have
104 been clobbered by whatever caused the original exception. */
105 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
106 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
108 else
109 mmd->StartOfMemoryRange = addr.Offset;
111 else
112 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
113 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
117 /******************************************************************
118 * fetch_thread_info
120 * fetches some information about thread of id 'tid'
122 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
123 const MINIDUMP_EXCEPTION_INFORMATION* except,
124 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
126 DWORD tid = dc->threads[thd_idx].tid;
127 HANDLE hThread;
128 THREAD_BASIC_INFORMATION tbi;
130 memset(ctx, 0, sizeof(*ctx));
132 mdThd->ThreadId = tid;
133 mdThd->SuspendCount = 0;
134 mdThd->Teb = 0;
135 mdThd->Stack.StartOfMemoryRange = 0;
136 mdThd->Stack.Memory.DataSize = 0;
137 mdThd->Stack.Memory.Rva = 0;
138 mdThd->ThreadContext.DataSize = 0;
139 mdThd->ThreadContext.Rva = 0;
140 mdThd->PriorityClass = dc->threads[thd_idx].prio_class;
141 mdThd->Priority = dc->threads[thd_idx].curr_prio;
143 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
145 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
146 return FALSE;
149 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
150 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
152 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
153 if (tbi.ExitStatus == STILL_ACTIVE)
155 if (tid != GetCurrentThreadId() &&
156 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
158 ctx->ContextFlags = CONTEXT_FULL;
159 if (!GetThreadContext(hThread, ctx))
160 memset(ctx, 0, sizeof(*ctx));
162 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
163 ResumeThread(hThread);
165 else if (tid == GetCurrentThreadId() && except)
167 CONTEXT lctx, *pctx;
168 mdThd->SuspendCount = 1;
169 if (except->ClientPointers)
171 EXCEPTION_POINTERS ep;
173 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
174 &ep, sizeof(ep), NULL);
175 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
176 &lctx, sizeof(lctx), NULL);
177 pctx = &lctx;
179 else pctx = except->ExceptionPointers->ContextRecord;
181 *ctx = *pctx;
182 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
184 else mdThd->SuspendCount = 0;
187 CloseHandle(hThread);
188 return TRUE;
191 /******************************************************************
192 * add_module
194 * Add a module to a dump context
196 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
197 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
198 BOOL is_elf)
200 if (!dc->modules)
202 dc->alloc_modules = 32;
203 dc->modules = HeapAlloc(GetProcessHeap(), 0,
204 dc->alloc_modules * sizeof(*dc->modules));
206 else if(dc->num_modules >= dc->alloc_modules)
208 dc->alloc_modules *= 2;
209 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
210 dc->alloc_modules * sizeof(*dc->modules));
212 if (!dc->modules)
214 dc->alloc_modules = dc->num_modules = 0;
215 return FALSE;
217 if (is_elf ||
218 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base,
219 dc->modules[dc->num_modules].name,
220 ARRAY_SIZE(dc->modules[dc->num_modules].name)))
221 lstrcpynW(dc->modules[dc->num_modules].name, name,
222 ARRAY_SIZE(dc->modules[dc->num_modules].name));
223 dc->modules[dc->num_modules].base = base;
224 dc->modules[dc->num_modules].size = size;
225 dc->modules[dc->num_modules].timestamp = timestamp;
226 dc->modules[dc->num_modules].checksum = checksum;
227 dc->modules[dc->num_modules].is_elf = is_elf;
228 dc->num_modules++;
230 return TRUE;
233 /******************************************************************
234 * fetch_pe_module_info_cb
236 * Callback for accumulating in dump_context a PE modules set
238 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
239 PVOID user)
241 struct dump_context* dc = user;
242 IMAGE_NT_HEADERS nth;
244 if (!validate_addr64(base)) return FALSE;
246 if (pe_load_nt_header(dc->hProcess, base, &nth))
247 add_module(user, name, base, size,
248 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
249 FALSE);
250 return TRUE;
253 /******************************************************************
254 * fetch_elf_module_info_cb
256 * Callback for accumulating in dump_context an ELF modules set
258 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
259 void* user)
261 struct dump_context* dc = user;
262 DWORD_PTR rbase;
263 DWORD size, checksum;
265 /* FIXME: there's no relevant timestamp on ELF modules */
266 /* NB: if we have a non-null base from the live-target use it (whenever
267 * the ELF module is relocatable or not). If we have a null base (ELF
268 * module isn't relocatable) then grab its base address from ELF file
270 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
271 size = checksum = 0;
272 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
273 return TRUE;
276 /******************************************************************
277 * fetch_macho_module_info_cb
279 * Callback for accumulating in dump_context a Mach-O modules set
281 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
282 void* user)
284 struct dump_context* dc = (struct dump_context*)user;
285 DWORD_PTR rbase;
286 DWORD size, checksum;
288 /* FIXME: there's no relevant timestamp on Mach-O modules */
289 /* NB: if we have a non-null base from the live-target use it. If we have
290 * a null base, then grab its base address from Mach-O file.
292 if (!macho_fetch_file_info(dc->hProcess, name, base, &rbase, &size, &checksum))
293 size = checksum = 0;
294 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
295 return TRUE;
298 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
300 if (!dc->mem64)
302 dc->alloc_mem64 = 32;
303 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
305 else if (dc->num_mem64 >= dc->alloc_mem64)
307 dc->alloc_mem64 *= 2;
308 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
309 dc->alloc_mem64 * sizeof(*dc->mem64));
311 if (dc->mem64)
313 dc->mem64[dc->num_mem64].base = base;
314 dc->mem64[dc->num_mem64].size = size;
315 dc->num_mem64++;
317 else dc->num_mem64 = dc->alloc_mem64 = 0;
320 static void fetch_memory64_info(struct dump_context* dc)
322 ULONG_PTR addr;
323 MEMORY_BASIC_INFORMATION mbi;
325 addr = 0;
326 while (VirtualQueryEx(dc->hProcess, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
328 /* Memory regions with state MEM_COMMIT will be added to the dump */
329 if (mbi.State == MEM_COMMIT)
331 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
334 if ((addr + mbi.RegionSize) < addr)
335 break;
337 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
341 static void fetch_modules_info(struct dump_context* dc)
343 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
344 /* Since we include ELF modules in a separate stream from the regular PE ones,
345 * we can always include those ELF modules (they don't eat lots of space)
346 * And it's always a good idea to have a trace of the loaded ELF modules for
347 * a given application in a post mortem debugging condition.
349 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
350 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
353 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
355 DWORD handle;
356 DWORD sz;
357 static const WCHAR backslashW[] = {'\\', '\0'};
359 memset(ffi, 0, sizeof(*ffi));
360 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
362 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
363 if (info && GetFileVersionInfoW(filename, handle, sz, info))
365 VS_FIXEDFILEINFO* ptr;
366 UINT len;
368 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
369 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
371 HeapFree(GetProcessHeap(), 0, info);
375 /******************************************************************
376 * minidump_add_memory_block
378 * Add a memory block to be dumped in a minidump
379 * If rva is non 0, it's the rva in the minidump where has to be stored
380 * also the rva of the memory block when written (this allows us to reference
381 * a memory block from outside the list of memory blocks).
383 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
385 if (!dc->mem)
387 dc->alloc_mem = 32;
388 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
390 else if (dc->num_mem >= dc->alloc_mem)
392 dc->alloc_mem *= 2;
393 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
394 dc->alloc_mem * sizeof(*dc->mem));
396 if (dc->mem)
398 dc->mem[dc->num_mem].base = base;
399 dc->mem[dc->num_mem].size = size;
400 dc->mem[dc->num_mem].rva = rva;
401 dc->num_mem++;
403 else dc->num_mem = dc->alloc_mem = 0;
406 /******************************************************************
407 * writeat
409 * Writes a chunk of data at a given position in the minidump
411 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
413 DWORD written;
415 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
416 WriteFile(dc->hFile, data, size, &written, NULL);
419 /******************************************************************
420 * append
422 * writes a new chunk of data to the minidump, increasing the current
423 * rva in dc
425 static void append(struct dump_context* dc, const void* data, unsigned size)
427 writeat(dc, dc->rva, data, size);
428 dc->rva += size;
431 /******************************************************************
432 * dump_exception_info
434 * Write in File the exception information from pcs
436 static unsigned dump_exception_info(struct dump_context* dc,
437 const MINIDUMP_EXCEPTION_INFORMATION* except)
439 MINIDUMP_EXCEPTION_STREAM mdExcpt;
440 EXCEPTION_RECORD rec, *prec;
441 CONTEXT ctx, *pctx;
442 DWORD i;
444 mdExcpt.ThreadId = except->ThreadId;
445 mdExcpt.__alignment = 0;
446 if (except->ClientPointers)
448 EXCEPTION_POINTERS ep;
450 ReadProcessMemory(dc->hProcess,
451 except->ExceptionPointers, &ep, sizeof(ep), NULL);
452 ReadProcessMemory(dc->hProcess,
453 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
454 ReadProcessMemory(dc->hProcess,
455 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
456 prec = &rec;
457 pctx = &ctx;
459 else
461 prec = except->ExceptionPointers->ExceptionRecord;
462 pctx = except->ExceptionPointers->ContextRecord;
464 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
465 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
466 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
467 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
468 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
469 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
470 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
471 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
472 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
473 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
475 append(dc, &mdExcpt, sizeof(mdExcpt));
476 append(dc, pctx, sizeof(*pctx));
477 return sizeof(mdExcpt);
480 /******************************************************************
481 * dump_modules
483 * Write in File the modules from pcs
485 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
487 MINIDUMP_MODULE mdModule;
488 MINIDUMP_MODULE_LIST mdModuleList;
489 char tmp[1024];
490 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
491 ULONG i, nmod;
492 RVA rva_base;
493 DWORD flags_out;
494 unsigned sz;
496 for (i = nmod = 0; i < dc->num_modules; i++)
498 if ((dc->modules[i].is_elf && dump_elf) ||
499 (!dc->modules[i].is_elf && !dump_elf))
500 nmod++;
503 mdModuleList.NumberOfModules = 0;
504 /* reserve space for mdModuleList
505 * FIXME: since we don't support 0 length arrays, we cannot use the
506 * size of mdModuleList
507 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
510 /* the stream size is just the size of the module index. It does not include the data for the
511 names of each module. *Technically* the names are supposed to go into the common string table
512 in the minidump file. Since each string is referenced by RVA they can all safely be located
513 anywhere between streams in the file, so the end of this stream is sufficient. */
514 rva_base = dc->rva;
515 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
516 for (i = 0; i < dc->num_modules; i++)
518 if ((dc->modules[i].is_elf && !dump_elf) ||
519 (!dc->modules[i].is_elf && dump_elf))
520 continue;
522 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
523 if (dc->type & MiniDumpWithDataSegs)
524 flags_out |= ModuleWriteDataSeg;
525 if (dc->type & MiniDumpWithProcessThreadData)
526 flags_out |= ModuleWriteTlsData;
527 if (dc->type & MiniDumpWithCodeSegs)
528 flags_out |= ModuleWriteCodeSegs;
529 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
530 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
531 FIXME("Buffer overflow!!!\n");
532 lstrcpyW(ms->Buffer, dc->modules[i].name);
534 if (dc->cb)
536 MINIDUMP_CALLBACK_INPUT cbin;
537 MINIDUMP_CALLBACK_OUTPUT cbout;
539 cbin.ProcessId = dc->pid;
540 cbin.ProcessHandle = dc->hProcess;
541 cbin.CallbackType = ModuleCallback;
543 cbin.u.Module.FullPath = ms->Buffer;
544 cbin.u.Module.BaseOfImage = dc->modules[i].base;
545 cbin.u.Module.SizeOfImage = dc->modules[i].size;
546 cbin.u.Module.CheckSum = dc->modules[i].checksum;
547 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
548 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
549 cbin.u.Module.CvRecord = NULL;
550 cbin.u.Module.SizeOfCvRecord = 0;
551 cbin.u.Module.MiscRecord = NULL;
552 cbin.u.Module.SizeOfMiscRecord = 0;
554 cbout.u.ModuleWriteFlags = flags_out;
555 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
556 continue;
557 flags_out &= cbout.u.ModuleWriteFlags;
559 if (flags_out & ModuleWriteModule)
561 /* fetch CPU dependent module info (like UNWIND_INFO) */
562 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
564 mdModule.BaseOfImage = dc->modules[i].base;
565 mdModule.SizeOfImage = dc->modules[i].size;
566 mdModule.CheckSum = dc->modules[i].checksum;
567 mdModule.TimeDateStamp = dc->modules[i].timestamp;
568 mdModule.ModuleNameRva = dc->rva;
569 ms->Length -= sizeof(WCHAR);
570 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
571 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
572 mdModule.CvRecord.DataSize = 0; /* FIXME */
573 mdModule.CvRecord.Rva = 0; /* FIXME */
574 mdModule.MiscRecord.DataSize = 0; /* FIXME */
575 mdModule.MiscRecord.Rva = 0; /* FIXME */
576 mdModule.Reserved0 = 0; /* FIXME */
577 mdModule.Reserved1 = 0; /* FIXME */
578 writeat(dc,
579 rva_base + sizeof(mdModuleList.NumberOfModules) +
580 mdModuleList.NumberOfModules++ * sizeof(mdModule),
581 &mdModule, sizeof(mdModule));
584 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
585 sizeof(mdModuleList.NumberOfModules));
587 return sz;
590 #ifdef __i386__
591 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
592 __ASM_GLOBAL_FUNC( do_x86cpuid,
593 "pushl %esi\n\t"
594 "pushl %ebx\n\t"
595 "movl 12(%esp),%eax\n\t"
596 "movl 16(%esp),%esi\n\t"
597 "cpuid\n\t"
598 "movl %eax,(%esi)\n\t"
599 "movl %ebx,4(%esi)\n\t"
600 "movl %ecx,8(%esi)\n\t"
601 "movl %edx,12(%esi)\n\t"
602 "popl %ebx\n\t"
603 "popl %esi\n\t"
604 "ret" )
605 extern int have_x86cpuid(void);
606 __ASM_GLOBAL_FUNC( have_x86cpuid,
607 "pushfl\n\t"
608 "pushfl\n\t"
609 "movl (%esp),%ecx\n\t"
610 "xorl $0x00200000,(%esp)\n\t"
611 "popfl\n\t"
612 "pushfl\n\t"
613 "popl %eax\n\t"
614 "popfl\n\t"
615 "xorl %ecx,%eax\n\t"
616 "andl $0x00200000,%eax\n\t"
617 "ret" )
618 #else
619 static void do_x86cpuid(unsigned int ax, unsigned int *p)
623 static int have_x86cpuid(void)
625 return 0;
627 #endif
629 /******************************************************************
630 * dump_system_info
632 * Dumps into File the information about the system
634 static unsigned dump_system_info(struct dump_context* dc)
636 MINIDUMP_SYSTEM_INFO mdSysInfo;
637 SYSTEM_INFO sysInfo;
638 OSVERSIONINFOW osInfo;
639 DWORD written;
640 ULONG slen;
641 DWORD wine_extra = 0;
643 const char *(CDECL *wine_get_build_id)(void);
644 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
645 const char* build_id = NULL;
646 const char* sys_name = NULL;
647 const char* release_name = NULL;
649 GetSystemInfo(&sysInfo);
650 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
651 GetVersionExW(&osInfo);
653 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
654 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
655 if (wine_get_build_id && wine_get_host_version)
657 /* cheat minidump system information by adding specific wine information */
658 wine_extra = 4 + 4 * sizeof(slen);
659 build_id = wine_get_build_id();
660 wine_get_host_version(&sys_name, &release_name);
661 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
664 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
665 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
666 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
667 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
668 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
669 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
670 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
671 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
672 mdSysInfo.PlatformId = osInfo.dwPlatformId;
674 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
675 mdSysInfo.u1.Reserved1 = 0;
676 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
678 if (have_x86cpuid())
680 unsigned regs0[4], regs1[4];
682 do_x86cpuid(0, regs0);
683 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
684 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
685 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
686 do_x86cpuid(1, regs1);
687 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
688 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
689 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
690 if (regs0[1] == 0x68747541 /* "Auth" */ &&
691 regs0[3] == 0x69746e65 /* "enti" */ &&
692 regs0[2] == 0x444d4163 /* "cAMD" */)
694 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
695 if (regs1[0] >= 0x80000001)
697 do_x86cpuid(0x80000001, regs1); /* get vendor features */
698 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
702 else
704 unsigned i;
705 ULONG64 one = 1;
707 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
708 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
710 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
711 if (IsProcessorFeaturePresent(i))
712 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
714 append(dc, &mdSysInfo, sizeof(mdSysInfo));
716 /* write Wine specific system information just behind the structure, and before any string */
717 if (wine_extra)
719 static const char code[] = {'W','I','N','E'};
721 WriteFile(dc->hFile, code, 4, &written, NULL);
722 /* number of sub-info, so that we can extend structure if needed */
723 slen = 3;
724 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
725 /* we store offsets from just after the WINE marker */
726 slen = 4 * sizeof(DWORD);
727 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
728 slen += strlen(build_id) + 1;
729 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
730 slen += strlen(sys_name) + 1;
731 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
732 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
733 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
734 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
735 dc->rva += wine_extra;
738 /* write the service pack version string after this stream. It is referenced within the
739 stream by its RVA in the file. */
740 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
741 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
742 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
743 dc->rva += sizeof(ULONG) + slen;
745 return sizeof(mdSysInfo);
748 /******************************************************************
749 * dump_threads
751 * Dumps into File the information about running threads
753 static unsigned dump_threads(struct dump_context* dc,
754 const MINIDUMP_EXCEPTION_INFORMATION* except)
756 MINIDUMP_THREAD mdThd;
757 MINIDUMP_THREAD_LIST mdThdList;
758 unsigned i, sz;
759 RVA rva_base;
760 DWORD flags_out;
761 CONTEXT ctx;
763 mdThdList.NumberOfThreads = 0;
765 rva_base = dc->rva;
766 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
768 for (i = 0; i < dc->num_threads; i++)
770 fetch_thread_info(dc, i, except, &mdThd, &ctx);
772 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
773 ThreadWriteInstructionWindow;
774 if (dc->type & MiniDumpWithProcessThreadData)
775 flags_out |= ThreadWriteThreadData;
776 if (dc->type & MiniDumpWithThreadInfo)
777 flags_out |= ThreadWriteThreadInfo;
779 if (dc->cb)
781 MINIDUMP_CALLBACK_INPUT cbin;
782 MINIDUMP_CALLBACK_OUTPUT cbout;
784 cbin.ProcessId = dc->pid;
785 cbin.ProcessHandle = dc->hProcess;
786 cbin.CallbackType = ThreadCallback;
787 cbin.u.Thread.ThreadId = dc->threads[i].tid;
788 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
789 cbin.u.Thread.Context = ctx;
790 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
791 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
792 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
793 mdThd.Stack.Memory.DataSize;
795 cbout.u.ThreadWriteFlags = flags_out;
796 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
797 continue;
798 flags_out &= cbout.u.ThreadWriteFlags;
800 if (flags_out & ThreadWriteThread)
802 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
804 mdThd.ThreadContext.Rva = dc->rva;
805 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
806 append(dc, &ctx, sizeof(CONTEXT));
808 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
810 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
811 mdThd.Stack.Memory.DataSize,
812 rva_base + sizeof(mdThdList.NumberOfThreads) +
813 mdThdList.NumberOfThreads * sizeof(mdThd) +
814 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
816 writeat(dc,
817 rva_base + sizeof(mdThdList.NumberOfThreads) +
818 mdThdList.NumberOfThreads * sizeof(mdThd),
819 &mdThd, sizeof(mdThd));
820 mdThdList.NumberOfThreads++;
822 /* fetch CPU dependent thread info (like 256 bytes around program counter */
823 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
825 writeat(dc, rva_base,
826 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
828 return sz;
831 /******************************************************************
832 * dump_memory_info
834 * dumps information about the memory of the process (stack of the threads)
836 static unsigned dump_memory_info(struct dump_context* dc)
838 MINIDUMP_MEMORY_LIST mdMemList;
839 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
840 DWORD written;
841 unsigned i, pos, len, sz;
842 RVA rva_base;
843 char tmp[1024];
845 mdMemList.NumberOfMemoryRanges = dc->num_mem;
846 append(dc, &mdMemList.NumberOfMemoryRanges,
847 sizeof(mdMemList.NumberOfMemoryRanges));
848 rva_base = dc->rva;
849 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
850 dc->rva += sz;
851 sz += sizeof(mdMemList.NumberOfMemoryRanges);
853 for (i = 0; i < dc->num_mem; i++)
855 mdMem.StartOfMemoryRange = dc->mem[i].base;
856 mdMem.Memory.Rva = dc->rva;
857 mdMem.Memory.DataSize = dc->mem[i].size;
858 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
859 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
861 len = min(dc->mem[i].size - pos, sizeof(tmp));
862 if (ReadProcessMemory(dc->hProcess,
863 (void*)(DWORD_PTR)(dc->mem[i].base + pos),
864 tmp, len, NULL))
865 WriteFile(dc->hFile, tmp, len, &written, NULL);
867 dc->rva += mdMem.Memory.DataSize;
868 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
869 if (dc->mem[i].rva)
871 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
875 return sz;
878 /******************************************************************
879 * dump_memory64_info
881 * dumps information about the memory of the process (virtual memory)
883 static unsigned dump_memory64_info(struct dump_context* dc)
885 MINIDUMP_MEMORY64_LIST mdMem64List;
886 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64;
887 DWORD written;
888 unsigned i, len, sz;
889 RVA rva_base;
890 char tmp[1024];
891 ULONG64 pos;
892 LARGE_INTEGER filepos;
894 sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
895 sizeof(mdMem64List.BaseRva) +
896 dc->num_mem64 * sizeof(mdMem64);
898 mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
899 mdMem64List.BaseRva = dc->rva + sz;
901 append(dc, &mdMem64List.NumberOfMemoryRanges,
902 sizeof(mdMem64List.NumberOfMemoryRanges));
903 append(dc, &mdMem64List.BaseRva,
904 sizeof(mdMem64List.BaseRva));
906 rva_base = dc->rva;
907 dc->rva += dc->num_mem64 * sizeof(mdMem64);
909 /* dc->rva is not updated past this point. The end of the dump
910 * is just the full memory data. */
911 filepos.QuadPart = dc->rva;
912 for (i = 0; i < dc->num_mem64; i++)
914 mdMem64.StartOfMemoryRange = dc->mem64[i].base;
915 mdMem64.DataSize = dc->mem64[i].size;
916 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
917 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
919 len = min(dc->mem64[i].size - pos, sizeof(tmp));
920 if (ReadProcessMemory(dc->hProcess,
921 (void*)(ULONG_PTR)(dc->mem64[i].base + pos),
922 tmp, len, NULL))
923 WriteFile(dc->hFile, tmp, len, &written, NULL);
925 filepos.QuadPart += mdMem64.DataSize;
926 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
929 return sz;
932 static unsigned dump_misc_info(struct dump_context* dc)
934 MINIDUMP_MISC_INFO mmi;
936 mmi.SizeOfInfo = sizeof(mmi);
937 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
938 mmi.ProcessId = dc->pid;
939 /* FIXME: create/user/kernel time */
940 mmi.ProcessCreateTime = 0;
941 mmi.ProcessKernelTime = 0;
942 mmi.ProcessUserTime = 0;
944 append(dc, &mmi, sizeof(mmi));
945 return sizeof(mmi);
948 /******************************************************************
949 * MiniDumpWriteDump (DEBUGHLP.@)
952 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
953 MINIDUMP_TYPE DumpType,
954 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
955 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
956 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
958 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
959 MINIDUMP_HEADER mdHead;
960 MINIDUMP_DIRECTORY mdDir;
961 DWORD i, nStreams, idx_stream;
962 struct dump_context dc;
964 dc.hProcess = hProcess;
965 dc.hFile = hFile;
966 dc.pid = pid;
967 dc.modules = NULL;
968 dc.num_modules = 0;
969 dc.alloc_modules = 0;
970 dc.threads = NULL;
971 dc.num_threads = 0;
972 dc.cb = CallbackParam;
973 dc.type = DumpType;
974 dc.mem = NULL;
975 dc.num_mem = 0;
976 dc.alloc_mem = 0;
977 dc.mem64 = NULL;
978 dc.num_mem64 = 0;
979 dc.alloc_mem64 = 0;
980 dc.rva = 0;
982 if (!fetch_process_info(&dc)) return FALSE;
983 fetch_modules_info(&dc);
985 /* 1) init */
986 nStreams = 6 + (ExceptionParam ? 1 : 0) +
987 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
989 /* pad the directory size to a multiple of 4 for alignment purposes */
990 nStreams = (nStreams + 3) & ~3;
992 if (DumpType & MiniDumpWithDataSegs)
993 FIXME("NIY MiniDumpWithDataSegs\n");
994 if (DumpType & MiniDumpWithHandleData)
995 FIXME("NIY MiniDumpWithHandleData\n");
996 if (DumpType & MiniDumpFilterMemory)
997 FIXME("NIY MiniDumpFilterMemory\n");
998 if (DumpType & MiniDumpScanMemory)
999 FIXME("NIY MiniDumpScanMemory\n");
1001 /* 2) write header */
1002 mdHead.Signature = MINIDUMP_SIGNATURE;
1003 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
1004 mdHead.NumberOfStreams = nStreams;
1005 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
1006 mdHead.StreamDirectoryRva = sizeof(mdHead);
1007 mdHead.u.TimeDateStamp = time(NULL);
1008 mdHead.Flags = DumpType;
1009 append(&dc, &mdHead, sizeof(mdHead));
1011 /* 3) write stream directories */
1012 dc.rva += nStreams * sizeof(mdDir);
1013 idx_stream = 0;
1015 /* 3.1) write data stream directories */
1017 /* must be first in minidump */
1018 mdDir.StreamType = SystemInfoStream;
1019 mdDir.Location.Rva = dc.rva;
1020 mdDir.Location.DataSize = dump_system_info(&dc);
1021 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1022 &mdDir, sizeof(mdDir));
1024 mdDir.StreamType = ThreadListStream;
1025 mdDir.Location.Rva = dc.rva;
1026 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
1027 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1028 &mdDir, sizeof(mdDir));
1030 mdDir.StreamType = ModuleListStream;
1031 mdDir.Location.Rva = dc.rva;
1032 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
1033 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1034 &mdDir, sizeof(mdDir));
1036 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1037 mdDir.Location.Rva = dc.rva;
1038 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
1039 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1040 &mdDir, sizeof(mdDir));
1043 if (!(DumpType & MiniDumpWithFullMemory))
1045 mdDir.StreamType = MemoryListStream;
1046 mdDir.Location.Rva = dc.rva;
1047 mdDir.Location.DataSize = dump_memory_info(&dc);
1048 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1049 &mdDir, sizeof(mdDir));
1052 mdDir.StreamType = MiscInfoStream;
1053 mdDir.Location.Rva = dc.rva;
1054 mdDir.Location.DataSize = dump_misc_info(&dc);
1055 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1056 &mdDir, sizeof(mdDir));
1058 /* 3.2) write exception information (if any) */
1059 if (ExceptionParam)
1061 mdDir.StreamType = ExceptionStream;
1062 mdDir.Location.Rva = dc.rva;
1063 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
1064 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1065 &mdDir, sizeof(mdDir));
1068 /* 3.3) write user defined streams (if any) */
1069 if (UserStreamParam)
1071 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
1073 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
1074 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
1075 mdDir.Location.Rva = dc.rva;
1076 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1077 &mdDir, sizeof(mdDir));
1078 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
1079 UserStreamParam->UserStreamArray[i].BufferSize);
1083 /* 3.4) write full memory (if requested) */
1084 if (DumpType & MiniDumpWithFullMemory)
1086 fetch_memory64_info(&dc);
1088 mdDir.StreamType = Memory64ListStream;
1089 mdDir.Location.Rva = dc.rva;
1090 mdDir.Location.DataSize = dump_memory64_info(&dc);
1091 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1092 &mdDir, sizeof(mdDir));
1095 /* fill the remaining directory entries with 0's (unused stream types) */
1096 /* NOTE: this should always come last in the dump! */
1097 for (i = idx_stream; i < nStreams; i++)
1098 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1100 HeapFree(GetProcessHeap(), 0, dc.mem);
1101 HeapFree(GetProcessHeap(), 0, dc.mem64);
1102 HeapFree(GetProcessHeap(), 0, dc.modules);
1103 HeapFree(GetProcessHeap(), 0, dc.threads);
1105 return TRUE;
1108 /******************************************************************
1109 * MiniDumpReadDumpStream (DEBUGHLP.@)
1113 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1114 PMINIDUMP_DIRECTORY* pdir,
1115 PVOID* stream, ULONG* size)
1117 MINIDUMP_HEADER* mdHead = base;
1119 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1121 MINIDUMP_DIRECTORY* dir;
1122 DWORD i;
1124 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1125 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1127 if (dir->StreamType == str_idx)
1129 if (pdir) *pdir = dir;
1130 if (stream) *stream = (char*)base + dir->Location.Rva;
1131 if (size) *size = dir->Location.DataSize;
1132 return TRUE;
1136 SetLastError(ERROR_INVALID_PARAMETER);
1137 return FALSE;