oleaut32: Thousands separator support for VarFormat.
[wine/hacks.git] / dlls / dbghelp / minidump.c
blob52458f24f0170354df815b9df0881db388407308
1 /*
2 * File minidump.c - management of dumps (read & write)
4 * Copyright (C) 2004-2005, Eric Pouech
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <time.h>
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
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"
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
35 struct dump_memory
37 ULONG base;
38 ULONG size;
39 ULONG rva;
42 struct dump_module
44 unsigned is_elf;
45 ULONG base;
46 ULONG size;
47 DWORD timestamp;
48 DWORD checksum;
49 WCHAR name[MAX_PATH];
52 struct dump_context
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;
73 /******************************************************************
74 * fetch_processes_info
76 * reads system wide process information, and make spi point to the record
77 * for process of id 'pid'
79 static BOOL fetch_processes_info(struct dump_context* dc)
81 ULONG buf_size = 0x1000;
82 NTSTATUS nts;
84 dc->pcs_buffer = NULL;
85 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
86 for (;;)
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;
96 if (nts == STATUS_SUCCESS)
98 dc->spi = dc->pcs_buffer;
99 for (;;)
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);
107 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
108 dc->pcs_buffer = NULL;
109 dc->spi = NULL;
110 return FALSE;
113 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
114 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
116 NT_TIB tib;
118 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
120 #ifdef __i386__
121 /* limiting the stack dumping to the size actually used */
122 if (ctx->Esp){
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;
129 else
130 mmd->StartOfMemoryRange = (ctx->Esp - 4);
133 else
134 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
136 #elif defined(__powerpc__)
137 if (ctx->Iar){
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;
144 else
145 mmd->StartOfMemoryRange = (ctx->Iar - 4);
148 else
149 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
151 #elif defined(__x86_64__)
152 if (ctx->Rsp){
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;
159 else
160 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
163 else
164 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
166 #else
167 #error unsupported CPU
168 #endif
169 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
173 /******************************************************************
174 * fetch_thread_info
176 * fetches some information about thread of id 'tid'
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)
182 DWORD tid = dc->spi->ti[thd_idx].dwThreadID;
183 HANDLE hThread;
184 THREAD_BASIC_INFORMATION tbi;
186 memset(ctx, 0, sizeof(*ctx));
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;
199 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
201 FIXME("Couldn't open thread %u (%u)\n",
202 dc->spi->ti[thd_idx].dwThreadID, GetLastError());
203 return FALSE;
206 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
207 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
209 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
210 if (tbi.ExitStatus == STILL_ACTIVE)
212 if (tid != GetCurrentThreadId() &&
213 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
215 ctx->ContextFlags = CONTEXT_FULL;
216 if (!GetThreadContext(hThread, ctx))
217 memset(ctx, 0, sizeof(*ctx));
219 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
220 ResumeThread(hThread);
222 else if (tid == GetCurrentThreadId() && except)
224 CONTEXT lctx, *pctx;
225 mdThd->SuspendCount = 1;
226 if (except->ClientPointers)
228 EXCEPTION_POINTERS ep;
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;
236 else pctx = except->ExceptionPointers->ContextRecord;
238 *ctx = *pctx;
239 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
241 else mdThd->SuspendCount = 0;
244 CloseHandle(hThread);
245 return TRUE;
248 /******************************************************************
249 * add_module
251 * Add a module to a dump context
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)
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;
276 return TRUE;
279 /******************************************************************
280 * fetch_pe_module_info_cb
282 * Callback for accumulating in dump_context a PE modules set
284 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
285 PVOID user)
287 struct dump_context* dc = (struct dump_context*)user;
288 IMAGE_NT_HEADERS nth;
290 if (!validate_addr64(base)) return FALSE;
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;
299 /******************************************************************
300 * fetch_elf_module_info_cb
302 * Callback for accumulating in dump_context an ELF modules set
304 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
305 void* user)
307 struct dump_context* dc = (struct dump_context*)user;
308 DWORD rbase, size, checksum;
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
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;
321 static void fetch_modules_info(struct dump_context* dc)
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.
329 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
332 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
334 DWORD handle;
335 DWORD sz;
336 static const WCHAR backslashW[] = {'\\', '\0'};
338 memset(ffi, 0, sizeof(*ffi));
339 if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
341 void* info = HeapAlloc(GetProcessHeap(), 0, sz);
342 if (info && GetFileVersionInfoW(filename, handle, sz, info))
344 VS_FIXEDFILEINFO* ptr;
345 UINT len;
347 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
348 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
350 HeapFree(GetProcessHeap(), 0, info);
354 /******************************************************************
355 * add_memory_block
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).
362 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
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)
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;
375 else dc->num_mem = 0;
378 /******************************************************************
379 * writeat
381 * Writes a chunk of data at a given position in the minidump
383 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
385 DWORD written;
387 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
388 WriteFile(dc->hFile, data, size, &written, NULL);
391 /******************************************************************
392 * append
394 * writes a new chunk of data to the minidump, increasing the current
395 * rva in dc
397 static void append(struct dump_context* dc, void* data, unsigned size)
399 writeat(dc, dc->rva, data, size);
400 dc->rva += size;
403 /******************************************************************
404 * dump_exception_info
406 * Write in File the exception information from pcs
408 static unsigned dump_exception_info(struct dump_context* dc,
409 const MINIDUMP_EXCEPTION_INFORMATION* except)
411 MINIDUMP_EXCEPTION_STREAM mdExcpt;
412 EXCEPTION_RECORD rec, *prec;
413 CONTEXT ctx, *pctx;
414 int i;
416 mdExcpt.ThreadId = except->ThreadId;
417 mdExcpt.__alignment = 0;
418 if (except->ClientPointers)
420 EXCEPTION_POINTERS ep;
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;
431 else
433 prec = except->ExceptionPointers->ExceptionRecord;
434 pctx = except->ExceptionPointers->ContextRecord;
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);
447 append(dc, &mdExcpt, sizeof(mdExcpt));
448 append(dc, pctx, sizeof(*pctx));
449 return sizeof(mdExcpt);
452 /******************************************************************
453 * dump_modules
455 * Write in File the modules from pcs
457 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf)
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;
468 for (i = nmod = 0; i < dc->num_modules; i++)
470 if ((dc->modules[i].is_elf && dump_elf) ||
471 (!dc->modules[i].is_elf && !dump_elf))
472 nmod++;
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
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++)
490 if ((dc->modules[i].is_elf && !dump_elf) ||
491 (!dc->modules[i].is_elf && dump_elf))
492 continue;
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);
506 if (dc->cb)
508 MINIDUMP_CALLBACK_INPUT cbin;
509 MINIDUMP_CALLBACK_OUTPUT cbout;
511 cbin.ProcessId = dc->pid;
512 cbin.ProcessHandle = dc->hProcess;
513 cbin.CallbackType = ModuleCallback;
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;
526 cbout.u.ModuleWriteFlags = flags_out;
527 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
528 continue;
529 flags_out &= cbout.u.ModuleWriteFlags;
531 if (flags_out & ModuleWriteModule)
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));
553 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
554 sizeof(mdModuleList.NumberOfModules));
556 return sz;
559 /******************************************************************
560 * dump_system_info
562 * Dumps into File the information about the system
564 static unsigned dump_system_info(struct dump_context* dc)
566 MINIDUMP_SYSTEM_INFO mdSysInfo;
567 SYSTEM_INFO sysInfo;
568 OSVERSIONINFOW osInfo;
569 DWORD written;
570 ULONG slen;
572 GetSystemInfo(&sysInfo);
573 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
574 GetVersionExW(&osInfo);
576 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
577 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
578 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
579 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
580 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
581 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
582 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
583 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
584 mdSysInfo.PlatformId = osInfo.dwPlatformId;
586 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
587 mdSysInfo.u1.Reserved1 = 0;
588 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
590 FIXME("fill in CPU vendorID and feature set\n");
591 memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
593 append(dc, &mdSysInfo, sizeof(mdSysInfo));
595 /* write the service pack version string after this stream. It is referenced within the
596 stream by its RVA in the file. */
597 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
598 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
599 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
600 dc->rva += sizeof(ULONG) + slen;
602 return sizeof(mdSysInfo);
605 /******************************************************************
606 * dump_threads
608 * Dumps into File the information about running threads
610 static unsigned dump_threads(struct dump_context* dc,
611 const MINIDUMP_EXCEPTION_INFORMATION* except)
613 MINIDUMP_THREAD mdThd;
614 MINIDUMP_THREAD_LIST mdThdList;
615 unsigned i;
616 RVA rva_base;
617 DWORD flags_out;
618 CONTEXT ctx;
620 mdThdList.NumberOfThreads = 0;
622 rva_base = dc->rva;
623 dc->rva += sizeof(mdThdList.NumberOfThreads) +
624 dc->spi->dwThreadCount * sizeof(mdThd);
626 for (i = 0; i < dc->spi->dwThreadCount; i++)
628 fetch_thread_info(dc, i, except, &mdThd, &ctx);
630 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
631 ThreadWriteInstructionWindow;
632 if (dc->type & MiniDumpWithProcessThreadData)
633 flags_out |= ThreadWriteThreadData;
634 if (dc->type & MiniDumpWithThreadInfo)
635 flags_out |= ThreadWriteThreadInfo;
637 if (dc->cb)
639 MINIDUMP_CALLBACK_INPUT cbin;
640 MINIDUMP_CALLBACK_OUTPUT cbout;
642 cbin.ProcessId = dc->pid;
643 cbin.ProcessHandle = dc->hProcess;
644 cbin.CallbackType = ThreadCallback;
645 cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
646 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
647 cbin.u.Thread.Context = ctx;
648 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
649 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
650 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
651 mdThd.Stack.Memory.DataSize;
653 cbout.u.ThreadWriteFlags = flags_out;
654 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
655 continue;
656 flags_out &= cbout.u.ThreadWriteFlags;
658 if (flags_out & ThreadWriteThread)
660 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
662 mdThd.ThreadContext.Rva = dc->rva;
663 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
664 append(dc, &ctx, sizeof(CONTEXT));
666 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
668 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
669 mdThd.Stack.Memory.DataSize,
670 rva_base + sizeof(mdThdList.NumberOfThreads) +
671 mdThdList.NumberOfThreads * sizeof(mdThd) +
672 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
674 writeat(dc,
675 rva_base + sizeof(mdThdList.NumberOfThreads) +
676 mdThdList.NumberOfThreads * sizeof(mdThd),
677 &mdThd, sizeof(mdThd));
678 mdThdList.NumberOfThreads++;
680 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
682 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
683 * - also crop values across module boundaries,
684 * - and don't make it i386 dependent
686 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
689 writeat(dc, rva_base,
690 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
692 return dc->rva - rva_base;
695 /******************************************************************
696 * dump_memory_info
698 * dumps information about the memory of the process (stack of the threads)
700 static unsigned dump_memory_info(struct dump_context* dc)
702 MINIDUMP_MEMORY_LIST mdMemList;
703 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
704 DWORD written;
705 unsigned i, pos, len, sz;
706 RVA rva_base;
707 char tmp[1024];
709 mdMemList.NumberOfMemoryRanges = dc->num_mem;
710 append(dc, &mdMemList.NumberOfMemoryRanges,
711 sizeof(mdMemList.NumberOfMemoryRanges));
712 rva_base = dc->rva;
713 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
714 dc->rva += sz;
715 sz += sizeof(mdMemList.NumberOfMemoryRanges);
717 for (i = 0; i < dc->num_mem; i++)
719 mdMem.StartOfMemoryRange = dc->mem[i].base;
720 mdMem.Memory.Rva = dc->rva;
721 mdMem.Memory.DataSize = dc->mem[i].size;
722 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
723 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
725 len = min(dc->mem[i].size - pos, sizeof(tmp));
726 if (ReadProcessMemory(dc->hProcess,
727 (void*)(dc->mem[i].base + pos),
728 tmp, len, NULL))
729 WriteFile(dc->hFile, tmp, len, &written, NULL);
731 dc->rva += mdMem.Memory.DataSize;
732 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
733 if (dc->mem[i].rva)
735 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
739 return sz;
742 static unsigned dump_misc_info(struct dump_context* dc)
744 MINIDUMP_MISC_INFO mmi;
746 mmi.SizeOfInfo = sizeof(mmi);
747 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
748 mmi.ProcessId = dc->pid;
749 /* FIXME: create/user/kernel time */
750 mmi.ProcessCreateTime = 0;
751 mmi.ProcessKernelTime = 0;
752 mmi.ProcessUserTime = 0;
754 append(dc, &mmi, sizeof(mmi));
755 return sizeof(mmi);
758 /******************************************************************
759 * MiniDumpWriteDump (DEBUGHLP.@)
762 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
763 MINIDUMP_TYPE DumpType,
764 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
765 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
766 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
768 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
769 MINIDUMP_HEADER mdHead;
770 MINIDUMP_DIRECTORY mdDir;
771 DWORD i, nStreams, idx_stream;
772 struct dump_context dc;
774 dc.hProcess = hProcess;
775 dc.hFile = hFile;
776 dc.pid = pid;
777 dc.modules = NULL;
778 dc.num_modules = 0;
779 dc.cb = CallbackParam;
780 dc.type = DumpType;
781 dc.mem = NULL;
782 dc.num_mem = 0;
783 dc.rva = 0;
785 if (!fetch_processes_info(&dc)) return FALSE;
786 fetch_modules_info(&dc);
788 /* 1) init */
789 nStreams = 6 + (ExceptionParam ? 1 : 0) +
790 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
792 /* pad the directory size to a multiple of 4 for alignment purposes */
793 nStreams = (nStreams + 3) & ~3;
795 if (DumpType & MiniDumpWithDataSegs)
796 FIXME("NIY MiniDumpWithDataSegs\n");
797 if (DumpType & MiniDumpWithFullMemory)
798 FIXME("NIY MiniDumpWithFullMemory\n");
799 if (DumpType & MiniDumpWithHandleData)
800 FIXME("NIY MiniDumpWithHandleData\n");
801 if (DumpType & MiniDumpFilterMemory)
802 FIXME("NIY MiniDumpFilterMemory\n");
803 if (DumpType & MiniDumpScanMemory)
804 FIXME("NIY MiniDumpScanMemory\n");
806 /* 2) write header */
807 mdHead.Signature = MINIDUMP_SIGNATURE;
808 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
809 mdHead.NumberOfStreams = nStreams;
810 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */
811 mdHead.StreamDirectoryRva = sizeof(mdHead);
812 mdHead.u.TimeDateStamp = time(NULL);
813 mdHead.Flags = DumpType;
814 append(&dc, &mdHead, sizeof(mdHead));
816 /* 3) write stream directories */
817 dc.rva += nStreams * sizeof(mdDir);
818 idx_stream = 0;
820 /* 3.1) write data stream directories */
822 /* must be first in minidump */
823 mdDir.StreamType = SystemInfoStream;
824 mdDir.Location.Rva = dc.rva;
825 mdDir.Location.DataSize = dump_system_info(&dc);
826 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
827 &mdDir, sizeof(mdDir));
829 mdDir.StreamType = ThreadListStream;
830 mdDir.Location.Rva = dc.rva;
831 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
832 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
833 &mdDir, sizeof(mdDir));
835 mdDir.StreamType = ModuleListStream;
836 mdDir.Location.Rva = dc.rva;
837 mdDir.Location.DataSize = dump_modules(&dc, FALSE);
838 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
839 &mdDir, sizeof(mdDir));
841 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
842 mdDir.Location.Rva = dc.rva;
843 mdDir.Location.DataSize = dump_modules(&dc, TRUE);
844 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
845 &mdDir, sizeof(mdDir));
847 mdDir.StreamType = MemoryListStream;
848 mdDir.Location.Rva = dc.rva;
849 mdDir.Location.DataSize = dump_memory_info(&dc);
850 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
851 &mdDir, sizeof(mdDir));
853 mdDir.StreamType = MiscInfoStream;
854 mdDir.Location.Rva = dc.rva;
855 mdDir.Location.DataSize = dump_misc_info(&dc);
856 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
857 &mdDir, sizeof(mdDir));
859 /* 3.2) write exception information (if any) */
860 if (ExceptionParam)
862 mdDir.StreamType = ExceptionStream;
863 mdDir.Location.Rva = dc.rva;
864 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
865 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
866 &mdDir, sizeof(mdDir));
869 /* 3.3) write user defined streams (if any) */
870 if (UserStreamParam)
872 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
874 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
875 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
876 mdDir.Location.Rva = dc.rva;
877 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
878 &mdDir, sizeof(mdDir));
879 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
880 UserStreamParam->UserStreamArray[i].BufferSize);
884 /* fill the remaining directory entries with 0's (unused stream types) */
885 /* NOTE: this should always come last in the dump! */
886 for (i = idx_stream; i < nStreams; i++)
887 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
889 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
890 HeapFree(GetProcessHeap(), 0, dc.mem);
891 HeapFree(GetProcessHeap(), 0, dc.modules);
893 return TRUE;
896 /******************************************************************
897 * MiniDumpReadDumpStream (DEBUGHLP.@)
901 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
902 PMINIDUMP_DIRECTORY* pdir,
903 PVOID* stream, ULONG* size)
905 MINIDUMP_HEADER* mdHead = (MINIDUMP_HEADER*)base;
907 if (mdHead->Signature == MINIDUMP_SIGNATURE)
909 MINIDUMP_DIRECTORY* dir;
910 int i;
912 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
913 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
915 if (dir->StreamType == str_idx)
917 *pdir = dir;
918 *stream = (char*)base + dir->Location.Rva;
919 *size = dir->Location.DataSize;
920 return TRUE;
924 SetLastError(ERROR_INVALID_PARAMETER);
925 return FALSE;