comctl32/tests: Avoid a structure initialization warning.
[wine.git] / dlls / dbghelp / minidump.c
blob04dc775ba768d0b39043309db7b63981c4cc4be2
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/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->hProcess, 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 %u (%u)\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->hProcess, except->ExceptionPointers,
173 &ep, sizeof(ep), NULL);
174 ReadProcessMemory(dc->hProcess, 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 if (is_elf ||
217 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base,
218 dc->modules[dc->num_modules].name,
219 ARRAY_SIZE(dc->modules[dc->num_modules].name)))
220 lstrcpynW(dc->modules[dc->num_modules].name, name,
221 ARRAY_SIZE(dc->modules[dc->num_modules].name));
222 dc->modules[dc->num_modules].base = base;
223 dc->modules[dc->num_modules].size = size;
224 dc->modules[dc->num_modules].timestamp = timestamp;
225 dc->modules[dc->num_modules].checksum = checksum;
226 dc->modules[dc->num_modules].is_elf = is_elf;
227 dc->num_modules++;
229 return TRUE;
232 /******************************************************************
233 * fetch_pe_module_info_cb
235 * Callback for accumulating in dump_context a PE modules set
237 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
238 PVOID user)
240 struct dump_context* dc = user;
241 IMAGE_NT_HEADERS nth;
243 if (!validate_addr64(base)) return FALSE;
245 if (pe_load_nt_header(dc->hProcess, base, &nth))
246 add_module(user, name, base, size,
247 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
248 FALSE);
249 return TRUE;
252 /******************************************************************
253 * fetch_elf_module_info_cb
255 * Callback for accumulating in dump_context an ELF modules set
257 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
258 void* user)
260 struct dump_context* dc = user;
261 DWORD_PTR rbase;
262 DWORD size, checksum;
264 /* FIXME: there's no relevant timestamp on ELF modules */
265 /* NB: if we have a non-null base from the live-target use it (whenever
266 * the ELF module is relocatable or not). If we have a null base (ELF
267 * module isn't relocatable) then grab its base address from ELF file
269 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
270 size = checksum = 0;
271 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
272 return TRUE;
275 /******************************************************************
276 * fetch_macho_module_info_cb
278 * Callback for accumulating in dump_context a Mach-O modules set
280 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
281 void* user)
283 struct dump_context* dc = (struct dump_context*)user;
284 DWORD_PTR rbase;
285 DWORD size, checksum;
287 /* FIXME: there's no relevant timestamp on Mach-O modules */
288 /* NB: if we have a non-null base from the live-target use it. If we have
289 * a null base, then grab its base address from Mach-O file.
291 if (!macho_fetch_file_info(dc->hProcess, name, base, &rbase, &size, &checksum))
292 size = checksum = 0;
293 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
294 return TRUE;
297 static void fetch_modules_info(struct dump_context* dc)
299 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
300 /* Since we include ELF modules in a separate stream from the regular PE ones,
301 * we can always include those ELF modules (they don't eat lots of space)
302 * And it's always a good idea to have a trace of the loaded ELF modules for
303 * a given application in a post mortem debugging condition.
305 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
306 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
309 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
311 DWORD handle;
312 DWORD sz;
313 static const WCHAR backslashW[] = {'\\', '\0'};
315 memset(ffi, 0, sizeof(*ffi));
316 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
318 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
319 if (info && GetFileVersionInfoW(filename, handle, sz, info))
321 VS_FIXEDFILEINFO* ptr;
322 UINT len;
324 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
325 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
327 HeapFree(GetProcessHeap(), 0, info);
331 /******************************************************************
332 * minidump_add_memory_block
334 * Add a memory block to be dumped in a minidump
335 * If rva is non 0, it's the rva in the minidump where has to be stored
336 * also the rva of the memory block when written (this allows us to reference
337 * a memory block from outside the list of memory blocks).
339 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
341 if (!dc->mem)
343 dc->alloc_mem = 32;
344 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
346 else if (dc->num_mem >= dc->alloc_mem)
348 dc->alloc_mem *= 2;
349 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
350 dc->alloc_mem * sizeof(*dc->mem));
352 if (dc->mem)
354 dc->mem[dc->num_mem].base = base;
355 dc->mem[dc->num_mem].size = size;
356 dc->mem[dc->num_mem].rva = rva;
357 dc->num_mem++;
359 else dc->num_mem = dc->alloc_mem = 0;
362 /******************************************************************
363 * writeat
365 * Writes a chunk of data at a given position in the minidump
367 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
369 DWORD written;
371 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
372 WriteFile(dc->hFile, data, size, &written, NULL);
375 /******************************************************************
376 * append
378 * writes a new chunk of data to the minidump, increasing the current
379 * rva in dc
381 static void append(struct dump_context* dc, const void* data, unsigned size)
383 writeat(dc, dc->rva, data, size);
384 dc->rva += size;
387 /******************************************************************
388 * dump_exception_info
390 * Write in File the exception information from pcs
392 static unsigned dump_exception_info(struct dump_context* dc,
393 const MINIDUMP_EXCEPTION_INFORMATION* except)
395 MINIDUMP_EXCEPTION_STREAM mdExcpt;
396 EXCEPTION_RECORD rec, *prec;
397 CONTEXT ctx, *pctx;
398 DWORD i;
400 mdExcpt.ThreadId = except->ThreadId;
401 mdExcpt.__alignment = 0;
402 if (except->ClientPointers)
404 EXCEPTION_POINTERS ep;
406 ReadProcessMemory(dc->hProcess,
407 except->ExceptionPointers, &ep, sizeof(ep), NULL);
408 ReadProcessMemory(dc->hProcess,
409 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
410 ReadProcessMemory(dc->hProcess,
411 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
412 prec = &rec;
413 pctx = &ctx;
415 else
417 prec = except->ExceptionPointers->ExceptionRecord;
418 pctx = except->ExceptionPointers->ContextRecord;
420 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
421 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
422 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
423 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
424 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
425 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
426 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
427 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
428 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
429 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
431 append(dc, &mdExcpt, sizeof(mdExcpt));
432 append(dc, pctx, sizeof(*pctx));
433 return sizeof(mdExcpt);
436 /******************************************************************
437 * dump_modules
439 * Write in File the modules from pcs
441 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
443 MINIDUMP_MODULE mdModule;
444 MINIDUMP_MODULE_LIST mdModuleList;
445 char tmp[1024];
446 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
447 ULONG i, nmod;
448 RVA rva_base;
449 DWORD flags_out;
450 unsigned sz;
452 for (i = nmod = 0; i < dc->num_modules; i++)
454 if ((dc->modules[i].is_elf && dump_elf) ||
455 (!dc->modules[i].is_elf && !dump_elf))
456 nmod++;
459 mdModuleList.NumberOfModules = 0;
460 /* reserve space for mdModuleList
461 * FIXME: since we don't support 0 length arrays, we cannot use the
462 * size of mdModuleList
463 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
466 /* the stream size is just the size of the module index. It does not include the data for the
467 names of each module. *Technically* the names are supposed to go into the common string table
468 in the minidump file. Since each string is referenced by RVA they can all safely be located
469 anywhere between streams in the file, so the end of this stream is sufficient. */
470 rva_base = dc->rva;
471 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
472 for (i = 0; i < dc->num_modules; i++)
474 if ((dc->modules[i].is_elf && !dump_elf) ||
475 (!dc->modules[i].is_elf && dump_elf))
476 continue;
478 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
479 if (dc->type & MiniDumpWithDataSegs)
480 flags_out |= ModuleWriteDataSeg;
481 if (dc->type & MiniDumpWithProcessThreadData)
482 flags_out |= ModuleWriteTlsData;
483 if (dc->type & MiniDumpWithCodeSegs)
484 flags_out |= ModuleWriteCodeSegs;
485 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
486 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
487 FIXME("Buffer overflow!!!\n");
488 lstrcpyW(ms->Buffer, dc->modules[i].name);
490 if (dc->cb)
492 MINIDUMP_CALLBACK_INPUT cbin;
493 MINIDUMP_CALLBACK_OUTPUT cbout;
495 cbin.ProcessId = dc->pid;
496 cbin.ProcessHandle = dc->hProcess;
497 cbin.CallbackType = ModuleCallback;
499 cbin.u.Module.FullPath = ms->Buffer;
500 cbin.u.Module.BaseOfImage = dc->modules[i].base;
501 cbin.u.Module.SizeOfImage = dc->modules[i].size;
502 cbin.u.Module.CheckSum = dc->modules[i].checksum;
503 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
504 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
505 cbin.u.Module.CvRecord = NULL;
506 cbin.u.Module.SizeOfCvRecord = 0;
507 cbin.u.Module.MiscRecord = NULL;
508 cbin.u.Module.SizeOfMiscRecord = 0;
510 cbout.u.ModuleWriteFlags = flags_out;
511 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
512 continue;
513 flags_out &= cbout.u.ModuleWriteFlags;
515 if (flags_out & ModuleWriteModule)
517 /* fetch CPU dependent module info (like UNWIND_INFO) */
518 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
520 mdModule.BaseOfImage = dc->modules[i].base;
521 mdModule.SizeOfImage = dc->modules[i].size;
522 mdModule.CheckSum = dc->modules[i].checksum;
523 mdModule.TimeDateStamp = dc->modules[i].timestamp;
524 mdModule.ModuleNameRva = dc->rva;
525 ms->Length -= sizeof(WCHAR);
526 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
527 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
528 mdModule.CvRecord.DataSize = 0; /* FIXME */
529 mdModule.CvRecord.Rva = 0; /* FIXME */
530 mdModule.MiscRecord.DataSize = 0; /* FIXME */
531 mdModule.MiscRecord.Rva = 0; /* FIXME */
532 mdModule.Reserved0 = 0; /* FIXME */
533 mdModule.Reserved1 = 0; /* FIXME */
534 writeat(dc,
535 rva_base + sizeof(mdModuleList.NumberOfModules) +
536 mdModuleList.NumberOfModules++ * sizeof(mdModule),
537 &mdModule, sizeof(mdModule));
540 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
541 sizeof(mdModuleList.NumberOfModules));
543 return sz;
546 #ifdef __i386__
547 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
548 __ASM_GLOBAL_FUNC( do_x86cpuid,
549 "pushl %esi\n\t"
550 "pushl %ebx\n\t"
551 "movl 12(%esp),%eax\n\t"
552 "movl 16(%esp),%esi\n\t"
553 "cpuid\n\t"
554 "movl %eax,(%esi)\n\t"
555 "movl %ebx,4(%esi)\n\t"
556 "movl %ecx,8(%esi)\n\t"
557 "movl %edx,12(%esi)\n\t"
558 "popl %ebx\n\t"
559 "popl %esi\n\t"
560 "ret" )
561 extern int have_x86cpuid(void);
562 __ASM_GLOBAL_FUNC( have_x86cpuid,
563 "pushfl\n\t"
564 "pushfl\n\t"
565 "movl (%esp),%ecx\n\t"
566 "xorl $0x00200000,(%esp)\n\t"
567 "popfl\n\t"
568 "pushfl\n\t"
569 "popl %eax\n\t"
570 "popfl\n\t"
571 "xorl %ecx,%eax\n\t"
572 "andl $0x00200000,%eax\n\t"
573 "ret" )
574 #else
575 static void do_x86cpuid(unsigned int ax, unsigned int *p)
579 static int have_x86cpuid(void)
581 return 0;
583 #endif
585 /******************************************************************
586 * dump_system_info
588 * Dumps into File the information about the system
590 static unsigned dump_system_info(struct dump_context* dc)
592 MINIDUMP_SYSTEM_INFO mdSysInfo;
593 SYSTEM_INFO sysInfo;
594 OSVERSIONINFOW osInfo;
595 DWORD written;
596 ULONG slen;
597 DWORD wine_extra = 0;
599 const char *(CDECL *wine_get_build_id)(void);
600 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
601 const char* build_id = NULL;
602 const char* sys_name = NULL;
603 const char* release_name = NULL;
605 GetSystemInfo(&sysInfo);
606 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
607 GetVersionExW(&osInfo);
609 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
610 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
611 if (wine_get_build_id && wine_get_host_version)
613 /* cheat minidump system information by adding specific wine information */
614 wine_extra = 4 + 4 * sizeof(slen);
615 build_id = wine_get_build_id();
616 wine_get_host_version(&sys_name, &release_name);
617 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
620 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
621 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
622 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
623 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
624 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
625 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
626 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
627 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
628 mdSysInfo.PlatformId = osInfo.dwPlatformId;
630 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
631 mdSysInfo.u1.Reserved1 = 0;
632 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
634 if (have_x86cpuid())
636 unsigned regs0[4], regs1[4];
638 do_x86cpuid(0, regs0);
639 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
640 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
641 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
642 do_x86cpuid(1, regs1);
643 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
644 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
645 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
646 if (regs0[1] == 0x68747541 /* "Auth" */ &&
647 regs0[3] == 0x69746e65 /* "enti" */ &&
648 regs0[2] == 0x444d4163 /* "cAMD" */)
650 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
651 if (regs1[0] >= 0x80000001)
653 do_x86cpuid(0x80000001, regs1); /* get vendor features */
654 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
658 else
660 unsigned i;
661 ULONG64 one = 1;
663 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
664 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
666 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
667 if (IsProcessorFeaturePresent(i))
668 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
670 append(dc, &mdSysInfo, sizeof(mdSysInfo));
672 /* write Wine specific system information just behind the structure, and before any string */
673 if (wine_extra)
675 static const char code[] = {'W','I','N','E'};
677 WriteFile(dc->hFile, code, 4, &written, NULL);
678 /* number of sub-info, so that we can extend structure if needed */
679 slen = 3;
680 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
681 /* we store offsets from just after the WINE marker */
682 slen = 4 * sizeof(DWORD);
683 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
684 slen += strlen(build_id) + 1;
685 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
686 slen += strlen(sys_name) + 1;
687 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
688 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
689 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
690 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
691 dc->rva += wine_extra;
694 /* write the service pack version string after this stream. It is referenced within the
695 stream by its RVA in the file. */
696 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
697 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
698 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
699 dc->rva += sizeof(ULONG) + slen;
701 return sizeof(mdSysInfo);
704 /******************************************************************
705 * dump_threads
707 * Dumps into File the information about running threads
709 static unsigned dump_threads(struct dump_context* dc,
710 const MINIDUMP_EXCEPTION_INFORMATION* except)
712 MINIDUMP_THREAD mdThd;
713 MINIDUMP_THREAD_LIST mdThdList;
714 unsigned i, sz;
715 RVA rva_base;
716 DWORD flags_out;
717 CONTEXT ctx;
719 mdThdList.NumberOfThreads = 0;
721 rva_base = dc->rva;
722 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
724 for (i = 0; i < dc->num_threads; i++)
726 fetch_thread_info(dc, i, except, &mdThd, &ctx);
728 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
729 ThreadWriteInstructionWindow;
730 if (dc->type & MiniDumpWithProcessThreadData)
731 flags_out |= ThreadWriteThreadData;
732 if (dc->type & MiniDumpWithThreadInfo)
733 flags_out |= ThreadWriteThreadInfo;
735 if (dc->cb)
737 MINIDUMP_CALLBACK_INPUT cbin;
738 MINIDUMP_CALLBACK_OUTPUT cbout;
740 cbin.ProcessId = dc->pid;
741 cbin.ProcessHandle = dc->hProcess;
742 cbin.CallbackType = ThreadCallback;
743 cbin.u.Thread.ThreadId = dc->threads[i].tid;
744 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
745 cbin.u.Thread.Context = ctx;
746 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
747 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
748 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
749 mdThd.Stack.Memory.DataSize;
751 cbout.u.ThreadWriteFlags = flags_out;
752 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
753 continue;
754 flags_out &= cbout.u.ThreadWriteFlags;
756 if (flags_out & ThreadWriteThread)
758 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
760 mdThd.ThreadContext.Rva = dc->rva;
761 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
762 append(dc, &ctx, sizeof(CONTEXT));
764 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
766 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
767 mdThd.Stack.Memory.DataSize,
768 rva_base + sizeof(mdThdList.NumberOfThreads) +
769 mdThdList.NumberOfThreads * sizeof(mdThd) +
770 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
772 writeat(dc,
773 rva_base + sizeof(mdThdList.NumberOfThreads) +
774 mdThdList.NumberOfThreads * sizeof(mdThd),
775 &mdThd, sizeof(mdThd));
776 mdThdList.NumberOfThreads++;
778 /* fetch CPU dependent thread info (like 256 bytes around program counter */
779 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
781 writeat(dc, rva_base,
782 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
784 return sz;
787 /******************************************************************
788 * dump_memory_info
790 * dumps information about the memory of the process (stack of the threads)
792 static unsigned dump_memory_info(struct dump_context* dc)
794 MINIDUMP_MEMORY_LIST mdMemList;
795 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
796 DWORD written;
797 unsigned i, pos, len, sz;
798 RVA rva_base;
799 char tmp[1024];
801 mdMemList.NumberOfMemoryRanges = dc->num_mem;
802 append(dc, &mdMemList.NumberOfMemoryRanges,
803 sizeof(mdMemList.NumberOfMemoryRanges));
804 rva_base = dc->rva;
805 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
806 dc->rva += sz;
807 sz += sizeof(mdMemList.NumberOfMemoryRanges);
809 for (i = 0; i < dc->num_mem; i++)
811 mdMem.StartOfMemoryRange = dc->mem[i].base;
812 mdMem.Memory.Rva = dc->rva;
813 mdMem.Memory.DataSize = dc->mem[i].size;
814 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
815 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
817 len = min(dc->mem[i].size - pos, sizeof(tmp));
818 if (ReadProcessMemory(dc->hProcess,
819 (void*)(DWORD_PTR)(dc->mem[i].base + pos),
820 tmp, len, NULL))
821 WriteFile(dc->hFile, tmp, len, &written, NULL);
823 dc->rva += mdMem.Memory.DataSize;
824 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
825 if (dc->mem[i].rva)
827 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
831 return sz;
834 static unsigned dump_misc_info(struct dump_context* dc)
836 MINIDUMP_MISC_INFO mmi;
838 mmi.SizeOfInfo = sizeof(mmi);
839 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
840 mmi.ProcessId = dc->pid;
841 /* FIXME: create/user/kernel time */
842 mmi.ProcessCreateTime = 0;
843 mmi.ProcessKernelTime = 0;
844 mmi.ProcessUserTime = 0;
846 append(dc, &mmi, sizeof(mmi));
847 return sizeof(mmi);
850 /******************************************************************
851 * MiniDumpWriteDump (DEBUGHLP.@)
854 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
855 MINIDUMP_TYPE DumpType,
856 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
857 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
858 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
860 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
861 MINIDUMP_HEADER mdHead;
862 MINIDUMP_DIRECTORY mdDir;
863 DWORD i, nStreams, idx_stream;
864 struct dump_context dc;
866 dc.hProcess = hProcess;
867 dc.hFile = hFile;
868 dc.pid = pid;
869 dc.modules = NULL;
870 dc.num_modules = 0;
871 dc.alloc_modules = 0;
872 dc.threads = NULL;
873 dc.num_threads = 0;
874 dc.cb = CallbackParam;
875 dc.type = DumpType;
876 dc.mem = NULL;
877 dc.num_mem = 0;
878 dc.alloc_mem = 0;
879 dc.rva = 0;
881 if (!fetch_process_info(&dc)) return FALSE;
882 fetch_modules_info(&dc);
884 /* 1) init */
885 nStreams = 6 + (ExceptionParam ? 1 : 0) +
886 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
888 /* pad the directory size to a multiple of 4 for alignment purposes */
889 nStreams = (nStreams + 3) & ~3;
891 if (DumpType & MiniDumpWithDataSegs)
892 FIXME("NIY MiniDumpWithDataSegs\n");
893 if (DumpType & MiniDumpWithFullMemory)
894 FIXME("NIY MiniDumpWithFullMemory\n");
895 if (DumpType & MiniDumpWithHandleData)
896 FIXME("NIY MiniDumpWithHandleData\n");
897 if (DumpType & MiniDumpFilterMemory)
898 FIXME("NIY MiniDumpFilterMemory\n");
899 if (DumpType & MiniDumpScanMemory)
900 FIXME("NIY MiniDumpScanMemory\n");
902 /* 2) write header */
903 mdHead.Signature = MINIDUMP_SIGNATURE;
904 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
905 mdHead.NumberOfStreams = nStreams;
906 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
907 mdHead.StreamDirectoryRva = sizeof(mdHead);
908 mdHead.u.TimeDateStamp = time(NULL);
909 mdHead.Flags = DumpType;
910 append(&dc, &mdHead, sizeof(mdHead));
912 /* 3) write stream directories */
913 dc.rva += nStreams * sizeof(mdDir);
914 idx_stream = 0;
916 /* 3.1) write data stream directories */
918 /* must be first in minidump */
919 mdDir.StreamType = SystemInfoStream;
920 mdDir.Location.Rva = dc.rva;
921 mdDir.Location.DataSize = dump_system_info(&dc);
922 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
923 &mdDir, sizeof(mdDir));
925 mdDir.StreamType = ThreadListStream;
926 mdDir.Location.Rva = dc.rva;
927 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
928 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
929 &mdDir, sizeof(mdDir));
931 mdDir.StreamType = ModuleListStream;
932 mdDir.Location.Rva = dc.rva;
933 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
934 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
935 &mdDir, sizeof(mdDir));
937 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
938 mdDir.Location.Rva = dc.rva;
939 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
940 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
941 &mdDir, sizeof(mdDir));
943 mdDir.StreamType = MemoryListStream;
944 mdDir.Location.Rva = dc.rva;
945 mdDir.Location.DataSize = dump_memory_info(&dc);
946 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
947 &mdDir, sizeof(mdDir));
949 mdDir.StreamType = MiscInfoStream;
950 mdDir.Location.Rva = dc.rva;
951 mdDir.Location.DataSize = dump_misc_info(&dc);
952 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
953 &mdDir, sizeof(mdDir));
955 /* 3.2) write exception information (if any) */
956 if (ExceptionParam)
958 mdDir.StreamType = ExceptionStream;
959 mdDir.Location.Rva = dc.rva;
960 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
961 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
962 &mdDir, sizeof(mdDir));
965 /* 3.3) write user defined streams (if any) */
966 if (UserStreamParam)
968 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
970 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
971 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
972 mdDir.Location.Rva = dc.rva;
973 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
974 &mdDir, sizeof(mdDir));
975 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
976 UserStreamParam->UserStreamArray[i].BufferSize);
980 /* fill the remaining directory entries with 0's (unused stream types) */
981 /* NOTE: this should always come last in the dump! */
982 for (i = idx_stream; i < nStreams; i++)
983 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
985 HeapFree(GetProcessHeap(), 0, dc.mem);
986 HeapFree(GetProcessHeap(), 0, dc.modules);
987 HeapFree(GetProcessHeap(), 0, dc.threads);
989 return TRUE;
992 /******************************************************************
993 * MiniDumpReadDumpStream (DEBUGHLP.@)
997 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
998 PMINIDUMP_DIRECTORY* pdir,
999 PVOID* stream, ULONG* size)
1001 MINIDUMP_HEADER* mdHead = base;
1003 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1005 MINIDUMP_DIRECTORY* dir;
1006 DWORD i;
1008 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1009 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1011 if (dir->StreamType == str_idx)
1013 if (pdir) *pdir = dir;
1014 if (stream) *stream = (char*)base + dir->Location.Rva;
1015 if (size) *size = dir->Location.DataSize;
1016 return TRUE;
1020 SetLastError(ERROR_INVALID_PARAMETER);
1021 return FALSE;