Added support for minidump (read & write).
[wine.git] / dlls / dbghelp / minidump.c
blob8a3adb0cb0d125e584b7ef12531fbd2822437405
1 /*
2 * File minidump.c - management of dumps (read & write)
4 * Copyright (C) 2004, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <time.h>
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
26 #include "dbghelp_private.h"
27 #include "ntstatus.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "psapi.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
36 struct dump_memory
38 ULONG base;
39 ULONG size;
40 ULONG rva;
43 struct dump_module
45 unsigned is_elf;
46 ULONG base;
47 ULONG size;
48 char name[MAX_PATH];
51 struct dump_context
53 /* process & thread information */
54 HANDLE hProcess;
55 DWORD pid;
56 void* pcs_buffer;
57 SYSTEM_PROCESS_INFORMATION* spi;
58 /* module information */
59 struct dump_module* module;
60 unsigned num_module;
61 /* exception information */
62 /* output information */
63 MINIDUMP_TYPE type;
64 HANDLE hFile;
65 RVA rva;
66 struct dump_memory* mem;
67 unsigned num_mem;
68 /* callback information */
69 MINIDUMP_CALLBACK_INFORMATION* cb;
72 /******************************************************************
73 * fetch_process_info
75 * reads system wide process information, and make spi point to the record
76 * for process of id 'pid'
78 static BOOL fetch_process_info(struct dump_context* dc)
80 ULONG buf_size = 0x1000;
81 NTSTATUS nts;
83 dc->pcs_buffer = NULL;
84 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
85 for (;;)
87 nts = NtQuerySystemInformation(SystemProcessInformation,
88 dc->pcs_buffer, buf_size, NULL);
89 if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
90 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
91 buf_size *= 2);
92 if (!dc->pcs_buffer) return FALSE;
95 if (nts == STATUS_SUCCESS)
97 dc->spi = dc->pcs_buffer;
98 for (;;)
100 if (dc->spi->dwProcessID == dc->pid) return TRUE;
101 if (!dc->spi->dwOffset) break;
102 dc->spi = (SYSTEM_PROCESS_INFORMATION*)
103 ((char*)dc->spi + dc->spi->dwOffset);
106 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
107 dc->pcs_buffer = NULL;
108 dc->spi = NULL;
109 return FALSE;
112 /******************************************************************
113 * fetch_thread_info
115 * fetches some information about thread of id 'tid'
117 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
118 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
120 NT_TIB tib;
121 DWORD tid = dc->spi->ti[thd_idx].dwThreadID;
122 HANDLE hThread;
123 THREAD_BASIC_INFORMATION tbi;
125 memset(ctx, 0, sizeof(*ctx));
127 mdThd->ThreadId = dc->spi->ti[thd_idx].dwThreadID;
128 mdThd->SuspendCount = 0;
129 mdThd->Teb = 0;
130 mdThd->Stack.StartOfMemoryRange = 0;
131 mdThd->Stack.Memory.DataSize = 0;
132 mdThd->Stack.Memory.Rva = 0;
133 mdThd->ThreadContext.DataSize = 0;
134 mdThd->ThreadContext.Rva = 0;
135 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
136 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
138 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
140 FIXME("Couldn't open thread %lu (%lu)\n",
141 dc->spi->ti[thd_idx].dwThreadID, GetLastError());
142 return FALSE;
145 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
146 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
148 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
149 if (tbi.ExitStatus == STILL_ACTIVE && tid != GetCurrentThreadId() &&
150 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
152 mdThd->SuspendCount--;
153 ctx->ContextFlags = CONTEXT_FULL;
154 if (!GetThreadContext(hThread, ctx))
155 memset(ctx, 0, sizeof(*ctx));
157 if (ReadProcessMemory(dc->hProcess, tbi.TebBaseAddress,
158 &tib, sizeof(tib), NULL))
160 #ifdef __i386__
161 /* limiting the stack dumping to the size actually used */
162 if (ctx->Esp)
163 mdThd->Stack.StartOfMemoryRange = (ctx->Esp - 4);
164 else
165 mdThd->Stack.StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
166 mdThd->Stack.Memory.DataSize = (ULONG_PTR)tib.StackBase -
167 mdThd->Stack.StartOfMemoryRange;
168 #else
169 #error unsupported CPU
170 #endif
172 ResumeThread(hThread);
175 CloseHandle(hThread);
176 return TRUE;
179 /******************************************************************
180 * add_module
182 * Add a module to a dump context
184 static BOOL add_module(struct dump_context* dc, const char* name,
185 DWORD base, DWORD size, BOOL is_elf)
187 if (!dc->module)
188 dc->module = HeapAlloc(GetProcessHeap(), 0, ++dc->num_module * sizeof(*dc->module));
189 else
190 dc->module = HeapReAlloc(GetProcessHeap(), 0, dc->module, ++dc->num_module * sizeof(*dc->module));
191 if (!dc->module) return FALSE;
192 if (is_elf ||
193 !GetModuleFileNameExA(dc->hProcess, (HMODULE)base,
194 dc->module[dc->num_module - 1].name,
195 sizeof(dc->module[dc->num_module - 1].name)))
196 lstrcpynA(dc->module[dc->num_module - 1].name, name,
197 sizeof(dc->module[dc->num_module - 1].name));
198 dc->module[dc->num_module - 1].base = base;
199 dc->module[dc->num_module - 1].size = size;
200 dc->module[dc->num_module - 1].is_elf = is_elf;
202 return TRUE;
205 /******************************************************************
206 * fetch_pe_module_info_cb
208 * Callback for accumulating in dump_context a PE modules set
210 static BOOL WINAPI fetch_pe_module_info_cb(char* name, DWORD base, DWORD size,
211 void* user)
213 return add_module((struct dump_context*)user, name, base, size, FALSE);
216 /******************************************************************
217 * fetch_elf_module_info_cb
219 * Callback for accumulating in dump_context a ELF modules set
221 static BOOL fetch_elf_module_info_cb(const char* name, unsigned long base,
222 void* user)
224 return add_module((struct dump_context*)user, name,
225 base, 0 /* FIXME */, TRUE);
228 static void fetch_module_info(struct dump_context* dc)
230 WINE_FIXME("--> %p\n", dc->hProcess);
231 EnumerateLoadedModules(dc->hProcess, fetch_pe_module_info_cb, dc);
232 /* Since we include ELF modules in a separate stream from the regular PE ones,
233 * we can always include those ELF modules (they don't eat lots of space)
234 * And it's always a good idea to have a trace of the loaded ELF modules for
235 * a given application in a post mortem debugging condition.
237 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
240 /******************************************************************
241 * add_memory_block
243 * Add a memory block to be dumped in a minidump
244 * If rva is non 0, it's the rva in the minidump where has to be stored
245 * also the rva of the memory block when written (this allows to reference
246 * a memory block from outside the list of memory blocks).
248 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
250 if (dc->mem)
251 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
252 ++dc->num_mem * sizeof(*dc->mem));
253 else
254 dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
255 if (dc->mem)
257 dc->mem[dc->num_mem - 1].base = base;
258 dc->mem[dc->num_mem - 1].size = size;
259 dc->mem[dc->num_mem - 1].rva = rva;
261 else dc->num_mem = 0;
264 /******************************************************************
265 * writeat
267 * Writes a chunk of data at a given position in the minidump
269 static void writeat(struct dump_context* dc, RVA rva, void* data, unsigned size)
271 DWORD written;
273 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
274 WriteFile(dc->hFile, data, size, &written, NULL);
277 /******************************************************************
278 * append
280 * writes a new chunk of data to the minidump, increasing the current
281 * rva in dc
283 static void append(struct dump_context* dc, void* data, unsigned size)
285 writeat(dc, dc->rva, data, size);
286 dc->rva += size;
289 /******************************************************************
290 * dump_exception_info
292 * Write in File the exception information from pcs
294 static void dump_exception_info(struct dump_context* dc,
295 const MINIDUMP_EXCEPTION_INFORMATION* except)
297 MINIDUMP_EXCEPTION_STREAM mdExcpt;
298 EXCEPTION_RECORD rec, *prec;
299 CONTEXT ctx, *pctx;
300 int i;
302 mdExcpt.ThreadId = except->ThreadId;
303 mdExcpt.__alignment = 0;
304 if (except->ClientPointers)
306 EXCEPTION_POINTERS ep;
308 ReadProcessMemory(dc->hProcess,
309 except->ExceptionPointers, &ep, sizeof(ep), NULL);
310 ReadProcessMemory(dc->hProcess,
311 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
312 ReadProcessMemory(dc->hProcess,
313 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
314 prec = &rec;
315 pctx = &ctx;
318 else
320 prec = except->ExceptionPointers->ExceptionRecord;
321 pctx = except->ExceptionPointers->ContextRecord;
323 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
324 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
325 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
326 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
327 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
328 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
329 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
330 mdExcpt.ExceptionRecord.ExceptionInformation[i] = (DWORD_PTR)prec->ExceptionInformation[i];
331 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
332 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
334 append(dc, &mdExcpt, sizeof(mdExcpt));
335 append(dc, pctx, sizeof(*pctx));
338 /******************************************************************
339 * dump_modules
341 * Write in File the modules from pcs
343 static void dump_modules(struct dump_context* dc, BOOL dump_elf)
345 MINIDUMP_MODULE mdModule;
346 MINIDUMP_MODULE_LIST mdModuleList;
347 char tmp[1024];
348 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
349 ULONG i, nmod;
350 RVA rva_base;
351 DWORD flags_out;
353 for (i = nmod = 0; i < dc->num_module; i++)
355 if ((dc->module[i].is_elf && dump_elf) || (!dc->module[i].is_elf && !dump_elf))
356 nmod++;
359 mdModuleList.NumberOfModules = 0;
360 /* reserve space for mdModuleList
361 * FIXME: since we don't support 0 length arrays, we cannot use the
362 * size of mdModuleList
363 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
365 rva_base = dc->rva;
366 dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
367 for (i = 0; i < dc->num_module; i++)
369 if ((dc->module[i].is_elf && !dump_elf) || (!dc->module[i].is_elf && dump_elf))
370 continue;
372 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
373 if (dc->type & MiniDumpWithDataSegs)
374 flags_out |= ModuleWriteDataSeg;
375 if (dc->type & MiniDumpWithProcessThreadData)
376 flags_out |= ModuleWriteTlsData;
377 if (dc->type & MiniDumpWithCodeSegs)
378 flags_out |= ModuleWriteCodeSegs;
379 ms->Length = MultiByteToWideChar(CP_ACP, 0,
380 dc->module[i].name, -1,
381 NULL, 0) * sizeof(WCHAR);
382 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
383 FIXME("Buffer overflow!!!\n");
384 MultiByteToWideChar(CP_ACP, 0, dc->module[i].name, -1,
385 ms->Buffer, ms->Length);
387 if (dc->cb)
389 MINIDUMP_CALLBACK_INPUT cbin;
390 MINIDUMP_CALLBACK_OUTPUT cbout;
392 cbin.ProcessId = dc->pid;
393 cbin.ProcessHandle = dc->hProcess;
394 cbin.CallbackType = ModuleCallback;
396 cbin.u.Module.FullPath = ms->Buffer;
397 cbin.u.Module.BaseOfImage = dc->module[i].base;
398 cbin.u.Module.SizeOfImage = dc->module[i].size;
399 cbin.u.Module.CheckSum = 0; /* FIXME */
400 cbin.u.Module.TimeDateStamp = 0; /* FIXME */
401 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
402 cbin.u.Module.CvRecord = NULL;
403 cbin.u.Module.SizeOfCvRecord = 0;
404 cbin.u.Module.MiscRecord = NULL;
405 cbin.u.Module.SizeOfMiscRecord = 0;
407 cbout.u.ModuleWriteFlags = flags_out;
408 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
409 continue;
410 flags_out &= cbout.u.ModuleWriteFlags;
412 if (flags_out & ModuleWriteModule)
414 mdModule.BaseOfImage = dc->module[i].base;
415 mdModule.SizeOfImage = dc->module[i].size;
416 mdModule.CheckSum = 0; /* FIXME */
417 mdModule.TimeDateStamp = 0; /* FIXME */
418 mdModule.ModuleNameRva = dc->rva;
419 ms->Length -= sizeof(WCHAR);
420 append(dc, ms, sizeof(ULONG) + ms->Length);
421 memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */
422 mdModule.CvRecord.DataSize = 0; /* FIXME */
423 mdModule.CvRecord.Rva = 0; /* FIXME */
424 mdModule.MiscRecord.DataSize = 0; /* FIXME */
425 mdModule.MiscRecord.Rva = 0; /* FIXME */
426 mdModule.Reserved0 = 0; /* FIXME */
427 mdModule.Reserved1 = 0; /* FIXME */
428 writeat(dc,
429 rva_base + sizeof(mdModuleList.NumberOfModules) +
430 mdModuleList.NumberOfModules++ * sizeof(mdModule),
431 &mdModule, sizeof(mdModule));
434 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
435 sizeof(mdModuleList.NumberOfModules));
438 /******************************************************************
439 * dump_system_info
441 * Dumps into File the information about the system
443 static void dump_system_info(struct dump_context* dc)
445 MINIDUMP_SYSTEM_INFO mdSysInfo;
446 SYSTEM_INFO sysInfo;
447 OSVERSIONINFOW osInfo;
448 DWORD written;
449 ULONG slen;
451 GetSystemInfo(&sysInfo);
452 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
453 GetVersionExW(&osInfo);
455 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
456 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
457 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
458 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
459 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
460 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
461 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
462 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
463 mdSysInfo.PlatformId = osInfo.dwPlatformId;
465 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
466 mdSysInfo.u1.Reserved1 = 0;
468 memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
470 append(dc, &mdSysInfo, sizeof(mdSysInfo));
472 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
473 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
474 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
475 dc->rva += sizeof(ULONG) + slen;
478 /******************************************************************
479 * dump_threads
481 * Dumps into File the information about running threads
483 static void dump_threads(struct dump_context* dc)
485 MINIDUMP_THREAD mdThd;
486 MINIDUMP_THREAD_LIST mdThdList;
487 unsigned i;
488 RVA rva_base;
489 DWORD flags_out;
490 CONTEXT ctx;
492 mdThdList.NumberOfThreads = 0;
494 rva_base = dc->rva;
495 dc->rva += sizeof(mdThdList.NumberOfThreads) +
496 dc->spi->dwThreadCount * sizeof(mdThd);
498 for (i = 0; i < dc->spi->dwThreadCount; i++)
500 fetch_thread_info(dc, i, &mdThd, &ctx);
502 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
503 ThreadWriteInstructionWindow;
504 if (dc->type & MiniDumpWithProcessThreadData)
505 flags_out |= ThreadWriteThreadData;
506 if (dc->type & MiniDumpWithThreadInfo)
507 flags_out |= ThreadWriteThreadInfo;
509 if (dc->cb)
511 MINIDUMP_CALLBACK_INPUT cbin;
512 MINIDUMP_CALLBACK_OUTPUT cbout;
514 cbin.ProcessId = dc->pid;
515 cbin.ProcessHandle = dc->hProcess;
516 cbin.CallbackType = ThreadCallback;
517 cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
518 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
519 memcpy(&cbin.u.Thread.Context, &ctx, sizeof(CONTEXT));
520 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
521 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
522 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
523 mdThd.Stack.Memory.DataSize;
525 cbout.u.ThreadWriteFlags = flags_out;
526 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
527 continue;
528 flags_out &= cbout.u.ThreadWriteFlags;
530 if (flags_out & ThreadWriteThread)
532 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
534 mdThd.ThreadContext.Rva = dc->rva;
535 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
536 append(dc, &ctx, sizeof(CONTEXT));
538 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
540 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
541 mdThd.Stack.Memory.DataSize,
542 rva_base + sizeof(mdThdList.NumberOfThreads) +
543 mdThdList.NumberOfThreads * sizeof(mdThd) +
544 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
546 writeat(dc,
547 rva_base + sizeof(mdThdList.NumberOfThreads) +
548 mdThdList.NumberOfThreads * sizeof(mdThd),
549 &mdThd, sizeof(mdThd));
550 mdThdList.NumberOfThreads++;
552 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
554 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
555 * - also crop values across module boundaries,
556 * - and don't make it i386 dependent
558 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
561 writeat(dc, rva_base,
562 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
565 /******************************************************************
566 * dump_memory_info
568 * dumps information about the memory of the process (stack of the threads)
570 static void dump_memory_info(struct dump_context* dc)
572 MINIDUMP_MEMORY_LIST mdMemList;
573 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
574 DWORD written;
575 unsigned i, pos, len;
576 RVA rva_base;
577 char tmp[1024];
579 mdMemList.NumberOfMemoryRanges = dc->num_mem;
580 append(dc, &mdMemList.NumberOfMemoryRanges,
581 sizeof(mdMemList.NumberOfMemoryRanges));
582 rva_base = dc->rva;
583 dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
585 for (i = 0; i < dc->num_mem; i++)
587 mdMem.StartOfMemoryRange = dc->mem[i].base;
588 mdMem.Memory.Rva = dc->rva;
589 mdMem.Memory.DataSize = dc->mem[i].size;
590 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
591 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
593 len = min(dc->mem[i].size - pos, sizeof(tmp));
594 if (ReadProcessMemory(dc->hProcess,
595 (void*)(ULONG)(dc->mem[i].base + pos),
596 tmp, len, NULL))
597 WriteFile(dc->hFile, tmp, len, &written, NULL);
599 dc->rva += mdMem.Memory.DataSize;
600 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
601 if (dc->mem[i].rva)
603 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
608 static void dump_misc_info(struct dump_context* dc)
610 MINIDUMP_MISC_INFO mmi;
612 mmi.SizeOfInfo = sizeof(mmi);
613 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
614 mmi.ProcessId = dc->pid;
615 /* FIXME: create/user/kernel time */
616 append(dc, &mmi, sizeof(mmi));
619 /******************************************************************
620 * MiniDumpWriteDump (DEBUGHLP.@)
624 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
625 MINIDUMP_TYPE DumpType,
626 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
627 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
628 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
630 MINIDUMP_HEADER mdHead;
631 MINIDUMP_DIRECTORY mdDir;
632 DWORD i, nStreams, idx_stream;
633 struct dump_context dc;
635 dc.hProcess = hProcess;
636 dc.hFile = hFile;
637 dc.pid = pid;
638 dc.module = NULL;
639 dc.num_module = 0;
640 dc.cb = CallbackParam;
641 dc.type = DumpType;
642 dc.mem = NULL;
643 dc.num_mem = 0;
644 dc.rva = 0;
646 if (!fetch_process_info(&dc)) return FALSE;
647 fetch_module_info(&dc);
649 /* 1) init */
650 nStreams = 6 + (ExceptionParam ? 1 : 0) +
651 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
653 if (DumpType & MiniDumpWithDataSegs)
654 FIXME("NIY MiniDumpWithDataSegs\n");
655 if (DumpType & MiniDumpWithFullMemory)
656 FIXME("NIY MiniDumpWithFullMemory\n");
657 if (DumpType & MiniDumpWithHandleData)
658 FIXME("NIY MiniDumpWithHandleData\n");
659 if (DumpType & MiniDumpFilterMemory)
660 FIXME("NIY MiniDumpFilterMemory\n");
661 if (DumpType & MiniDumpScanMemory)
662 FIXME("NIY MiniDumpScanMemory\n");
664 /* 2) write header */
665 mdHead.Signature = MINIDUMP_SIGNATURE;
666 mdHead.Version = MINIDUMP_VERSION;
667 mdHead.NumberOfStreams = nStreams;
668 mdHead.StreamDirectoryRva = sizeof(mdHead);
669 mdHead.u.TimeDateStamp = time(NULL);
670 mdHead.Flags = DumpType;
671 append(&dc, &mdHead, sizeof(mdHead));
673 /* 3) write stream directories */
674 dc.rva += nStreams * sizeof(mdDir);
675 idx_stream = 0;
677 /* 3.1) write data stream directories */
679 mdDir.StreamType = ThreadListStream;
680 mdDir.Location.Rva = dc.rva;
681 dump_threads(&dc);
682 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
683 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
684 &mdDir, sizeof(mdDir));
686 mdDir.StreamType = ModuleListStream;
687 mdDir.Location.Rva = dc.rva;
688 dump_modules(&dc, FALSE);
689 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
690 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
691 &mdDir, sizeof(mdDir));
693 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
694 mdDir.Location.Rva = dc.rva;
695 dump_modules(&dc, TRUE);
696 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
697 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
698 &mdDir, sizeof(mdDir));
700 mdDir.StreamType = MemoryListStream;
701 mdDir.Location.Rva = dc.rva;
702 dump_memory_info(&dc);
703 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
704 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
705 &mdDir, sizeof(mdDir));
707 mdDir.StreamType = SystemInfoStream;
708 mdDir.Location.Rva = dc.rva;
709 dump_system_info(&dc);
710 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
711 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
712 &mdDir, sizeof(mdDir));
714 mdDir.StreamType = MiscInfoStream;
715 mdDir.Location.Rva = dc.rva;
716 dump_misc_info(&dc);
717 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
718 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
719 &mdDir, sizeof(mdDir));
721 /* 3.2) write exception information (if any) */
722 if (ExceptionParam)
724 mdDir.StreamType = ExceptionStream;
725 mdDir.Location.Rva = dc.rva;
726 dump_exception_info(&dc, ExceptionParam);
727 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
728 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
729 &mdDir, sizeof(mdDir));
732 /* 3.3) write user defined streams (if any) */
733 if (UserStreamParam)
735 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
737 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
738 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
739 mdDir.Location.Rva = dc.rva;
740 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
741 &mdDir, sizeof(mdDir));
742 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
743 UserStreamParam->UserStreamArray[i].BufferSize);
747 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
748 HeapFree(GetProcessHeap(), 0, dc.mem);
749 HeapFree(GetProcessHeap(), 0, dc.module);
751 return TRUE;
754 /******************************************************************
755 * MiniDumpReadDumpStream (DEBUGHLP.@)
759 BOOL WINAPI MiniDumpReadDumpStream(void* base, ULONG str_idx,
760 PMINIDUMP_DIRECTORY* pdir,
761 void** stream, ULONG* size)
763 MINIDUMP_HEADER* mdHead = (MINIDUMP_HEADER*)base;
765 if (mdHead->Signature == MINIDUMP_SIGNATURE)
767 MINIDUMP_DIRECTORY* dir;
768 int i;
770 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
771 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
773 if (dir->StreamType == str_idx)
775 *pdir = dir;
776 *stream = (char*)base + dir->Location.Rva;
777 *size = dir->Location.DataSize;
778 return TRUE;
782 SetLastError(ERROR_INVALID_PARAMETER);
783 return FALSE;