include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / dbghelp / minidump.c
blob054aaee22a9115d5de95c76832e3e6f1bb6e944e
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 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "dbghelp_private.h"
26 #include "winternl.h"
27 #include "psapi.h"
28 #include "wine/asm.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
33 /******************************************************************
34 * fetch_process_info
36 * reads system wide process information, and gather from it the threads information
37 * for process of id 'pid'
39 static BOOL fetch_process_info(struct dump_context* dc)
41 ULONG buf_size = 0x1000;
42 NTSTATUS nts;
43 void* pcs_buffer = NULL;
45 if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
46 for (;;)
48 nts = NtQuerySystemInformation(SystemProcessInformation,
49 pcs_buffer, buf_size, NULL);
50 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
51 pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2);
52 if (!pcs_buffer) return FALSE;
55 if (nts == STATUS_SUCCESS)
57 SYSTEM_PROCESS_INFORMATION* spi = pcs_buffer;
58 unsigned i;
60 for (;;)
62 if (HandleToUlong(spi->UniqueProcessId) == dc->pid)
64 dc->num_threads = 0;
65 dc->threads = HeapAlloc(GetProcessHeap(), 0,
66 spi->dwThreadCount * sizeof(dc->threads[0]));
67 if (!dc->threads) break;
68 for (i = 0; i < spi->dwThreadCount; i++)
70 /* don't include current thread */
71 if (HandleToULong(spi->ti[i].ClientId.UniqueThread) == GetCurrentThreadId())
72 continue;
73 dc->threads[dc->num_threads].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
74 dc->threads[dc->num_threads].prio_class = spi->ti[i].dwBasePriority; /* FIXME */
75 dc->threads[dc->num_threads].curr_prio = spi->ti[i].dwCurrentPriority;
76 dc->num_threads++;
78 HeapFree(GetProcessHeap(), 0, pcs_buffer);
79 return TRUE;
81 if (!spi->NextEntryOffset) break;
82 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset);
85 HeapFree(GetProcessHeap(), 0, pcs_buffer);
86 return FALSE;
89 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
90 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
92 NT_TIB tib;
93 ADDRESS64 addr;
95 if (ReadProcessMemory(dc->process->handle, teb_addr, &tib, sizeof(tib), NULL) &&
96 dbghelp_current_cpu &&
97 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
99 if (addr.Offset)
101 addr.Offset -= dbghelp_current_cpu->word_size;
102 /* make sure stack pointer is within the established range of the stack. It could have
103 been clobbered by whatever caused the original exception. */
104 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
105 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
107 else
108 mmd->StartOfMemoryRange = addr.Offset;
110 else
111 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
112 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
116 /******************************************************************
117 * fetch_thread_info
119 * fetches some information about thread of id 'tid'
121 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
122 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
124 DWORD tid = dc->threads[thd_idx].tid;
125 HANDLE hThread;
126 THREAD_BASIC_INFORMATION tbi;
128 memset(ctx, 0, sizeof(*ctx));
130 mdThd->ThreadId = tid;
131 mdThd->SuspendCount = 0;
132 mdThd->Teb = 0;
133 mdThd->Stack.StartOfMemoryRange = 0;
134 mdThd->Stack.Memory.DataSize = 0;
135 mdThd->Stack.Memory.Rva = 0;
136 mdThd->ThreadContext.DataSize = 0;
137 mdThd->ThreadContext.Rva = 0;
138 mdThd->PriorityClass = dc->threads[thd_idx].prio_class;
139 mdThd->Priority = dc->threads[thd_idx].curr_prio;
141 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
143 FIXME("Couldn't open thread %lu (%lu)\n", tid, GetLastError());
144 return FALSE;
147 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
148 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
150 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
151 if (tbi.ExitStatus == STILL_ACTIVE)
153 mdThd->SuspendCount = SuspendThread(hThread);
154 ctx->ContextFlags = CONTEXT_ALL;
155 if (!GetThreadContext(hThread, ctx))
156 memset(ctx, 0, sizeof(*ctx));
157 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
158 ResumeThread(hThread);
160 else
161 mdThd->SuspendCount = (DWORD)-1;
163 CloseHandle(hThread);
164 return TRUE;
167 /******************************************************************
168 * add_module
170 * Add a module to a dump context
172 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
173 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
174 BOOL is_elf)
176 if (!dc->modules)
178 dc->alloc_modules = 32;
179 dc->modules = HeapAlloc(GetProcessHeap(), 0,
180 dc->alloc_modules * sizeof(*dc->modules));
182 else if(dc->num_modules >= dc->alloc_modules)
184 dc->alloc_modules *= 2;
185 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
186 dc->alloc_modules * sizeof(*dc->modules));
188 if (!dc->modules)
190 dc->alloc_modules = dc->num_modules = 0;
191 return FALSE;
193 lstrcpynW(dc->modules[dc->num_modules].name, name,
194 ARRAY_SIZE(dc->modules[dc->num_modules].name));
195 dc->modules[dc->num_modules].base = base;
196 dc->modules[dc->num_modules].size = size;
197 dc->modules[dc->num_modules].timestamp = timestamp;
198 dc->modules[dc->num_modules].checksum = checksum;
199 dc->modules[dc->num_modules].is_elf = is_elf;
200 dc->num_modules++;
202 return TRUE;
205 /******************************************************************
206 * fetch_pe_module_info_cb
208 * Callback for accumulating in dump_context a PE modules set
210 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
211 PVOID user)
213 struct dump_context* dc = user;
214 IMAGE_NT_HEADERS nth;
216 if (!validate_addr64(base)) return FALSE;
218 if (pe_load_nt_header(dc->process->handle, base, &nth, NULL))
219 add_module(user, name, base, size,
220 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
221 FALSE);
222 return TRUE;
225 /******************************************************************
226 * fetch_elf_module_info_cb
228 * Callback for accumulating in dump_context an host modules set
230 static BOOL fetch_host_module_info_cb(const WCHAR* name, ULONG_PTR base,
231 void* user)
233 struct dump_context* dc = user;
234 DWORD_PTR rbase;
235 DWORD size, checksum;
237 /* FIXME: there's no relevant timestamp on ELF modules */
238 if (!dc->process->loader->fetch_file_info(dc->process, name, base, &rbase, &size, &checksum))
239 size = checksum = 0;
240 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
241 return TRUE;
244 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
246 if (!dc->mem64)
248 dc->alloc_mem64 = 32;
249 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
251 else if (dc->num_mem64 >= dc->alloc_mem64)
253 dc->alloc_mem64 *= 2;
254 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
255 dc->alloc_mem64 * sizeof(*dc->mem64));
257 if (dc->mem64)
259 dc->mem64[dc->num_mem64].base = base;
260 dc->mem64[dc->num_mem64].size = size;
261 dc->num_mem64++;
263 else dc->num_mem64 = dc->alloc_mem64 = 0;
266 static void fetch_memory64_info(struct dump_context* dc)
268 ULONG_PTR addr;
269 MEMORY_BASIC_INFORMATION mbi;
271 addr = 0;
272 while (VirtualQueryEx(dc->process->handle, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
274 /* Memory regions with state MEM_COMMIT will be added to the dump */
275 if (mbi.State == MEM_COMMIT)
277 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
280 if ((addr + mbi.RegionSize) < addr)
281 break;
283 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
287 static void fetch_modules_info(struct dump_context* dc)
289 EnumerateLoadedModulesW64(dc->process->handle, fetch_pe_module_info_cb, dc);
290 /* Since we include ELF modules in a separate stream from the regular PE ones,
291 * we can always include those ELF modules (they don't eat lots of space)
292 * And it's always a good idea to have a trace of the loaded ELF modules for
293 * a given application in a post mortem debugging condition.
295 dc->process->loader->enum_modules(dc->process, fetch_host_module_info_cb, dc);
298 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
300 DWORD handle;
301 DWORD sz;
303 memset(ffi, 0, sizeof(*ffi));
304 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
306 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
307 if (info && GetFileVersionInfoW(filename, handle, sz, info))
309 VS_FIXEDFILEINFO* ptr;
310 UINT len;
312 if (VerQueryValueW(info, L"\\", (void*)&ptr, &len))
313 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
315 HeapFree(GetProcessHeap(), 0, info);
319 /******************************************************************
320 * minidump_add_memory_block
322 * Add a memory block to be dumped in a minidump
323 * If rva is non 0, it's the rva in the minidump where has to be stored
324 * also the rva of the memory block when written (this allows us to reference
325 * a memory block from outside the list of memory blocks).
327 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
329 if (!dc->mem)
331 dc->alloc_mem = 32;
332 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
334 else if (dc->num_mem >= dc->alloc_mem)
336 dc->alloc_mem *= 2;
337 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
338 dc->alloc_mem * sizeof(*dc->mem));
340 if (dc->mem)
342 dc->mem[dc->num_mem].base = base;
343 dc->mem[dc->num_mem].size = size;
344 dc->mem[dc->num_mem].rva = rva;
345 dc->num_mem++;
347 else dc->num_mem = dc->alloc_mem = 0;
350 /******************************************************************
351 * writeat
353 * Writes a chunk of data at a given position in the minidump
355 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
357 DWORD written;
359 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
360 WriteFile(dc->hFile, data, size, &written, NULL);
363 /******************************************************************
364 * append
366 * writes a new chunk of data to the minidump, increasing the current
367 * rva in dc
369 static void append(struct dump_context* dc, const void* data, unsigned size)
371 writeat(dc, dc->rva, data, size);
372 dc->rva += size;
375 /******************************************************************
376 * dump_exception_info
378 * Write in File the exception information from pcs
380 static unsigned dump_exception_info(struct dump_context* dc)
382 MINIDUMP_EXCEPTION_STREAM mdExcpt;
383 EXCEPTION_RECORD rec, *prec;
384 CONTEXT ctx, *pctx;
385 DWORD i;
387 mdExcpt.ThreadId = dc->except_param->ThreadId;
388 mdExcpt.__alignment = 0;
389 if (dc->except_param->ClientPointers)
391 EXCEPTION_POINTERS ep;
393 ReadProcessMemory(dc->process->handle,
394 dc->except_param->ExceptionPointers, &ep, sizeof(ep), NULL);
395 ReadProcessMemory(dc->process->handle,
396 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
397 ReadProcessMemory(dc->process->handle,
398 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
399 prec = &rec;
400 pctx = &ctx;
402 else
404 prec = dc->except_param->ExceptionPointers->ExceptionRecord;
405 pctx = dc->except_param->ExceptionPointers->ContextRecord;
407 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
408 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
409 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
410 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
411 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
412 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
413 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
414 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
415 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
416 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
418 append(dc, &mdExcpt, sizeof(mdExcpt));
419 append(dc, pctx, sizeof(*pctx));
420 return sizeof(mdExcpt);
423 /******************************************************************
424 * dump_modules
426 * Write in File the modules from pcs
428 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
430 MINIDUMP_MODULE mdModule;
431 MINIDUMP_MODULE_LIST mdModuleList;
432 char tmp[1024];
433 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
434 ULONG i, nmod;
435 RVA rva_base;
436 DWORD flags_out;
437 unsigned sz;
439 for (i = nmod = 0; i < dc->num_modules; i++)
441 if ((dc->modules[i].is_elf && dump_elf) ||
442 (!dc->modules[i].is_elf && !dump_elf))
443 nmod++;
446 mdModuleList.NumberOfModules = 0;
447 /* reserve space for mdModuleList
448 * FIXME: since we don't support 0 length arrays, we cannot use the
449 * size of mdModuleList
450 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
453 /* the stream size is just the size of the module index. It does not include the data for the
454 names of each module. *Technically* the names are supposed to go into the common string table
455 in the minidump file. Since each string is referenced by RVA they can all safely be located
456 anywhere between streams in the file, so the end of this stream is sufficient. */
457 rva_base = dc->rva;
458 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
459 for (i = 0; i < dc->num_modules; i++)
461 if ((dc->modules[i].is_elf && !dump_elf) ||
462 (!dc->modules[i].is_elf && dump_elf))
463 continue;
465 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
466 if (dc->type & MiniDumpWithDataSegs)
467 flags_out |= ModuleWriteDataSeg;
468 if (dc->type & MiniDumpWithProcessThreadData)
469 flags_out |= ModuleWriteTlsData;
470 if (dc->type & MiniDumpWithCodeSegs)
471 flags_out |= ModuleWriteCodeSegs;
472 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
473 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
474 FIXME("Buffer overflow!!!\n");
475 lstrcpyW(ms->Buffer, dc->modules[i].name);
477 if (dc->cb)
479 MINIDUMP_CALLBACK_INPUT cbin;
480 MINIDUMP_CALLBACK_OUTPUT cbout;
482 cbin.ProcessId = dc->pid;
483 cbin.ProcessHandle = dc->process->handle;
484 cbin.CallbackType = ModuleCallback;
486 cbin.Module.FullPath = ms->Buffer;
487 cbin.Module.BaseOfImage = dc->modules[i].base;
488 cbin.Module.SizeOfImage = dc->modules[i].size;
489 cbin.Module.CheckSum = dc->modules[i].checksum;
490 cbin.Module.TimeDateStamp = dc->modules[i].timestamp;
491 memset(&cbin.Module.VersionInfo, 0, sizeof(cbin.Module.VersionInfo));
492 cbin.Module.CvRecord = NULL;
493 cbin.Module.SizeOfCvRecord = 0;
494 cbin.Module.MiscRecord = NULL;
495 cbin.Module.SizeOfMiscRecord = 0;
497 cbout.ModuleWriteFlags = flags_out;
498 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
499 continue;
500 flags_out &= cbout.ModuleWriteFlags;
502 if (flags_out & ModuleWriteModule)
504 /* fetch CPU dependent module info (like UNWIND_INFO) */
505 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
507 mdModule.BaseOfImage = dc->modules[i].base;
508 mdModule.SizeOfImage = dc->modules[i].size;
509 mdModule.CheckSum = dc->modules[i].checksum;
510 mdModule.TimeDateStamp = dc->modules[i].timestamp;
511 mdModule.ModuleNameRva = dc->rva;
512 ms->Length -= sizeof(WCHAR);
513 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
514 if (!dump_elf) fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
515 else memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo));
516 mdModule.CvRecord.DataSize = 0; /* FIXME */
517 mdModule.CvRecord.Rva = 0; /* FIXME */
518 mdModule.MiscRecord.DataSize = 0; /* FIXME */
519 mdModule.MiscRecord.Rva = 0; /* FIXME */
520 mdModule.Reserved0 = 0; /* FIXME */
521 mdModule.Reserved1 = 0; /* FIXME */
522 writeat(dc,
523 rva_base + sizeof(mdModuleList.NumberOfModules) +
524 mdModuleList.NumberOfModules++ * sizeof(mdModule),
525 &mdModule, sizeof(mdModule));
528 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
529 sizeof(mdModuleList.NumberOfModules));
531 return sz;
534 #ifdef __i386__
535 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
536 __ASM_GLOBAL_FUNC( do_x86cpuid,
537 "pushl %esi\n\t"
538 "pushl %ebx\n\t"
539 "movl 12(%esp),%eax\n\t"
540 "movl 16(%esp),%esi\n\t"
541 "cpuid\n\t"
542 "movl %eax,(%esi)\n\t"
543 "movl %ebx,4(%esi)\n\t"
544 "movl %ecx,8(%esi)\n\t"
545 "movl %edx,12(%esi)\n\t"
546 "popl %ebx\n\t"
547 "popl %esi\n\t"
548 "ret" )
549 extern int have_x86cpuid(void);
550 __ASM_GLOBAL_FUNC( have_x86cpuid,
551 "pushfl\n\t"
552 "pushfl\n\t"
553 "movl (%esp),%ecx\n\t"
554 "xorl $0x00200000,(%esp)\n\t"
555 "popfl\n\t"
556 "pushfl\n\t"
557 "popl %eax\n\t"
558 "popfl\n\t"
559 "xorl %ecx,%eax\n\t"
560 "andl $0x00200000,%eax\n\t"
561 "ret" )
562 #else
563 static void do_x86cpuid(unsigned int ax, unsigned int *p)
567 static int have_x86cpuid(void)
569 return 0;
571 #endif
573 /******************************************************************
574 * dump_system_info
576 * Dumps into File the information about the system
578 static unsigned dump_system_info(struct dump_context* dc)
580 MINIDUMP_SYSTEM_INFO mdSysInfo;
581 SYSTEM_INFO sysInfo;
582 RTL_OSVERSIONINFOEXW osInfo;
583 DWORD written;
584 ULONG slen;
585 DWORD wine_extra = 0;
587 const char *(CDECL *wine_get_build_id)(void);
588 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
589 const char* build_id = NULL;
590 const char* sys_name = NULL;
591 const char* release_name = NULL;
593 GetSystemInfo(&sysInfo);
594 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
595 RtlGetVersion(&osInfo);
597 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
598 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
599 if (wine_get_build_id && wine_get_host_version)
601 /* cheat minidump system information by adding specific wine information */
602 wine_extra = 4 + 4 * sizeof(slen);
603 build_id = wine_get_build_id();
604 wine_get_host_version(&sys_name, &release_name);
605 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
608 mdSysInfo.ProcessorArchitecture = sysInfo.wProcessorArchitecture;
609 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
610 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
611 mdSysInfo.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
612 mdSysInfo.ProductType = VER_NT_WORKSTATION; /* FIXME */
613 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
614 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
615 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
616 mdSysInfo.PlatformId = osInfo.dwPlatformId;
618 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
619 mdSysInfo.Reserved1 = 0;
620 mdSysInfo.SuiteMask = VER_SUITE_TERMINAL;
622 if (have_x86cpuid())
624 unsigned regs0[4], regs1[4];
626 do_x86cpuid(0, regs0);
627 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
628 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
629 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
630 do_x86cpuid(1, regs1);
631 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
632 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
633 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
634 if (regs0[1] == 0x68747541 /* "Auth" */ &&
635 regs0[3] == 0x69746e65 /* "enti" */ &&
636 regs0[2] == 0x444d4163 /* "cAMD" */)
638 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
639 if (regs1[0] >= 0x80000001)
641 do_x86cpuid(0x80000001, regs1); /* get vendor features */
642 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
646 else
648 unsigned i;
649 ULONG64 one = 1;
651 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
652 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
654 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
655 if (IsProcessorFeaturePresent(i))
656 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
658 append(dc, &mdSysInfo, sizeof(mdSysInfo));
660 /* write Wine specific system information just behind the structure, and before any string */
661 if (wine_extra)
663 static const char code[] = {'W','I','N','E'};
665 WriteFile(dc->hFile, code, 4, &written, NULL);
666 /* number of sub-info, so that we can extend structure if needed */
667 slen = 3;
668 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
669 /* we store offsets from just after the WINE marker */
670 slen = 4 * sizeof(DWORD);
671 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
672 slen += strlen(build_id) + 1;
673 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
674 slen += strlen(sys_name) + 1;
675 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
676 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
677 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
678 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
679 dc->rva += wine_extra;
682 /* write the service pack version string after this stream. It is referenced within the
683 stream by its RVA in the file. */
684 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
685 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
686 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
687 dc->rva += sizeof(ULONG) + slen;
689 return sizeof(mdSysInfo);
692 /******************************************************************
693 * dump_threads
695 * Dumps into File the information about running threads
697 static unsigned dump_threads(struct dump_context* dc)
699 MINIDUMP_THREAD mdThd;
700 MINIDUMP_THREAD_LIST mdThdList;
701 unsigned i, sz;
702 RVA rva_base;
703 DWORD flags_out;
704 CONTEXT ctx;
706 mdThdList.NumberOfThreads = 0;
708 rva_base = dc->rva;
709 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
711 for (i = 0; i < dc->num_threads; i++)
713 fetch_thread_info(dc, i, &mdThd, &ctx);
715 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
716 ThreadWriteInstructionWindow;
717 if (dc->type & MiniDumpWithProcessThreadData)
718 flags_out |= ThreadWriteThreadData;
719 if (dc->type & MiniDumpWithThreadInfo)
720 flags_out |= ThreadWriteThreadInfo;
722 if (dc->cb)
724 MINIDUMP_CALLBACK_INPUT cbin;
725 MINIDUMP_CALLBACK_OUTPUT cbout;
727 cbin.ProcessId = dc->pid;
728 cbin.ProcessHandle = dc->process->handle;
729 cbin.CallbackType = ThreadCallback;
730 cbin.Thread.ThreadId = dc->threads[i].tid;
731 cbin.Thread.ThreadHandle = 0; /* FIXME */
732 cbin.Thread.Context = ctx;
733 cbin.Thread.SizeOfContext = sizeof(CONTEXT);
734 cbin.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
735 cbin.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
736 mdThd.Stack.Memory.DataSize;
738 cbout.ThreadWriteFlags = flags_out;
739 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
740 continue;
741 flags_out &= cbout.ThreadWriteFlags;
743 if (flags_out & ThreadWriteThread)
745 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
747 mdThd.ThreadContext.Rva = dc->rva;
748 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
749 append(dc, &ctx, sizeof(CONTEXT));
751 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
753 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
754 mdThd.Stack.Memory.DataSize,
755 rva_base + sizeof(mdThdList.NumberOfThreads) +
756 mdThdList.NumberOfThreads * sizeof(mdThd) +
757 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
759 writeat(dc,
760 rva_base + sizeof(mdThdList.NumberOfThreads) +
761 mdThdList.NumberOfThreads * sizeof(mdThd),
762 &mdThd, sizeof(mdThd));
763 mdThdList.NumberOfThreads++;
765 /* fetch CPU dependent thread info (like 256 bytes around program counter */
766 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
768 writeat(dc, rva_base,
769 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
771 return sz;
774 /******************************************************************
775 * dump_threads_names
777 * Dumps into File the information about threads's name
779 static unsigned dump_threads_names(struct dump_context* dc)
781 MINIDUMP_THREAD_NAME md_thread_name;
782 MINIDUMP_THREAD_NAME_LIST md_thread_name_list;
783 unsigned i, sz;
784 RVA rva_base;
786 /* FIXME this could be optimized
787 * (we use dc->num_threads disk space, could be optimized to the number of threads with name)
790 rva_base = dc->rva;
791 dc->rva += sz = offsetof(MINIDUMP_THREAD_NAME_LIST, ThreadNames[dc->num_threads]);
793 md_thread_name_list.NumberOfThreadNames = 0;
795 for (i = 0; i < dc->num_threads; i++)
797 HANDLE thread;
798 WCHAR *thread_name;
800 if ((thread = OpenThread(THREAD_ALL_ACCESS, FALSE, dc->threads[i].tid)) != NULL)
802 if (GetThreadDescription(thread, &thread_name))
804 MINIDUMP_STRING md_string;
806 md_thread_name.ThreadId = dc->threads[i].tid;
807 md_thread_name.RvaOfThreadName = dc->rva;
809 md_string.Length = wcslen(thread_name) * sizeof(WCHAR);
810 append(dc, &md_string.Length, sizeof(md_string.Length));
811 append(dc, thread_name, md_string.Length);
813 writeat(dc,
814 rva_base + offsetof(MINIDUMP_THREAD_NAME_LIST, ThreadNames[md_thread_name_list.NumberOfThreadNames]),
815 &md_thread_name, sizeof(md_thread_name));
816 md_thread_name_list.NumberOfThreadNames++;
817 LocalFree(thread_name);
819 CloseHandle(thread);
822 if (!md_thread_name_list.NumberOfThreadNames) return 0;
824 writeat(dc, rva_base, &md_thread_name_list.NumberOfThreadNames, sizeof(md_thread_name_list.NumberOfThreadNames));
825 return sz;
828 /******************************************************************
829 * dump_memory_info
831 * dumps information about the memory of the process (stack of the threads)
833 static unsigned dump_memory_info(struct dump_context* dc)
835 MINIDUMP_MEMORY_LIST mdMemList;
836 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
837 DWORD written;
838 unsigned i, pos, len, sz;
839 RVA rva_base;
840 char tmp[1024];
842 mdMemList.NumberOfMemoryRanges = dc->num_mem;
843 append(dc, &mdMemList.NumberOfMemoryRanges,
844 sizeof(mdMemList.NumberOfMemoryRanges));
845 rva_base = dc->rva;
846 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
847 dc->rva += sz;
848 sz += sizeof(mdMemList.NumberOfMemoryRanges);
850 for (i = 0; i < dc->num_mem; i++)
852 mdMem.StartOfMemoryRange = dc->mem[i].base;
853 mdMem.Memory.Rva = dc->rva;
854 mdMem.Memory.DataSize = dc->mem[i].size;
855 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
856 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
858 len = min(dc->mem[i].size - pos, sizeof(tmp));
859 if (read_process_memory(dc->process, dc->mem[i].base + pos, tmp, len))
860 WriteFile(dc->hFile, tmp, len, &written, NULL);
862 dc->rva += mdMem.Memory.DataSize;
863 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
864 if (dc->mem[i].rva)
866 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
870 return sz;
873 /******************************************************************
874 * dump_memory64_info
876 * dumps information about the memory of the process (virtual memory)
878 static unsigned dump_memory64_info(struct dump_context* dc)
880 MINIDUMP_MEMORY64_LIST mdMem64List;
881 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64;
882 DWORD written;
883 unsigned i, len, sz;
884 RVA rva_base;
885 char tmp[1024];
886 ULONG64 pos;
887 LARGE_INTEGER filepos;
889 sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
890 sizeof(mdMem64List.BaseRva) +
891 dc->num_mem64 * sizeof(mdMem64);
893 mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
894 mdMem64List.BaseRva = dc->rva + sz;
896 append(dc, &mdMem64List.NumberOfMemoryRanges,
897 sizeof(mdMem64List.NumberOfMemoryRanges));
898 append(dc, &mdMem64List.BaseRva,
899 sizeof(mdMem64List.BaseRva));
901 rva_base = dc->rva;
902 dc->rva += dc->num_mem64 * sizeof(mdMem64);
904 /* dc->rva is not updated past this point. The end of the dump
905 * is just the full memory data. */
906 filepos.QuadPart = dc->rva;
907 for (i = 0; i < dc->num_mem64; i++)
909 mdMem64.StartOfMemoryRange = dc->mem64[i].base;
910 mdMem64.DataSize = dc->mem64[i].size;
911 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
912 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
914 len = min(dc->mem64[i].size - pos, sizeof(tmp));
915 if (read_process_memory(dc->process, dc->mem64[i].base + pos, tmp, len))
916 WriteFile(dc->hFile, tmp, len, &written, NULL);
918 filepos.QuadPart += mdMem64.DataSize;
919 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
922 return sz;
925 static unsigned dump_misc_info(struct dump_context* dc)
927 MINIDUMP_MISC_INFO mmi;
929 mmi.SizeOfInfo = sizeof(mmi);
930 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
931 mmi.ProcessId = dc->pid;
932 /* FIXME: create/user/kernel time */
933 mmi.ProcessCreateTime = 0;
934 mmi.ProcessKernelTime = 0;
935 mmi.ProcessUserTime = 0;
937 append(dc, &mmi, sizeof(mmi));
938 return sizeof(mmi);
941 static DWORD CALLBACK write_minidump(void *_args)
943 struct dump_context *dc = _args;
944 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
945 MINIDUMP_HEADER mdHead;
946 MINIDUMP_DIRECTORY mdDir;
947 DWORD i, nStreams, idx_stream;
949 if (!fetch_process_info(dc)) return FALSE;
950 fetch_modules_info(dc);
952 /* 1) init */
953 nStreams = 7 + (dc->except_param ? 1 : 0) +
954 (dc->user_stream ? dc->user_stream->UserStreamCount : 0);
956 /* pad the directory size to a multiple of 4 for alignment purposes */
957 nStreams = (nStreams + 3) & ~3;
959 /* 2) write header */
960 mdHead.Signature = MINIDUMP_SIGNATURE;
961 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
962 mdHead.NumberOfStreams = nStreams;
963 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
964 mdHead.StreamDirectoryRva = sizeof(mdHead);
965 mdHead.TimeDateStamp = time(NULL);
966 mdHead.Flags = dc->type;
967 append(dc, &mdHead, sizeof(mdHead));
969 /* 3) write stream directories */
970 dc->rva += nStreams * sizeof(mdDir);
971 idx_stream = 0;
973 /* 3.1) write data stream directories */
975 /* must be first in minidump */
976 mdDir.StreamType = SystemInfoStream;
977 mdDir.Location.Rva = dc->rva;
978 mdDir.Location.DataSize = dump_system_info(dc);
979 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
980 &mdDir, sizeof(mdDir));
982 mdDir.StreamType = ThreadListStream;
983 mdDir.Location.Rva = dc->rva;
984 mdDir.Location.DataSize = dump_threads(dc);
985 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
986 &mdDir, sizeof(mdDir));
988 mdDir.StreamType = ThreadNamesStream;
989 mdDir.Location.Rva = dc->rva;
990 if ((mdDir.Location.DataSize = dump_threads_names(dc)))
991 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
992 &mdDir, sizeof(mdDir));
994 mdDir.StreamType = ModuleListStream;
995 mdDir.Location.Rva = dc->rva;
996 mdDir.Location.DataSize = dump_modules(dc, FALSE);
997 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
998 &mdDir, sizeof(mdDir));
1000 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1001 mdDir.Location.Rva = dc->rva;
1002 mdDir.Location.DataSize = dump_modules(dc, TRUE);
1003 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1004 &mdDir, sizeof(mdDir));
1007 if (!(dc->type & MiniDumpWithFullMemory))
1009 mdDir.StreamType = MemoryListStream;
1010 mdDir.Location.Rva = dc->rva;
1011 mdDir.Location.DataSize = dump_memory_info(dc);
1012 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1013 &mdDir, sizeof(mdDir));
1016 mdDir.StreamType = MiscInfoStream;
1017 mdDir.Location.Rva = dc->rva;
1018 mdDir.Location.DataSize = dump_misc_info(dc);
1019 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1020 &mdDir, sizeof(mdDir));
1022 /* 3.2) write exception information (if any) */
1023 if (dc->except_param)
1025 mdDir.StreamType = ExceptionStream;
1026 mdDir.Location.Rva = dc->rva;
1027 mdDir.Location.DataSize = dump_exception_info(dc);
1028 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1029 &mdDir, sizeof(mdDir));
1032 /* 3.3) write user defined streams (if any) */
1033 if (dc->user_stream)
1035 for (i = 0; i < dc->user_stream->UserStreamCount; i++)
1037 mdDir.StreamType = dc->user_stream->UserStreamArray[i].Type;
1038 mdDir.Location.DataSize = dc->user_stream->UserStreamArray[i].BufferSize;
1039 mdDir.Location.Rva = dc->rva;
1040 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1041 &mdDir, sizeof(mdDir));
1042 append(dc, dc->user_stream->UserStreamArray[i].Buffer,
1043 dc->user_stream->UserStreamArray[i].BufferSize);
1047 /* 3.4) write full memory (if requested) */
1048 if (dc->type & MiniDumpWithFullMemory)
1050 fetch_memory64_info(dc);
1052 mdDir.StreamType = Memory64ListStream;
1053 mdDir.Location.Rva = dc->rva;
1054 mdDir.Location.DataSize = dump_memory64_info(dc);
1055 writeat(dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1056 &mdDir, sizeof(mdDir));
1059 /* fill the remaining directory entries with 0's (unused stream types) */
1060 /* NOTE: this should always come last in the dump! */
1061 for (i = idx_stream; i < nStreams; i++)
1062 writeat(dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1064 return TRUE;
1067 /******************************************************************
1068 * MiniDumpWriteDump (DEBUGHLP.@)
1071 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
1072 MINIDUMP_TYPE DumpType,
1073 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
1074 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
1075 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
1077 struct dump_context dc;
1078 BOOL sym_initialized = FALSE;
1079 BOOL ret = FALSE;
1081 if (!(dc.process = process_find_by_handle(hProcess)))
1083 if (!(sym_initialized = SymInitializeW(hProcess, NULL, TRUE)))
1085 WARN("failed to initialize process\n");
1086 return FALSE;
1088 dc.process = process_find_by_handle(hProcess);
1091 if (DumpType & MiniDumpWithDataSegs)
1092 FIXME("NIY MiniDumpWithDataSegs\n");
1093 if (DumpType & MiniDumpWithHandleData)
1094 FIXME("NIY MiniDumpWithHandleData\n");
1095 if (DumpType & MiniDumpFilterMemory)
1096 FIXME("NIY MiniDumpFilterMemory\n");
1097 if (DumpType & MiniDumpScanMemory)
1098 FIXME("NIY MiniDumpScanMemory\n");
1100 dc.hFile = hFile;
1101 dc.pid = pid;
1102 dc.modules = NULL;
1103 dc.num_modules = 0;
1104 dc.alloc_modules = 0;
1105 dc.threads = NULL;
1106 dc.num_threads = 0;
1107 dc.cb = CallbackParam;
1108 dc.type = DumpType;
1109 dc.mem = NULL;
1110 dc.num_mem = 0;
1111 dc.alloc_mem = 0;
1112 dc.mem64 = NULL;
1113 dc.num_mem64 = 0;
1114 dc.alloc_mem64 = 0;
1115 dc.rva = 0;
1116 dc.except_param = ExceptionParam;
1117 dc.user_stream = UserStreamParam;
1119 /* have a dedicated thread for fetching info on self */
1120 if (dc.pid != GetCurrentProcessId())
1121 ret = write_minidump(&dc);
1122 else
1124 DWORD exit_code;
1125 HANDLE h = CreateThread(NULL, 0, write_minidump, &dc, 0, NULL);
1126 if (h)
1128 if (WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0 && GetExitCodeThread(h, &exit_code))
1129 ret = exit_code;
1130 else
1131 TerminateThread(h, 0);
1132 CloseHandle(h);
1136 if (sym_initialized)
1137 SymCleanup(hProcess);
1139 HeapFree(GetProcessHeap(), 0, dc.mem);
1140 HeapFree(GetProcessHeap(), 0, dc.mem64);
1141 HeapFree(GetProcessHeap(), 0, dc.modules);
1142 HeapFree(GetProcessHeap(), 0, dc.threads);
1144 return ret;
1147 /******************************************************************
1148 * MiniDumpReadDumpStream (DEBUGHLP.@)
1152 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1153 PMINIDUMP_DIRECTORY* pdir,
1154 PVOID* stream, ULONG* size)
1156 MINIDUMP_HEADER* mdHead = base;
1158 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1160 MINIDUMP_DIRECTORY* dir;
1161 DWORD i;
1163 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1164 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1166 if (dir->StreamType == str_idx)
1168 if (pdir) *pdir = dir;
1169 if (stream) *stream = (char*)base + dir->Location.Rva;
1170 if (size) *size = dir->Location.DataSize;
1171 return TRUE;
1175 SetLastError(ERROR_INVALID_PARAMETER);
1176 return FALSE;