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