dbghelp: Don't double memory allocation on every add.
[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     unsigned                            alloc_modules;
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     unsigned                            alloc_mem;
71     /* callback information */
72     MINIDUMP_CALLBACK_INFORMATION*      cb;
73 };
74
75 /******************************************************************
76  *              fetch_processes_info
77  *
78  * reads system wide process information, and make spi point to the record
79  * for process of id 'pid'
80  */
81 static BOOL fetch_processes_info(struct dump_context* dc)
82 {
83     ULONG       buf_size = 0x1000;
84     NTSTATUS    nts;
85
86     dc->pcs_buffer = NULL;
87     if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
88     for (;;)
89     {
90         nts = NtQuerySystemInformation(SystemProcessInformation, 
91                                        dc->pcs_buffer, buf_size, NULL);
92         if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
93         dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer, 
94                                      buf_size *= 2);
95         if (!dc->pcs_buffer) return FALSE;
96     }
97
98     if (nts == STATUS_SUCCESS)
99     {
100         dc->spi = dc->pcs_buffer;
101         for (;;)
102         {
103             if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE;
104             if (!dc->spi->NextEntryOffset) break;
105             dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset);
106         }
107     }
108     HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
109     dc->pcs_buffer = NULL;
110     dc->spi = NULL;
111     return FALSE;
112 }
113
114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
115                                const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
116 {
117     NT_TIB      tib;
118
119     if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
120     {
121 #ifdef __i386__
122         /* limiting the stack dumping to the size actually used */
123         if (ctx->Esp){
124
125             /* make sure ESP is within the established range of the stack.  It could have
126                been clobbered by whatever caused the original exception. */
127             if (ctx->Esp - 4 < (ULONG_PTR)tib.StackLimit || ctx->Esp - 4 > (ULONG_PTR)tib.StackBase)
128                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
129
130             else
131                 mmd->StartOfMemoryRange = (ctx->Esp - 4);
132         }
133
134         else
135             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
136
137 #elif defined(__powerpc__)
138         if (ctx->Iar){
139
140             /* make sure IAR is within the established range of the stack.  It could have
141                been clobbered by whatever caused the original exception. */
142             if (ctx->Iar - 4 < (ULONG_PTR)tib.StackLimit || ctx->Iar - 4 > (ULONG_PTR)tib.StackBase)
143                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
144
145             else
146                 mmd->StartOfMemoryRange = (ctx->Iar - 4);
147         }
148
149         else
150             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
151
152 #elif defined(__x86_64__)
153         if (ctx->Rsp){
154
155             /* make sure RSP is within the established range of the stack.  It could have
156                been clobbered by whatever caused the original exception. */
157             if (ctx->Rsp - 8 < (ULONG_PTR)tib.StackLimit || ctx->Rsp - 8 > (ULONG_PTR)tib.StackBase)
158                 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
159
160             else
161                 mmd->StartOfMemoryRange = (ctx->Rsp - 8);
162         }
163
164         else
165             mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
166
167 #else
168 #error unsupported CPU
169 #endif
170         mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
171     }
172 }
173
174 /******************************************************************
175  *              fetch_thread_info
176  *
177  * fetches some information about thread of id 'tid'
178  */
179 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
180                               const MINIDUMP_EXCEPTION_INFORMATION* except,
181                               MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
182 {
183     DWORD                       tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread);
184     HANDLE                      hThread;
185     THREAD_BASIC_INFORMATION    tbi;
186
187     memset(ctx, 0, sizeof(*ctx));
188
189     mdThd->ThreadId = tid;
190     mdThd->SuspendCount = 0;
191     mdThd->Teb = 0;
192     mdThd->Stack.StartOfMemoryRange = 0;
193     mdThd->Stack.Memory.DataSize = 0;
194     mdThd->Stack.Memory.Rva = 0;
195     mdThd->ThreadContext.DataSize = 0;
196     mdThd->ThreadContext.Rva = 0;
197     mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
198     mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
199
200     if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
201     {
202         FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError());
203         return FALSE;
204     }
205     
206     if (NtQueryInformationThread(hThread, ThreadBasicInformation,
207                                  &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
208     {
209         mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
210         if (tbi.ExitStatus == STILL_ACTIVE)
211         {
212             if (tid != GetCurrentThreadId() &&
213                 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
214             {
215                 ctx->ContextFlags = CONTEXT_FULL;
216                 if (!GetThreadContext(hThread, ctx))
217                     memset(ctx, 0, sizeof(*ctx));
218
219                 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
220                 ResumeThread(hThread);
221             }
222             else if (tid == GetCurrentThreadId() && except)
223             {
224                 CONTEXT lctx, *pctx;
225                 mdThd->SuspendCount = 1;
226                 if (except->ClientPointers)
227                 {
228                     EXCEPTION_POINTERS      ep;
229
230                     ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
231                                       &ep, sizeof(ep), NULL);
232                     ReadProcessMemory(dc->hProcess, ep.ContextRecord,
233                                       &ctx, sizeof(ctx), NULL);
234                     pctx = &lctx;
235                 }
236                 else pctx = except->ExceptionPointers->ContextRecord;
237
238                 *ctx = *pctx;
239                 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
240             }
241             else mdThd->SuspendCount = 0;
242         }
243     }
244     CloseHandle(hThread);
245     return TRUE;
246 }
247
248 /******************************************************************
249  *              add_module
250  *
251  * Add a module to a dump context
252  */
253 static BOOL add_module(struct dump_context* dc, const WCHAR* name,
254                        DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
255                        BOOL is_elf)
256 {
257     if (!dc->modules)
258     {
259         dc->alloc_modules = 32;
260         dc->modules = HeapAlloc(GetProcessHeap(), 0,
261                                 dc->alloc_modules * sizeof(*dc->modules));
262     }
263     else if(dc->num_modules >= dc->alloc_modules)
264     {
265         dc->alloc_modules *= 2;
266         dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules,
267                                   dc->alloc_modules * sizeof(*dc->modules));
268     }
269     if (!dc->modules)
270     {
271         dc->alloc_modules = dc->num_modules = 0;
272         return FALSE;
273     }
274     if (is_elf ||
275         !GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
276                               dc->modules[dc->num_modules].name,
277                               sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)))
278         lstrcpynW(dc->modules[dc->num_modules].name, name,
279                   sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR));
280     dc->modules[dc->num_modules].base = base;
281     dc->modules[dc->num_modules].size = size;
282     dc->modules[dc->num_modules].timestamp = timestamp;
283     dc->modules[dc->num_modules].checksum = checksum;
284     dc->modules[dc->num_modules].is_elf = is_elf;
285     dc->num_modules++;
286
287     return TRUE;
288 }
289
290 /******************************************************************
291  *              fetch_pe_module_info_cb
292  *
293  * Callback for accumulating in dump_context a PE modules set
294  */
295 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size,
296                                            PVOID user)
297 {
298     struct dump_context*        dc = user;
299     IMAGE_NT_HEADERS            nth;
300
301     if (!validate_addr64(base)) return FALSE;
302
303     if (pe_load_nt_header(dc->hProcess, base, &nth))
304         add_module(user, name, base, size,
305                    nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
306                    FALSE);
307     return TRUE;
308 }
309
310 /******************************************************************
311  *              fetch_elf_module_info_cb
312  *
313  * Callback for accumulating in dump_context an ELF modules set
314  */
315 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
316                                      void* user)
317 {
318     struct dump_context*        dc = user;
319     DWORD                       rbase, size, checksum;
320
321     /* FIXME: there's no relevant timestamp on ELF modules */
322     /* NB: if we have a non-null base from the live-target use it (whenever
323      * the ELF module is relocatable or not). If we have a null base (ELF
324      * module isn't relocatable) then grab its base address from ELF file
325      */
326     if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
327         size = checksum = 0;
328     add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
329     return TRUE;
330 }
331
332 /******************************************************************
333  *              fetch_macho_module_info_cb
334  *
335  * Callback for accumulating in dump_context a Mach-O modules set
336  */
337 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base,
338                                        void* user)
339 {
340     struct dump_context*        dc = (struct dump_context*)user;
341     DWORD                       rbase, size, checksum;
342
343     /* FIXME: there's no relevant timestamp on Mach-O modules */
344     /* NB: if we have a non-null base from the live-target use it.  If we have
345      * a null base, then grab its base address from Mach-O file.
346      */
347     if (!macho_fetch_file_info(name, &rbase, &size, &checksum))
348         size = checksum = 0;
349     add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
350     return TRUE;
351 }
352
353 static void fetch_modules_info(struct dump_context* dc)
354 {
355     EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
356     /* Since we include ELF modules in a separate stream from the regular PE ones,
357      * we can always include those ELF modules (they don't eat lots of space)
358      * And it's always a good idea to have a trace of the loaded ELF modules for
359      * a given application in a post mortem debugging condition.
360      */
361     elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
362     macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc);
363 }
364
365 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi)
366 {
367     DWORD       handle;
368     DWORD       sz;
369     static const WCHAR backslashW[] = {'\\', '\0'};
370
371     memset(ffi, 0, sizeof(*ffi));
372     if ((sz = GetFileVersionInfoSizeW(filename, &handle)))
373     {
374         void*   info = HeapAlloc(GetProcessHeap(), 0, sz);
375         if (info && GetFileVersionInfoW(filename, handle, sz, info))
376         {
377             VS_FIXEDFILEINFO*   ptr;
378             UINT    len;
379
380             if (VerQueryValueW(info, backslashW, (void*)&ptr, &len))
381                 memcpy(ffi, ptr, min(len, sizeof(*ffi)));
382         }
383         HeapFree(GetProcessHeap(), 0, info);
384     }
385 }
386
387 /******************************************************************
388  *              add_memory_block
389  *
390  * Add a memory block to be dumped in a minidump
391  * If rva is non 0, it's the rva in the minidump where has to be stored
392  * also the rva of the memory block when written (this allows to reference
393  * a memory block from outside the list of memory blocks).
394  */
395 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
396 {
397     if (!dc->mem)
398     {
399         dc->alloc_mem = 32;
400         dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem));
401     }
402     else if (dc->num_mem >= dc->alloc_mem)
403     {
404         dc->alloc_mem *= 2;
405         dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
406                               dc->alloc_mem * sizeof(*dc->mem));
407     }
408     if (dc->mem)
409     {
410         dc->mem[dc->num_mem].base = base;
411         dc->mem[dc->num_mem].size = size;
412         dc->mem[dc->num_mem].rva  = rva;
413         dc->num_mem++;
414     }
415     else dc->num_mem = dc->alloc_mem = 0;
416 }
417
418 /******************************************************************
419  *              writeat
420  *
421  * Writes a chunk of data at a given position in the minidump
422  */
423 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
424 {
425     DWORD       written;
426
427     SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
428     WriteFile(dc->hFile, data, size, &written, NULL);
429 }
430
431 /******************************************************************
432  *              append
433  *
434  * writes a new chunk of data to the minidump, increasing the current
435  * rva in dc
436  */
437 static void append(struct dump_context* dc, const void* data, unsigned size)
438 {
439     writeat(dc, dc->rva, data, size);
440     dc->rva += size;
441 }
442
443 /******************************************************************
444  *              dump_exception_info
445  *
446  * Write in File the exception information from pcs
447  */
448 static  unsigned        dump_exception_info(struct dump_context* dc,
449                                             const MINIDUMP_EXCEPTION_INFORMATION* except)
450 {
451     MINIDUMP_EXCEPTION_STREAM   mdExcpt;
452     EXCEPTION_RECORD            rec, *prec;
453     CONTEXT                     ctx, *pctx;
454     DWORD                       i;
455
456     mdExcpt.ThreadId = except->ThreadId;
457     mdExcpt.__alignment = 0;
458     if (except->ClientPointers)
459     {
460         EXCEPTION_POINTERS      ep;
461
462         ReadProcessMemory(dc->hProcess, 
463                           except->ExceptionPointers, &ep, sizeof(ep), NULL);
464         ReadProcessMemory(dc->hProcess, 
465                           ep.ExceptionRecord, &rec, sizeof(rec), NULL);
466         ReadProcessMemory(dc->hProcess, 
467                           ep.ContextRecord, &ctx, sizeof(ctx), NULL);
468         prec = &rec;
469         pctx = &ctx;
470     }
471     else
472     {
473         prec = except->ExceptionPointers->ExceptionRecord;
474         pctx = except->ExceptionPointers->ContextRecord;
475     }
476     mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
477     mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
478     mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
479     mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
480     mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
481     mdExcpt.ExceptionRecord.__unusedAlignment = 0;
482     for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
483         mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i];
484     mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
485     mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
486
487     append(dc, &mdExcpt, sizeof(mdExcpt));
488     append(dc, pctx, sizeof(*pctx));
489     return sizeof(mdExcpt);
490 }
491
492 /******************************************************************
493  *              dump_modules
494  *
495  * Write in File the modules from pcs
496  */
497 static  unsigned        dump_modules(struct dump_context* dc, BOOL dump_elf)
498 {
499     MINIDUMP_MODULE             mdModule;
500     MINIDUMP_MODULE_LIST        mdModuleList;
501     char                        tmp[1024];
502     MINIDUMP_STRING*            ms = (MINIDUMP_STRING*)tmp;
503     ULONG                       i, nmod;
504     RVA                         rva_base;
505     DWORD                       flags_out;
506     unsigned                    sz;
507
508     for (i = nmod = 0; i < dc->num_modules; i++)
509     {
510         if ((dc->modules[i].is_elf && dump_elf) ||
511             (!dc->modules[i].is_elf && !dump_elf))
512             nmod++;
513     }
514
515     mdModuleList.NumberOfModules = 0;
516     /* reserve space for mdModuleList
517      * FIXME: since we don't support 0 length arrays, we cannot use the
518      * size of mdModuleList
519      * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
520      */
521
522     /* the stream size is just the size of the module index.  It does not include the data for the
523        names of each module.  *Technically* the names are supposed to go into the common string table
524        in the minidump file.  Since each string is referenced by RVA they can all safely be located
525        anywhere between streams in the file, so the end of this stream is sufficient. */
526     rva_base = dc->rva;
527     dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
528     for (i = 0; i < dc->num_modules; i++)
529     {
530         if ((dc->modules[i].is_elf && !dump_elf) ||
531             (!dc->modules[i].is_elf && dump_elf))
532             continue;
533
534         flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
535         if (dc->type & MiniDumpWithDataSegs)
536             flags_out |= ModuleWriteDataSeg;
537         if (dc->type & MiniDumpWithProcessThreadData)
538             flags_out |= ModuleWriteTlsData;
539         if (dc->type & MiniDumpWithCodeSegs)
540             flags_out |= ModuleWriteCodeSegs;
541         ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR);
542         if (sizeof(ULONG) + ms->Length > sizeof(tmp))
543             FIXME("Buffer overflow!!!\n");
544         lstrcpyW(ms->Buffer, dc->modules[i].name);
545
546         if (dc->cb)
547         {
548             MINIDUMP_CALLBACK_INPUT     cbin;
549             MINIDUMP_CALLBACK_OUTPUT    cbout;
550
551             cbin.ProcessId = dc->pid;
552             cbin.ProcessHandle = dc->hProcess;
553             cbin.CallbackType = ModuleCallback;
554
555             cbin.u.Module.FullPath = ms->Buffer;
556             cbin.u.Module.BaseOfImage = dc->modules[i].base;
557             cbin.u.Module.SizeOfImage = dc->modules[i].size;
558             cbin.u.Module.CheckSum = dc->modules[i].checksum;
559             cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp;
560             memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
561             cbin.u.Module.CvRecord = NULL;
562             cbin.u.Module.SizeOfCvRecord = 0;
563             cbin.u.Module.MiscRecord = NULL;
564             cbin.u.Module.SizeOfMiscRecord = 0;
565
566             cbout.u.ModuleWriteFlags = flags_out;
567             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
568                 continue;
569             flags_out &= cbout.u.ModuleWriteFlags;
570         }
571         if (flags_out & ModuleWriteModule)
572         {
573             mdModule.BaseOfImage = dc->modules[i].base;
574             mdModule.SizeOfImage = dc->modules[i].size;
575             mdModule.CheckSum = dc->modules[i].checksum;
576             mdModule.TimeDateStamp = dc->modules[i].timestamp;
577             mdModule.ModuleNameRva = dc->rva;
578             ms->Length -= sizeof(WCHAR);
579             append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
580             fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo);
581             mdModule.CvRecord.DataSize = 0; /* FIXME */
582             mdModule.CvRecord.Rva = 0; /* FIXME */
583             mdModule.MiscRecord.DataSize = 0; /* FIXME */
584             mdModule.MiscRecord.Rva = 0; /* FIXME */
585             mdModule.Reserved0 = 0; /* FIXME */
586             mdModule.Reserved1 = 0; /* FIXME */
587             writeat(dc,
588                     rva_base + sizeof(mdModuleList.NumberOfModules) + 
589                         mdModuleList.NumberOfModules++ * sizeof(mdModule), 
590                     &mdModule, sizeof(mdModule));
591         }
592     }
593     writeat(dc, rva_base, &mdModuleList.NumberOfModules, 
594             sizeof(mdModuleList.NumberOfModules));
595
596     return sz;
597 }
598
599 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
600  * We are compiled with -fPIC, so we can't clobber ebx.
601  */
602 static inline void do_x86cpuid(unsigned int ax, unsigned int *p)
603 {
604 #if defined(__GNUC__) && defined(__i386__)
605     __asm__("pushl %%ebx\n\t"
606             "cpuid\n\t"
607             "movl %%ebx, %%esi\n\t"
608             "popl %%ebx"
609             : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
610             :  "0" (ax));
611 #endif
612 }
613
614 /* From xf86info havecpuid.c 1.11 */
615 static inline int have_x86cpuid(void)
616 {
617 #if defined(__GNUC__) && defined(__i386__)
618     unsigned int f1, f2;
619     __asm__("pushfl\n\t"
620             "pushfl\n\t"
621             "popl %0\n\t"
622             "movl %0,%1\n\t"
623             "xorl %2,%0\n\t"
624             "pushl %0\n\t"
625             "popfl\n\t"
626             "pushfl\n\t"
627             "popl %0\n\t"
628             "popfl"
629             : "=&r" (f1), "=&r" (f2)
630             : "ir" (0x00200000));
631     return ((f1^f2) & 0x00200000) != 0;
632 #else
633     return 0;
634 #endif
635 }
636
637 /******************************************************************
638  *              dump_system_info
639  *
640  * Dumps into File the information about the system
641  */
642 static  unsigned        dump_system_info(struct dump_context* dc)
643 {
644     MINIDUMP_SYSTEM_INFO        mdSysInfo;
645     SYSTEM_INFO                 sysInfo;
646     OSVERSIONINFOW              osInfo;
647     DWORD                       written;
648     ULONG                       slen;
649
650     GetSystemInfo(&sysInfo);
651     osInfo.dwOSVersionInfoSize = sizeof(osInfo);
652     GetVersionExW(&osInfo);
653
654     mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
655     mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
656     mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
657     mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
658     mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
659     mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
660     mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
661     mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
662     mdSysInfo.PlatformId = osInfo.dwPlatformId;
663
664     mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
665     mdSysInfo.u1.Reserved1 = 0;
666     mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL;
667
668     if (have_x86cpuid())
669     {
670         unsigned        regs0[4], regs1[4];
671
672         do_x86cpuid(0, regs0);
673         mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1];
674         mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2];
675         mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3];
676         do_x86cpuid(1, regs1);
677         mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0];
678         mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3];
679         mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0;
680         if (regs0[1] == 0x68747541 /* "Auth" */ &&
681             regs0[3] == 0x69746e65 /* "enti" */ &&
682             regs0[2] == 0x444d4163 /* "cAMD" */)
683         {
684             do_x86cpuid(0x80000000, regs1);  /* get vendor cpuid level */
685             if (regs1[0] >= 0x80000001)
686             {
687                 do_x86cpuid(0x80000001, regs1);  /* get vendor features */
688                 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3];
689             }
690         }
691     }
692     else
693     {
694         unsigned        i;
695         ULONG64         one = 1;
696
697         mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
698         mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
699
700         for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++)
701             if (IsProcessorFeaturePresent(i))
702                 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i;
703     }
704     append(dc, &mdSysInfo, sizeof(mdSysInfo));
705
706     /* write the service pack version string after this stream.  It is referenced within the
707        stream by its RVA in the file. */
708     slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
709     WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
710     WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
711     dc->rva += sizeof(ULONG) + slen;
712
713     return sizeof(mdSysInfo);
714 }
715
716 /******************************************************************
717  *              dump_threads
718  *
719  * Dumps into File the information about running threads
720  */
721 static  unsigned        dump_threads(struct dump_context* dc,
722                                      const MINIDUMP_EXCEPTION_INFORMATION* except)
723 {
724     MINIDUMP_THREAD             mdThd;
725     MINIDUMP_THREAD_LIST        mdThdList;
726     unsigned                    i;
727     RVA                         rva_base;
728     DWORD                       flags_out;
729     CONTEXT                     ctx;
730
731     mdThdList.NumberOfThreads = 0;
732
733     rva_base = dc->rva;
734     dc->rva += sizeof(mdThdList.NumberOfThreads) +
735         dc->spi->dwThreadCount * sizeof(mdThd);
736
737     for (i = 0; i < dc->spi->dwThreadCount; i++)
738     {
739         fetch_thread_info(dc, i, except, &mdThd, &ctx);
740
741         flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
742             ThreadWriteInstructionWindow;
743         if (dc->type & MiniDumpWithProcessThreadData)
744             flags_out |= ThreadWriteThreadData;
745         if (dc->type & MiniDumpWithThreadInfo)
746             flags_out |= ThreadWriteThreadInfo;
747
748         if (dc->cb)
749         {
750             MINIDUMP_CALLBACK_INPUT     cbin;
751             MINIDUMP_CALLBACK_OUTPUT    cbout;
752
753             cbin.ProcessId = dc->pid;
754             cbin.ProcessHandle = dc->hProcess;
755             cbin.CallbackType = ThreadCallback;
756             cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread);
757             cbin.u.Thread.ThreadHandle = 0; /* FIXME */
758             cbin.u.Thread.Context = ctx;
759             cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
760             cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
761             cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
762                 mdThd.Stack.Memory.DataSize;
763
764             cbout.u.ThreadWriteFlags = flags_out;
765             if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
766                 continue;
767             flags_out &= cbout.u.ThreadWriteFlags;
768         }
769         if (flags_out & ThreadWriteThread)
770         {
771             if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
772             {
773                 mdThd.ThreadContext.Rva = dc->rva;
774                 mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
775                 append(dc, &ctx, sizeof(CONTEXT));
776             }
777             if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
778             {
779                 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
780                                  mdThd.Stack.Memory.DataSize,
781                                  rva_base + sizeof(mdThdList.NumberOfThreads) +
782                                      mdThdList.NumberOfThreads * sizeof(mdThd) +
783                                      FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
784             }
785             writeat(dc, 
786                     rva_base + sizeof(mdThdList.NumberOfThreads) +
787                         mdThdList.NumberOfThreads * sizeof(mdThd),
788                     &mdThd, sizeof(mdThd));
789             mdThdList.NumberOfThreads++;
790         }
791         if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
792         {
793             /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
794              *        - also crop values across module boundaries, 
795              *        - and don't make it i386 dependent 
796              */
797             /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
798         }
799     }
800     writeat(dc, rva_base,
801             &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
802
803     return dc->rva - rva_base;
804 }
805
806 /******************************************************************
807  *              dump_memory_info
808  *
809  * dumps information about the memory of the process (stack of the threads)
810  */
811 static unsigned         dump_memory_info(struct dump_context* dc)
812 {
813     MINIDUMP_MEMORY_LIST        mdMemList;
814     MINIDUMP_MEMORY_DESCRIPTOR  mdMem;
815     DWORD                       written;
816     unsigned                    i, pos, len, sz;
817     RVA                         rva_base;
818     char                        tmp[1024];
819
820     mdMemList.NumberOfMemoryRanges = dc->num_mem;
821     append(dc, &mdMemList.NumberOfMemoryRanges,
822            sizeof(mdMemList.NumberOfMemoryRanges));
823     rva_base = dc->rva;
824     sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
825     dc->rva += sz;
826     sz += sizeof(mdMemList.NumberOfMemoryRanges);
827
828     for (i = 0; i < dc->num_mem; i++)
829     {
830         mdMem.StartOfMemoryRange = dc->mem[i].base;
831         mdMem.Memory.Rva = dc->rva;
832         mdMem.Memory.DataSize = dc->mem[i].size;
833         SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
834         for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
835         {
836             len = min(dc->mem[i].size - pos, sizeof(tmp));
837             if (ReadProcessMemory(dc->hProcess, 
838                                   (void*)(dc->mem[i].base + pos),
839                                   tmp, len, NULL))
840                 WriteFile(dc->hFile, tmp, len, &written, NULL);
841         }
842         dc->rva += mdMem.Memory.DataSize;
843         writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
844         if (dc->mem[i].rva)
845         {
846             writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
847         }
848     }
849
850     return sz;
851 }
852
853 static unsigned         dump_misc_info(struct dump_context* dc)
854 {
855     MINIDUMP_MISC_INFO  mmi;
856
857     mmi.SizeOfInfo = sizeof(mmi);
858     mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
859     mmi.ProcessId = dc->pid;
860     /* FIXME: create/user/kernel time */
861     mmi.ProcessCreateTime = 0;
862     mmi.ProcessKernelTime = 0;
863     mmi.ProcessUserTime = 0;
864
865     append(dc, &mmi, sizeof(mmi));
866     return sizeof(mmi);
867 }
868
869 /******************************************************************
870  *              MiniDumpWriteDump (DEBUGHLP.@)
871  *
872  */
873 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
874                               MINIDUMP_TYPE DumpType,
875                               PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
876                               PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
877                               PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
878 {
879     static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}};
880     MINIDUMP_HEADER     mdHead;
881     MINIDUMP_DIRECTORY  mdDir;
882     DWORD               i, nStreams, idx_stream;
883     struct dump_context dc;
884
885     dc.hProcess = hProcess;
886     dc.hFile = hFile;
887     dc.pid = pid;
888     dc.modules = NULL;
889     dc.num_modules = 0;
890     dc.alloc_modules = 0;
891     dc.cb = CallbackParam;
892     dc.type = DumpType;
893     dc.mem = NULL;
894     dc.num_mem = 0;
895     dc.alloc_mem = 0;
896     dc.rva = 0;
897
898     if (!fetch_processes_info(&dc)) return FALSE;
899     fetch_modules_info(&dc);
900
901     /* 1) init */
902     nStreams = 6 + (ExceptionParam ? 1 : 0) +
903         (UserStreamParam ? UserStreamParam->UserStreamCount : 0);
904
905     /* pad the directory size to a multiple of 4 for alignment purposes */
906     nStreams = (nStreams + 3) & ~3;
907
908     if (DumpType & MiniDumpWithDataSegs)
909         FIXME("NIY MiniDumpWithDataSegs\n");
910     if (DumpType & MiniDumpWithFullMemory)
911         FIXME("NIY MiniDumpWithFullMemory\n");
912     if (DumpType & MiniDumpWithHandleData)
913         FIXME("NIY MiniDumpWithHandleData\n");
914     if (DumpType & MiniDumpFilterMemory)
915         FIXME("NIY MiniDumpFilterMemory\n");
916     if (DumpType & MiniDumpScanMemory)
917         FIXME("NIY MiniDumpScanMemory\n");
918
919     /* 2) write header */
920     mdHead.Signature = MINIDUMP_SIGNATURE;
921     mdHead.Version = MINIDUMP_VERSION;  /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
922     mdHead.NumberOfStreams = nStreams;
923     mdHead.CheckSum = 0;                /* native sets a 0 checksum in its files */
924     mdHead.StreamDirectoryRva = sizeof(mdHead);
925     mdHead.u.TimeDateStamp = time(NULL);
926     mdHead.Flags = DumpType;
927     append(&dc, &mdHead, sizeof(mdHead));
928
929     /* 3) write stream directories */
930     dc.rva += nStreams * sizeof(mdDir);
931     idx_stream = 0;
932
933     /* 3.1) write data stream directories */
934
935     /* must be first in minidump */
936     mdDir.StreamType = SystemInfoStream;
937     mdDir.Location.Rva = dc.rva;
938     mdDir.Location.DataSize = dump_system_info(&dc);
939     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
940             &mdDir, sizeof(mdDir));
941
942     mdDir.StreamType = ThreadListStream;
943     mdDir.Location.Rva = dc.rva;
944     mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam);
945     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 
946             &mdDir, sizeof(mdDir));
947
948     mdDir.StreamType = ModuleListStream;
949     mdDir.Location.Rva = dc.rva;
950     mdDir.Location.DataSize = dump_modules(&dc, FALSE);
951     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
952             &mdDir, sizeof(mdDir));
953
954     mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
955     mdDir.Location.Rva = dc.rva;
956     mdDir.Location.DataSize = dump_modules(&dc, TRUE);
957     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
958             &mdDir, sizeof(mdDir));
959
960     mdDir.StreamType = MemoryListStream;
961     mdDir.Location.Rva = dc.rva;
962     mdDir.Location.DataSize = dump_memory_info(&dc);
963     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
964             &mdDir, sizeof(mdDir));
965
966     mdDir.StreamType = MiscInfoStream;
967     mdDir.Location.Rva = dc.rva;
968     mdDir.Location.DataSize = dump_misc_info(&dc);
969     writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
970             &mdDir, sizeof(mdDir));
971
972     /* 3.2) write exception information (if any) */
973     if (ExceptionParam)
974     {
975         mdDir.StreamType = ExceptionStream;
976         mdDir.Location.Rva = dc.rva;
977         mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam);
978         writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
979                 &mdDir, sizeof(mdDir));
980     }
981
982     /* 3.3) write user defined streams (if any) */
983     if (UserStreamParam)
984     {
985         for (i = 0; i < UserStreamParam->UserStreamCount; i++)
986         {
987             mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
988             mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
989             mdDir.Location.Rva = dc.rva;
990             writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
991                     &mdDir, sizeof(mdDir));
992             append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 
993                    UserStreamParam->UserStreamArray[i].BufferSize);
994         }
995     }
996
997     /* fill the remaining directory entries with 0's (unused stream types) */
998     /* NOTE: this should always come last in the dump! */
999     for (i = idx_stream; i < nStreams; i++)
1000         writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir));
1001
1002     HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
1003     HeapFree(GetProcessHeap(), 0, dc.mem);
1004     HeapFree(GetProcessHeap(), 0, dc.modules);
1005
1006     return TRUE;
1007 }
1008
1009 /******************************************************************
1010  *              MiniDumpReadDumpStream (DEBUGHLP.@)
1011  *
1012  *
1013  */
1014 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx,
1015                                    PMINIDUMP_DIRECTORY* pdir,
1016                                    PVOID* stream, ULONG* size)
1017 {
1018     MINIDUMP_HEADER*    mdHead = base;
1019
1020     if (mdHead->Signature == MINIDUMP_SIGNATURE)
1021     {
1022         MINIDUMP_DIRECTORY* dir;
1023         DWORD               i;
1024
1025         dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
1026         for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
1027         {
1028             if (dir->StreamType == str_idx)
1029             {
1030                 if (pdir) *pdir = dir;
1031                 if (stream) *stream = (char*)base + dir->Location.Rva;
1032                 if (size) *size = dir->Location.DataSize;
1033                 return TRUE;
1034             }
1035         }
1036     }
1037     SetLastError(ERROR_INVALID_PARAMETER);
1038     return FALSE;
1039 }