2 * File minidump.c - management of dumps (read & write)
4 * Copyright (C) 2004-2005, Eric Pouech
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.
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.
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
21 #include "dbghelp_private.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
27 /******************************************************************
30 * reads system wide process information, and gather from it the threads information
31 * for process of id 'pid'
33 static BOOL
fetch_process_info(struct dump_context
* dc
)
35 ULONG buf_size
= 0x1000;
37 void* pcs_buffer
= NULL
;
39 if (!(pcs_buffer
= HeapAlloc(GetProcessHeap(), 0, buf_size
))) return FALSE
;
42 nts
= NtQuerySystemInformation(SystemProcessInformation
,
43 pcs_buffer
, buf_size
, NULL
);
44 if (nts
!= STATUS_INFO_LENGTH_MISMATCH
) break;
45 pcs_buffer
= HeapReAlloc(GetProcessHeap(), 0, pcs_buffer
, buf_size
*= 2);
46 if (!pcs_buffer
) return FALSE
;
49 if (nts
== STATUS_SUCCESS
)
51 SYSTEM_PROCESS_INFORMATION
* spi
= pcs_buffer
;
56 if (HandleToUlong(spi
->UniqueProcessId
) == dc
->pid
)
58 dc
->num_threads
= spi
->dwThreadCount
;
59 dc
->threads
= HeapAlloc(GetProcessHeap(), 0,
60 dc
->num_threads
* sizeof(dc
->threads
[0]));
61 if (!dc
->threads
) goto failed
;
62 for (i
= 0; i
< dc
->num_threads
; i
++)
64 dc
->threads
[i
].tid
= HandleToULong(spi
->ti
[i
].ClientId
.UniqueThread
);
65 dc
->threads
[i
].prio_class
= spi
->ti
[i
].dwBasePriority
; /* FIXME */
66 dc
->threads
[i
].curr_prio
= spi
->ti
[i
].dwCurrentPriority
;
68 HeapFree(GetProcessHeap(), 0, pcs_buffer
);
71 if (!spi
->NextEntryOffset
) break;
72 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ spi
->NextEntryOffset
);
76 HeapFree(GetProcessHeap(), 0, pcs_buffer
);
80 static void fetch_thread_stack(struct dump_context
* dc
, const void* teb_addr
,
81 const CONTEXT
* ctx
, MINIDUMP_MEMORY_DESCRIPTOR
* mmd
)
86 if (ReadProcessMemory(dc
->hProcess
, teb_addr
, &tib
, sizeof(tib
), NULL
) &&
87 dbghelp_current_cpu
&&
88 dbghelp_current_cpu
->get_addr(NULL
/* FIXME */, ctx
, cpu_addr_stack
, &addr
) && addr
.Mode
== AddrModeFlat
)
92 addr
.Offset
-= dbghelp_current_cpu
->word_size
;
93 /* make sure stack pointer is within the established range of the stack. It could have
94 been clobbered by whatever caused the original exception. */
95 if (addr
.Offset
< (ULONG_PTR
)tib
.StackLimit
|| addr
.Offset
> (ULONG_PTR
)tib
.StackBase
)
96 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
99 mmd
->StartOfMemoryRange
= addr
.Offset
;
102 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
103 mmd
->Memory
.DataSize
= (ULONG_PTR
)tib
.StackBase
- mmd
->StartOfMemoryRange
;
107 /******************************************************************
110 * fetches some information about thread of id 'tid'
112 static BOOL
fetch_thread_info(struct dump_context
* dc
, int thd_idx
,
113 const MINIDUMP_EXCEPTION_INFORMATION
* except
,
114 MINIDUMP_THREAD
* mdThd
, CONTEXT
* ctx
)
116 DWORD tid
= dc
->threads
[thd_idx
].tid
;
118 THREAD_BASIC_INFORMATION tbi
;
120 memset(ctx
, 0, sizeof(*ctx
));
122 mdThd
->ThreadId
= tid
;
123 mdThd
->SuspendCount
= 0;
125 mdThd
->Stack
.StartOfMemoryRange
= 0;
126 mdThd
->Stack
.Memory
.DataSize
= 0;
127 mdThd
->Stack
.Memory
.Rva
= 0;
128 mdThd
->ThreadContext
.DataSize
= 0;
129 mdThd
->ThreadContext
.Rva
= 0;
130 mdThd
->PriorityClass
= dc
->threads
[thd_idx
].prio_class
;
131 mdThd
->Priority
= dc
->threads
[thd_idx
].curr_prio
;
133 if ((hThread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, tid
)) == NULL
)
135 FIXME("Couldn't open thread %u (%u)\n", tid
, GetLastError());
139 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
,
140 &tbi
, sizeof(tbi
), NULL
) == STATUS_SUCCESS
)
142 mdThd
->Teb
= (ULONG_PTR
)tbi
.TebBaseAddress
;
143 if (tbi
.ExitStatus
== STILL_ACTIVE
)
145 if (tid
!= GetCurrentThreadId() &&
146 (mdThd
->SuspendCount
= SuspendThread(hThread
)) != (DWORD
)-1)
148 ctx
->ContextFlags
= CONTEXT_FULL
;
149 if (!GetThreadContext(hThread
, ctx
))
150 memset(ctx
, 0, sizeof(*ctx
));
152 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, ctx
, &mdThd
->Stack
);
153 ResumeThread(hThread
);
155 else if (tid
== GetCurrentThreadId() && except
)
158 mdThd
->SuspendCount
= 1;
159 if (except
->ClientPointers
)
161 EXCEPTION_POINTERS ep
;
163 ReadProcessMemory(dc
->hProcess
, except
->ExceptionPointers
,
164 &ep
, sizeof(ep
), NULL
);
165 ReadProcessMemory(dc
->hProcess
, ep
.ContextRecord
,
166 &lctx
, sizeof(lctx
), NULL
);
169 else pctx
= except
->ExceptionPointers
->ContextRecord
;
172 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, pctx
, &mdThd
->Stack
);
174 else mdThd
->SuspendCount
= 0;
177 CloseHandle(hThread
);
181 /******************************************************************
184 * Add a module to a dump context
186 static BOOL
add_module(struct dump_context
* dc
, const WCHAR
* name
,
187 DWORD64 base
, DWORD size
, DWORD timestamp
, DWORD checksum
,
192 dc
->alloc_modules
= 32;
193 dc
->modules
= HeapAlloc(GetProcessHeap(), 0,
194 dc
->alloc_modules
* sizeof(*dc
->modules
));
196 else if(dc
->num_modules
>= dc
->alloc_modules
)
198 dc
->alloc_modules
*= 2;
199 dc
->modules
= HeapReAlloc(GetProcessHeap(), 0, dc
->modules
,
200 dc
->alloc_modules
* sizeof(*dc
->modules
));
204 dc
->alloc_modules
= dc
->num_modules
= 0;
208 !GetModuleFileNameExW(dc
->hProcess
, (HMODULE
)(DWORD_PTR
)base
,
209 dc
->modules
[dc
->num_modules
].name
,
210 sizeof(dc
->modules
[dc
->num_modules
].name
) / sizeof(WCHAR
)))
211 lstrcpynW(dc
->modules
[dc
->num_modules
].name
, name
,
212 sizeof(dc
->modules
[dc
->num_modules
].name
) / sizeof(WCHAR
));
213 dc
->modules
[dc
->num_modules
].base
= base
;
214 dc
->modules
[dc
->num_modules
].size
= size
;
215 dc
->modules
[dc
->num_modules
].timestamp
= timestamp
;
216 dc
->modules
[dc
->num_modules
].checksum
= checksum
;
217 dc
->modules
[dc
->num_modules
].is_elf
= is_elf
;
223 /******************************************************************
224 * fetch_pe_module_info_cb
226 * Callback for accumulating in dump_context a PE modules set
228 static BOOL WINAPI
fetch_pe_module_info_cb(PCWSTR name
, DWORD64 base
, ULONG size
,
231 struct dump_context
* dc
= user
;
232 IMAGE_NT_HEADERS nth
;
234 if (!validate_addr64(base
)) return FALSE
;
236 if (pe_load_nt_header(dc
->hProcess
, base
, &nth
))
237 add_module(user
, name
, base
, size
,
238 nth
.FileHeader
.TimeDateStamp
, nth
.OptionalHeader
.CheckSum
,
243 /******************************************************************
244 * fetch_elf_module_info_cb
246 * Callback for accumulating in dump_context an ELF modules set
248 static BOOL
fetch_elf_module_info_cb(const WCHAR
* name
, unsigned long base
,
251 struct dump_context
* dc
= user
;
253 DWORD size
, checksum
;
255 /* FIXME: there's no relevant timestamp on ELF modules */
256 /* NB: if we have a non-null base from the live-target use it (whenever
257 * the ELF module is relocatable or not). If we have a null base (ELF
258 * module isn't relocatable) then grab its base address from ELF file
260 if (!elf_fetch_file_info(name
, &rbase
, &size
, &checksum
))
262 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
266 /******************************************************************
267 * fetch_macho_module_info_cb
269 * Callback for accumulating in dump_context a Mach-O modules set
271 static BOOL
fetch_macho_module_info_cb(const WCHAR
* name
, unsigned long base
,
274 struct dump_context
* dc
= (struct dump_context
*)user
;
276 DWORD size
, checksum
;
278 /* FIXME: there's no relevant timestamp on Mach-O modules */
279 /* NB: if we have a non-null base from the live-target use it. If we have
280 * a null base, then grab its base address from Mach-O file.
282 if (!macho_fetch_file_info(dc
->hProcess
, name
, base
, &rbase
, &size
, &checksum
))
284 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
288 static void fetch_modules_info(struct dump_context
* dc
)
290 EnumerateLoadedModulesW64(dc
->hProcess
, fetch_pe_module_info_cb
, dc
);
291 /* Since we include ELF modules in a separate stream from the regular PE ones,
292 * we can always include those ELF modules (they don't eat lots of space)
293 * And it's always a good idea to have a trace of the loaded ELF modules for
294 * a given application in a post mortem debugging condition.
296 elf_enum_modules(dc
->hProcess
, fetch_elf_module_info_cb
, dc
);
297 macho_enum_modules(dc
->hProcess
, fetch_macho_module_info_cb
, dc
);
300 static void fetch_module_versioninfo(LPCWSTR filename
, VS_FIXEDFILEINFO
* ffi
)
304 static const WCHAR backslashW
[] = {'\\', '\0'};
306 memset(ffi
, 0, sizeof(*ffi
));
307 if ((sz
= GetFileVersionInfoSizeW(filename
, &handle
)))
309 void* info
= HeapAlloc(GetProcessHeap(), 0, sz
);
310 if (info
&& GetFileVersionInfoW(filename
, handle
, sz
, info
))
312 VS_FIXEDFILEINFO
* ptr
;
315 if (VerQueryValueW(info
, backslashW
, (void*)&ptr
, &len
))
316 memcpy(ffi
, ptr
, min(len
, sizeof(*ffi
)));
318 HeapFree(GetProcessHeap(), 0, info
);
322 /******************************************************************
323 * minidump_add_memory_block
325 * Add a memory block to be dumped in a minidump
326 * If rva is non 0, it's the rva in the minidump where has to be stored
327 * also the rva of the memory block when written (this allows us to reference
328 * a memory block from outside the list of memory blocks).
330 void minidump_add_memory_block(struct dump_context
* dc
, ULONG64 base
, ULONG size
, ULONG rva
)
335 dc
->mem
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem
* sizeof(*dc
->mem
));
337 else if (dc
->num_mem
>= dc
->alloc_mem
)
340 dc
->mem
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem
,
341 dc
->alloc_mem
* sizeof(*dc
->mem
));
345 dc
->mem
[dc
->num_mem
].base
= base
;
346 dc
->mem
[dc
->num_mem
].size
= size
;
347 dc
->mem
[dc
->num_mem
].rva
= rva
;
350 else dc
->num_mem
= dc
->alloc_mem
= 0;
353 /******************************************************************
356 * Writes a chunk of data at a given position in the minidump
358 static void writeat(struct dump_context
* dc
, RVA rva
, const void* data
, unsigned size
)
362 SetFilePointer(dc
->hFile
, rva
, NULL
, FILE_BEGIN
);
363 WriteFile(dc
->hFile
, data
, size
, &written
, NULL
);
366 /******************************************************************
369 * writes a new chunk of data to the minidump, increasing the current
372 static void append(struct dump_context
* dc
, const void* data
, unsigned size
)
374 writeat(dc
, dc
->rva
, data
, size
);
378 /******************************************************************
379 * dump_exception_info
381 * Write in File the exception information from pcs
383 static unsigned dump_exception_info(struct dump_context
* dc
,
384 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
386 MINIDUMP_EXCEPTION_STREAM mdExcpt
;
387 EXCEPTION_RECORD rec
, *prec
;
391 mdExcpt
.ThreadId
= except
->ThreadId
;
392 mdExcpt
.__alignment
= 0;
393 if (except
->ClientPointers
)
395 EXCEPTION_POINTERS ep
;
397 ReadProcessMemory(dc
->hProcess
,
398 except
->ExceptionPointers
, &ep
, sizeof(ep
), NULL
);
399 ReadProcessMemory(dc
->hProcess
,
400 ep
.ExceptionRecord
, &rec
, sizeof(rec
), NULL
);
401 ReadProcessMemory(dc
->hProcess
,
402 ep
.ContextRecord
, &ctx
, sizeof(ctx
), NULL
);
408 prec
= except
->ExceptionPointers
->ExceptionRecord
;
409 pctx
= except
->ExceptionPointers
->ContextRecord
;
411 mdExcpt
.ExceptionRecord
.ExceptionCode
= prec
->ExceptionCode
;
412 mdExcpt
.ExceptionRecord
.ExceptionFlags
= prec
->ExceptionFlags
;
413 mdExcpt
.ExceptionRecord
.ExceptionRecord
= (DWORD_PTR
)prec
->ExceptionRecord
;
414 mdExcpt
.ExceptionRecord
.ExceptionAddress
= (DWORD_PTR
)prec
->ExceptionAddress
;
415 mdExcpt
.ExceptionRecord
.NumberParameters
= prec
->NumberParameters
;
416 mdExcpt
.ExceptionRecord
.__unusedAlignment
= 0;
417 for (i
= 0; i
< mdExcpt
.ExceptionRecord
.NumberParameters
; i
++)
418 mdExcpt
.ExceptionRecord
.ExceptionInformation
[i
] = prec
->ExceptionInformation
[i
];
419 mdExcpt
.ThreadContext
.DataSize
= sizeof(*pctx
);
420 mdExcpt
.ThreadContext
.Rva
= dc
->rva
+ sizeof(mdExcpt
);
422 append(dc
, &mdExcpt
, sizeof(mdExcpt
));
423 append(dc
, pctx
, sizeof(*pctx
));
424 return sizeof(mdExcpt
);
427 /******************************************************************
430 * Write in File the modules from pcs
432 static unsigned dump_modules(struct dump_context
* dc
, BOOL dump_elf
)
434 MINIDUMP_MODULE mdModule
;
435 MINIDUMP_MODULE_LIST mdModuleList
;
437 MINIDUMP_STRING
* ms
= (MINIDUMP_STRING
*)tmp
;
443 for (i
= nmod
= 0; i
< dc
->num_modules
; i
++)
445 if ((dc
->modules
[i
].is_elf
&& dump_elf
) ||
446 (!dc
->modules
[i
].is_elf
&& !dump_elf
))
450 mdModuleList
.NumberOfModules
= 0;
451 /* reserve space for mdModuleList
452 * FIXME: since we don't support 0 length arrays, we cannot use the
453 * size of mdModuleList
454 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
457 /* the stream size is just the size of the module index. It does not include the data for the
458 names of each module. *Technically* the names are supposed to go into the common string table
459 in the minidump file. Since each string is referenced by RVA they can all safely be located
460 anywhere between streams in the file, so the end of this stream is sufficient. */
462 dc
->rva
+= sz
= sizeof(mdModuleList
.NumberOfModules
) + sizeof(mdModule
) * nmod
;
463 for (i
= 0; i
< dc
->num_modules
; i
++)
465 if ((dc
->modules
[i
].is_elf
&& !dump_elf
) ||
466 (!dc
->modules
[i
].is_elf
&& dump_elf
))
469 flags_out
= ModuleWriteModule
| ModuleWriteMiscRecord
| ModuleWriteCvRecord
;
470 if (dc
->type
& MiniDumpWithDataSegs
)
471 flags_out
|= ModuleWriteDataSeg
;
472 if (dc
->type
& MiniDumpWithProcessThreadData
)
473 flags_out
|= ModuleWriteTlsData
;
474 if (dc
->type
& MiniDumpWithCodeSegs
)
475 flags_out
|= ModuleWriteCodeSegs
;
476 ms
->Length
= (lstrlenW(dc
->modules
[i
].name
) + 1) * sizeof(WCHAR
);
477 if (sizeof(ULONG
) + ms
->Length
> sizeof(tmp
))
478 FIXME("Buffer overflow!!!\n");
479 lstrcpyW(ms
->Buffer
, dc
->modules
[i
].name
);
483 MINIDUMP_CALLBACK_INPUT cbin
;
484 MINIDUMP_CALLBACK_OUTPUT cbout
;
486 cbin
.ProcessId
= dc
->pid
;
487 cbin
.ProcessHandle
= dc
->hProcess
;
488 cbin
.CallbackType
= ModuleCallback
;
490 cbin
.u
.Module
.FullPath
= ms
->Buffer
;
491 cbin
.u
.Module
.BaseOfImage
= dc
->modules
[i
].base
;
492 cbin
.u
.Module
.SizeOfImage
= dc
->modules
[i
].size
;
493 cbin
.u
.Module
.CheckSum
= dc
->modules
[i
].checksum
;
494 cbin
.u
.Module
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
495 memset(&cbin
.u
.Module
.VersionInfo
, 0, sizeof(cbin
.u
.Module
.VersionInfo
));
496 cbin
.u
.Module
.CvRecord
= NULL
;
497 cbin
.u
.Module
.SizeOfCvRecord
= 0;
498 cbin
.u
.Module
.MiscRecord
= NULL
;
499 cbin
.u
.Module
.SizeOfMiscRecord
= 0;
501 cbout
.u
.ModuleWriteFlags
= flags_out
;
502 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
504 flags_out
&= cbout
.u
.ModuleWriteFlags
;
506 if (flags_out
& ModuleWriteModule
)
508 /* fetch CPU dependent module info (like UNWIND_INFO) */
509 dbghelp_current_cpu
->fetch_minidump_module(dc
, i
, flags_out
);
511 mdModule
.BaseOfImage
= dc
->modules
[i
].base
;
512 mdModule
.SizeOfImage
= dc
->modules
[i
].size
;
513 mdModule
.CheckSum
= dc
->modules
[i
].checksum
;
514 mdModule
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
515 mdModule
.ModuleNameRva
= dc
->rva
;
516 ms
->Length
-= sizeof(WCHAR
);
517 append(dc
, ms
, sizeof(ULONG
) + ms
->Length
+ sizeof(WCHAR
));
518 fetch_module_versioninfo(ms
->Buffer
, &mdModule
.VersionInfo
);
519 mdModule
.CvRecord
.DataSize
= 0; /* FIXME */
520 mdModule
.CvRecord
.Rva
= 0; /* FIXME */
521 mdModule
.MiscRecord
.DataSize
= 0; /* FIXME */
522 mdModule
.MiscRecord
.Rva
= 0; /* FIXME */
523 mdModule
.Reserved0
= 0; /* FIXME */
524 mdModule
.Reserved1
= 0; /* FIXME */
526 rva_base
+ sizeof(mdModuleList
.NumberOfModules
) +
527 mdModuleList
.NumberOfModules
++ * sizeof(mdModule
),
528 &mdModule
, sizeof(mdModule
));
531 writeat(dc
, rva_base
, &mdModuleList
.NumberOfModules
,
532 sizeof(mdModuleList
.NumberOfModules
));
537 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
538 * We are compiled with -fPIC, so we can't clobber ebx.
540 static inline void do_x86cpuid(unsigned int ax
, unsigned int *p
)
542 #if defined(__GNUC__) && defined(__i386__)
543 __asm__("pushl %%ebx\n\t"
545 "movl %%ebx, %%esi\n\t"
547 : "=a" (p
[0]), "=S" (p
[1]), "=c" (p
[2]), "=d" (p
[3])
552 /* From xf86info havecpuid.c 1.11 */
553 static inline int have_x86cpuid(void)
555 #if defined(__GNUC__) && defined(__i386__)
567 : "=&r" (f1
), "=&r" (f2
)
568 : "ir" (0x00200000));
569 return ((f1
^f2
) & 0x00200000) != 0;
575 /******************************************************************
578 * Dumps into File the information about the system
580 static unsigned dump_system_info(struct dump_context
* dc
)
582 MINIDUMP_SYSTEM_INFO mdSysInfo
;
584 OSVERSIONINFOW osInfo
;
587 DWORD wine_extra
= 0;
589 const char *(CDECL
*wine_get_build_id
)(void);
590 void (CDECL
*wine_get_host_version
)(const char **sysname
, const char **release
);
591 const char* build_id
= NULL
;
592 const char* sys_name
= NULL
;
593 const char* release_name
= NULL
;
595 GetSystemInfo(&sysInfo
);
596 osInfo
.dwOSVersionInfoSize
= sizeof(osInfo
);
597 GetVersionExW(&osInfo
);
599 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
600 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
601 if (wine_get_build_id
&& wine_get_host_version
)
603 /* cheat minidump system information by adding specific wine information */
604 wine_extra
= 4 + 4 * sizeof(slen
);
605 build_id
= wine_get_build_id();
606 wine_get_host_version(&sys_name
, &release_name
);
607 wine_extra
+= strlen(build_id
) + 1 + strlen(sys_name
) + 1 + strlen(release_name
) + 1;
610 mdSysInfo
.ProcessorArchitecture
= sysInfo
.u
.s
.wProcessorArchitecture
;
611 mdSysInfo
.ProcessorLevel
= sysInfo
.wProcessorLevel
;
612 mdSysInfo
.ProcessorRevision
= sysInfo
.wProcessorRevision
;
613 mdSysInfo
.u
.s
.NumberOfProcessors
= sysInfo
.dwNumberOfProcessors
;
614 mdSysInfo
.u
.s
.ProductType
= VER_NT_WORKSTATION
; /* FIXME */
615 mdSysInfo
.MajorVersion
= osInfo
.dwMajorVersion
;
616 mdSysInfo
.MinorVersion
= osInfo
.dwMinorVersion
;
617 mdSysInfo
.BuildNumber
= osInfo
.dwBuildNumber
;
618 mdSysInfo
.PlatformId
= osInfo
.dwPlatformId
;
620 mdSysInfo
.CSDVersionRva
= dc
->rva
+ sizeof(mdSysInfo
) + wine_extra
;
621 mdSysInfo
.u1
.Reserved1
= 0;
622 mdSysInfo
.u1
.s
.SuiteMask
= VER_SUITE_TERMINAL
;
626 unsigned regs0
[4], regs1
[4];
628 do_x86cpuid(0, regs0
);
629 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[0] = regs0
[1];
630 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[1] = regs0
[3];
631 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[2] = regs0
[2];
632 do_x86cpuid(1, regs1
);
633 mdSysInfo
.Cpu
.X86CpuInfo
.VersionInformation
= regs1
[0];
634 mdSysInfo
.Cpu
.X86CpuInfo
.FeatureInformation
= regs1
[3];
635 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= 0;
636 if (regs0
[1] == 0x68747541 /* "Auth" */ &&
637 regs0
[3] == 0x69746e65 /* "enti" */ &&
638 regs0
[2] == 0x444d4163 /* "cAMD" */)
640 do_x86cpuid(0x80000000, regs1
); /* get vendor cpuid level */
641 if (regs1
[0] >= 0x80000001)
643 do_x86cpuid(0x80000001, regs1
); /* get vendor features */
644 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= regs1
[3];
653 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] = 0;
654 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[1] = 0;
656 for (i
= 0; i
< sizeof(mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0]) * 8; i
++)
657 if (IsProcessorFeaturePresent(i
))
658 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] |= one
<< i
;
660 append(dc
, &mdSysInfo
, sizeof(mdSysInfo
));
662 /* write Wine specific system information just behind the structure, and before any string */
665 char code
[] = {'W','I','N','E'};
667 WriteFile(dc
->hFile
, code
, 4, &written
, NULL
);
668 /* number of sub-info, so that we can extend structure if needed */
670 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
671 /* we store offsets from just after the WINE marker */
672 slen
= 4 * sizeof(DWORD
);
673 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
674 slen
+= strlen(build_id
) + 1;
675 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
676 slen
+= strlen(sys_name
) + 1;
677 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
678 WriteFile(dc
->hFile
, build_id
, strlen(build_id
) + 1, &written
, NULL
);
679 WriteFile(dc
->hFile
, sys_name
, strlen(sys_name
) + 1, &written
, NULL
);
680 WriteFile(dc
->hFile
, release_name
, strlen(release_name
) + 1, &written
, NULL
);
681 dc
->rva
+= wine_extra
;
684 /* write the service pack version string after this stream. It is referenced within the
685 stream by its RVA in the file. */
686 slen
= lstrlenW(osInfo
.szCSDVersion
) * sizeof(WCHAR
);
687 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
688 WriteFile(dc
->hFile
, osInfo
.szCSDVersion
, slen
, &written
, NULL
);
689 dc
->rva
+= sizeof(ULONG
) + slen
;
691 return sizeof(mdSysInfo
);
694 /******************************************************************
697 * Dumps into File the information about running threads
699 static unsigned dump_threads(struct dump_context
* dc
,
700 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
702 MINIDUMP_THREAD mdThd
;
703 MINIDUMP_THREAD_LIST mdThdList
;
709 mdThdList
.NumberOfThreads
= 0;
712 dc
->rva
+= sz
= sizeof(mdThdList
.NumberOfThreads
) + dc
->num_threads
* sizeof(mdThd
);
714 for (i
= 0; i
< dc
->num_threads
; i
++)
716 fetch_thread_info(dc
, i
, except
, &mdThd
, &ctx
);
718 flags_out
= ThreadWriteThread
| ThreadWriteStack
| ThreadWriteContext
|
719 ThreadWriteInstructionWindow
;
720 if (dc
->type
& MiniDumpWithProcessThreadData
)
721 flags_out
|= ThreadWriteThreadData
;
722 if (dc
->type
& MiniDumpWithThreadInfo
)
723 flags_out
|= ThreadWriteThreadInfo
;
727 MINIDUMP_CALLBACK_INPUT cbin
;
728 MINIDUMP_CALLBACK_OUTPUT cbout
;
730 cbin
.ProcessId
= dc
->pid
;
731 cbin
.ProcessHandle
= dc
->hProcess
;
732 cbin
.CallbackType
= ThreadCallback
;
733 cbin
.u
.Thread
.ThreadId
= dc
->threads
[i
].tid
;
734 cbin
.u
.Thread
.ThreadHandle
= 0; /* FIXME */
735 cbin
.u
.Thread
.Context
= ctx
;
736 cbin
.u
.Thread
.SizeOfContext
= sizeof(CONTEXT
);
737 cbin
.u
.Thread
.StackBase
= mdThd
.Stack
.StartOfMemoryRange
;
738 cbin
.u
.Thread
.StackEnd
= mdThd
.Stack
.StartOfMemoryRange
+
739 mdThd
.Stack
.Memory
.DataSize
;
741 cbout
.u
.ThreadWriteFlags
= flags_out
;
742 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
744 flags_out
&= cbout
.u
.ThreadWriteFlags
;
746 if (flags_out
& ThreadWriteThread
)
748 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteContext
))
750 mdThd
.ThreadContext
.Rva
= dc
->rva
;
751 mdThd
.ThreadContext
.DataSize
= sizeof(CONTEXT
);
752 append(dc
, &ctx
, sizeof(CONTEXT
));
754 if (mdThd
.Stack
.Memory
.DataSize
&& (flags_out
& ThreadWriteStack
))
756 minidump_add_memory_block(dc
, mdThd
.Stack
.StartOfMemoryRange
,
757 mdThd
.Stack
.Memory
.DataSize
,
758 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
759 mdThdList
.NumberOfThreads
* sizeof(mdThd
) +
760 FIELD_OFFSET(MINIDUMP_THREAD
, Stack
.Memory
.Rva
));
763 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
764 mdThdList
.NumberOfThreads
* sizeof(mdThd
),
765 &mdThd
, sizeof(mdThd
));
766 mdThdList
.NumberOfThreads
++;
768 /* fetch CPU dependent thread info (like 256 bytes around program counter */
769 dbghelp_current_cpu
->fetch_minidump_thread(dc
, i
, flags_out
, &ctx
);
771 writeat(dc
, rva_base
,
772 &mdThdList
.NumberOfThreads
, sizeof(mdThdList
.NumberOfThreads
));
777 /******************************************************************
780 * dumps information about the memory of the process (stack of the threads)
782 static unsigned dump_memory_info(struct dump_context
* dc
)
784 MINIDUMP_MEMORY_LIST mdMemList
;
785 MINIDUMP_MEMORY_DESCRIPTOR mdMem
;
787 unsigned i
, pos
, len
, sz
;
791 mdMemList
.NumberOfMemoryRanges
= dc
->num_mem
;
792 append(dc
, &mdMemList
.NumberOfMemoryRanges
,
793 sizeof(mdMemList
.NumberOfMemoryRanges
));
795 sz
= mdMemList
.NumberOfMemoryRanges
* sizeof(mdMem
);
797 sz
+= sizeof(mdMemList
.NumberOfMemoryRanges
);
799 for (i
= 0; i
< dc
->num_mem
; i
++)
801 mdMem
.StartOfMemoryRange
= dc
->mem
[i
].base
;
802 mdMem
.Memory
.Rva
= dc
->rva
;
803 mdMem
.Memory
.DataSize
= dc
->mem
[i
].size
;
804 SetFilePointer(dc
->hFile
, dc
->rva
, NULL
, FILE_BEGIN
);
805 for (pos
= 0; pos
< dc
->mem
[i
].size
; pos
+= sizeof(tmp
))
807 len
= min(dc
->mem
[i
].size
- pos
, sizeof(tmp
));
808 if (ReadProcessMemory(dc
->hProcess
,
809 (void*)(DWORD_PTR
)(dc
->mem
[i
].base
+ pos
),
811 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
813 dc
->rva
+= mdMem
.Memory
.DataSize
;
814 writeat(dc
, rva_base
+ i
* sizeof(mdMem
), &mdMem
, sizeof(mdMem
));
817 writeat(dc
, dc
->mem
[i
].rva
, &mdMem
.Memory
.Rva
, sizeof(mdMem
.Memory
.Rva
));
824 static unsigned dump_misc_info(struct dump_context
* dc
)
826 MINIDUMP_MISC_INFO mmi
;
828 mmi
.SizeOfInfo
= sizeof(mmi
);
829 mmi
.Flags1
= MINIDUMP_MISC1_PROCESS_ID
;
830 mmi
.ProcessId
= dc
->pid
;
831 /* FIXME: create/user/kernel time */
832 mmi
.ProcessCreateTime
= 0;
833 mmi
.ProcessKernelTime
= 0;
834 mmi
.ProcessUserTime
= 0;
836 append(dc
, &mmi
, sizeof(mmi
));
840 /******************************************************************
841 * MiniDumpWriteDump (DEBUGHLP.@)
844 BOOL WINAPI
MiniDumpWriteDump(HANDLE hProcess
, DWORD pid
, HANDLE hFile
,
845 MINIDUMP_TYPE DumpType
,
846 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
,
847 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
,
848 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
)
850 static const MINIDUMP_DIRECTORY emptyDir
= {UnusedStream
, {0, 0}};
851 MINIDUMP_HEADER mdHead
;
852 MINIDUMP_DIRECTORY mdDir
;
853 DWORD i
, nStreams
, idx_stream
;
854 struct dump_context dc
;
856 dc
.hProcess
= hProcess
;
861 dc
.alloc_modules
= 0;
864 dc
.cb
= CallbackParam
;
871 if (!fetch_process_info(&dc
)) return FALSE
;
872 fetch_modules_info(&dc
);
875 nStreams
= 6 + (ExceptionParam
? 1 : 0) +
876 (UserStreamParam
? UserStreamParam
->UserStreamCount
: 0);
878 /* pad the directory size to a multiple of 4 for alignment purposes */
879 nStreams
= (nStreams
+ 3) & ~3;
881 if (DumpType
& MiniDumpWithDataSegs
)
882 FIXME("NIY MiniDumpWithDataSegs\n");
883 if (DumpType
& MiniDumpWithFullMemory
)
884 FIXME("NIY MiniDumpWithFullMemory\n");
885 if (DumpType
& MiniDumpWithHandleData
)
886 FIXME("NIY MiniDumpWithHandleData\n");
887 if (DumpType
& MiniDumpFilterMemory
)
888 FIXME("NIY MiniDumpFilterMemory\n");
889 if (DumpType
& MiniDumpScanMemory
)
890 FIXME("NIY MiniDumpScanMemory\n");
892 /* 2) write header */
893 mdHead
.Signature
= MINIDUMP_SIGNATURE
;
894 mdHead
.Version
= MINIDUMP_VERSION
; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
895 mdHead
.NumberOfStreams
= nStreams
;
896 mdHead
.CheckSum
= 0; /* native sets a 0 checksum in its files */
897 mdHead
.StreamDirectoryRva
= sizeof(mdHead
);
898 mdHead
.u
.TimeDateStamp
= time(NULL
);
899 mdHead
.Flags
= DumpType
;
900 append(&dc
, &mdHead
, sizeof(mdHead
));
902 /* 3) write stream directories */
903 dc
.rva
+= nStreams
* sizeof(mdDir
);
906 /* 3.1) write data stream directories */
908 /* must be first in minidump */
909 mdDir
.StreamType
= SystemInfoStream
;
910 mdDir
.Location
.Rva
= dc
.rva
;
911 mdDir
.Location
.DataSize
= dump_system_info(&dc
);
912 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
913 &mdDir
, sizeof(mdDir
));
915 mdDir
.StreamType
= ThreadListStream
;
916 mdDir
.Location
.Rva
= dc
.rva
;
917 mdDir
.Location
.DataSize
= dump_threads(&dc
, ExceptionParam
);
918 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
919 &mdDir
, sizeof(mdDir
));
921 mdDir
.StreamType
= ModuleListStream
;
922 mdDir
.Location
.Rva
= dc
.rva
;
923 mdDir
.Location
.DataSize
= dump_modules(&dc
, FALSE
);
924 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
925 &mdDir
, sizeof(mdDir
));
927 mdDir
.StreamType
= 0xfff0; /* FIXME: this is part of MS reserved streams */
928 mdDir
.Location
.Rva
= dc
.rva
;
929 mdDir
.Location
.DataSize
= dump_modules(&dc
, TRUE
);
930 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
931 &mdDir
, sizeof(mdDir
));
933 mdDir
.StreamType
= MemoryListStream
;
934 mdDir
.Location
.Rva
= dc
.rva
;
935 mdDir
.Location
.DataSize
= dump_memory_info(&dc
);
936 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
937 &mdDir
, sizeof(mdDir
));
939 mdDir
.StreamType
= MiscInfoStream
;
940 mdDir
.Location
.Rva
= dc
.rva
;
941 mdDir
.Location
.DataSize
= dump_misc_info(&dc
);
942 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
943 &mdDir
, sizeof(mdDir
));
945 /* 3.2) write exception information (if any) */
948 mdDir
.StreamType
= ExceptionStream
;
949 mdDir
.Location
.Rva
= dc
.rva
;
950 mdDir
.Location
.DataSize
= dump_exception_info(&dc
, ExceptionParam
);
951 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
952 &mdDir
, sizeof(mdDir
));
955 /* 3.3) write user defined streams (if any) */
958 for (i
= 0; i
< UserStreamParam
->UserStreamCount
; i
++)
960 mdDir
.StreamType
= UserStreamParam
->UserStreamArray
[i
].Type
;
961 mdDir
.Location
.DataSize
= UserStreamParam
->UserStreamArray
[i
].BufferSize
;
962 mdDir
.Location
.Rva
= dc
.rva
;
963 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
964 &mdDir
, sizeof(mdDir
));
965 append(&dc
, UserStreamParam
->UserStreamArray
[i
].Buffer
,
966 UserStreamParam
->UserStreamArray
[i
].BufferSize
);
970 /* fill the remaining directory entries with 0's (unused stream types) */
971 /* NOTE: this should always come last in the dump! */
972 for (i
= idx_stream
; i
< nStreams
; i
++)
973 writeat(&dc
, mdHead
.StreamDirectoryRva
+ i
* sizeof(emptyDir
), &emptyDir
, sizeof(emptyDir
));
975 HeapFree(GetProcessHeap(), 0, dc
.mem
);
976 HeapFree(GetProcessHeap(), 0, dc
.modules
);
977 HeapFree(GetProcessHeap(), 0, dc
.threads
);
982 /******************************************************************
983 * MiniDumpReadDumpStream (DEBUGHLP.@)
987 BOOL WINAPI
MiniDumpReadDumpStream(PVOID base
, ULONG str_idx
,
988 PMINIDUMP_DIRECTORY
* pdir
,
989 PVOID
* stream
, ULONG
* size
)
991 MINIDUMP_HEADER
* mdHead
= base
;
993 if (mdHead
->Signature
== MINIDUMP_SIGNATURE
)
995 MINIDUMP_DIRECTORY
* dir
;
998 dir
= (MINIDUMP_DIRECTORY
*)((char*)base
+ mdHead
->StreamDirectoryRva
);
999 for (i
= 0; i
< mdHead
->NumberOfStreams
; i
++, dir
++)
1001 if (dir
->StreamType
== str_idx
)
1003 if (pdir
) *pdir
= dir
;
1004 if (stream
) *stream
= (char*)base
+ dir
->Location
.Rva
;
1005 if (size
) *size
= dir
->Location
.DataSize
;
1010 SetLastError(ERROR_INVALID_PARAMETER
);