cmd: DIR command outputs free space for the path.
[wine.git] / dlls / dbghelp / minidump.c
blobc1b156f1aecc0308d89271ad8133bf6f3cb7549d
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 = spi->dwThreadCount;
65 dc->threads = HeapAlloc(GetProcessHeap(), 0,
66 dc->num_threads * sizeof(dc->threads[0]));
67 if (!dc->threads) goto failed;
68 for (i = 0; i < dc->num_threads; i++)
70 dc->threads[i].tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
71 dc->threads[i].prio_class = spi->ti[i].dwBasePriority; /* FIXME */
72 dc->threads[i].curr_prio = spi->ti[i].dwCurrentPriority;
74 HeapFree(GetProcessHeap(), 0, pcs_buffer);
75 return TRUE;
77 if (!spi->NextEntryOffset) break;
78 spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset);
81 failed:
82 HeapFree(GetProcessHeap(), 0, pcs_buffer);
83 return FALSE;
86 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
87 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
89 NT_TIB tib;
90 ADDRESS64 addr;
92 if (ReadProcessMemory(dc->process->handle, teb_addr, &tib, sizeof(tib), NULL) &&
93 dbghelp_current_cpu &&
94 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
96 if (addr.Offset)
98 addr.Offset -= dbghelp_current_cpu->word_size;
99 /* make sure stack pointer is within the established range of the stack. It could have
100 been clobbered by whatever caused the original exception. */
101 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
102 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
104 else
105 mmd->StartOfMemoryRange = addr.Offset;
107 else
108 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
109 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
113 /******************************************************************
114 * fetch_thread_info
116 * fetches some information about thread of id 'tid'
118 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
119 const MINIDUMP_EXCEPTION_INFORMATION* except,
120 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
122 DWORD tid = dc->threads[thd_idx].tid;
123 HANDLE hThread;
124 THREAD_BASIC_INFORMATION tbi;
126 memset(ctx, 0, sizeof(*ctx));
128 mdThd->ThreadId = tid;
129 mdThd->SuspendCount = 0;
130 mdThd->Teb = 0;
131 mdThd->Stack.StartOfMemoryRange = 0;
132 mdThd->Stack.Memory.DataSize = 0;
133 mdThd->Stack.Memory.Rva = 0;
134 mdThd->ThreadContext.DataSize = 0;
135 mdThd->ThreadContext.Rva = 0;
136 mdThd->PriorityClass = dc->threads[thd_idx].prio_class;
137 mdThd->Priority = dc->threads[thd_idx].curr_prio;
139 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
141 FIXME("Couldn't open thread %lu (%lu)\n", tid, GetLastError());
142 return FALSE;
145 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
146 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
148 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
149 if (tbi.ExitStatus == STILL_ACTIVE)
151 if (tid != GetCurrentThreadId() &&
152 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
154 ctx->ContextFlags = CONTEXT_FULL;
155 if (!GetThreadContext(hThread, ctx))
156 memset(ctx, 0, sizeof(*ctx));
158 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
159 ResumeThread(hThread);
161 else if (tid == GetCurrentThreadId() && except)
163 CONTEXT lctx, *pctx;
164 mdThd->SuspendCount = 1;
165 if (except->ClientPointers)
167 EXCEPTION_POINTERS ep;
169 ReadProcessMemory(dc->process->handle, except->ExceptionPointers,
170 &ep, sizeof(ep), NULL);
171 ReadProcessMemory(dc->process->handle, ep.ContextRecord,
172 &lctx, sizeof(lctx), NULL);
173 pctx = &lctx;
175 else pctx = except->ExceptionPointers->ContextRecord;
177 *ctx = *pctx;
178 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
180 else mdThd->SuspendCount = 0;
183 CloseHandle(hThread);
184 return TRUE;
187 /******************************************************************
188 * add_module
190 * Add a module to a dump context
192 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
193 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
194 BOOL is_elf)
196 if (!dc->modules)
198 dc->alloc_modules = 32;
199 dc->modules = HeapAlloc(GetProcessHeap(), 0,
200 dc->alloc_modules * sizeof(*dc->modules));
202 else if(dc->num_modules >= dc->alloc_modules)
204 dc->alloc_modules *= 2;
205 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
206 dc->alloc_modules * sizeof(*dc->modules));
208 if (!dc->modules)
210 dc->alloc_modules = dc->num_modules = 0;
211 return FALSE;
213 lstrcpynW(dc->modules[dc->num_modules].name, name,
214 ARRAY_SIZE(dc->modules[dc->num_modules].name));
215 dc->modules[dc->num_modules].base = base;
216 dc->modules[dc->num_modules].size = size;
217 dc->modules[dc->num_modules].timestamp = timestamp;
218 dc->modules[dc->num_modules].checksum = checksum;
219 dc->modules[dc->num_modules].is_elf = is_elf;
220 dc->num_modules++;
222 return TRUE;
225 /******************************************************************
226 * fetch_pe_module_info_cb
228 * Callback for accumulating in dump_context a PE modules set
230 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
231 PVOID user)
233 struct dump_context* dc = user;
234 IMAGE_NT_HEADERS nth;
236 if (!validate_addr64(base)) return FALSE;
238 if (pe_load_nt_header(dc->process->handle, base, &nth))
239 add_module(user, name, base, size,
240 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
241 FALSE);
242 return TRUE;
245 /******************************************************************
246 * fetch_elf_module_info_cb
248 * Callback for accumulating in dump_context an host modules set
250 static BOOL fetch_host_module_info_cb(const WCHAR* name, ULONG_PTR base,
251 void* user)
253 struct dump_context* dc = user;
254 DWORD_PTR rbase;
255 DWORD size, checksum;
257 /* FIXME: there's no relevant timestamp on ELF modules */
258 if (!dc->process->loader->fetch_file_info(dc->process, name, base, &rbase, &size, &checksum))
259 size = checksum = 0;
260 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
261 return TRUE;
264 static void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
266 if (!dc->mem64)
268 dc->alloc_mem64 = 32;
269 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
271 else if (dc->num_mem64 >= dc->alloc_mem64)
273 dc->alloc_mem64 *= 2;
274 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
275 dc->alloc_mem64 * sizeof(*dc->mem64));
277 if (dc->mem64)
279 dc->mem64[dc->num_mem64].base = base;
280 dc->mem64[dc->num_mem64].size = size;
281 dc->num_mem64++;
283 else dc->num_mem64 = dc->alloc_mem64 = 0;
286 static void fetch_memory64_info(struct dump_context* dc)
288 ULONG_PTR addr;
289 MEMORY_BASIC_INFORMATION mbi;
291 addr = 0;
292 while (VirtualQueryEx(dc->process->handle, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
294 /* Memory regions with state MEM_COMMIT will be added to the dump */
295 if (mbi.State == MEM_COMMIT)
297 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
300 if ((addr + mbi.RegionSize) < addr)
301 break;
303 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
307 static void fetch_modules_info(struct dump_context* dc)
309 EnumerateLoadedModulesW64(dc->process->handle, fetch_pe_module_info_cb, dc);
310 /* Since we include ELF modules in a separate stream from the regular PE ones,
311 * we can always include those ELF modules (they don't eat lots of space)
312 * And it's always a good idea to have a trace of the loaded ELF modules for
313 * a given application in a post mortem debugging condition.
315 dc->process->loader->enum_modules(dc->process, fetch_host_module_info_cb, dc);
318 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
320 DWORD handle;
321 DWORD sz;
323 memset(ffi, 0, sizeof(*ffi));
324 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
326 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
327 if (info && GetFileVersionInfoW(filename, handle, sz, info))
329 VS_FIXEDFILEINFO* ptr;
330 UINT len;
332 if (VerQueryValueW(info, L"\\", (void*)&ptr, &len))
333 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
335 HeapFree(GetProcessHeap(), 0, info);
339 /******************************************************************
340 * minidump_add_memory_block
342 * Add a memory block to be dumped in a minidump
343 * If rva is non 0, it's the rva in the minidump where has to be stored
344 * also the rva of the memory block when written (this allows us to reference
345 * a memory block from outside the list of memory blocks).
347 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
349 if (!dc->mem)
351 dc->alloc_mem = 32;
352 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
354 else if (dc->num_mem >= dc->alloc_mem)
356 dc->alloc_mem *= 2;
357 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
358 dc->alloc_mem * sizeof(*dc->mem));
360 if (dc->mem)
362 dc->mem[dc->num_mem].base = base;
363 dc->mem[dc->num_mem].size = size;
364 dc->mem[dc->num_mem].rva = rva;
365 dc->num_mem++;
367 else dc->num_mem = dc->alloc_mem = 0;
370 /******************************************************************
371 * writeat
373 * Writes a chunk of data at a given position in the minidump
375 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
377 DWORD written;
379 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
380 WriteFile(dc->hFile, data, size, &written, NULL);
383 /******************************************************************
384 * append
386 * writes a new chunk of data to the minidump, increasing the current
387 * rva in dc
389 static void append(struct dump_context* dc, const void* data, unsigned size)
391 writeat(dc, dc->rva, data, size);
392 dc->rva += size;
395 /******************************************************************
396 * dump_exception_info
398 * Write in File the exception information from pcs
400 static unsigned dump_exception_info(struct dump_context* dc,
401 const MINIDUMP_EXCEPTION_INFORMATION* except)
403 MINIDUMP_EXCEPTION_STREAM mdExcpt;
404 EXCEPTION_RECORD rec, *prec;
405 CONTEXT ctx, *pctx;
406 DWORD i;
408 mdExcpt.ThreadId = except->ThreadId;
409 mdExcpt.__alignment = 0;
410 if (except->ClientPointers)
412 EXCEPTION_POINTERS ep;
414 ReadProcessMemory(dc->process->handle,
415 except->ExceptionPointers, &ep, sizeof(ep), NULL);
416 ReadProcessMemory(dc->process->handle,
417 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
418 ReadProcessMemory(dc->process->handle,
419 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
420 prec = &rec;
421 pctx = &ctx;
423 else
425 prec = except->ExceptionPointers->ExceptionRecord;
426 pctx = except->ExceptionPointers->ContextRecord;
428 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
429 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
430 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
431 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
432 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
433 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
434 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
435 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
436 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
437 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
439 append(dc, &mdExcpt, sizeof(mdExcpt));
440 append(dc, pctx, sizeof(*pctx));
441 return sizeof(mdExcpt);
444 /******************************************************************
445 * dump_modules
447 * Write in File the modules from pcs
449 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
451 MINIDUMP_MODULE mdModule;
452 MINIDUMP_MODULE_LIST mdModuleList;
453 char tmp[1024];
454 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
455 ULONG i, nmod;
456 RVA rva_base;
457 DWORD flags_out;
458 unsigned sz;
460 for (i = nmod = 0; i < dc->num_modules; i++)
462 if ((dc->modules[i].is_elf && dump_elf) ||
463 (!dc->modules[i].is_elf && !dump_elf))
464 nmod++;
467 mdModuleList.NumberOfModules = 0;
468 /* reserve space for mdModuleList
469 * FIXME: since we don't support 0 length arrays, we cannot use the
470 * size of mdModuleList
471 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
474 /* the stream size is just the size of the module index. It does not include the data for the
475 names of each module. *Technically* the names are supposed to go into the common string table
476 in the minidump file. Since each string is referenced by RVA they can all safely be located
477 anywhere between streams in the file, so the end of this stream is sufficient. */
478 rva_base = dc->rva;
479 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
480 for (i = 0; i < dc->num_modules; i++)
482 if ((dc->modules[i].is_elf && !dump_elf) ||
483 (!dc->modules[i].is_elf && dump_elf))
484 continue;
486 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
487 if (dc->type & MiniDumpWithDataSegs)
488 flags_out |= ModuleWriteDataSeg;
489 if (dc->type & MiniDumpWithProcessThreadData)
490 flags_out |= ModuleWriteTlsData;
491 if (dc->type & MiniDumpWithCodeSegs)
492 flags_out |= ModuleWriteCodeSegs;
493 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
494 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
495 FIXME("Buffer overflow!!!\n");
496 lstrcpyW(ms->Buffer, dc->modules[i].name);
498 if (dc->cb)
500 MINIDUMP_CALLBACK_INPUT cbin;
501 MINIDUMP_CALLBACK_OUTPUT cbout;
503 cbin.ProcessId = dc->pid;
504 cbin.ProcessHandle = dc->process->handle;
505 cbin.CallbackType = ModuleCallback;
507 cbin.Module.FullPath = ms->Buffer;
508 cbin.Module.BaseOfImage = dc->modules[i].base;
509 cbin.Module.SizeOfImage = dc->modules[i].size;
510 cbin.Module.CheckSum = dc->modules[i].checksum;
511 cbin.Module.TimeDateStamp = dc->modules[i].timestamp;
512 memset(&cbin.Module.VersionInfo, 0, sizeof(cbin.Module.VersionInfo));
513 cbin.Module.CvRecord = NULL;
514 cbin.Module.SizeOfCvRecord = 0;
515 cbin.Module.MiscRecord = NULL;
516 cbin.Module.SizeOfMiscRecord = 0;
518 cbout.ModuleWriteFlags = flags_out;
519 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
520 continue;
521 flags_out &= cbout.ModuleWriteFlags;
523 if (flags_out & ModuleWriteModule)
525 /* fetch CPU dependent module info (like UNWIND_INFO) */
526 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
528 mdModule.BaseOfImage = dc->modules[i].base;
529 mdModule.SizeOfImage = dc->modules[i].size;
530 mdModule.CheckSum = dc->modules[i].checksum;
531 mdModule.TimeDateStamp = dc->modules[i].timestamp;
532 mdModule.ModuleNameRva = dc->rva;
533 ms->Length -= sizeof(WCHAR);
534 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
535 if (!dump_elf) fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
536 else memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo));
537 mdModule.CvRecord.DataSize = 0; /* FIXME */
538 mdModule.CvRecord.Rva = 0; /* FIXME */
539 mdModule.MiscRecord.DataSize = 0; /* FIXME */
540 mdModule.MiscRecord.Rva = 0; /* FIXME */
541 mdModule.Reserved0 = 0; /* FIXME */
542 mdModule.Reserved1 = 0; /* FIXME */
543 writeat(dc,
544 rva_base + sizeof(mdModuleList.NumberOfModules) +
545 mdModuleList.NumberOfModules++ * sizeof(mdModule),
546 &mdModule, sizeof(mdModule));
549 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
550 sizeof(mdModuleList.NumberOfModules));
552 return sz;
555 #ifdef __i386__
556 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
557 __ASM_GLOBAL_FUNC( do_x86cpuid,
558 "pushl %esi\n\t"
559 "pushl %ebx\n\t"
560 "movl 12(%esp),%eax\n\t"
561 "movl 16(%esp),%esi\n\t"
562 "cpuid\n\t"
563 "movl %eax,(%esi)\n\t"
564 "movl %ebx,4(%esi)\n\t"
565 "movl %ecx,8(%esi)\n\t"
566 "movl %edx,12(%esi)\n\t"
567 "popl %ebx\n\t"
568 "popl %esi\n\t"
569 "ret" )
570 extern int have_x86cpuid(void);
571 __ASM_GLOBAL_FUNC( have_x86cpuid,
572 "pushfl\n\t"
573 "pushfl\n\t"
574 "movl (%esp),%ecx\n\t"
575 "xorl $0x00200000,(%esp)\n\t"
576 "popfl\n\t"
577 "pushfl\n\t"
578 "popl %eax\n\t"
579 "popfl\n\t"
580 "xorl %ecx,%eax\n\t"
581 "andl $0x00200000,%eax\n\t"
582 "ret" )
583 #else
584 static void do_x86cpuid(unsigned int ax, unsigned int *p)
588 static int have_x86cpuid(void)
590 return 0;
592 #endif
594 /******************************************************************
595 * dump_system_info
597 * Dumps into File the information about the system
599 static unsigned dump_system_info(struct dump_context* dc)
601 MINIDUMP_SYSTEM_INFO mdSysInfo;
602 SYSTEM_INFO sysInfo;
603 RTL_OSVERSIONINFOEXW osInfo;
604 DWORD written;
605 ULONG slen;
606 DWORD wine_extra = 0;
608 const char *(CDECL *wine_get_build_id)(void);
609 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
610 const char* build_id = NULL;
611 const char* sys_name = NULL;
612 const char* release_name = NULL;
614 GetSystemInfo(&sysInfo);
615 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
616 RtlGetVersion(&osInfo);
618 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
619 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
620 if (wine_get_build_id && wine_get_host_version)
622 /* cheat minidump system information by adding specific wine information */
623 wine_extra = 4 + 4 * sizeof(slen);
624 build_id = wine_get_build_id();
625 wine_get_host_version(&sys_name, &release_name);
626 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
629 mdSysInfo.ProcessorArchitecture = sysInfo.wProcessorArchitecture;
630 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
631 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
632 mdSysInfo.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
633 mdSysInfo.ProductType = VER_NT_WORKSTATION; /* FIXME */
634 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
635 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
636 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
637 mdSysInfo.PlatformId = osInfo.dwPlatformId;
639 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
640 mdSysInfo.Reserved1 = 0;
641 mdSysInfo.SuiteMask = VER_SUITE_TERMINAL;
643 if (have_x86cpuid())
645 unsigned regs0[4], regs1[4];
647 do_x86cpuid(0, regs0);
648 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
649 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
650 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
651 do_x86cpuid(1, regs1);
652 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
653 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
654 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
655 if (regs0[1] == 0x68747541 /* "Auth" */ &&
656 regs0[3] == 0x69746e65 /* "enti" */ &&
657 regs0[2] == 0x444d4163 /* "cAMD" */)
659 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
660 if (regs1[0] >= 0x80000001)
662 do_x86cpuid(0x80000001, regs1); /* get vendor features */
663 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
667 else
669 unsigned i;
670 ULONG64 one = 1;
672 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
673 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
675 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
676 if (IsProcessorFeaturePresent(i))
677 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
679 append(dc, &mdSysInfo, sizeof(mdSysInfo));
681 /* write Wine specific system information just behind the structure, and before any string */
682 if (wine_extra)
684 static const char code[] = {'W','I','N','E'};
686 WriteFile(dc->hFile, code, 4, &written, NULL);
687 /* number of sub-info, so that we can extend structure if needed */
688 slen = 3;
689 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
690 /* we store offsets from just after the WINE marker */
691 slen = 4 * sizeof(DWORD);
692 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
693 slen += strlen(build_id) + 1;
694 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
695 slen += strlen(sys_name) + 1;
696 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
697 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
698 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
699 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
700 dc->rva += wine_extra;
703 /* write the service pack version string after this stream. It is referenced within the
704 stream by its RVA in the file. */
705 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
706 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
707 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
708 dc->rva += sizeof(ULONG) + slen;
710 return sizeof(mdSysInfo);
713 /******************************************************************
714 * dump_threads
716 * Dumps into File the information about running threads
718 static unsigned dump_threads(struct dump_context* dc,
719 const MINIDUMP_EXCEPTION_INFORMATION* except)
721 MINIDUMP_THREAD mdThd;
722 MINIDUMP_THREAD_LIST mdThdList;
723 unsigned i, sz;
724 RVA rva_base;
725 DWORD flags_out;
726 CONTEXT ctx;
728 mdThdList.NumberOfThreads = 0;
730 rva_base = dc->rva;
731 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
733 for (i = 0; i < dc->num_threads; i++)
735 fetch_thread_info(dc, i, except, &mdThd, &ctx);
737 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
738 ThreadWriteInstructionWindow;
739 if (dc->type & MiniDumpWithProcessThreadData)
740 flags_out |= ThreadWriteThreadData;
741 if (dc->type & MiniDumpWithThreadInfo)
742 flags_out |= ThreadWriteThreadInfo;
744 if (dc->cb)
746 MINIDUMP_CALLBACK_INPUT cbin;
747 MINIDUMP_CALLBACK_OUTPUT cbout;
749 cbin.ProcessId = dc->pid;
750 cbin.ProcessHandle = dc->process->handle;
751 cbin.CallbackType = ThreadCallback;
752 cbin.Thread.ThreadId = dc->threads[i].tid;
753 cbin.Thread.ThreadHandle = 0; /* FIXME */
754 cbin.Thread.Context = ctx;
755 cbin.Thread.SizeOfContext = sizeof(CONTEXT);
756 cbin.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
757 cbin.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
758 mdThd.Stack.Memory.DataSize;
760 cbout.ThreadWriteFlags = flags_out;
761 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
762 continue;
763 flags_out &= cbout.ThreadWriteFlags;
765 if (flags_out & ThreadWriteThread)
767 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
769 mdThd.ThreadContext.Rva = dc->rva;
770 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
771 append(dc, &ctx, sizeof(CONTEXT));
773 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
775 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
776 mdThd.Stack.Memory.DataSize,
777 rva_base + sizeof(mdThdList.NumberOfThreads) +
778 mdThdList.NumberOfThreads * sizeof(mdThd) +
779 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
781 writeat(dc,
782 rva_base + sizeof(mdThdList.NumberOfThreads) +
783 mdThdList.NumberOfThreads * sizeof(mdThd),
784 &mdThd, sizeof(mdThd));
785 mdThdList.NumberOfThreads++;
787 /* fetch CPU dependent thread info (like 256 bytes around program counter */
788 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
790 writeat(dc, rva_base,
791 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
793 return sz;
796 /******************************************************************
797 * dump_memory_info
799 * dumps information about the memory of the process (stack of the threads)
801 static unsigned dump_memory_info(struct dump_context* dc)
803 MINIDUMP_MEMORY_LIST mdMemList;
804 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
805 DWORD written;
806 unsigned i, pos, len, sz;
807 RVA rva_base;
808 char tmp[1024];
810 mdMemList.NumberOfMemoryRanges = dc->num_mem;
811 append(dc, &mdMemList.NumberOfMemoryRanges,
812 sizeof(mdMemList.NumberOfMemoryRanges));
813 rva_base = dc->rva;
814 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
815 dc->rva += sz;
816 sz += sizeof(mdMemList.NumberOfMemoryRanges);
818 for (i = 0; i < dc->num_mem; i++)
820 mdMem.StartOfMemoryRange = dc->mem[i].base;
821 mdMem.Memory.Rva = dc->rva;
822 mdMem.Memory.DataSize = dc->mem[i].size;
823 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
824 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
826 len = min(dc->mem[i].size - pos, sizeof(tmp));
827 if (read_process_memory(dc->process, dc->mem[i].base + pos, tmp, len))
828 WriteFile(dc->hFile, tmp, len, &written, NULL);
830 dc->rva += mdMem.Memory.DataSize;
831 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
832 if (dc->mem[i].rva)
834 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
838 return sz;
841 /******************************************************************
842 * dump_memory64_info
844 * dumps information about the memory of the process (virtual memory)
846 static unsigned dump_memory64_info(struct dump_context* dc)
848 MINIDUMP_MEMORY64_LIST mdMem64List;
849 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64;
850 DWORD written;
851 unsigned i, len, sz;
852 RVA rva_base;
853 char tmp[1024];
854 ULONG64 pos;
855 LARGE_INTEGER filepos;
857 sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
858 sizeof(mdMem64List.BaseRva) +
859 dc->num_mem64 * sizeof(mdMem64);
861 mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
862 mdMem64List.BaseRva = dc->rva + sz;
864 append(dc, &mdMem64List.NumberOfMemoryRanges,
865 sizeof(mdMem64List.NumberOfMemoryRanges));
866 append(dc, &mdMem64List.BaseRva,
867 sizeof(mdMem64List.BaseRva));
869 rva_base = dc->rva;
870 dc->rva += dc->num_mem64 * sizeof(mdMem64);
872 /* dc->rva is not updated past this point. The end of the dump
873 * is just the full memory data. */
874 filepos.QuadPart = dc->rva;
875 for (i = 0; i < dc->num_mem64; i++)
877 mdMem64.StartOfMemoryRange = dc->mem64[i].base;
878 mdMem64.DataSize = dc->mem64[i].size;
879 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
880 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
882 len = min(dc->mem64[i].size - pos, sizeof(tmp));
883 if (read_process_memory(dc->process, dc->mem64[i].base + pos, tmp, len))
884 WriteFile(dc->hFile, tmp, len, &written, NULL);
886 filepos.QuadPart += mdMem64.DataSize;
887 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
890 return sz;
893 static unsigned dump_misc_info(struct dump_context* dc)
895 MINIDUMP_MISC_INFO mmi;
897 mmi.SizeOfInfo = sizeof(mmi);
898 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
899 mmi.ProcessId = dc->pid;
900 /* FIXME: create/user/kernel time */
901 mmi.ProcessCreateTime = 0;
902 mmi.ProcessKernelTime = 0;
903 mmi.ProcessUserTime = 0;
905 append(dc, &mmi, sizeof(mmi));
906 return sizeof(mmi);
909 /******************************************************************
910 * MiniDumpWriteDump (DEBUGHLP.@)
913 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
914 MINIDUMP_TYPE DumpType,
915 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
916 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
917 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
919 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
920 MINIDUMP_HEADER mdHead;
921 MINIDUMP_DIRECTORY mdDir;
922 DWORD i, nStreams, idx_stream;
923 struct dump_context dc;
924 BOOL sym_initialized = FALSE;
926 if (!(dc.process = process_find_by_handle(hProcess)))
928 if (!(sym_initialized = SymInitializeW(hProcess, NULL, TRUE)))
930 WARN("failed to initialize process\n");
931 return FALSE;
933 dc.process = process_find_by_handle(hProcess);
936 dc.hFile = hFile;
937 dc.pid = pid;
938 dc.modules = NULL;
939 dc.num_modules = 0;
940 dc.alloc_modules = 0;
941 dc.threads = NULL;
942 dc.num_threads = 0;
943 dc.cb = CallbackParam;
944 dc.type = DumpType;
945 dc.mem = NULL;
946 dc.num_mem = 0;
947 dc.alloc_mem = 0;
948 dc.mem64 = NULL;
949 dc.num_mem64 = 0;
950 dc.alloc_mem64 = 0;
951 dc.rva = 0;
953 if (!fetch_process_info(&dc)) return FALSE;
954 fetch_modules_info(&dc);
956 /* 1) init */
957 nStreams = 6 + (ExceptionParam ? 1 : 0) +
958 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
960 /* pad the directory size to a multiple of 4 for alignment purposes */
961 nStreams = (nStreams + 3) & ~3;
963 if (DumpType & MiniDumpWithDataSegs)
964 FIXME("NIY MiniDumpWithDataSegs\n");
965 if (DumpType & MiniDumpWithHandleData)
966 FIXME("NIY MiniDumpWithHandleData\n");
967 if (DumpType & MiniDumpFilterMemory)
968 FIXME("NIY MiniDumpFilterMemory\n");
969 if (DumpType & MiniDumpScanMemory)
970 FIXME("NIY MiniDumpScanMemory\n");
972 /* 2) write header */
973 mdHead.Signature = MINIDUMP_SIGNATURE;
974 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
975 mdHead.NumberOfStreams = nStreams;
976 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
977 mdHead.StreamDirectoryRva = sizeof(mdHead);
978 mdHead.TimeDateStamp = time(NULL);
979 mdHead.Flags = DumpType;
980 append(&dc, &mdHead, sizeof(mdHead));
982 /* 3) write stream directories */
983 dc.rva += nStreams * sizeof(mdDir);
984 idx_stream = 0;
986 /* 3.1) write data stream directories */
988 /* must be first in minidump */
989 mdDir.StreamType = SystemInfoStream;
990 mdDir.Location.Rva = dc.rva;
991 mdDir.Location.DataSize = dump_system_info(&dc);
992 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
993 &mdDir, sizeof(mdDir));
995 mdDir.StreamType = ThreadListStream;
996 mdDir.Location.Rva = dc.rva;
997 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
998 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
999 &mdDir, sizeof(mdDir));
1001 mdDir.StreamType = ModuleListStream;
1002 mdDir.Location.Rva = dc.rva;
1003 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
1004 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1005 &mdDir, sizeof(mdDir));
1007 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1008 mdDir.Location.Rva = dc.rva;
1009 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
1010 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1011 &mdDir, sizeof(mdDir));
1014 if (!(DumpType & MiniDumpWithFullMemory))
1016 mdDir.StreamType = MemoryListStream;
1017 mdDir.Location.Rva = dc.rva;
1018 mdDir.Location.DataSize = dump_memory_info(&dc);
1019 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1020 &mdDir, sizeof(mdDir));
1023 mdDir.StreamType = MiscInfoStream;
1024 mdDir.Location.Rva = dc.rva;
1025 mdDir.Location.DataSize = dump_misc_info(&dc);
1026 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1027 &mdDir, sizeof(mdDir));
1029 /* 3.2) write exception information (if any) */
1030 if (ExceptionParam)
1032 mdDir.StreamType = ExceptionStream;
1033 mdDir.Location.Rva = dc.rva;
1034 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
1035 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1036 &mdDir, sizeof(mdDir));
1039 /* 3.3) write user defined streams (if any) */
1040 if (UserStreamParam)
1042 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
1044 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
1045 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
1046 mdDir.Location.Rva = dc.rva;
1047 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1048 &mdDir, sizeof(mdDir));
1049 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
1050 UserStreamParam->UserStreamArray[i].BufferSize);
1054 /* 3.4) write full memory (if requested) */
1055 if (DumpType & MiniDumpWithFullMemory)
1057 fetch_memory64_info(&dc);
1059 mdDir.StreamType = Memory64ListStream;
1060 mdDir.Location.Rva = dc.rva;
1061 mdDir.Location.DataSize = dump_memory64_info(&dc);
1062 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1063 &mdDir, sizeof(mdDir));
1066 /* fill the remaining directory entries with 0's (unused stream types) */
1067 /* NOTE: this should always come last in the dump! */
1068 for (i = idx_stream; i < nStreams; i++)
1069 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1071 if (sym_initialized)
1072 SymCleanup(hProcess);
1074 HeapFree(GetProcessHeap(), 0, dc.mem);
1075 HeapFree(GetProcessHeap(), 0, dc.mem64);
1076 HeapFree(GetProcessHeap(), 0, dc.modules);
1077 HeapFree(GetProcessHeap(), 0, dc.threads);
1079 return TRUE;
1082 /******************************************************************
1083 * MiniDumpReadDumpStream (DEBUGHLP.@)
1087 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1088 PMINIDUMP_DIRECTORY* pdir,
1089 PVOID* stream, ULONG* size)
1091 MINIDUMP_HEADER* mdHead = base;
1093 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1095 MINIDUMP_DIRECTORY* dir;
1096 DWORD i;
1098 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1099 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1101 if (dir->StreamType == str_idx)
1103 if (pdir) *pdir = dir;
1104 if (stream) *stream = (char*)base + dir->Location.Rva;
1105 if (size) *size = dir->Location.DataSize;
1106 return TRUE;
1110 SetLastError(ERROR_INVALID_PARAMETER);
1111 return FALSE;