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