dbghelp: Make elf_enum_modules a Unicode function.
[wine/multimedia.git] / dlls / dbghelp / minidump.c
blobf51258056d6352bb01e62cd89277f557e8ce3412
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 "winnls.h"
30 #include "winreg.h"
31 #include "winternl.h"
32 #include "psapi.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
37 struct dump_memory
39 ULONG base;
40 ULONG size;
41 ULONG rva;
44 struct dump_module
46 unsigned is_elf;
47 ULONG base;
48 ULONG size;
49 DWORD timestamp;
50 DWORD checksum;
51 WCHAR name[MAX_PATH];
54 struct dump_context
56 /* process & thread information */
57 HANDLE hProcess;
58 DWORD pid;
59 void* pcs_buffer;
60 SYSTEM_PROCESS_INFORMATION* spi;
61 /* module information */
62 struct dump_module* module;
63 unsigned num_module;
64 /* exception information */
65 /* output information */
66 MINIDUMP_TYPE type;
67 HANDLE hFile;
68 RVA rva;
69 struct dump_memory* mem;
70 unsigned num_mem;
71 /* callback information */
72 MINIDUMP_CALLBACK_INFORMATION* cb;
75 /******************************************************************
76 * fetch_process_info
78 * reads system wide process information, and make spi point to the record
79 * for process of id 'pid'
81 static BOOL fetch_process_info(struct dump_context* dc)
83 ULONG buf_size = 0x1000;
84 NTSTATUS nts;
86 dc->pcs_buffer = NULL;
87 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
88 for (;;)
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;
98 if (nts == STATUS_SUCCESS)
100 dc->spi = dc->pcs_buffer;
101 for (;;)
103 if (dc->spi->dwProcessID == dc->pid) return TRUE;
104 if (!dc->spi->dwOffset) break;
105 dc->spi = (SYSTEM_PROCESS_INFORMATION*)
106 ((char*)dc->spi + dc->spi->dwOffset);
109 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
110 dc->pcs_buffer = NULL;
111 dc->spi = NULL;
112 return FALSE;
115 static void fetch_thread_stack(struct dump_context* dc, void* teb_addr,
116 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
118 NT_TIB tib;
120 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
122 #ifdef __i386__
123 /* limiting the stack dumping to the size actually used */
124 if (ctx->Esp)
125 mmd->StartOfMemoryRange = (ctx->Esp - 4);
126 else
127 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
128 #elif defined(__powerpc__)
129 if (ctx->Iar)
130 mmd->StartOfMemoryRange = ctx->Iar - 4;
131 else
132 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
133 #elif defined(__x86_64__)
134 if (ctx->Rsp)
135 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
136 else
137 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
138 #else
139 #error unsupported CPU
140 #endif
141 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
145 /******************************************************************
146 * fetch_thread_info
148 * fetches some information about thread of id 'tid'
150 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
151 const MINIDUMP_EXCEPTION_INFORMATION* except,
152 MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
154 DWORD tid = dc->spi->ti[thd_idx].dwThreadID;
155 HANDLE hThread;
156 THREAD_BASIC_INFORMATION tbi;
158 memset(ctx, 0, sizeof(*ctx));
160 mdThd->ThreadId = dc->spi->ti[thd_idx].dwThreadID;
161 mdThd->SuspendCount = 0;
162 mdThd->Teb = 0;
163 mdThd->Stack.StartOfMemoryRange = 0;
164 mdThd->Stack.Memory.DataSize = 0;
165 mdThd->Stack.Memory.Rva = 0;
166 mdThd->ThreadContext.DataSize = 0;
167 mdThd->ThreadContext.Rva = 0;
168 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
169 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
171 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
173 FIXME("Couldn't open thread %u (%u)\n",
174 dc->spi->ti[thd_idx].dwThreadID, GetLastError());
175 return FALSE;
178 if (NtQueryInformationThread(hThread, ThreadBasicInformation,
179 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
181 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
182 if (tbi.ExitStatus == STILL_ACTIVE)
184 if (tid != GetCurrentThreadId() &&
185 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
187 mdThd->SuspendCount--;
188 ctx->ContextFlags = CONTEXT_FULL;
189 if (!GetThreadContext(hThread, ctx))
190 memset(ctx, 0, sizeof(*ctx));
192 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
193 ResumeThread(hThread);
195 else if (tid == GetCurrentThreadId() && except)
197 CONTEXT lctx, *pctx;
198 if (except->ClientPointers)
200 EXCEPTION_POINTERS ep;
202 ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
203 &ep, sizeof(ep), NULL);
204 ReadProcessMemory(dc->hProcess, ep.ContextRecord,
205 &ctx, sizeof(ctx), NULL);
206 pctx = &lctx;
208 else pctx = except->ExceptionPointers->ContextRecord;
209 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
213 CloseHandle(hThread);
214 return TRUE;
217 /******************************************************************
218 * add_module
220 * Add a module to a dump context
222 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
223 DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
224 BOOL is_elf)
226 if (!dc->module)
227 dc->module = HeapAlloc(GetProcessHeap(), 0,
228 ++dc->num_module * sizeof(*dc->module));
229 else
230 dc->module = HeapReAlloc(GetProcessHeap(), 0, dc->module,
231 ++dc->num_module * sizeof(*dc->module));
232 if (!dc->module) return FALSE;
233 if (is_elf ||
234 !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
235 dc->module[dc->num_module - 1].name,
236 sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR)))
237 lstrcpynW(dc->module[dc->num_module - 1].name, name,
238 sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR));
239 dc->module[dc->num_module - 1].base = base;
240 dc->module[dc->num_module - 1].size = size;
241 dc->module[dc->num_module - 1].timestamp = timestamp;
242 dc->module[dc->num_module - 1].checksum = checksum;
243 dc->module[dc->num_module - 1].is_elf = is_elf;
245 return TRUE;
248 /******************************************************************
249 * fetch_pe_module_info_cb
251 * Callback for accumulating in dump_context a PE modules set
253 static BOOL WINAPI fetch_pe_module_info_cb(WCHAR* name, DWORD64 base, DWORD size,
254 void* user)
256 struct dump_context* dc = (struct dump_context*)user;
257 IMAGE_NT_HEADERS nth;
259 if (!validate_addr64(base)) return FALSE;
261 if (pe_load_nt_header(dc->hProcess, base, &nth))
262 add_module((struct dump_context*)user, name, base, size,
263 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
264 FALSE);
265 return TRUE;
268 /******************************************************************
269 * fetch_elf_module_info_cb
271 * Callback for accumulating in dump_context an ELF modules set
273 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
274 void* user)
276 struct dump_context* dc = (struct dump_context*)user;
277 DWORD rbase, size, checksum;
278 char tmp[MAX_PATH];
280 /* FIXME: there's no relevant timestamp on ELF modules */
281 /* NB: if we have a non-null base from the live-target use it (whenever
282 * the ELF module is relocatable or not). If we have a null base (ELF
283 * module isn't relocatable) then grab its base address from ELF file
285 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, tmp, sizeof(tmp), 0, 0);
286 if (!elf_fetch_file_info(tmp, &rbase, &size, &checksum))
287 size = checksum = 0;
288 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
289 return TRUE;
292 static void fetch_module_info(struct dump_context* dc)
294 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
295 /* Since we include ELF modules in a separate stream from the regular PE ones,
296 * we can always include those ELF modules (they don't eat lots of space)
297 * And it's always a good idea to have a trace of the loaded ELF modules for
298 * a given application in a post mortem debugging condition.
300 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
303 /******************************************************************
304 * add_memory_block
306 * Add a memory block to be dumped in a minidump
307 * If rva is non 0, it's the rva in the minidump where has to be stored
308 * also the rva of the memory block when written (this allows to reference
309 * a memory block from outside the list of memory blocks).
311 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
313 if (dc->mem)
314 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
315 ++dc->num_mem * sizeof(*dc->mem));
316 else
317 dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
318 if (dc->mem)
320 dc->mem[dc->num_mem - 1].base = base;
321 dc->mem[dc->num_mem - 1].size = size;
322 dc->mem[dc->num_mem - 1].rva = rva;
324 else dc->num_mem = 0;
327 /******************************************************************
328 * writeat
330 * Writes a chunk of data at a given position in the minidump
332 static void writeat(struct dump_context* dc, RVA rva, void* data, unsigned size)
334 DWORD written;
336 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
337 WriteFile(dc->hFile, data, size, &written, NULL);
340 /******************************************************************
341 * append
343 * writes a new chunk of data to the minidump, increasing the current
344 * rva in dc
346 static void append(struct dump_context* dc, void* data, unsigned size)
348 writeat(dc, dc->rva, data, size);
349 dc->rva += size;
352 /******************************************************************
353 * dump_exception_info
355 * Write in File the exception information from pcs
357 static void dump_exception_info(struct dump_context* dc,
358 const MINIDUMP_EXCEPTION_INFORMATION* except)
360 MINIDUMP_EXCEPTION_STREAM mdExcpt;
361 EXCEPTION_RECORD rec, *prec;
362 CONTEXT ctx, *pctx;
363 int i;
365 mdExcpt.ThreadId = except->ThreadId;
366 mdExcpt.__alignment = 0;
367 if (except->ClientPointers)
369 EXCEPTION_POINTERS ep;
371 ReadProcessMemory(dc->hProcess,
372 except->ExceptionPointers, &ep, sizeof(ep), NULL);
373 ReadProcessMemory(dc->hProcess,
374 ep.ExceptionRecord, &rec, sizeof(rec), NULL);
375 ReadProcessMemory(dc->hProcess,
376 ep.ContextRecord, &ctx, sizeof(ctx), NULL);
377 prec = &rec;
378 pctx = &ctx;
380 else
382 prec = except->ExceptionPointers->ExceptionRecord;
383 pctx = except->ExceptionPointers->ContextRecord;
385 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
386 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
387 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
388 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
389 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
390 mdExcpt.ExceptionRecord.__unusedAlignment = 0;
391 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
392 mdExcpt.ExceptionRecord.ExceptionInformation[i] = (DWORD_PTR)prec->ExceptionInformation[i];
393 mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
394 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
396 append(dc, &mdExcpt, sizeof(mdExcpt));
397 append(dc, pctx, sizeof(*pctx));
400 /******************************************************************
401 * dump_modules
403 * Write in File the modules from pcs
405 static void dump_modules(struct dump_context* dc, BOOL dump_elf)
407 MINIDUMP_MODULE mdModule;
408 MINIDUMP_MODULE_LIST mdModuleList;
409 char tmp[1024];
410 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
411 ULONG i, nmod;
412 RVA rva_base;
413 DWORD flags_out;
415 for (i = nmod = 0; i < dc->num_module; i++)
417 if ((dc->module[i].is_elf && dump_elf) ||
418 (!dc->module[i].is_elf && !dump_elf))
419 nmod++;
422 mdModuleList.NumberOfModules = 0;
423 /* reserve space for mdModuleList
424 * FIXME: since we don't support 0 length arrays, we cannot use the
425 * size of mdModuleList
426 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
428 rva_base = dc->rva;
429 dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
430 for (i = 0; i < dc->num_module; i++)
432 if ((dc->module[i].is_elf && !dump_elf) ||
433 (!dc->module[i].is_elf && dump_elf))
434 continue;
436 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
437 if (dc->type & MiniDumpWithDataSegs)
438 flags_out |= ModuleWriteDataSeg;
439 if (dc->type & MiniDumpWithProcessThreadData)
440 flags_out |= ModuleWriteTlsData;
441 if (dc->type & MiniDumpWithCodeSegs)
442 flags_out |= ModuleWriteCodeSegs;
443 ms->Length = (lstrlenW(dc->module[i].name) + 1) * sizeof(WCHAR);
444 if (sizeof(ULONG) + ms->Length > sizeof(tmp))
445 FIXME("Buffer overflow!!!\n");
446 lstrcpyW(ms->Buffer, dc->module[i].name);
448 if (dc->cb)
450 MINIDUMP_CALLBACK_INPUT cbin;
451 MINIDUMP_CALLBACK_OUTPUT cbout;
453 cbin.ProcessId = dc->pid;
454 cbin.ProcessHandle = dc->hProcess;
455 cbin.CallbackType = ModuleCallback;
457 cbin.u.Module.FullPath = ms->Buffer;
458 cbin.u.Module.BaseOfImage = dc->module[i].base;
459 cbin.u.Module.SizeOfImage = dc->module[i].size;
460 cbin.u.Module.CheckSum = dc->module[i].checksum;
461 cbin.u.Module.TimeDateStamp = dc->module[i].timestamp;
462 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
463 cbin.u.Module.CvRecord = NULL;
464 cbin.u.Module.SizeOfCvRecord = 0;
465 cbin.u.Module.MiscRecord = NULL;
466 cbin.u.Module.SizeOfMiscRecord = 0;
468 cbout.u.ModuleWriteFlags = flags_out;
469 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
470 continue;
471 flags_out &= cbout.u.ModuleWriteFlags;
473 if (flags_out & ModuleWriteModule)
475 mdModule.BaseOfImage = dc->module[i].base;
476 mdModule.SizeOfImage = dc->module[i].size;
477 mdModule.CheckSum = dc->module[i].checksum;
478 mdModule.TimeDateStamp = dc->module[i].timestamp;
479 mdModule.ModuleNameRva = dc->rva;
480 ms->Length -= sizeof(WCHAR);
481 append(dc, ms, sizeof(ULONG) + ms->Length);
482 memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */
483 mdModule.CvRecord.DataSize = 0; /* FIXME */
484 mdModule.CvRecord.Rva = 0; /* FIXME */
485 mdModule.MiscRecord.DataSize = 0; /* FIXME */
486 mdModule.MiscRecord.Rva = 0; /* FIXME */
487 mdModule.Reserved0 = 0; /* FIXME */
488 mdModule.Reserved1 = 0; /* FIXME */
489 writeat(dc,
490 rva_base + sizeof(mdModuleList.NumberOfModules) +
491 mdModuleList.NumberOfModules++ * sizeof(mdModule),
492 &mdModule, sizeof(mdModule));
495 writeat(dc, rva_base, &mdModuleList.NumberOfModules,
496 sizeof(mdModuleList.NumberOfModules));
499 /******************************************************************
500 * dump_system_info
502 * Dumps into File the information about the system
504 static void dump_system_info(struct dump_context* dc)
506 MINIDUMP_SYSTEM_INFO mdSysInfo;
507 SYSTEM_INFO sysInfo;
508 OSVERSIONINFOW osInfo;
509 DWORD written;
510 ULONG slen;
512 GetSystemInfo(&sysInfo);
513 osInfo.dwOSVersionInfoSize = sizeof(osInfo);
514 GetVersionExW(&osInfo);
516 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
517 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
518 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
519 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
520 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
521 mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
522 mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
523 mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
524 mdSysInfo.PlatformId = osInfo.dwPlatformId;
526 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
527 mdSysInfo.u1.Reserved1 = 0;
529 memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
531 append(dc, &mdSysInfo, sizeof(mdSysInfo));
533 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
534 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
535 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
536 dc->rva += sizeof(ULONG) + slen;
539 /******************************************************************
540 * dump_threads
542 * Dumps into File the information about running threads
544 static void dump_threads(struct dump_context* dc,
545 const MINIDUMP_EXCEPTION_INFORMATION* except)
547 MINIDUMP_THREAD mdThd;
548 MINIDUMP_THREAD_LIST mdThdList;
549 unsigned i;
550 RVA rva_base;
551 DWORD flags_out;
552 CONTEXT ctx;
554 mdThdList.NumberOfThreads = 0;
556 rva_base = dc->rva;
557 dc->rva += sizeof(mdThdList.NumberOfThreads) +
558 dc->spi->dwThreadCount * sizeof(mdThd);
560 for (i = 0; i < dc->spi->dwThreadCount; i++)
562 fetch_thread_info(dc, i, except, &mdThd, &ctx);
564 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
565 ThreadWriteInstructionWindow;
566 if (dc->type & MiniDumpWithProcessThreadData)
567 flags_out |= ThreadWriteThreadData;
568 if (dc->type & MiniDumpWithThreadInfo)
569 flags_out |= ThreadWriteThreadInfo;
571 if (dc->cb)
573 MINIDUMP_CALLBACK_INPUT cbin;
574 MINIDUMP_CALLBACK_OUTPUT cbout;
576 cbin.ProcessId = dc->pid;
577 cbin.ProcessHandle = dc->hProcess;
578 cbin.CallbackType = ThreadCallback;
579 cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
580 cbin.u.Thread.ThreadHandle = 0; /* FIXME */
581 memcpy(&cbin.u.Thread.Context, &ctx, sizeof(CONTEXT));
582 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
583 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
584 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
585 mdThd.Stack.Memory.DataSize;
587 cbout.u.ThreadWriteFlags = flags_out;
588 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
589 continue;
590 flags_out &= cbout.u.ThreadWriteFlags;
592 if (flags_out & ThreadWriteThread)
594 if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
596 mdThd.ThreadContext.Rva = dc->rva;
597 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
598 append(dc, &ctx, sizeof(CONTEXT));
600 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
602 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
603 mdThd.Stack.Memory.DataSize,
604 rva_base + sizeof(mdThdList.NumberOfThreads) +
605 mdThdList.NumberOfThreads * sizeof(mdThd) +
606 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
608 writeat(dc,
609 rva_base + sizeof(mdThdList.NumberOfThreads) +
610 mdThdList.NumberOfThreads * sizeof(mdThd),
611 &mdThd, sizeof(mdThd));
612 mdThdList.NumberOfThreads++;
614 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
616 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
617 * - also crop values across module boundaries,
618 * - and don't make it i386 dependent
620 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
623 writeat(dc, rva_base,
624 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
627 /******************************************************************
628 * dump_memory_info
630 * dumps information about the memory of the process (stack of the threads)
632 static void dump_memory_info(struct dump_context* dc)
634 MINIDUMP_MEMORY_LIST mdMemList;
635 MINIDUMP_MEMORY_DESCRIPTOR mdMem;
636 DWORD written;
637 unsigned i, pos, len;
638 RVA rva_base;
639 char tmp[1024];
641 mdMemList.NumberOfMemoryRanges = dc->num_mem;
642 append(dc, &mdMemList.NumberOfMemoryRanges,
643 sizeof(mdMemList.NumberOfMemoryRanges));
644 rva_base = dc->rva;
645 dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
647 for (i = 0; i < dc->num_mem; i++)
649 mdMem.StartOfMemoryRange = dc->mem[i].base;
650 mdMem.Memory.Rva = dc->rva;
651 mdMem.Memory.DataSize = dc->mem[i].size;
652 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
653 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
655 len = min(dc->mem[i].size - pos, sizeof(tmp));
656 if (ReadProcessMemory(dc->hProcess,
657 (void*)(ULONG)(dc->mem[i].base + pos),
658 tmp, len, NULL))
659 WriteFile(dc->hFile, tmp, len, &written, NULL);
661 dc->rva += mdMem.Memory.DataSize;
662 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
663 if (dc->mem[i].rva)
665 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
670 static void dump_misc_info(struct dump_context* dc)
672 MINIDUMP_MISC_INFO mmi;
674 mmi.SizeOfInfo = sizeof(mmi);
675 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
676 mmi.ProcessId = dc->pid;
677 /* FIXME: create/user/kernel time */
678 append(dc, &mmi, sizeof(mmi));
681 /******************************************************************
682 * MiniDumpWriteDump (DEBUGHLP.@)
685 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
686 MINIDUMP_TYPE DumpType,
687 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
688 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
689 PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
691 MINIDUMP_HEADER mdHead;
692 MINIDUMP_DIRECTORY mdDir;
693 DWORD i, nStreams, idx_stream;
694 struct dump_context dc;
696 dc.hProcess = hProcess;
697 dc.hFile = hFile;
698 dc.pid = pid;
699 dc.module = NULL;
700 dc.num_module = 0;
701 dc.cb = CallbackParam;
702 dc.type = DumpType;
703 dc.mem = NULL;
704 dc.num_mem = 0;
705 dc.rva = 0;
707 if (!fetch_process_info(&dc)) return FALSE;
708 fetch_module_info(&dc);
710 /* 1) init */
711 nStreams = 6 + (ExceptionParam ? 1 : 0) +
712 (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
714 if (DumpType & MiniDumpWithDataSegs)
715 FIXME("NIY MiniDumpWithDataSegs\n");
716 if (DumpType & MiniDumpWithFullMemory)
717 FIXME("NIY MiniDumpWithFullMemory\n");
718 if (DumpType & MiniDumpWithHandleData)
719 FIXME("NIY MiniDumpWithHandleData\n");
720 if (DumpType & MiniDumpFilterMemory)
721 FIXME("NIY MiniDumpFilterMemory\n");
722 if (DumpType & MiniDumpScanMemory)
723 FIXME("NIY MiniDumpScanMemory\n");
725 /* 2) write header */
726 mdHead.Signature = MINIDUMP_SIGNATURE;
727 mdHead.Version = MINIDUMP_VERSION;
728 mdHead.NumberOfStreams = nStreams;
729 mdHead.StreamDirectoryRva = sizeof(mdHead);
730 mdHead.u.TimeDateStamp = time(NULL);
731 mdHead.Flags = DumpType;
732 append(&dc, &mdHead, sizeof(mdHead));
734 /* 3) write stream directories */
735 dc.rva += nStreams * sizeof(mdDir);
736 idx_stream = 0;
738 /* 3.1) write data stream directories */
740 mdDir.StreamType = ThreadListStream;
741 mdDir.Location.Rva = dc.rva;
742 dump_threads(&dc, ExceptionParam);
743 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
744 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
745 &mdDir, sizeof(mdDir));
747 mdDir.StreamType = ModuleListStream;
748 mdDir.Location.Rva = dc.rva;
749 dump_modules(&dc, FALSE);
750 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
751 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
752 &mdDir, sizeof(mdDir));
754 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
755 mdDir.Location.Rva = dc.rva;
756 dump_modules(&dc, TRUE);
757 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
758 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
759 &mdDir, sizeof(mdDir));
761 mdDir.StreamType = MemoryListStream;
762 mdDir.Location.Rva = dc.rva;
763 dump_memory_info(&dc);
764 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
765 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
766 &mdDir, sizeof(mdDir));
768 mdDir.StreamType = SystemInfoStream;
769 mdDir.Location.Rva = dc.rva;
770 dump_system_info(&dc);
771 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
772 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
773 &mdDir, sizeof(mdDir));
775 mdDir.StreamType = MiscInfoStream;
776 mdDir.Location.Rva = dc.rva;
777 dump_misc_info(&dc);
778 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
779 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
780 &mdDir, sizeof(mdDir));
782 /* 3.2) write exception information (if any) */
783 if (ExceptionParam)
785 mdDir.StreamType = ExceptionStream;
786 mdDir.Location.Rva = dc.rva;
787 dump_exception_info(&dc, ExceptionParam);
788 mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
789 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
790 &mdDir, sizeof(mdDir));
793 /* 3.3) write user defined streams (if any) */
794 if (UserStreamParam)
796 for (i = 0; i < UserStreamParam->UserStreamCount; i++)
798 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
799 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
800 mdDir.Location.Rva = dc.rva;
801 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
802 &mdDir, sizeof(mdDir));
803 append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
804 UserStreamParam->UserStreamArray[i].BufferSize);
808 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
809 HeapFree(GetProcessHeap(), 0, dc.mem);
810 HeapFree(GetProcessHeap(), 0, dc.module);
812 return TRUE;
815 /******************************************************************
816 * MiniDumpReadDumpStream (DEBUGHLP.@)
820 BOOL WINAPI MiniDumpReadDumpStream(void* base, ULONG str_idx,
821 PMINIDUMP_DIRECTORY* pdir,
822 void** stream, ULONG* size)
824 MINIDUMP_HEADER* mdHead = (MINIDUMP_HEADER*)base;
826 if (mdHead->Signature == MINIDUMP_SIGNATURE)
828 MINIDUMP_DIRECTORY* dir;
829 int i;
831 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
832 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
834 if (dir->StreamType == str_idx)
836 *pdir = dir;
837 *stream = (char*)base + dir->Location.Rva;
838 *size = dir->Location.DataSize;
839 return TRUE;
843 SetLastError(ERROR_INVALID_PARAMETER);
844 return FALSE;