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