kernel32/tests: Add a test to check some fields in fake dlls.
[wine.git] / dlls / dbghelp / minidump.c
blobb096476aaf354c26235cfd8029e06a9fc2542274
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 void minidump_add_memory64_block(struct dump_context* dc, ULONG64 base, ULONG64 size)
299 if (!dc->mem64)
301 dc->alloc_mem64 = 32;
302 dc->mem64 = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem64 * sizeof(*dc->mem64));
304 else if (dc->num_mem64 >= dc->alloc_mem64)
306 dc->alloc_mem64 *= 2;
307 dc->mem64 = HeapReAlloc(GetProcessHeap(), 0, dc->mem64,
308 dc->alloc_mem64 * sizeof(*dc->mem64));
310 if (dc->mem64)
312 dc->mem64[dc->num_mem64].base = base;
313 dc->mem64[dc->num_mem64].size = size;
314 dc->num_mem64++;
316 else dc->num_mem64 = dc->alloc_mem64 = 0;
319 static void fetch_memory64_info(struct dump_context* dc)
321 ULONG_PTR addr;
322 MEMORY_BASIC_INFORMATION mbi;
324 addr = 0;
325 while (VirtualQueryEx(dc->hProcess, (LPCVOID)addr, &mbi, sizeof(mbi)) != 0)
327 /* Memory regions with state MEM_COMMIT will be added to the dump */
328 if (mbi.State == MEM_COMMIT)
330 minidump_add_memory64_block(dc, (ULONG_PTR)mbi.BaseAddress, mbi.RegionSize);
333 if ((addr + mbi.RegionSize) < addr)
334 break;
336 addr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
340 static void fetch_modules_info(struct dump_context* dc)
342 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
343 /* Since we include ELF modules in a separate stream from the regular PE ones,
344 * we can always include those ELF modules (they don't eat lots of space)
345 * And it's always a good idea to have a trace of the loaded ELF modules for
346 * a given application in a post mortem debugging condition.
348 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
349 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
352 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
354 DWORD handle;
355 DWORD sz;
356 static const WCHAR backslashW[] = {'\\', '\0'};
358 memset(ffi, 0, sizeof(*ffi));
359 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
361 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
362 if (info && GetFileVersionInfoW(filename, handle, sz, info))
364 VS_FIXEDFILEINFO* ptr;
365 UINT len;
367 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
368 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
370 HeapFree(GetProcessHeap(), 0, info);
374 /******************************************************************
375 * minidump_add_memory_block
377 * Add a memory block to be dumped in a minidump
378 * If rva is non 0, it's the rva in the minidump where has to be stored
379 * also the rva of the memory block when written (this allows us to reference
380 * a memory block from outside the list of memory blocks).
382 void minidump_add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
384 if (!dc->mem)
386 dc->alloc_mem = 32;
387 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
389 else if (dc->num_mem >= dc->alloc_mem)
391 dc->alloc_mem *= 2;
392 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
393 dc->alloc_mem * sizeof(*dc->mem));
395 if (dc->mem)
397 dc->mem[dc->num_mem].base = base;
398 dc->mem[dc->num_mem].size = size;
399 dc->mem[dc->num_mem].rva = rva;
400 dc->num_mem++;
402 else dc->num_mem = dc->alloc_mem = 0;
405 /******************************************************************
406 * writeat
408 * Writes a chunk of data at a given position in the minidump
410 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
412 DWORD written;
414 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
415 WriteFile(dc->hFile, data, size, &written, NULL);
418 /******************************************************************
419 * append
421 * writes a new chunk of data to the minidump, increasing the current
422 * rva in dc
424 static void append(struct dump_context* dc, const void* data, unsigned size)
426 writeat(dc, dc->rva, data, size);
427 dc->rva += size;
430 /******************************************************************
431 * dump_exception_info
433 * Write in File the exception information from pcs
435 static unsigned dump_exception_info(struct dump_context* dc,
436 const MINIDUMP_EXCEPTION_INFORMATION* except)
438 MINIDUMP_EXCEPTION_STREAM mdExcpt;
439 EXCEPTION_RECORD rec, *prec;
440 CONTEXT ctx, *pctx;
441 DWORD i;
443 mdExcpt.ThreadId = except->ThreadId;
444 mdExcpt.__alignment = 0;
445 if (except->ClientPointers)
447 EXCEPTION_POINTERS ep;
449 ReadProcessMemory(dc->hProcess,
450 except->ExceptionPointers, &ep, sizeof(ep), NULL);
451 ReadProcessMemory(dc->hProcess,
452 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
453 ReadProcessMemory(dc->hProcess,
454 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
455 prec = &rec;
456 pctx = &ctx;
458 else
460 prec = except->ExceptionPointers->ExceptionRecord;
461 pctx = except->ExceptionPointers->ContextRecord;
463 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
464 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
465 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
466 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
467 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
468 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
469 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
470 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
471 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
472 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
474 append(dc, &mdExcpt, sizeof(mdExcpt));
475 append(dc, pctx, sizeof(*pctx));
476 return sizeof(mdExcpt);
479 /******************************************************************
480 * dump_modules
482 * Write in File the modules from pcs
484 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
486 MINIDUMP_MODULE mdModule;
487 MINIDUMP_MODULE_LIST mdModuleList;
488 char tmp[1024];
489 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
490 ULONG i, nmod;
491 RVA rva_base;
492 DWORD flags_out;
493 unsigned sz;
495 for (i = nmod = 0; i < dc->num_modules; i++)
497 if ((dc->modules[i].is_elf && dump_elf) ||
498 (!dc->modules[i].is_elf && !dump_elf))
499 nmod++;
502 mdModuleList.NumberOfModules = 0;
503 /* reserve space for mdModuleList
504 * FIXME: since we don't support 0 length arrays, we cannot use the
505 * size of mdModuleList
506 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
509 /* the stream size is just the size of the module index. It does not include the data for the
510 names of each module. *Technically* the names are supposed to go into the common string table
511 in the minidump file. Since each string is referenced by RVA they can all safely be located
512 anywhere between streams in the file, so the end of this stream is sufficient. */
513 rva_base = dc->rva;
514 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
515 for (i = 0; i < dc->num_modules; i++)
517 if ((dc->modules[i].is_elf && !dump_elf) ||
518 (!dc->modules[i].is_elf && dump_elf))
519 continue;
521 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
522 if (dc->type & MiniDumpWithDataSegs)
523 flags_out |= ModuleWriteDataSeg;
524 if (dc->type & MiniDumpWithProcessThreadData)
525 flags_out |= ModuleWriteTlsData;
526 if (dc->type & MiniDumpWithCodeSegs)
527 flags_out |= ModuleWriteCodeSegs;
528 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
529 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
530 FIXME("Buffer overflow!!!\n");
531 lstrcpyW(ms->Buffer, dc->modules[i].name);
533 if (dc->cb)
535 MINIDUMP_CALLBACK_INPUT cbin;
536 MINIDUMP_CALLBACK_OUTPUT cbout;
538 cbin.ProcessId = dc->pid;
539 cbin.ProcessHandle = dc->hProcess;
540 cbin.CallbackType = ModuleCallback;
542 cbin.u.Module.FullPath = ms->Buffer;
543 cbin.u.Module.BaseOfImage = dc->modules[i].base;
544 cbin.u.Module.SizeOfImage = dc->modules[i].size;
545 cbin.u.Module.CheckSum = dc->modules[i].checksum;
546 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
547 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
548 cbin.u.Module.CvRecord = NULL;
549 cbin.u.Module.SizeOfCvRecord = 0;
550 cbin.u.Module.MiscRecord = NULL;
551 cbin.u.Module.SizeOfMiscRecord = 0;
553 cbout.u.ModuleWriteFlags = flags_out;
554 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
555 continue;
556 flags_out &= cbout.u.ModuleWriteFlags;
558 if (flags_out & ModuleWriteModule)
560 /* fetch CPU dependent module info (like UNWIND_INFO) */
561 dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out);
563 mdModule.BaseOfImage = dc->modules[i].base;
564 mdModule.SizeOfImage = dc->modules[i].size;
565 mdModule.CheckSum = dc->modules[i].checksum;
566 mdModule.TimeDateStamp = dc->modules[i].timestamp;
567 mdModule.ModuleNameRva = dc->rva;
568 ms->Length -= sizeof(WCHAR);
569 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
570 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
571 mdModule.CvRecord.DataSize = 0; /* FIXME */
572 mdModule.CvRecord.Rva = 0; /* FIXME */
573 mdModule.MiscRecord.DataSize = 0; /* FIXME */
574 mdModule.MiscRecord.Rva = 0; /* FIXME */
575 mdModule.Reserved0 = 0; /* FIXME */
576 mdModule.Reserved1 = 0; /* FIXME */
577 writeat(dc,
578 rva_base + sizeof(mdModuleList.NumberOfModules) +
579 mdModuleList.NumberOfModules++ * sizeof(mdModule),
580 &mdModule, sizeof(mdModule));
583 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
584 sizeof(mdModuleList.NumberOfModules));
586 return sz;
589 #ifdef __i386__
590 extern void do_x86cpuid(unsigned int ax, unsigned int *p);
591 __ASM_GLOBAL_FUNC( do_x86cpuid,
592 "pushl %esi\n\t"
593 "pushl %ebx\n\t"
594 "movl 12(%esp),%eax\n\t"
595 "movl 16(%esp),%esi\n\t"
596 "cpuid\n\t"
597 "movl %eax,(%esi)\n\t"
598 "movl %ebx,4(%esi)\n\t"
599 "movl %ecx,8(%esi)\n\t"
600 "movl %edx,12(%esi)\n\t"
601 "popl %ebx\n\t"
602 "popl %esi\n\t"
603 "ret" )
604 extern int have_x86cpuid(void);
605 __ASM_GLOBAL_FUNC( have_x86cpuid,
606 "pushfl\n\t"
607 "pushfl\n\t"
608 "movl (%esp),%ecx\n\t"
609 "xorl $0x00200000,(%esp)\n\t"
610 "popfl\n\t"
611 "pushfl\n\t"
612 "popl %eax\n\t"
613 "popfl\n\t"
614 "xorl %ecx,%eax\n\t"
615 "andl $0x00200000,%eax\n\t"
616 "ret" )
617 #else
618 static void do_x86cpuid(unsigned int ax, unsigned int *p)
622 static int have_x86cpuid(void)
624 return 0;
626 #endif
628 /******************************************************************
629 * dump_system_info
631 * Dumps into File the information about the system
633 static unsigned dump_system_info(struct dump_context* dc)
635 MINIDUMP_SYSTEM_INFO mdSysInfo;
636 SYSTEM_INFO sysInfo;
637 OSVERSIONINFOW osInfo;
638 DWORD written;
639 ULONG slen;
640 DWORD wine_extra = 0;
642 const char *(CDECL *wine_get_build_id)(void);
643 void (CDECL *wine_get_host_version)(const char **sysname, const char **release);
644 const char* build_id = NULL;
645 const char* sys_name = NULL;
646 const char* release_name = NULL;
648 GetSystemInfo(&sysInfo);
649 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
650 GetVersionExW(&osInfo);
652 wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
653 wine_get_host_version = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
654 if (wine_get_build_id && wine_get_host_version)
656 /* cheat minidump system information by adding specific wine information */
657 wine_extra = 4 + 4 * sizeof(slen);
658 build_id = wine_get_build_id();
659 wine_get_host_version(&sys_name, &release_name);
660 wine_extra += strlen(build_id) + 1 + strlen(sys_name) + 1 + strlen(release_name) + 1;
663 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
664 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
665 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
666 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
667 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
668 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
669 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
670 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
671 mdSysInfo.PlatformId = osInfo.dwPlatformId;
673 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo) + wine_extra;
674 mdSysInfo.u1.Reserved1 = 0;
675 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
677 if (have_x86cpuid())
679 unsigned regs0[4], regs1[4];
681 do_x86cpuid(0, regs0);
682 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
683 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[3];
684 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[2];
685 do_x86cpuid(1, regs1);
686 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
687 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
688 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
689 if (regs0[1] == 0x68747541 /* "Auth" */ &&
690 regs0[3] == 0x69746e65 /* "enti" */ &&
691 regs0[2] == 0x444d4163 /* "cAMD" */)
693 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
694 if (regs1[0] >= 0x80000001)
696 do_x86cpuid(0x80000001, regs1); /* get vendor features */
697 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
701 else
703 unsigned i;
704 ULONG64 one = 1;
706 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
707 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
709 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
710 if (IsProcessorFeaturePresent(i))
711 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
713 append(dc, &mdSysInfo, sizeof(mdSysInfo));
715 /* write Wine specific system information just behind the structure, and before any string */
716 if (wine_extra)
718 static const char code[] = {'W','I','N','E'};
720 WriteFile(dc->hFile, code, 4, &written, NULL);
721 /* number of sub-info, so that we can extend structure if needed */
722 slen = 3;
723 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
724 /* we store offsets from just after the WINE marker */
725 slen = 4 * sizeof(DWORD);
726 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
727 slen += strlen(build_id) + 1;
728 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
729 slen += strlen(sys_name) + 1;
730 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
731 WriteFile(dc->hFile, build_id, strlen(build_id) + 1, &written, NULL);
732 WriteFile(dc->hFile, sys_name, strlen(sys_name) + 1, &written, NULL);
733 WriteFile(dc->hFile, release_name, strlen(release_name) + 1, &written, NULL);
734 dc->rva += wine_extra;
737 /* write the service pack version string after this stream. It is referenced within the
738 stream by its RVA in the file. */
739 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
740 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
741 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
742 dc->rva += sizeof(ULONG) + slen;
744 return sizeof(mdSysInfo);
747 /******************************************************************
748 * dump_threads
750 * Dumps into File the information about running threads
752 static unsigned dump_threads(struct dump_context* dc,
753 const MINIDUMP_EXCEPTION_INFORMATION* except)
755 MINIDUMP_THREAD mdThd;
756 MINIDUMP_THREAD_LIST mdThdList;
757 unsigned i, sz;
758 RVA rva_base;
759 DWORD flags_out;
760 CONTEXT ctx;
762 mdThdList.NumberOfThreads = 0;
764 rva_base = dc->rva;
765 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->num_threads * sizeof(mdThd);
767 for (i = 0; i < dc->num_threads; i++)
769 fetch_thread_info(dc, i, except, &mdThd, &ctx);
771 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
772 ThreadWriteInstructionWindow;
773 if (dc->type & MiniDumpWithProcessThreadData)
774 flags_out |= ThreadWriteThreadData;
775 if (dc->type & MiniDumpWithThreadInfo)
776 flags_out |= ThreadWriteThreadInfo;
778 if (dc->cb)
780 MINIDUMP_CALLBACK_INPUT cbin;
781 MINIDUMP_CALLBACK_OUTPUT cbout;
783 cbin.ProcessId = dc->pid;
784 cbin.ProcessHandle = dc->hProcess;
785 cbin.CallbackType = ThreadCallback;
786 cbin.u.Thread.ThreadId = dc->threads[i].tid;
787 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
788 cbin.u.Thread.Context = ctx;
789 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
790 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
791 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
792 mdThd.Stack.Memory.DataSize;
794 cbout.u.ThreadWriteFlags = flags_out;
795 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
796 continue;
797 flags_out &= cbout.u.ThreadWriteFlags;
799 if (flags_out & ThreadWriteThread)
801 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
803 mdThd.ThreadContext.Rva = dc->rva;
804 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
805 append(dc, &ctx, sizeof(CONTEXT));
807 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
809 minidump_add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
810 mdThd.Stack.Memory.DataSize,
811 rva_base + sizeof(mdThdList.NumberOfThreads) +
812 mdThdList.NumberOfThreads * sizeof(mdThd) +
813 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
815 writeat(dc,
816 rva_base + sizeof(mdThdList.NumberOfThreads) +
817 mdThdList.NumberOfThreads * sizeof(mdThd),
818 &mdThd, sizeof(mdThd));
819 mdThdList.NumberOfThreads++;
821 /* fetch CPU dependent thread info (like 256 bytes around program counter */
822 dbghelp_current_cpu->fetch_minidump_thread(dc, i, flags_out, &ctx);
824 writeat(dc, rva_base,
825 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
827 return sz;
830 /******************************************************************
831 * dump_memory_info
833 * dumps information about the memory of the process (stack of the threads)
835 static unsigned dump_memory_info(struct dump_context* dc)
837 MINIDUMP_MEMORY_LIST mdMemList;
838 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
839 DWORD written;
840 unsigned i, pos, len, sz;
841 RVA rva_base;
842 char tmp[1024];
844 mdMemList.NumberOfMemoryRanges = dc->num_mem;
845 append(dc, &mdMemList.NumberOfMemoryRanges,
846 sizeof(mdMemList.NumberOfMemoryRanges));
847 rva_base = dc->rva;
848 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
849 dc->rva += sz;
850 sz += sizeof(mdMemList.NumberOfMemoryRanges);
852 for (i = 0; i < dc->num_mem; i++)
854 mdMem.StartOfMemoryRange = dc->mem[i].base;
855 mdMem.Memory.Rva = dc->rva;
856 mdMem.Memory.DataSize = dc->mem[i].size;
857 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
858 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
860 len = min(dc->mem[i].size - pos, sizeof(tmp));
861 if (ReadProcessMemory(dc->hProcess,
862 (void*)(DWORD_PTR)(dc->mem[i].base + pos),
863 tmp, len, NULL))
864 WriteFile(dc->hFile, tmp, len, &written, NULL);
866 dc->rva += mdMem.Memory.DataSize;
867 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
868 if (dc->mem[i].rva)
870 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
874 return sz;
877 /******************************************************************
878 * dump_memory64_info
880 * dumps information about the memory of the process (virtual memory)
882 static unsigned dump_memory64_info(struct dump_context* dc)
884 MINIDUMP_MEMORY64_LIST mdMem64List;
885 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64;
886 DWORD written;
887 unsigned i, len, sz;
888 RVA rva_base;
889 char tmp[1024];
890 ULONG64 pos;
891 LARGE_INTEGER filepos;
893 sz = sizeof(mdMem64List.NumberOfMemoryRanges) +
894 sizeof(mdMem64List.BaseRva) +
895 dc->num_mem64 * sizeof(mdMem64);
897 mdMem64List.NumberOfMemoryRanges = dc->num_mem64;
898 mdMem64List.BaseRva = dc->rva + sz;
900 append(dc, &mdMem64List.NumberOfMemoryRanges,
901 sizeof(mdMem64List.NumberOfMemoryRanges));
902 append(dc, &mdMem64List.BaseRva,
903 sizeof(mdMem64List.BaseRva));
905 rva_base = dc->rva;
906 dc->rva += dc->num_mem64 * sizeof(mdMem64);
908 /* dc->rva is not updated past this point. The end of the dump
909 * is just the full memory data. */
910 filepos.QuadPart = dc->rva;
911 for (i = 0; i < dc->num_mem64; i++)
913 mdMem64.StartOfMemoryRange = dc->mem64[i].base;
914 mdMem64.DataSize = dc->mem64[i].size;
915 SetFilePointerEx(dc->hFile, filepos, NULL, FILE_BEGIN);
916 for (pos = 0; pos < dc->mem64[i].size; pos += sizeof(tmp))
918 len = min(dc->mem64[i].size - pos, sizeof(tmp));
919 if (ReadProcessMemory(dc->hProcess,
920 (void*)(ULONG_PTR)(dc->mem64[i].base + pos),
921 tmp, len, NULL))
922 WriteFile(dc->hFile, tmp, len, &written, NULL);
924 filepos.QuadPart += mdMem64.DataSize;
925 writeat(dc, rva_base + i * sizeof(mdMem64), &mdMem64, sizeof(mdMem64));
928 return sz;
931 static unsigned dump_misc_info(struct dump_context* dc)
933 MINIDUMP_MISC_INFO mmi;
935 mmi.SizeOfInfo = sizeof(mmi);
936 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
937 mmi.ProcessId = dc->pid;
938 /* FIXME: create/user/kernel time */
939 mmi.ProcessCreateTime = 0;
940 mmi.ProcessKernelTime = 0;
941 mmi.ProcessUserTime = 0;
943 append(dc, &mmi, sizeof(mmi));
944 return sizeof(mmi);
947 /******************************************************************
948 * MiniDumpWriteDump (DEBUGHLP.@)
951 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
952 MINIDUMP_TYPE DumpType,
953 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
954 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
955 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
957 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
958 MINIDUMP_HEADER mdHead;
959 MINIDUMP_DIRECTORY mdDir;
960 DWORD i, nStreams, idx_stream;
961 struct dump_context dc;
963 dc.hProcess = hProcess;
964 dc.hFile = hFile;
965 dc.pid = pid;
966 dc.modules = NULL;
967 dc.num_modules = 0;
968 dc.alloc_modules = 0;
969 dc.threads = NULL;
970 dc.num_threads = 0;
971 dc.cb = CallbackParam;
972 dc.type = DumpType;
973 dc.mem = NULL;
974 dc.num_mem = 0;
975 dc.alloc_mem = 0;
976 dc.mem64 = NULL;
977 dc.num_mem64 = 0;
978 dc.alloc_mem64 = 0;
979 dc.rva = 0;
981 if (!fetch_process_info(&dc)) return FALSE;
982 fetch_modules_info(&dc);
984 /* 1) init */
985 nStreams = 6 + (ExceptionParam ? 1 : 0) +
986 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
988 /* pad the directory size to a multiple of 4 for alignment purposes */
989 nStreams = (nStreams + 3) & ~3;
991 if (DumpType & MiniDumpWithDataSegs)
992 FIXME("NIY MiniDumpWithDataSegs\n");
993 if (DumpType & MiniDumpWithHandleData)
994 FIXME("NIY MiniDumpWithHandleData\n");
995 if (DumpType & MiniDumpFilterMemory)
996 FIXME("NIY MiniDumpFilterMemory\n");
997 if (DumpType & MiniDumpScanMemory)
998 FIXME("NIY MiniDumpScanMemory\n");
1000 /* 2) write header */
1001 mdHead.Signature = MINIDUMP_SIGNATURE;
1002 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
1003 mdHead.NumberOfStreams = nStreams;
1004 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
1005 mdHead.StreamDirectoryRva = sizeof(mdHead);
1006 mdHead.u.TimeDateStamp = time(NULL);
1007 mdHead.Flags = DumpType;
1008 append(&dc, &mdHead, sizeof(mdHead));
1010 /* 3) write stream directories */
1011 dc.rva += nStreams * sizeof(mdDir);
1012 idx_stream = 0;
1014 /* 3.1) write data stream directories */
1016 /* must be first in minidump */
1017 mdDir.StreamType = SystemInfoStream;
1018 mdDir.Location.Rva = dc.rva;
1019 mdDir.Location.DataSize = dump_system_info(&dc);
1020 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1021 &mdDir, sizeof(mdDir));
1023 mdDir.StreamType = ThreadListStream;
1024 mdDir.Location.Rva = dc.rva;
1025 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
1026 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1027 &mdDir, sizeof(mdDir));
1029 mdDir.StreamType = ModuleListStream;
1030 mdDir.Location.Rva = dc.rva;
1031 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
1032 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1033 &mdDir, sizeof(mdDir));
1035 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
1036 mdDir.Location.Rva = dc.rva;
1037 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
1038 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1039 &mdDir, sizeof(mdDir));
1042 if (!(DumpType & MiniDumpWithFullMemory))
1044 mdDir.StreamType = MemoryListStream;
1045 mdDir.Location.Rva = dc.rva;
1046 mdDir.Location.DataSize = dump_memory_info(&dc);
1047 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1048 &mdDir, sizeof(mdDir));
1051 mdDir.StreamType = MiscInfoStream;
1052 mdDir.Location.Rva = dc.rva;
1053 mdDir.Location.DataSize = dump_misc_info(&dc);
1054 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1055 &mdDir, sizeof(mdDir));
1057 /* 3.2) write exception information (if any) */
1058 if (ExceptionParam)
1060 mdDir.StreamType = ExceptionStream;
1061 mdDir.Location.Rva = dc.rva;
1062 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
1063 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1064 &mdDir, sizeof(mdDir));
1067 /* 3.3) write user defined streams (if any) */
1068 if (UserStreamParam)
1070 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
1072 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
1073 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
1074 mdDir.Location.Rva = dc.rva;
1075 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1076 &mdDir, sizeof(mdDir));
1077 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
1078 UserStreamParam->UserStreamArray[i].BufferSize);
1082 /* 3.4) write full memory (if requested) */
1083 if (DumpType & MiniDumpWithFullMemory)
1085 fetch_memory64_info(&dc);
1087 mdDir.StreamType = Memory64ListStream;
1088 mdDir.Location.Rva = dc.rva;
1089 mdDir.Location.DataSize = dump_memory64_info(&dc);
1090 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
1091 &mdDir, sizeof(mdDir));
1094 /* fill the remaining directory entries with 0's (unused stream types) */
1095 /* NOTE: this should always come last in the dump! */
1096 for (i = idx_stream; i < nStreams; i++)
1097 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1099 HeapFree(GetProcessHeap(), 0, dc.mem);
1100 HeapFree(GetProcessHeap(), 0, dc.mem64);
1101 HeapFree(GetProcessHeap(), 0, dc.modules);
1102 HeapFree(GetProcessHeap(), 0, dc.threads);
1104 return TRUE;
1107 /******************************************************************
1108 * MiniDumpReadDumpStream (DEBUGHLP.@)
1112 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1113 PMINIDUMP_DIRECTORY* pdir,
1114 PVOID* stream, ULONG* size)
1116 MINIDUMP_HEADER* mdHead = base;
1118 if (mdHead->Signature == MINIDUMP_SIGNATURE)
1120 MINIDUMP_DIRECTORY* dir;
1121 DWORD i;
1123 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1124 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1126 if (dir->StreamType == str_idx)
1128 if (pdir) *pdir = dir;
1129 if (stream) *stream = (char*)base + dir->Location.Rva;
1130 if (size) *size = dir->Location.DataSize;
1131 return TRUE;
1135 SetLastError(ERROR_INVALID_PARAMETER);
1136 return FALSE;