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