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