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