shlwapi: Fix memory leak in test.
[wine] / dlls / 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*                 module;
61     unsigned                            num_module;
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_process_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_process_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             mmd->StartOfMemoryRange = (ctx->Esp - 4);
124         else
125             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
126 #elif defined(__powerpc__)
127         if (ctx->Iar)
128             mmd->StartOfMemoryRange = ctx->Iar - 4;
129         else
130             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
131 #elif defined(__x86_64__)
132         if (ctx->Rsp)
133             mmd->StartOfMemoryRange = (ctx->Rsp - 8);
134         else
135             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
136 #else
137 #error unsupported CPU
138 #endif
139         mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
140     }
141 }
142
143 /******************************************************************
144  *              fetch_thread_info
145  *
146  * fetches some information about thread of id 'tid'
147  */
148 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
149                               const MINIDUMP_EXCEPTION_INFORMATION* except,
150                               MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
151 {
152     DWORD                       tid = dc->spi->ti[thd_idx].dwThreadID;
153     HANDLE                      hThread;
154     THREAD_BASIC_INFORMATION    tbi;
155
156     memset(ctx, 0, sizeof(*ctx));
157
158     mdThd->ThreadId = dc->spi->ti[thd_idx].dwThreadID;
159     mdThd->SuspendCount = 0;
160     mdThd->Teb = 0;
161     mdThd->Stack.StartOfMemoryRange = 0;
162     mdThd->Stack.Memory.DataSize = 0;
163     mdThd->Stack.Memory.Rva = 0;
164     mdThd->ThreadContext.DataSize = 0;
165     mdThd->ThreadContext.Rva = 0;
166     mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
167     mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
168
169     if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
170     {
171         FIXME("Couldn't open thread %u (%u)\n",
172               dc->spi->ti[thd_idx].dwThreadID, GetLastError());
173         return FALSE;
174     }
175     
176     if (NtQueryInformationThread(hThread, ThreadBasicInformation,
177                                  &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
178     {
179         mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
180         if (tbi.ExitStatus == STILL_ACTIVE)
181         {
182             if (tid != GetCurrentThreadId() &&
183                 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
184             {
185                 mdThd->SuspendCount--;
186                 ctx->ContextFlags = CONTEXT_FULL;
187                 if (!GetThreadContext(hThread, ctx))
188                     memset(ctx, 0, sizeof(*ctx));
189
190                 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
191                 ResumeThread(hThread);
192             }
193             else if (tid == GetCurrentThreadId() && except)
194             {
195                 CONTEXT lctx, *pctx;
196                 if (except->ClientPointers)
197                 {
198                     EXCEPTION_POINTERS      ep;
199
200                     ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
201                                       &ep, sizeof(ep), NULL);
202                     ReadProcessMemory(dc->hProcess, ep.ContextRecord,
203                                       &ctx, sizeof(ctx), NULL);
204                     pctx = &lctx;
205                 }
206                 else pctx = except->ExceptionPointers->ContextRecord;
207                 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
208             }
209         }
210     }
211     CloseHandle(hThread);
212     return TRUE;
213 }
214
215 /******************************************************************
216  *              add_module
217  *
218  * Add a module to a dump context
219  */
220 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
221                        DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
222                        BOOL is_elf)
223 {
224     if (!dc->module)
225         dc->module = HeapAlloc(GetProcessHeap(), 0,
226                                ++dc->num_module * sizeof(*dc->module));
227     else
228         dc->module = HeapReAlloc(GetProcessHeap(), 0, dc->module,
229                                  ++dc->num_module * sizeof(*dc->module));
230     if (!dc->module) return FALSE;
231     if (is_elf ||
232         !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
233                               dc->module[dc->num_module - 1].name,
234                               sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR)))
235         lstrcpynW(dc->module[dc->num_module - 1].name, name,
236                   sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR));
237     dc->module[dc->num_module - 1].base = base;
238     dc->module[dc->num_module - 1].size = size;
239     dc->module[dc->num_module - 1].timestamp = timestamp;
240     dc->module[dc->num_module - 1].checksum = checksum;
241     dc->module[dc->num_module - 1].is_elf = is_elf;
242
243     return TRUE;
244 }
245
246 /******************************************************************
247  *              fetch_pe_module_info_cb
248  *
249  * Callback for accumulating in dump_context a PE modules set
250  */
251 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
252                                            PVOID user)
253 {
254     struct dump_context*        dc = (struct dump_context*)user;
255     IMAGE_NT_HEADERS            nth;
256
257     if (!validate_addr64(base)) return FALSE;
258
259     if (pe_load_nt_header(dc->hProcess, base, &nth))
260         add_module((struct dump_context*)user, name, base, size,
261                    nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
262                    FALSE);
263     return TRUE;
264 }
265
266 /******************************************************************
267  *              fetch_elf_module_info_cb
268  *
269  * Callback for accumulating in dump_context an ELF modules set
270  */
271 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
272                                      void* user)
273 {
274     struct dump_context*        dc = (struct dump_context*)user;
275     DWORD                       rbase, size, checksum;
276
277     /* FIXME: there's no relevant timestamp on ELF modules */
278     /* NB: if we have a non-null base from the live-target use it (whenever
279      * the ELF module is relocatable or not). If we have a null base (ELF
280      * module isn't relocatable) then grab its base address from ELF file
281      */
282     if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
283         size = checksum = 0;
284     add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
285     return TRUE;
286 }
287
288 static void fetch_module_info(struct dump_context* dc)
289 {
290     EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
291     /* Since we include ELF modules in a separate stream from the regular PE ones,
292      * we can always include those ELF modules (they don't eat lots of space)
293      * And it's always a good idea to have a trace of the loaded ELF modules for
294      * a given application in a post mortem debugging condition.
295      */
296     elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
297 }
298
299 /******************************************************************
300  *              add_memory_block
301  *
302  * Add a memory block to be dumped in a minidump
303  * If rva is non 0, it's the rva in the minidump where has to be stored
304  * also the rva of the memory block when written (this allows to reference
305  * a memory block from outside the list of memory blocks).
306  */
307 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
308 {
309     if (dc->mem)
310         dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 
311                               ++dc->num_mem * sizeof(*dc->mem));
312     else
313         dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
314     if (dc->mem)
315     {
316         dc->mem[dc->num_mem - 1].base = base;
317         dc->mem[dc->num_mem - 1].size = size;
318         dc->mem[dc->num_mem - 1].rva  = rva;
319     }
320     else dc->num_mem = 0;
321 }
322
323 /******************************************************************
324  *              writeat
325  *
326  * Writes a chunk of data at a given position in the minidump
327  */
328 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
329 {
330     DWORD       written;
331
332     SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
333     WriteFile(dc->hFile, data, size, &written, NULL);
334 }
335
336 /******************************************************************
337  *              append
338  *
339  * writes a new chunk of data to the minidump, increasing the current
340  * rva in dc
341  */
342 static void append(struct dump_context* dc, void* data, unsigned size)
343 {
344     writeat(dc, dc->rva, data, size);
345     dc->rva += size;
346 }
347
348 /******************************************************************
349  *              dump_exception_info
350  *
351  * Write in File the exception information from pcs
352  */
353 static  void    dump_exception_info(struct dump_context* dc,
354                                     const MINIDUMP_EXCEPTION_INFORMATION* except)
355 {
356     MINIDUMP_EXCEPTION_STREAM   mdExcpt;
357     EXCEPTION_RECORD            rec, *prec;
358     CONTEXT                     ctx, *pctx;
359     int                         i;
360
361     mdExcpt.ThreadId = except->ThreadId;
362     mdExcpt.__alignment = 0;
363     if (except->ClientPointers)
364     {
365         EXCEPTION_POINTERS      ep;
366
367         ReadProcessMemory(dc->hProcess, 
368                           except->ExceptionPointers, &ep, sizeof(ep), NULL);
369         ReadProcessMemory(dc->hProcess, 
370                           ep.ExceptionRecord, &rec, sizeof(rec), NULL);
371         ReadProcessMemory(dc->hProcess, 
372                           ep.ContextRecord, &ctx, sizeof(ctx), NULL);
373         prec = &rec;
374         pctx = &ctx;
375     }
376     else
377     {
378         prec = except->ExceptionPointers->ExceptionRecord;
379         pctx = except->ExceptionPointers->ContextRecord;
380     }
381     mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
382     mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
383     mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
384     mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
385     mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
386     mdExcpt.ExceptionRecord.__unusedAlignment = 0;
387     for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
388         mdExcpt.ExceptionRecord.ExceptionInformation[i] = (DWORD_PTR)prec->ExceptionInformation[i];
389     mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
390     mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
391
392     append(dc, &mdExcpt, sizeof(mdExcpt));
393     append(dc, pctx, sizeof(*pctx));
394 }
395
396 /******************************************************************
397  *              dump_modules
398  *
399  * Write in File the modules from pcs
400  */
401 static  void    dump_modules(struct dump_context* dc, BOOL dump_elf)
402 {
403     MINIDUMP_MODULE             mdModule;
404     MINIDUMP_MODULE_LIST        mdModuleList;
405     char                        tmp[1024];
406     MINIDUMP_STRING*            ms = (MINIDUMP_STRING*)tmp;
407     ULONG                       i, nmod;
408     RVA                         rva_base;
409     DWORD                       flags_out;
410
411     for (i = nmod = 0; i < dc->num_module; i++)
412     {
413         if ((dc->module[i].is_elf && dump_elf) ||
414             (!dc->module[i].is_elf && !dump_elf))
415             nmod++;
416     }
417
418     mdModuleList.NumberOfModules = 0;
419     /* reserve space for mdModuleList
420      * FIXME: since we don't support 0 length arrays, we cannot use the
421      * size of mdModuleList
422      * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
423      */
424     rva_base = dc->rva;
425     dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
426     for (i = 0; i < dc->num_module; i++)
427     {
428         if ((dc->module[i].is_elf && !dump_elf) ||
429             (!dc->module[i].is_elf && dump_elf))
430             continue;
431
432         flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
433         if (dc->type & MiniDumpWithDataSegs)
434             flags_out |= ModuleWriteDataSeg;
435         if (dc->type & MiniDumpWithProcessThreadData)
436             flags_out |= ModuleWriteTlsData;
437         if (dc->type & MiniDumpWithCodeSegs)
438             flags_out |= ModuleWriteCodeSegs;
439         ms->Length = (lstrlenW(dc->module[i].name) + 1) * sizeof(WCHAR);
440         if (sizeof(ULONG) + ms->Length > sizeof(tmp))
441             FIXME("Buffer overflow!!!\n");
442         lstrcpyW(ms->Buffer, dc->module[i].name);
443
444         if (dc->cb)
445         {
446             MINIDUMP_CALLBACK_INPUT     cbin;
447             MINIDUMP_CALLBACK_OUTPUT    cbout;
448
449             cbin.ProcessId = dc->pid;
450             cbin.ProcessHandle = dc->hProcess;
451             cbin.CallbackType = ModuleCallback;
452
453             cbin.u.Module.FullPath = ms->Buffer;
454             cbin.u.Module.BaseOfImage = dc->module[i].base;
455             cbin.u.Module.SizeOfImage = dc->module[i].size;
456             cbin.u.Module.CheckSum = dc->module[i].checksum;
457             cbin.u.Module.TimeDateStamp = dc->module[i].timestamp;
458             memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
459             cbin.u.Module.CvRecord = NULL;
460             cbin.u.Module.SizeOfCvRecord = 0;
461             cbin.u.Module.MiscRecord = NULL;
462             cbin.u.Module.SizeOfMiscRecord = 0;
463
464             cbout.u.ModuleWriteFlags = flags_out;
465             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
466                 continue;
467             flags_out &= cbout.u.ModuleWriteFlags;
468         }
469         if (flags_out & ModuleWriteModule)
470         {
471             mdModule.BaseOfImage = dc->module[i].base;
472             mdModule.SizeOfImage = dc->module[i].size;
473             mdModule.CheckSum = dc->module[i].checksum;
474             mdModule.TimeDateStamp = dc->module[i].timestamp;
475             mdModule.ModuleNameRva = dc->rva;
476             ms->Length -= sizeof(WCHAR);
477             append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
478             memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */
479             mdModule.CvRecord.DataSize = 0; /* FIXME */
480             mdModule.CvRecord.Rva = 0; /* FIXME */
481             mdModule.MiscRecord.DataSize = 0; /* FIXME */
482             mdModule.MiscRecord.Rva = 0; /* FIXME */
483             mdModule.Reserved0 = 0; /* FIXME */
484             mdModule.Reserved1 = 0; /* FIXME */
485             writeat(dc,
486                     rva_base + sizeof(mdModuleList.NumberOfModules) + 
487                         mdModuleList.NumberOfModules++ * sizeof(mdModule), 
488                     &mdModule, sizeof(mdModule));
489         }
490     }
491     writeat(dc, rva_base, &mdModuleList.NumberOfModules, 
492             sizeof(mdModuleList.NumberOfModules));
493 }
494
495 /******************************************************************
496  *              dump_system_info
497  *
498  * Dumps into File the information about the system
499  */
500 static  void    dump_system_info(struct dump_context* dc)
501 {
502     MINIDUMP_SYSTEM_INFO        mdSysInfo;
503     SYSTEM_INFO                 sysInfo;
504     OSVERSIONINFOW              osInfo;
505     DWORD                       written;
506     ULONG                       slen;
507
508     GetSystemInfo(&sysInfo);
509     osInfo.dwOSVersionInfoSize = sizeof(osInfo);
510     GetVersionExW(&osInfo);
511
512     mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
513     mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
514     mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
515     mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
516     mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
517     mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
518     mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
519     mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
520     mdSysInfo.PlatformId = osInfo.dwPlatformId;
521
522     mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
523     mdSysInfo.u1.Reserved1 = 0;
524
525     memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
526
527     append(dc, &mdSysInfo, sizeof(mdSysInfo));
528
529     slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
530     WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
531     WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
532     dc->rva += sizeof(ULONG) + slen;
533 }
534
535 /******************************************************************
536  *              dump_threads
537  *
538  * Dumps into File the information about running threads
539  */
540 static  void    dump_threads(struct dump_context* dc,
541                              const MINIDUMP_EXCEPTION_INFORMATION* except)
542 {
543     MINIDUMP_THREAD             mdThd;
544     MINIDUMP_THREAD_LIST        mdThdList;
545     unsigned                    i;
546     RVA                         rva_base;
547     DWORD                       flags_out;
548     CONTEXT                     ctx;
549
550     mdThdList.NumberOfThreads = 0;
551
552     rva_base = dc->rva;
553     dc->rva += sizeof(mdThdList.NumberOfThreads) +
554         dc->spi->dwThreadCount * sizeof(mdThd);
555
556     for (i = 0; i < dc->spi->dwThreadCount; i++)
557     {
558         fetch_thread_info(dc, i, except, &mdThd, &ctx);
559
560         flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
561             ThreadWriteInstructionWindow;
562         if (dc->type & MiniDumpWithProcessThreadData)
563             flags_out |= ThreadWriteThreadData;
564         if (dc->type & MiniDumpWithThreadInfo)
565             flags_out |= ThreadWriteThreadInfo;
566
567         if (dc->cb)
568         {
569             MINIDUMP_CALLBACK_INPUT     cbin;
570             MINIDUMP_CALLBACK_OUTPUT    cbout;
571
572             cbin.ProcessId = dc->pid;
573             cbin.ProcessHandle = dc->hProcess;
574             cbin.CallbackType = ThreadCallback;
575             cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
576             cbin.u.Thread.ThreadHandle = 0; /* FIXME */
577             memcpy(&cbin.u.Thread.Context, &ctx, sizeof(CONTEXT));
578             cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
579             cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
580             cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
581                 mdThd.Stack.Memory.DataSize;
582
583             cbout.u.ThreadWriteFlags = flags_out;
584             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
585                 continue;
586             flags_out &= cbout.u.ThreadWriteFlags;
587         }
588         if (flags_out & ThreadWriteThread)
589         {
590             if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
591             {
592                 mdThd.ThreadContext.Rva = dc->rva;
593                 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
594                 append(dc, &ctx, sizeof(CONTEXT));
595             }
596             if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
597             {
598                 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
599                                  mdThd.Stack.Memory.DataSize,
600                                  rva_base + sizeof(mdThdList.NumberOfThreads) +
601                                      mdThdList.NumberOfThreads * sizeof(mdThd) +
602                                      FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
603             }
604             writeat(dc, 
605                     rva_base + sizeof(mdThdList.NumberOfThreads) +
606                         mdThdList.NumberOfThreads * sizeof(mdThd),
607                     &mdThd, sizeof(mdThd));
608             mdThdList.NumberOfThreads++;
609         }
610         if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
611         {
612             /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
613              *        - also crop values across module boundaries, 
614              *        - and don't make it i386 dependent 
615              */
616             /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
617         }
618     }
619     writeat(dc, rva_base,
620             &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
621 }
622
623 /******************************************************************
624  *              dump_memory_info
625  *
626  * dumps information about the memory of the process (stack of the threads)
627  */
628 static void dump_memory_info(struct dump_context* dc)
629 {
630     MINIDUMP_MEMORY_LIST        mdMemList;
631     MINIDUMP_MEMORY_DESCRIPTOR  mdMem;
632     DWORD                       written;
633     unsigned                    i, pos, len;
634     RVA                         rva_base;
635     char                        tmp[1024];
636
637     mdMemList.NumberOfMemoryRanges = dc->num_mem;
638     append(dc, &mdMemList.NumberOfMemoryRanges,
639            sizeof(mdMemList.NumberOfMemoryRanges));
640     rva_base = dc->rva;
641     dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
642
643     for (i = 0; i < dc->num_mem; i++)
644     {
645         mdMem.StartOfMemoryRange = dc->mem[i].base;
646         mdMem.Memory.Rva = dc->rva;
647         mdMem.Memory.DataSize = dc->mem[i].size;
648         SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
649         for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
650         {
651             len = min(dc->mem[i].size - pos, sizeof(tmp));
652             if (ReadProcessMemory(dc->hProcess, 
653                                   (void*)(ULONG)(dc->mem[i].base + pos), 
654                                   tmp, len, NULL))
655                 WriteFile(dc->hFile, tmp, len, &written, NULL);
656         }
657         dc->rva += mdMem.Memory.DataSize;
658         writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
659         if (dc->mem[i].rva)
660         {
661             writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
662         }
663     }
664 }
665
666 static void dump_misc_info(struct dump_context* dc)
667 {
668     MINIDUMP_MISC_INFO  mmi;
669
670     mmi.SizeOfInfo = sizeof(mmi);
671     mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
672     mmi.ProcessId = dc->pid;
673     /* FIXME: create/user/kernel time */
674     append(dc, &mmi, sizeof(mmi));
675 }
676
677 /******************************************************************
678  *              MiniDumpWriteDump (DEBUGHLP.@)
679  *
680  */
681 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
682                               MINIDUMP_TYPE DumpType,
683                               PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
684                               PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
685                               PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
686 {
687     MINIDUMP_HEADER     mdHead;
688     MINIDUMP_DIRECTORY  mdDir;
689     DWORD               i, nStreams, idx_stream;
690     struct dump_context dc;
691
692     dc.hProcess = hProcess;
693     dc.hFile = hFile;
694     dc.pid = pid;
695     dc.module = NULL;
696     dc.num_module = 0;
697     dc.cb = CallbackParam;
698     dc.type = DumpType;
699     dc.mem = NULL;
700     dc.num_mem = 0;
701     dc.rva = 0;
702
703     if (!fetch_process_info(&dc)) return FALSE;
704     fetch_module_info(&dc);
705
706     /* 1) init */
707     nStreams = 6 + (ExceptionParam ? 1 : 0) +
708         (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
709
710     if (DumpType & MiniDumpWithDataSegs)
711         FIXME("NIY MiniDumpWithDataSegs\n");
712     if (DumpType & MiniDumpWithFullMemory)
713         FIXME("NIY MiniDumpWithFullMemory\n");
714     if (DumpType & MiniDumpWithHandleData)
715         FIXME("NIY MiniDumpWithHandleData\n");
716     if (DumpType & MiniDumpFilterMemory)
717         FIXME("NIY MiniDumpFilterMemory\n");
718     if (DumpType & MiniDumpScanMemory)
719         FIXME("NIY MiniDumpScanMemory\n");
720
721     /* 2) write header */
722     mdHead.Signature = MINIDUMP_SIGNATURE;
723     mdHead.Version = MINIDUMP_VERSION;
724     mdHead.NumberOfStreams = nStreams;
725     mdHead.StreamDirectoryRva = sizeof(mdHead);
726     mdHead.u.TimeDateStamp = time(NULL);
727     mdHead.Flags = DumpType;
728     append(&dc, &mdHead, sizeof(mdHead));
729
730     /* 3) write stream directories */
731     dc.rva += nStreams * sizeof(mdDir);
732     idx_stream = 0;
733
734     /* 3.1) write data stream directories */
735
736     mdDir.StreamType = ThreadListStream;
737     mdDir.Location.Rva = dc.rva;
738     dump_threads(&dc, ExceptionParam);
739     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
740     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 
741             &mdDir, sizeof(mdDir));
742
743     mdDir.StreamType = ModuleListStream;
744     mdDir.Location.Rva = dc.rva;
745     dump_modules(&dc, FALSE);
746     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
747     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
748             &mdDir, sizeof(mdDir));
749
750     mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
751     mdDir.Location.Rva = dc.rva;
752     dump_modules(&dc, TRUE);
753     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
754     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
755             &mdDir, sizeof(mdDir));
756
757     mdDir.StreamType = MemoryListStream;
758     mdDir.Location.Rva = dc.rva;
759     dump_memory_info(&dc);
760     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
761     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
762             &mdDir, sizeof(mdDir));
763
764     mdDir.StreamType = SystemInfoStream;
765     mdDir.Location.Rva = dc.rva;
766     dump_system_info(&dc);
767     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
768     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
769             &mdDir, sizeof(mdDir));
770
771     mdDir.StreamType = MiscInfoStream;
772     mdDir.Location.Rva = dc.rva;
773     dump_misc_info(&dc);
774     mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
775     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
776             &mdDir, sizeof(mdDir));
777
778     /* 3.2) write exception information (if any) */
779     if (ExceptionParam)
780     {
781         mdDir.StreamType = ExceptionStream;
782         mdDir.Location.Rva = dc.rva;
783         dump_exception_info(&dc, ExceptionParam);
784         mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
785         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
786                 &mdDir, sizeof(mdDir));
787     }
788
789     /* 3.3) write user defined streams (if any) */
790     if (UserStreamParam)
791     {
792         for (i = 0; i < UserStreamParam->UserStreamCount; i++)
793         {
794             mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
795             mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
796             mdDir.Location.Rva = dc.rva;
797             writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
798                     &mdDir, sizeof(mdDir));
799             append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 
800                    UserStreamParam->UserStreamArray[i].BufferSize);
801         }
802     }
803
804     HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
805     HeapFree(GetProcessHeap(), 0, dc.mem);
806     HeapFree(GetProcessHeap(), 0, dc.module);
807
808     return TRUE;
809 }
810
811 /******************************************************************
812  *              MiniDumpReadDumpStream (DEBUGHLP.@)
813  *
814  *
815  */
816 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
817                                    PMINIDUMP_DIRECTORY* pdir,
818                                    PVOID* stream, ULONG* size)
819 {
820     MINIDUMP_HEADER*    mdHead = (MINIDUMP_HEADER*)base;
821
822     if (mdHead->Signature == MINIDUMP_SIGNATURE)
823     {
824         MINIDUMP_DIRECTORY* dir;
825         int                 i;
826
827         dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
828         for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
829         {
830             if (dir->StreamType == str_idx)
831             {
832                 *pdir = dir;
833                 *stream = (char*)base + dir->Location.Rva;
834                 *size = dir->Location.DataSize;
835                 return TRUE;
836             }
837         }
838     }
839     SetLastError(ERROR_INVALID_PARAMETER);
840     return FALSE;
841 }