dinput: Clear DIA_APPNOMAP BuildActionMap flag with specific device semantic.
[wine.git] / dlls / dbghelp / minidump.c
blob11ce767aa71db8fb6a8ff6e6d9ce3a5381cc8332
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/asm.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
36 /******************************************************************
37 * fetch_process_info
39 * reads system wide process information, and gather from it the threads information
40 * for process of id 'pid'
42 static BOOL fetch_process_info(struct dump_context* dc)
44 ULONG buf_size = 0x1000;
45 NTSTATUS nts;
46 void* pcs_buffer = NULL;
48 if (!(pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
49 for (;;)
51 nts = NtQuerySystemInformation(SystemProcessInformation,
52 pcs_buffer, buf_size, NULL);
53 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
54 pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, pcs_buffer, buf_size *= 2);
55 if (!pcs_buffer) return FALSE;
58 if (nts == STATUS_SUCCESS)
60 SYSTEM_PROCESS_INFORMATION* spi = pcs_buffer;
61 unsigned i;
63 for (;;)
65 if (HandleToUlong(spi->UniqueProcessId) == dc->pid)
67 dc->num_threads = spi->dwThreadCount;
68 dc->threads = HeapAlloc(GetProcessHeap(), 0,
69 dc->num_threads * sizeof(dc->threads[0]));
70 if (!dc->threads) goto failed;
71 for (i = 0; i < dc->num_threads; i++)
73 dc->threads[i].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
74 dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */
75 dc->threads[i].curr_prio = spi->ti[i].dwCurrentPriority;
77 HeapFree(GetProcessHeap(), 0, pcs_buffer);
78 return TRUE;
80 if (!spi->NextEntryOffset) break;
81 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset);
84 failed:
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 const MINIDUMP_EXCEPTION_INFORMATION* except,
123 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
125 DWORD tid = dc->threads[thd_idx].tid;
126 HANDLE hThread;
127 THREAD_BASIC_INFORMATION tbi;
129 memset(ctx, 0, sizeof(*ctx));
131 mdThd->ThreadId = tid;
132 mdThd->SuspendCount = 0;
133 mdThd->Teb = 0;
134 mdThd->Stack.StartOfMemoryRange = 0;
135 mdThd->Stack.Memory.DataSize = 0;
136 mdThd->Stack.Memory.Rva = 0;
137 mdThd->ThreadContext.DataSize = 0;
138 mdThd->ThreadContext.Rva = 0;
139 mdThd->PriorityClass = dc->threads[thd_idx].prio_class;
140 mdThd->Priority = dc->threads[thd_idx].curr_prio;
142 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
144 FIXME("Couldn't open thread %lu (%lu)\n", tid, GetLastError());
145 return FALSE;
148 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
149 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
151 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
152 if (tbi.ExitStatus == STILL_ACTIVE)
154 if (tid != GetCurrentThreadId() &&
155 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
157 ctx->ContextFlags = CONTEXT_FULL;
158 if (!GetThreadContext(hThread, ctx))
159 memset(ctx, 0, sizeof(*ctx));
161 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
162 ResumeThread(hThread);
164 else if (tid == GetCurrentThreadId() && except)
166 CONTEXT lctx, *pctx;
167 mdThd->SuspendCount = 1;
168 if (except->ClientPointers)
170 EXCEPTION_POINTERS ep;
172 ReadProcessMemory(dc->process->handle, except->ExceptionPointers,
173 &ep, sizeof(ep), NULL);
174 ReadProcessMemory(dc->process->handle, ep.ContextRecord,
175 &lctx, sizeof(lctx), NULL);
176 pctx = &lctx;
178 else pctx = except->ExceptionPointers->ContextRecord;
180 *ctx = *pctx;
181 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
183 else mdThd->SuspendCount = 0;
186 CloseHandle(hThread);
187 return TRUE;
190 /******************************************************************
191 * add_module
193 * Add a module to a dump context
195 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
196 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
197 BOOL is_elf)
199 if (!dc->modules)
201 dc->alloc_modules = 32;
202 dc->modules = HeapAlloc(GetProcessHeap(), 0,
203 dc->alloc_modules * sizeof(*dc->modules));
205 else if(dc->num_modules >= dc->alloc_modules)
207 dc->alloc_modules *= 2;
208 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
209 dc->alloc_modules * sizeof(*dc->modules));
211 if (!dc->modules)
213 dc->alloc_modules = dc->num_modules = 0;
214 return FALSE;
216 lstrcpynW(dc->modules[dc->num_modules].name, name,
217 ARRAY_SIZE(dc->modules[dc->num_modules].name));
218 dc->modules[dc->num_modules].base = base;
219 dc->modules[dc->num_modules].size = size;
220 dc->modules[dc->num_modules].timestamp = timestamp;
221 dc->modules[dc->num_modules].checksum = checksum;
222 dc->modules[dc->num_modules].is_elf = is_elf;
223 dc->num_modules++;
225 return TRUE;
228 /******************************************************************
229 * fetch_pe_module_info_cb
231 * Callback for accumulating in dump_context a PE modules set
233 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
234 PVOID user)
236 struct dump_context* dc = user;
237 IMAGE_NT_HEADERS nth;
239 if (!validate_addr64(base)) return FALSE;
241 if (pe_load_nt_header(dc->process->handle, base, &nth))
242 add_module(user, name, base, size,
243 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
244 FALSE);
245 return TRUE;
248 /******************************************************************
249 * fetch_elf_module_info_cb
251 * Callback for accumulating in dump_context an host modules set
253 static BOOL fetch_host_module_info_cb(const WCHAR* name, ULONG_PTR base,
254 void* user)
256 struct dump_context* dc = user;
257 DWORD_PTR rbase;
258 DWORD size, checksum;
260 /* FIXME: there's no relevant timestamp on ELF modules */
261 if (!dc->process->loader->fetch_file_info(dc->process, name, base, &rbase, &size, &checksum))
262 size = checksum = 0;
263 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
264 return TRUE;
267 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
269 if (!dc->mem64)
271 dc->alloc_mem64 = 32;
272 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
274 else if (dc->num_mem64 >= dc->alloc_mem64)
276 dc->alloc_mem64 *= 2;
277 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
278 dc->alloc_mem64 * sizeof(*dc->mem64));
280 if (dc->mem64)
282 dc->mem64[dc->num_mem64].base = base;
283 dc->mem64[dc->num_mem64].size = size;
284 dc->num_mem64++;
286 else dc->num_mem64 = dc->alloc_mem64 = 0;
289 static void fetch_memory64_info(struct dump_context* dc)
291 ULONG_PTR addr;
292 MEMORY_BASIC_INFORMATION mbi;
294 addr = 0;
295 while (VirtualQueryEx(dc->process->handle, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
297 /* Memory regions with state MEM_COMMIT will be added to the dump */
298 if (mbi.State == MEM_COMMIT)
300 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
303 if ((addr + mbi.RegionSize) < addr)
304 break;
306 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
310 static void fetch_modules_info(struct dump_context* dc)
312 EnumerateLoadedModulesW64(dc->process->handle, fetch_pe_module_info_cb, dc);
313 /* Since we include ELF modules in a separate stream from the regular PE ones,
314 * we can always include those ELF modules (they don't eat lots of space)
315 * And it's always a good idea to have a trace of the loaded ELF modules for
316 * a given application in a post mortem debugging condition.
318 dc->process->loader->enum_modules(dc->process, fetch_host_module_info_cb, dc);
321 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
323 DWORD handle;
324 DWORD sz;
326 memset(ffi, 0, sizeof(*ffi));
327 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
329 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
330 if (info && GetFileVersionInfoW(filename, handle, sz, info))
332 VS_FIXEDFILEINFO* ptr;
333 UINT len;
335 if (VerQueryValueW(info, L"\\", (void*)&ptr, &len))
336 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
338 HeapFree(GetProcessHeap(), 0, info);
342 /******************************************************************
343 * minidump_add_memory_block
345 * Add a memory block to be dumped in a minidump
346 * If rva is non 0, it's the rva in the minidump where has to be stored
347 * also the rva of the memory block when written (this allows us to reference
348 * a memory block from outside the list of memory blocks).
350 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
352 if (!dc->mem)
354 dc->alloc_mem = 32;
355 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
357 else if (dc->num_mem >= dc->alloc_mem)
359 dc->alloc_mem *= 2;
360 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
361 dc->alloc_mem * sizeof(*dc->mem));
363 if (dc->mem)
365 dc->mem[dc->num_mem].base = base;
366 dc->mem[dc->num_mem].size = size;
367 dc->mem[dc->num_mem].rva = rva;
368 dc->num_mem++;
370 else dc->num_mem = dc->alloc_mem = 0;
373 /******************************************************************
374 * writeat
376 * Writes a chunk of data at a given position in the minidump
378 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
380 DWORD written;
382 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
383 WriteFile(dc->hFile, data, size, &written, NULL);
386 /******************************************************************
387 * append
389 * writes a new chunk of data to the minidump, increasing the current
390 * rva in dc
392 static void append(struct dump_context* dc, const void* data, unsigned size)
394 writeat(dc, dc->rva, data, size);
395 dc->rva += size;
398 /******************************************************************
399 * dump_exception_info
401 * Write in File the exception information from pcs
403 static unsigned dump_exception_info(struct dump_context* dc,
404 const MINIDUMP_EXCEPTION_INFORMATION* except)
406 MINIDUMP_EXCEPTION_STREAM mdExcpt;
407 EXCEPTION_RECORD rec, *prec;
408 CONTEXT ctx, *pctx;
409 DWORD i;
411 mdExcpt.ThreadId = except->ThreadId;
412 mdExcpt.__alignment = 0;
413 if (except->ClientPointers)
415 EXCEPTION_POINTERS ep;
417 ReadProcessMemory(dc->process->handle,
418 except->ExceptionPointers, &ep, sizeof(ep), NULL);
419 ReadProcessMemory(dc->process->handle,
420 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
421 ReadProcessMemory(dc->process->handle,
422 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
423 prec = &rec;
424 pctx = &ctx;
426 else
428 prec = except->ExceptionPointers->ExceptionRecord;
429 pctx = except->ExceptionPointers->ContextRecord;
431 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
432 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
433 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
434 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
435 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
436 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
437 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
438 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
439 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
440 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
442 append(dc, &mdExcpt, sizeof(mdExcpt));
443 append(dc, pctx, sizeof(*pctx));
444 return sizeof(mdExcpt);
447 /******************************************************************
448 * dump_modules
450 * Write in File the modules from pcs
452 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
454 MINIDUMP_MODULE mdModule;
455 MINIDUMP_MODULE_LIST mdModuleList;
456 char tmp[1024];
457 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
458 ULONG i, nmod;
459 RVA rva_base;
460 DWORD flags_out;
461 unsigned sz;
463 for (i = nmod = 0; i < dc->num_modules; i++)
465 if ((dc->modules[i].is_elf && dump_elf) ||
466 (!dc->modules[i].is_elf && !dump_elf))
467 nmod++;
470 mdModuleList.NumberOfModules = 0;
471 /* reserve space for mdModuleList
472 * FIXME: since we don't support 0 length arrays, we cannot use the
473 * size of mdModuleList
474 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
477 /* the stream size is just the size of the module index. It does not include the data for the
478 names of each module. *Technically* the names are supposed to go into the common string table
479 in the minidump file. Since each string is referenced by RVA they can all safely be located
480 anywhere between streams in the file, so the end of this stream is sufficient. */
481 rva_base = dc->rva;
482 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
483 for (i = 0; i < dc->num_modules; i++)
485 if ((dc->modules[i].is_elf && !dump_elf) ||
486 (!dc->modules[i].is_elf && dump_elf))
487 continue;
489 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
490 if (dc->type & MiniDumpWithDataSegs)
491 flags_out |= ModuleWriteDataSeg;
492 if (dc->type & MiniDumpWithProcessThreadData)
493 flags_out |= ModuleWriteTlsData;
494 if (dc->type & MiniDumpWithCodeSegs)
495 flags_out |= ModuleWriteCodeSegs;
496 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
497 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
498 FIXME("Buffer overflow!!!\n");
499 lstrcpyW(ms->Buffer, dc->modules[i].name);
501 if (dc->cb)
503 MINIDUMP_CALLBACK_INPUT cbin;
504 MINIDUMP_CALLBACK_OUTPUT cbout;
506 cbin.ProcessId = dc->pid;
507 cbin.ProcessHandle = dc->process->handle;
508 cbin.CallbackType = ModuleCallback;
510 cbin.u.Module.FullPath = ms->Buffer;
511 cbin.u.Module.BaseOfImage = dc->modules[i].base;
512 cbin.u.Module.SizeOfImage = dc->modules[i].size;
513 cbin.u.Module.CheckSum = dc->modules[i].checksum;
514 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
515 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
516 cbin.u.Module.CvRecord = NULL;
517 cbin.u.Module.SizeOfCvRecord = 0;
518 cbin.u.Module.MiscRecord = NULL;
519 cbin.u.Module.SizeOfMiscRecord = 0;
521 cbout.u.ModuleWriteFlags = flags_out;
522 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
523 continue;
524 flags_out &= cbout.u.ModuleWriteFlags;
526 if (flags_out & ModuleWriteModule)
528 /* fetch CPU dependent module info (like UNWIND_INFO) */
529 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
531 mdModule.BaseOfImage = dc->modules[i].base;
532 mdModule.SizeOfImage = dc->modules[i].size;
533 mdModule.CheckSum = dc->modules[i].checksum;
534 mdModule.TimeDateStamp = dc->modules[i].timestamp;
535 mdModule.ModuleNameRva = dc->rva;
536 ms->Length -= sizeof(WCHAR);
537 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
538 if (!dump_elf) fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
539 else memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo));
540 mdModule.CvRecord.DataSize = 0; /* FIXME */
541 mdModule.CvRecord.Rva = 0; /* FIXME */
542 mdModule.MiscRecord.DataSize = 0; /* FIXME */
543 mdModule.MiscRecord.Rva = 0; /* FIXME */
544 mdModule.Reserved0 = 0; /* FIXME */
545 mdModule.Reserved1 = 0; /* FIXME */
546 writeat(dc,
547 rva_base + sizeof(mdModuleList.NumberOfModules) +
548 mdModuleList.NumberOfModules++ * sizeof(mdModule),
549 &mdModule, sizeof(mdModule));
552 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
553 sizeof(mdModuleList.NumberOfModules));
555 return sz;
558 #ifdef __i386__
559 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
560 __ASM_GLOBAL_FUNC( do_x86cpuid,
561 "pushl %esi\n\t"
562 "pushl %ebx\n\t"
563 "movl 12(%esp),%eax\n\t"
564 "movl 16(%esp),%esi\n\t"
565 "cpuid\n\t"
566 "movl %eax,(%esi)\n\t"
567 "movl %ebx,4(%esi)\n\t"
568 "movl %ecx,8(%esi)\n\t"
569 "movl %edx,12(%esi)\n\t"
570 "popl %ebx\n\t"
571 "popl %esi\n\t"
572 "ret" )
573 extern int have_x86cpuid(void);
574 __ASM_GLOBAL_FUNC( have_x86cpuid,
575 "pushfl\n\t"
576 "pushfl\n\t"
577 "movl (%esp),%ecx\n\t"
578 "xorl $0x00200000,(%esp)\n\t"
579 "popfl\n\t"
580 "pushfl\n\t"
581 "popl %eax\n\t"
582 "popfl\n\t"
583 "xorl %ecx,%eax\n\t"
584 "andl $0x00200000,%eax\n\t"
585 "ret" )
586 #else
587 static void do_x86cpuid(unsigned int ax, unsigned int *p)
591 static int have_x86cpuid(void)
593 return 0;
595 #endif
597 /******************************************************************
598 * dump_system_info
600 * Dumps into File the information about the system
602 static unsigned dump_system_info(struct dump_context* dc)
604 MINIDUMP_SYSTEM_INFO mdSysInfo;
605 SYSTEM_INFO sysInfo;
606 RTL_OSVERSIONINFOEXW osInfo;
607 DWORD written;
608 ULONG slen;
609 DWORD wine_extra = 0;
611 const char *(CDECL *wine_get_build_id)(void);
612 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
613 const char* build_id = NULL;
614 const char* sys_name = NULL;
615 const char* release_name = NULL;
617 GetSystemInfo(&sysInfo);
618 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
619 RtlGetVersion(&osInfo);
621 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
622 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
623 if (wine_get_build_id && wine_get_host_version)
625 /* cheat minidump system information by adding specific wine information */
626 wine_extra = 4 + 4 * sizeof(slen);
627 build_id = wine_get_build_id();
628 wine_get_host_version(&sys_name, &release_name);
629 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
632 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
633 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
634 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
635 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
636 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
637 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
638 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
639 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
640 mdSysInfo.PlatformId = osInfo.dwPlatformId;
642 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
643 mdSysInfo.u1.Reserved1 = 0;
644 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
646 if (have_x86cpuid())
648 unsigned regs0[4], regs1[4];
650 do_x86cpuid(0, regs0);
651 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
652 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
653 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
654 do_x86cpuid(1, regs1);
655 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
656 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
657 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
658 if (regs0[1] == 0x68747541 /* "Auth" */ &&
659 regs0[3] == 0x69746e65 /* "enti" */ &&
660 regs0[2] == 0x444d4163 /* "cAMD" */)
662 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
663 if (regs1[0] >= 0x80000001)
665 do_x86cpuid(0x80000001, regs1); /* get vendor features */
666 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
670 else
672 unsigned i;
673 ULONG64 one = 1;
675 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
676 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
678 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
679 if (IsProcessorFeaturePresent(i))
680 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
682 append(dc, &mdSysInfo, sizeof(mdSysInfo));
684 /* write Wine specific system information just behind the structure, and before any string */
685 if (wine_extra)
687 static const char code[] = {'W','I','N','E'};
689 WriteFile(dc->hFile, code, 4, &written, NULL);
690 /* number of sub-info, so that we can extend structure if needed */
691 slen = 3;
692 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
693 /* we store offsets from just after the WINE marker */
694 slen = 4 * sizeof(DWORD);
695 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
696 slen += strlen(build_id) + 1;
697 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
698 slen += strlen(sys_name) + 1;
699 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
700 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
701 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
702 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
703 dc->rva += wine_extra;
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, sz;
727 RVA rva_base;
728 DWORD flags_out;
729 CONTEXT ctx;
731 mdThdList.NumberOfThreads = 0;
733 rva_base = dc->rva;
734 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
736 for (i = 0; i < dc->num_threads; i++)
738 fetch_thread_info(dc, i, except, &mdThd, &ctx);
740 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
741 ThreadWriteInstructionWindow;
742 if (dc->type & MiniDumpWithProcessThreadData)
743 flags_out |= ThreadWriteThreadData;
744 if (dc->type & MiniDumpWithThreadInfo)
745 flags_out |= ThreadWriteThreadInfo;
747 if (dc->cb)
749 MINIDUMP_CALLBACK_INPUT cbin;
750 MINIDUMP_CALLBACK_OUTPUT cbout;
752 cbin.ProcessId = dc->pid;
753 cbin.ProcessHandle = dc->process->handle;
754 cbin.CallbackType = ThreadCallback;
755 cbin.u.Thread.ThreadId = dc->threads[i].tid;
756 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
757 cbin.u.Thread.Context = ctx;
758 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
759 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
760 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
761 mdThd.Stack.Memory.DataSize;
763 cbout.u.ThreadWriteFlags = flags_out;
764 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
765 continue;
766 flags_out &= cbout.u.ThreadWriteFlags;
768 if (flags_out & ThreadWriteThread)
770 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
772 mdThd.ThreadContext.Rva = dc->rva;
773 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
774 append(dc, &ctx, sizeof(CONTEXT));
776 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
778 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
779 mdThd.Stack.Memory.DataSize,
780 rva_base + sizeof(mdThdList.NumberOfThreads) +
781 mdThdList.NumberOfThreads * sizeof(mdThd) +
782 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
784 writeat(dc,
785 rva_base + sizeof(mdThdList.NumberOfThreads) +
786 mdThdList.NumberOfThreads * sizeof(mdThd),
787 &mdThd, sizeof(mdThd));
788 mdThdList.NumberOfThreads++;
790 /* fetch CPU dependent thread info (like 256 bytes around program counter */
791 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
793 writeat(dc, rva_base,
794 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
796 return sz;
799 /******************************************************************
800 * dump_memory_info
802 * dumps information about the memory of the process (stack of the threads)
804 static unsigned dump_memory_info(struct dump_context* dc)
806 MINIDUMP_MEMORY_LIST mdMemList;
807 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
808 DWORD written;
809 unsigned i, pos, len, sz;
810 RVA rva_base;
811 char tmp[1024];
813 mdMemList.NumberOfMemoryRanges = dc->num_mem;
814 append(dc, &mdMemList.NumberOfMemoryRanges,
815 sizeof(mdMemList.NumberOfMemoryRanges));
816 rva_base = dc->rva;
817 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
818 dc->rva += sz;
819 sz += sizeof(mdMemList.NumberOfMemoryRanges);
821 for (i = 0; i < dc->num_mem; i++)
823 mdMem.StartOfMemoryRange = dc->mem[i].base;
824 mdMem.Memory.Rva = dc->rva;
825 mdMem.Memory.DataSize = dc->mem[i].size;
826 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
827 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
829 len = min(dc->mem[i].size - pos, sizeof(tmp));
830 if (read_process_memory(dc->process, dc->mem[i].base + pos, tmp, len))
831 WriteFile(dc->hFile, tmp, len, &written, NULL);
833 dc->rva += mdMem.Memory.DataSize;
834 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
835 if (dc->mem[i].rva)
837 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
841 return sz;
844 /******************************************************************
845 * dump_memory64_info
847 * dumps information about the memory of the process (virtual memory)
849 static unsigned dump_memory64_info(struct dump_context* dc)
851 MINIDUMP_MEMORY64_LIST mdMem64List;
852 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64;
853 DWORD written;
854 unsigned i, len, sz;
855 RVA rva_base;
856 char tmp[1024];
857 ULONG64 pos;
858 LARGE_INTEGER filepos;
860 sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
861 sizeof(mdMem64List.BaseRva) +
862 dc->num_mem64 * sizeof(mdMem64);
864 mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
865 mdMem64List.BaseRva = dc->rva + sz;
867 append(dc, &mdMem64List.NumberOfMemoryRanges,
868 sizeof(mdMem64List.NumberOfMemoryRanges));
869 append(dc, &mdMem64List.BaseRva,
870 sizeof(mdMem64List.BaseRva));
872 rva_base = dc->rva;
873 dc->rva += dc->num_mem64 * sizeof(mdMem64);
875 /* dc->rva is not updated past this point. The end of the dump
876 * is just the full memory data. */
877 filepos.QuadPart = dc->rva;
878 for (i = 0; i < dc->num_mem64; i++)
880 mdMem64.StartOfMemoryRange = dc->mem64[i].base;
881 mdMem64.DataSize = dc->mem64[i].size;
882 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
883 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
885 len = min(dc->mem64[i].size - pos, sizeof(tmp));
886 if (read_process_memory(dc->process, dc->mem64[i].base + pos, tmp, len))
887 WriteFile(dc->hFile, tmp, len, &written, NULL);
889 filepos.QuadPart += mdMem64.DataSize;
890 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
893 return sz;
896 static unsigned dump_misc_info(struct dump_context* dc)
898 MINIDUMP_MISC_INFO mmi;
900 mmi.SizeOfInfo = sizeof(mmi);
901 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
902 mmi.ProcessId = dc->pid;
903 /* FIXME: create/user/kernel time */
904 mmi.ProcessCreateTime = 0;
905 mmi.ProcessKernelTime = 0;
906 mmi.ProcessUserTime = 0;
908 append(dc, &mmi, sizeof(mmi));
909 return sizeof(mmi);
912 /******************************************************************
913 * MiniDumpWriteDump (DEBUGHLP.@)
916 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
917 MINIDUMP_TYPE DumpType,
918 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
919 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
920 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
922 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
923 MINIDUMP_HEADER mdHead;
924 MINIDUMP_DIRECTORY mdDir;
925 DWORD i, nStreams, idx_stream;
926 struct dump_context dc;
927 BOOL sym_initialized = FALSE;
929 if (!(dc.process = process_find_by_handle(hProcess)))
931 if (!(sym_initialized = SymInitializeW(hProcess, NULL, TRUE)))
933 WARN("failed to initialize process\n");
934 return FALSE;
936 dc.process = process_find_by_handle(hProcess);
939 dc.hFile = hFile;
940 dc.pid = pid;
941 dc.modules = NULL;
942 dc.num_modules = 0;
943 dc.alloc_modules = 0;
944 dc.threads = NULL;
945 dc.num_threads = 0;
946 dc.cb = CallbackParam;
947 dc.type = DumpType;
948 dc.mem = NULL;
949 dc.num_mem = 0;
950 dc.alloc_mem = 0;
951 dc.mem64 = NULL;
952 dc.num_mem64 = 0;
953 dc.alloc_mem64 = 0;
954 dc.rva = 0;
956 if (!fetch_process_info(&dc)) return FALSE;
957 fetch_modules_info(&dc);
959 /* 1) init */
960 nStreams = 6 + (ExceptionParam ? 1 : 0) +
961 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
963 /* pad the directory size to a multiple of 4 for alignment purposes */
964 nStreams = (nStreams + 3) & ~3;
966 if (DumpType & MiniDumpWithDataSegs)
967 FIXME("NIY MiniDumpWithDataSegs\n");
968 if (DumpType & MiniDumpWithHandleData)
969 FIXME("NIY MiniDumpWithHandleData\n");
970 if (DumpType & MiniDumpFilterMemory)
971 FIXME("NIY MiniDumpFilterMemory\n");
972 if (DumpType & MiniDumpScanMemory)
973 FIXME("NIY MiniDumpScanMemory\n");
975 /* 2) write header */
976 mdHead.Signature = MINIDUMP_SIGNATURE;
977 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
978 mdHead.NumberOfStreams = nStreams;
979 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
980 mdHead.StreamDirectoryRva = sizeof(mdHead);
981 mdHead.u.TimeDateStamp = time(NULL);
982 mdHead.Flags = DumpType;
983 append(&dc, &mdHead, sizeof(mdHead));
985 /* 3) write stream directories */
986 dc.rva += nStreams * sizeof(mdDir);
987 idx_stream = 0;
989 /* 3.1) write data stream directories */
991 /* must be first in minidump */
992 mdDir.StreamType = SystemInfoStream;
993 mdDir.Location.Rva = dc.rva;
994 mdDir.Location.DataSize = dump_system_info(&dc);
995 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
996 &mdDir, sizeof(mdDir));
998 mdDir.StreamType = ThreadListStream;
999 mdDir.Location.Rva = dc.rva;
1000 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
1001 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1002 &mdDir, sizeof(mdDir));
1004 mdDir.StreamType = ModuleListStream;
1005 mdDir.Location.Rva = dc.rva;
1006 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
1007 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1008 &mdDir, sizeof(mdDir));
1010 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1011 mdDir.Location.Rva = dc.rva;
1012 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
1013 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1014 &mdDir, sizeof(mdDir));
1017 if (!(DumpType & MiniDumpWithFullMemory))
1019 mdDir.StreamType = MemoryListStream;
1020 mdDir.Location.Rva = dc.rva;
1021 mdDir.Location.DataSize = dump_memory_info(&dc);
1022 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1023 &mdDir, sizeof(mdDir));
1026 mdDir.StreamType = MiscInfoStream;
1027 mdDir.Location.Rva = dc.rva;
1028 mdDir.Location.DataSize = dump_misc_info(&dc);
1029 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1030 &mdDir, sizeof(mdDir));
1032 /* 3.2) write exception information (if any) */
1033 if (ExceptionParam)
1035 mdDir.StreamType = ExceptionStream;
1036 mdDir.Location.Rva = dc.rva;
1037 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
1038 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1039 &mdDir, sizeof(mdDir));
1042 /* 3.3) write user defined streams (if any) */
1043 if (UserStreamParam)
1045 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
1047 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
1048 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
1049 mdDir.Location.Rva = dc.rva;
1050 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1051 &mdDir, sizeof(mdDir));
1052 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
1053 UserStreamParam->UserStreamArray[i].BufferSize);
1057 /* 3.4) write full memory (if requested) */
1058 if (DumpType & MiniDumpWithFullMemory)
1060 fetch_memory64_info(&dc);
1062 mdDir.StreamType = Memory64ListStream;
1063 mdDir.Location.Rva = dc.rva;
1064 mdDir.Location.DataSize = dump_memory64_info(&dc);
1065 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1066 &mdDir, sizeof(mdDir));
1069 /* fill the remaining directory entries with 0's (unused stream types) */
1070 /* NOTE: this should always come last in the dump! */
1071 for (i = idx_stream; i < nStreams; i++)
1072 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1074 if (sym_initialized)
1075 SymCleanup(hProcess);
1077 HeapFree(GetProcessHeap(), 0, dc.mem);
1078 HeapFree(GetProcessHeap(), 0, dc.mem64);
1079 HeapFree(GetProcessHeap(), 0, dc.modules);
1080 HeapFree(GetProcessHeap(), 0, dc.threads);
1082 return TRUE;
1085 /******************************************************************
1086 * MiniDumpReadDumpStream (DEBUGHLP.@)
1090 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1091 PMINIDUMP_DIRECTORY* pdir,
1092 PVOID* stream, ULONG* size)
1094 MINIDUMP_HEADER* mdHead = base;
1096 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1098 MINIDUMP_DIRECTORY* dir;
1099 DWORD i;
1101 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1102 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1104 if (dir->StreamType == str_idx)
1106 if (pdir) *pdir = dir;
1107 if (stream) *stream = (char*)base + dir->Location.Rva;
1108 if (size) *size = dir->Location.DataSize;
1109 return TRUE;
1113 SetLastError(ERROR_INVALID_PARAMETER);
1114 return FALSE;