sync with trunk (r46275)
[reactos.git] / dll / win32 / dbghelp / minidump.c
1 /*
2 * File minidump.c - management of dumps (read & write)
3 *
4 * Copyright (C) 2004-2005, Eric Pouech
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include <time.h>
22
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "dbghelp_private.h"
29 #include "winternl.h"
30 #include "psapi.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
34
35 struct dump_memory
36 {
37 ULONG64 base;
38 ULONG size;
39 ULONG rva;
40 };
41
42 struct dump_module
43 {
44 unsigned is_elf;
45 ULONG64 base;
46 ULONG size;
47 DWORD timestamp;
48 DWORD checksum;
49 WCHAR name[MAX_PATH];
50 };
51
52 struct dump_context
53 {
54 /* process & thread information */
55 HANDLE hProcess;
56 DWORD pid;
57 void* pcs_buffer;
58 SYSTEM_PROCESS_INFORMATION* spi;
59 /* module information */
60 struct dump_module* modules;
61 unsigned num_modules;
62 unsigned alloc_modules;
63 /* exception information */
64 /* output information */
65 MINIDUMP_TYPE type;
66 HANDLE hFile;
67 RVA rva;
68 struct dump_memory* mem;
69 unsigned num_mem;
70 unsigned alloc_mem;
71 /* callback information */
72 MINIDUMP_CALLBACK_INFORMATION* cb;
73 };
74
75 /******************************************************************
76 * fetch_processes_info
77 *
78 * reads system wide process information, and make spi point to the record
79 * for process of id 'pid'
80 */
81 static BOOL fetch_processes_info(struct dump_context* dc)
82 {
83 ULONG buf_size = 0x1000;
84 NTSTATUS nts;
85
86 dc->pcs_buffer = NULL;
87 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
88 for (;;)
89 {
90 nts = NtQuerySystemInformation(SystemProcessInformation,
91 dc->pcs_buffer, buf_size, NULL);
92 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
93 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
94 buf_size *= 2);
95 if (!dc->pcs_buffer) return FALSE;
96 }
97
98 if (nts == STATUS_SUCCESS)
99 {
100 dc->spi = dc->pcs_buffer;
101 for (;;)
102 {
103 if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
104 if (!dc->spi->NextEntryOffset) break;
105 dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
106 }
107 }
108 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
109 dc->pcs_buffer = NULL;
110 dc->spi = NULL;
111 return FALSE;
112 }
113
114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
115 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
116 {
117 NT_TIB tib;
118 ADDRESS64 addr;
119
120 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL) &&
121 dbghelp_current_cpu &&
122 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat)
123 {
124 if (addr.Offset)
125 {
126 addr.Offset -= dbghelp_current_cpu->word_size;
127 /* make sure stack pointer is within the established range of the stack. It could have
128 been clobbered by whatever caused the original exception. */
129 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase)
130 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
131
132 else
133 mmd->StartOfMemoryRange = addr.Offset;
134 }
135 else
136 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
137 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
138 }
139 }
140
141 /******************************************************************
142 * fetch_thread_info
143 *
144 * fetches some information about thread of id 'tid'
145 */
146 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
147 const MINIDUMP_EXCEPTION_INFORMATION* except,
148 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
149 {
150 DWORD tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
151 HANDLE hThread;
152 THREAD_BASIC_INFORMATION tbi;
153
154 memset(ctx, 0, sizeof(*ctx));
155
156 mdThd->ThreadId = tid;
157 mdThd->SuspendCount = 0;
158 mdThd->Teb = 0;
159 mdThd->Stack.StartOfMemoryRange = 0;
160 mdThd->Stack.Memory.DataSize = 0;
161 mdThd->Stack.Memory.Rva = 0;
162 mdThd->ThreadContext.DataSize = 0;
163 mdThd->ThreadContext.Rva = 0;
164 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
165 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
166
167 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
168 {
169 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
170 return FALSE;
171 }
172
173 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
174 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
175 {
176 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
177 if (tbi.ExitStatus == STILL_ACTIVE)
178 {
179 if (tid != GetCurrentThreadId() &&
180 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
181 {
182 ctx->ContextFlags = CONTEXT_FULL;
183 if (!GetThreadContext(hThread, ctx))
184 memset(ctx, 0, sizeof(*ctx));
185
186 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
187 ResumeThread(hThread);
188 }
189 else if (tid == GetCurrentThreadId() && except)
190 {
191 CONTEXT lctx, *pctx;
192 mdThd->SuspendCount = 1;
193 if (except->ClientPointers)
194 {
195 EXCEPTION_POINTERS ep;
196
197 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
198 &ep, sizeof(ep), NULL);
199 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
200 &lctx, sizeof(lctx), NULL);
201 pctx = &lctx;
202 }
203 else pctx = except->ExceptionPointers->ContextRecord;
204
205 *ctx = *pctx;
206 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
207 }
208 else mdThd->SuspendCount = 0;
209 }
210 }
211 CloseHandle(hThread);
212 return TRUE;
213 }
214
215 /******************************************************************
216 * add_module
217 *
218 * Add a module to a dump context
219 */
220 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
221 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum,
222 BOOL is_elf)
223 {
224 if (!dc->modules)
225 {
226 dc->alloc_modules = 32;
227 dc->modules = HeapAlloc(GetProcessHeap(), 0,
228 dc->alloc_modules * sizeof(*dc->modules));
229 }
230 else if(dc->num_modules >= dc->alloc_modules)
231 {
232 dc->alloc_modules *= 2;
233 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
234 dc->alloc_modules * sizeof(*dc->modules));
235 }
236 if (!dc->modules)
237 {
238 dc->alloc_modules = dc->num_modules = 0;
239 return FALSE;
240 }
241 if (is_elf ||
242 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base,
243 dc->modules[dc->num_modules].name,
244 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)))
245 lstrcpynW(dc->modules[dc->num_modules].name, name,
246 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR));
247 dc->modules[dc->num_modules].base = base;
248 dc->modules[dc->num_modules].size = size;
249 dc->modules[dc->num_modules].timestamp = timestamp;
250 dc->modules[dc->num_modules].checksum = checksum;
251 dc->modules[dc->num_modules].is_elf = is_elf;
252 dc->num_modules++;
253
254 return TRUE;
255 }
256
257 /******************************************************************
258 * fetch_pe_module_info_cb
259 *
260 * Callback for accumulating in dump_context a PE modules set
261 */
262 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
263 PVOID user)
264 {
265 struct dump_context* dc = user;
266 IMAGE_NT_HEADERS nth;
267
268 if (!validate_addr64(base)) return FALSE;
269
270 if (pe_load_nt_header(dc->hProcess, base, &nth))
271 add_module(user, name, base, size,
272 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
273 FALSE);
274 return TRUE;
275 }
276
277 /******************************************************************
278 * fetch_elf_module_info_cb
279 *
280 * Callback for accumulating in dump_context an ELF modules set
281 */
282 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
283 void* user)
284 {
285 struct dump_context* dc = user;
286 DWORD rbase, size, checksum;
287
288 /* FIXME: there's no relevant timestamp on ELF modules */
289 /* NB: if we have a non-null base from the live-target use it (whenever
290 * the ELF module is relocatable or not). If we have a null base (ELF
291 * module isn't relocatable) then grab its base address from ELF file
292 */
293 if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
294 size = checksum = 0;
295 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
296 return TRUE;
297 }
298
299 /******************************************************************
300 * fetch_macho_module_info_cb
301 *
302 * Callback for accumulating in dump_context a Mach-O modules set
303 */
304 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
305 void* user)
306 {
307 struct dump_context* dc = (struct dump_context*)user;
308 DWORD rbase, size, checksum;
309
310 /* FIXME: there's no relevant timestamp on Mach-O modules */
311 /* NB: if we have a non-null base from the live-target use it. If we have
312 * a null base, then grab its base address from Mach-O file.
313 */
314 if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
315 size = checksum = 0;
316 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
317 return TRUE;
318 }
319
320 static void fetch_modules_info(struct dump_context* dc)
321 {
322 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
323 /* Since we include ELF modules in a separate stream from the regular PE ones,
324 * we can always include those ELF modules (they don't eat lots of space)
325 * And it's always a good idea to have a trace of the loaded ELF modules for
326 * a given application in a post mortem debugging condition.
327 */
328 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
329 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
330 }
331
332 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
333 {
334 DWORD handle;
335 DWORD sz;
336 static const WCHAR backslashW[] = {'\\', '\0'};
337
338 memset(ffi, 0, sizeof(*ffi));
339 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
340 {
341 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
342 if (info && GetFileVersionInfoW(filename, handle, sz, info))
343 {
344 VS_FIXEDFILEINFO* ptr;
345 UINT len;
346
347 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
348 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
349 }
350 HeapFree(GetProcessHeap(), 0, info);
351 }
352 }
353
354 /******************************************************************
355 * add_memory_block
356 *
357 * Add a memory block to be dumped in a minidump
358 * If rva is non 0, it's the rva in the minidump where has to be stored
359 * also the rva of the memory block when written (this allows to reference
360 * a memory block from outside the list of memory blocks).
361 */
362 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
363 {
364 if (!dc->mem)
365 {
366 dc->alloc_mem = 32;
367 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
368 }
369 else if (dc->num_mem >= dc->alloc_mem)
370 {
371 dc->alloc_mem *= 2;
372 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
373 dc->alloc_mem * sizeof(*dc->mem));
374 }
375 if (dc->mem)
376 {
377 dc->mem[dc->num_mem].base = base;
378 dc->mem[dc->num_mem].size = size;
379 dc->mem[dc->num_mem].rva = rva;
380 dc->num_mem++;
381 }
382 else dc->num_mem = dc->alloc_mem = 0;
383 }
384
385 /******************************************************************
386 * writeat
387 *
388 * Writes a chunk of data at a given position in the minidump
389 */
390 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
391 {
392 DWORD written;
393
394 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
395 WriteFile(dc->hFile, data, size, &written, NULL);
396 }
397
398 /******************************************************************
399 * append
400 *
401 * writes a new chunk of data to the minidump, increasing the current
402 * rva in dc
403 */
404 static void append(struct dump_context* dc, const void* data, unsigned size)
405 {
406 writeat(dc, dc->rva, data, size);
407 dc->rva += size;
408 }
409
410 /******************************************************************
411 * dump_exception_info
412 *
413 * Write in File the exception information from pcs
414 */
415 static unsigned dump_exception_info(struct dump_context* dc,
416 const MINIDUMP_EXCEPTION_INFORMATION* except)
417 {
418 MINIDUMP_EXCEPTION_STREAM mdExcpt;
419 EXCEPTION_RECORD rec, *prec;
420 CONTEXT ctx, *pctx;
421 DWORD i;
422
423 mdExcpt.ThreadId = except->ThreadId;
424 mdExcpt.__alignment = 0;
425 if (except->ClientPointers)
426 {
427 EXCEPTION_POINTERS ep;
428
429 ReadProcessMemory(dc->hProcess,
430 except->ExceptionPointers, &ep, sizeof(ep), NULL);
431 ReadProcessMemory(dc->hProcess,
432 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
433 ReadProcessMemory(dc->hProcess,
434 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
435 prec = &rec;
436 pctx = &ctx;
437 }
438 else
439 {
440 prec = except->ExceptionPointers->ExceptionRecord;
441 pctx = except->ExceptionPointers->ContextRecord;
442 }
443 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
444 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
445 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
446 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
447 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
448 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
449 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
450 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
451 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
452 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
453
454 append(dc, &mdExcpt, sizeof(mdExcpt));
455 append(dc, pctx, sizeof(*pctx));
456 return sizeof(mdExcpt);
457 }
458
459 /******************************************************************
460 * dump_modules
461 *
462 * Write in File the modules from pcs
463 */
464 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
465 {
466 MINIDUMP_MODULE mdModule;
467 MINIDUMP_MODULE_LIST mdModuleList;
468 char tmp[1024];
469 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
470 ULONG i, nmod;
471 RVA rva_base;
472 DWORD flags_out;
473 unsigned sz;
474
475 for (i = nmod = 0; i < dc->num_modules; i++)
476 {
477 if ((dc->modules[i].is_elf && dump_elf) ||
478 (!dc->modules[i].is_elf && !dump_elf))
479 nmod++;
480 }
481
482 mdModuleList.NumberOfModules = 0;
483 /* reserve space for mdModuleList
484 * FIXME: since we don't support 0 length arrays, we cannot use the
485 * size of mdModuleList
486 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
487 */
488
489 /* the stream size is just the size of the module index. It does not include the data for the
490 names of each module. *Technically* the names are supposed to go into the common string table
491 in the minidump file. Since each string is referenced by RVA they can all safely be located
492 anywhere between streams in the file, so the end of this stream is sufficient. */
493 rva_base = dc->rva;
494 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
495 for (i = 0; i < dc->num_modules; i++)
496 {
497 if ((dc->modules[i].is_elf && !dump_elf) ||
498 (!dc->modules[i].is_elf && dump_elf))
499 continue;
500
501 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
502 if (dc->type & MiniDumpWithDataSegs)
503 flags_out |= ModuleWriteDataSeg;
504 if (dc->type & MiniDumpWithProcessThreadData)
505 flags_out |= ModuleWriteTlsData;
506 if (dc->type & MiniDumpWithCodeSegs)
507 flags_out |= ModuleWriteCodeSegs;
508 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
509 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
510 FIXME("Buffer overflow!!!\n");
511 lstrcpyW(ms->Buffer, dc->modules[i].name);
512
513 if (dc->cb)
514 {
515 MINIDUMP_CALLBACK_INPUT cbin;
516 MINIDUMP_CALLBACK_OUTPUT cbout;
517
518 cbin.ProcessId = dc->pid;
519 cbin.ProcessHandle = dc->hProcess;
520 cbin.CallbackType = ModuleCallback;
521
522 cbin.u.Module.FullPath = ms->Buffer;
523 cbin.u.Module.BaseOfImage = dc->modules[i].base;
524 cbin.u.Module.SizeOfImage = dc->modules[i].size;
525 cbin.u.Module.CheckSum = dc->modules[i].checksum;
526 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
527 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
528 cbin.u.Module.CvRecord = NULL;
529 cbin.u.Module.SizeOfCvRecord = 0;
530 cbin.u.Module.MiscRecord = NULL;
531 cbin.u.Module.SizeOfMiscRecord = 0;
532
533 cbout.u.ModuleWriteFlags = flags_out;
534 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
535 continue;
536 flags_out &= cbout.u.ModuleWriteFlags;
537 }
538 if (flags_out & ModuleWriteModule)
539 {
540 mdModule.BaseOfImage = dc->modules[i].base;
541 mdModule.SizeOfImage = dc->modules[i].size;
542 mdModule.CheckSum = dc->modules[i].checksum;
543 mdModule.TimeDateStamp = dc->modules[i].timestamp;
544 mdModule.ModuleNameRva = dc->rva;
545 ms->Length -= sizeof(WCHAR);
546 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
547 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
548 mdModule.CvRecord.DataSize = 0; /* FIXME */
549 mdModule.CvRecord.Rva = 0; /* FIXME */
550 mdModule.MiscRecord.DataSize = 0; /* FIXME */
551 mdModule.MiscRecord.Rva = 0; /* FIXME */
552 mdModule.Reserved0 = 0; /* FIXME */
553 mdModule.Reserved1 = 0; /* FIXME */
554 writeat(dc,
555 rva_base + sizeof(mdModuleList.NumberOfModules) +
556 mdModuleList.NumberOfModules++ * sizeof(mdModule),
557 &mdModule, sizeof(mdModule));
558 }
559 }
560 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
561 sizeof(mdModuleList.NumberOfModules));
562
563 return sz;
564 }
565
566 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
567 * We are compiled with -fPIC, so we can't clobber ebx.
568 */
569 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
570 {
571 #if defined(__GNUC__) && defined(__i386__)
572 __asm__("pushl %%ebx\n\t"
573 "cpuid\n\t"
574 "movl %%ebx, %%esi\n\t"
575 "popl %%ebx"
576 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
577 : "0" (ax));
578 #endif
579 }
580
581 /* From xf86info havecpuid.c 1.11 */
582 static inline int have_x86cpuid(void)
583 {
584 #if defined(__GNUC__) && defined(__i386__)
585 unsigned int f1, f2;
586 __asm__("pushfl\n\t"
587 "pushfl\n\t"
588 "popl %0\n\t"
589 "movl %0,%1\n\t"
590 "xorl %2,%0\n\t"
591 "pushl %0\n\t"
592 "popfl\n\t"
593 "pushfl\n\t"
594 "popl %0\n\t"
595 "popfl"
596 : "=&r" (f1), "=&r" (f2)
597 : "ir" (0x00200000));
598 return ((f1^f2) & 0x00200000) != 0;
599 #else
600 return 0;
601 #endif
602 }
603
604 /******************************************************************
605 * dump_system_info
606 *
607 * Dumps into File the information about the system
608 */
609 static unsigned dump_system_info(struct dump_context* dc)
610 {
611 MINIDUMP_SYSTEM_INFO mdSysInfo;
612 SYSTEM_INFO sysInfo;
613 OSVERSIONINFOW osInfo;
614 DWORD written;
615 ULONG slen;
616
617 GetSystemInfo(&sysInfo);
618 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
619 GetVersionExW(&osInfo);
620
621 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
622 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
623 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
624 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
625 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
626 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
627 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
628 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
629 mdSysInfo.PlatformId = osInfo.dwPlatformId;
630
631 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
632 mdSysInfo.u1.Reserved1 = 0;
633 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
634
635 if (have_x86cpuid())
636 {
637 unsigned regs0[4], regs1[4];
638
639 do_x86cpuid(0, regs0);
640 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
641 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
642 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
643 do_x86cpuid(1, regs1);
644 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
645 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
646 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
647 if (regs0[1] == 0x68747541 /* "Auth" */ &&
648 regs0[3] == 0x69746e65 /* "enti" */ &&
649 regs0[2] == 0x444d4163 /* "cAMD" */)
650 {
651 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */
652 if (regs1[0] >= 0x80000001)
653 {
654 do_x86cpuid(0x80000001, regs1); /* get vendor features */
655 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
656 }
657 }
658 }
659 else
660 {
661 unsigned i;
662 ULONG64 one = 1;
663
664 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
665 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
666
667 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
668 if (IsProcessorFeaturePresent(i))
669 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
670 }
671 append(dc, &mdSysInfo, sizeof(mdSysInfo));
672
673 /* write the service pack version string after this stream. It is referenced within the
674 stream by its RVA in the file. */
675 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
676 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
677 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
678 dc->rva += sizeof(ULONG) + slen;
679
680 return sizeof(mdSysInfo);
681 }
682
683 /******************************************************************
684 * dump_threads
685 *
686 * Dumps into File the information about running threads
687 */
688 static unsigned dump_threads(struct dump_context* dc,
689 const MINIDUMP_EXCEPTION_INFORMATION* except)
690 {
691 MINIDUMP_THREAD mdThd;
692 MINIDUMP_THREAD_LIST mdThdList;
693 unsigned i, sz;
694 RVA rva_base;
695 DWORD flags_out;
696 CONTEXT ctx;
697
698 mdThdList.NumberOfThreads = 0;
699
700 rva_base = dc->rva;
701 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->spi->dwThreadCount * sizeof(mdThd);
702
703 for (i = 0; i < dc->spi->dwThreadCount; i++)
704 {
705 fetch_thread_info(dc, i, except, &mdThd, &ctx);
706
707 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
708 ThreadWriteInstructionWindow;
709 if (dc->type & MiniDumpWithProcessThreadData)
710 flags_out |= ThreadWriteThreadData;
711 if (dc->type & MiniDumpWithThreadInfo)
712 flags_out |= ThreadWriteThreadInfo;
713
714 if (dc->cb)
715 {
716 MINIDUMP_CALLBACK_INPUT cbin;
717 MINIDUMP_CALLBACK_OUTPUT cbout;
718
719 cbin.ProcessId = dc->pid;
720 cbin.ProcessHandle = dc->hProcess;
721 cbin.CallbackType = ThreadCallback;
722 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
723 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
724 cbin.u.Thread.Context = ctx;
725 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
726 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
727 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
728 mdThd.Stack.Memory.DataSize;
729
730 cbout.u.ThreadWriteFlags = flags_out;
731 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
732 continue;
733 flags_out &= cbout.u.ThreadWriteFlags;
734 }
735 if (flags_out & ThreadWriteThread)
736 {
737 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
738 {
739 mdThd.ThreadContext.Rva = dc->rva;
740 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
741 append(dc, &ctx, sizeof(CONTEXT));
742 }
743 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
744 {
745 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
746 mdThd.Stack.Memory.DataSize,
747 rva_base + sizeof(mdThdList.NumberOfThreads) +
748 mdThdList.NumberOfThreads * sizeof(mdThd) +
749 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
750 }
751 writeat(dc,
752 rva_base + sizeof(mdThdList.NumberOfThreads) +
753 mdThdList.NumberOfThreads * sizeof(mdThd),
754 &mdThd, sizeof(mdThd));
755 mdThdList.NumberOfThreads++;
756 }
757 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
758 {
759 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
760 * - also crop values across module boundaries,
761 * - and don't make it i386 dependent
762 */
763 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
764 }
765 }
766 writeat(dc, rva_base,
767 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
768
769 return sz;
770 }
771
772 /******************************************************************
773 * dump_memory_info
774 *
775 * dumps information about the memory of the process (stack of the threads)
776 */
777 static unsigned dump_memory_info(struct dump_context* dc)
778 {
779 MINIDUMP_MEMORY_LIST mdMemList;
780 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
781 DWORD written;
782 unsigned i, pos, len, sz;
783 RVA rva_base;
784 char tmp[1024];
785
786 mdMemList.NumberOfMemoryRanges = dc->num_mem;
787 append(dc, &mdMemList.NumberOfMemoryRanges,
788 sizeof(mdMemList.NumberOfMemoryRanges));
789 rva_base = dc->rva;
790 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
791 dc->rva += sz;
792 sz += sizeof(mdMemList.NumberOfMemoryRanges);
793
794 for (i = 0; i < dc->num_mem; i++)
795 {
796 mdMem.StartOfMemoryRange = dc->mem[i].base;
797 mdMem.Memory.Rva = dc->rva;
798 mdMem.Memory.DataSize = dc->mem[i].size;
799 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
800 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
801 {
802 len = min(dc->mem[i].size - pos, sizeof(tmp));
803 if (ReadProcessMemory(dc->hProcess,
804 (void*)(DWORD_PTR)(dc->mem[i].base + pos),
805 tmp, len, NULL))
806 WriteFile(dc->hFile, tmp, len, &written, NULL);
807 }
808 dc->rva += mdMem.Memory.DataSize;
809 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
810 if (dc->mem[i].rva)
811 {
812 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
813 }
814 }
815
816 return sz;
817 }
818
819 static unsigned dump_misc_info(struct dump_context* dc)
820 {
821 MINIDUMP_MISC_INFO mmi;
822
823 mmi.SizeOfInfo = sizeof(mmi);
824 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
825 mmi.ProcessId = dc->pid;
826 /* FIXME: create/user/kernel time */
827 mmi.ProcessCreateTime = 0;
828 mmi.ProcessKernelTime = 0;
829 mmi.ProcessUserTime = 0;
830
831 append(dc, &mmi, sizeof(mmi));
832 return sizeof(mmi);
833 }
834
835 /******************************************************************
836 * MiniDumpWriteDump (DEBUGHLP.@)
837 *
838 */
839 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
840 MINIDUMP_TYPE DumpType,
841 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
842 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
843 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
844 {
845 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
846 MINIDUMP_HEADER mdHead;
847 MINIDUMP_DIRECTORY mdDir;
848 DWORD i, nStreams, idx_stream;
849 struct dump_context dc;
850
851 dc.hProcess = hProcess;
852 dc.hFile = hFile;
853 dc.pid = pid;
854 dc.modules = NULL;
855 dc.num_modules = 0;
856 dc.alloc_modules = 0;
857 dc.cb = CallbackParam;
858 dc.type = DumpType;
859 dc.mem = NULL;
860 dc.num_mem = 0;
861 dc.alloc_mem = 0;
862 dc.rva = 0;
863
864 if (!fetch_processes_info(&dc)) return FALSE;
865 fetch_modules_info(&dc);
866
867 /* 1) init */
868 nStreams = 6 + (ExceptionParam ? 1 : 0) +
869 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
870
871 /* pad the directory size to a multiple of 4 for alignment purposes */
872 nStreams = (nStreams + 3) & ~3;
873
874 if (DumpType & MiniDumpWithDataSegs)
875 FIXME("NIY MiniDumpWithDataSegs\n");
876 if (DumpType & MiniDumpWithFullMemory)
877 FIXME("NIY MiniDumpWithFullMemory\n");
878 if (DumpType & MiniDumpWithHandleData)
879 FIXME("NIY MiniDumpWithHandleData\n");
880 if (DumpType & MiniDumpFilterMemory)
881 FIXME("NIY MiniDumpFilterMemory\n");
882 if (DumpType & MiniDumpScanMemory)
883 FIXME("NIY MiniDumpScanMemory\n");
884
885 /* 2) write header */
886 mdHead.Signature = MINIDUMP_SIGNATURE;
887 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
888 mdHead.NumberOfStreams = nStreams;
889 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
890 mdHead.StreamDirectoryRva = sizeof(mdHead);
891 mdHead.u.TimeDateStamp = time(NULL);
892 mdHead.Flags = DumpType;
893 append(&dc, &mdHead, sizeof(mdHead));
894
895 /* 3) write stream directories */
896 dc.rva += nStreams * sizeof(mdDir);
897 idx_stream = 0;
898
899 /* 3.1) write data stream directories */
900
901 /* must be first in minidump */
902 mdDir.StreamType = SystemInfoStream;
903 mdDir.Location.Rva = dc.rva;
904 mdDir.Location.DataSize = dump_system_info(&dc);
905 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
906 &mdDir, sizeof(mdDir));
907
908 mdDir.StreamType = ThreadListStream;
909 mdDir.Location.Rva = dc.rva;
910 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
911 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
912 &mdDir, sizeof(mdDir));
913
914 mdDir.StreamType = ModuleListStream;
915 mdDir.Location.Rva = dc.rva;
916 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
917 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
918 &mdDir, sizeof(mdDir));
919
920 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
921 mdDir.Location.Rva = dc.rva;
922 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
923 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
924 &mdDir, sizeof(mdDir));
925
926 mdDir.StreamType = MemoryListStream;
927 mdDir.Location.Rva = dc.rva;
928 mdDir.Location.DataSize = dump_memory_info(&dc);
929 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
930 &mdDir, sizeof(mdDir));
931
932 mdDir.StreamType = MiscInfoStream;
933 mdDir.Location.Rva = dc.rva;
934 mdDir.Location.DataSize = dump_misc_info(&dc);
935 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
936 &mdDir, sizeof(mdDir));
937
938 /* 3.2) write exception information (if any) */
939 if (ExceptionParam)
940 {
941 mdDir.StreamType = ExceptionStream;
942 mdDir.Location.Rva = dc.rva;
943 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
944 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
945 &mdDir, sizeof(mdDir));
946 }
947
948 /* 3.3) write user defined streams (if any) */
949 if (UserStreamParam)
950 {
951 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
952 {
953 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
954 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
955 mdDir.Location.Rva = dc.rva;
956 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
957 &mdDir, sizeof(mdDir));
958 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
959 UserStreamParam->UserStreamArray[i].BufferSize);
960 }
961 }
962
963 /* fill the remaining directory entries with 0's (unused stream types) */
964 /* NOTE: this should always come last in the dump! */
965 for (i = idx_stream; i < nStreams; i++)
966 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
967
968 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
969 HeapFree(GetProcessHeap(), 0, dc.mem);
970 HeapFree(GetProcessHeap(), 0, dc.modules);
971
972 return TRUE;
973 }
974
975 /******************************************************************
976 * MiniDumpReadDumpStream (DEBUGHLP.@)
977 *
978 *
979 */
980 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
981 PMINIDUMP_DIRECTORY* pdir,
982 PVOID* stream, ULONG* size)
983 {
984 MINIDUMP_HEADER* mdHead = base;
985
986 if (mdHead->Signature == MINIDUMP_SIGNATURE)
987 {
988 MINIDUMP_DIRECTORY* dir;
989 DWORD i;
990
991 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
992 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
993 {
994 if (dir->StreamType == str_idx)
995 {
996 if (pdir) *pdir = dir;
997 if (stream) *stream = (char*)base + dir->Location.Rva;
998 if (size) *size = dir->Location.DataSize;
999 return TRUE;
1000 }
1001 }
1002 }
1003 SetLastError(ERROR_INVALID_PARAMETER);
1004 return FALSE;
1005 }