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