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
24 #define WIN32_NO_STATUS
25 #include "dbghelp_private.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
33 /******************************************************************
36 * reads system wide process information, and gather from it the threads information
37 * for process of id 'pid'
39 static BOOL
fetch_process_info(struct dump_context
* dc
)
41 ULONG buf_size
= 0x1000;
43 void* pcs_buffer
= NULL
;
45 if (!(pcs_buffer
= HeapAlloc(GetProcessHeap(), 0, buf_size
))) return FALSE
;
48 nts
= NtQuerySystemInformation(SystemProcessInformation
,
49 pcs_buffer
, buf_size
, NULL
);
50 if (nts
!= STATUS_INFO_LENGTH_MISMATCH
) break;
51 pcs_buffer
= HeapReAlloc(GetProcessHeap(), 0, pcs_buffer
, buf_size
*= 2);
52 if (!pcs_buffer
) return FALSE
;
55 if (nts
== STATUS_SUCCESS
)
57 SYSTEM_PROCESS_INFORMATION
* spi
= pcs_buffer
;
62 if (HandleToUlong(spi
->UniqueProcessId
) == dc
->pid
)
65 dc
->threads
= HeapAlloc(GetProcessHeap(), 0,
66 spi
->dwThreadCount
* sizeof(dc
->threads
[0]));
67 if (!dc
->threads
) break;
68 for (i
= 0; i
< spi
->dwThreadCount
; i
++)
70 /* don't include current thread */
71 if (HandleToULong(spi
->ti
[i
].ClientId
.UniqueThread
) == GetCurrentThreadId())
73 dc
->threads
[dc
->num_threads
].tid
= HandleToULong(spi
->ti
[i
].ClientId
.UniqueThread
);
74 dc
->threads
[dc
->num_threads
].prio_class
= spi
->ti
[i
].dwBasePriority
; /* FIXME */
75 dc
->threads
[dc
->num_threads
].curr_prio
= spi
->ti
[i
].dwCurrentPriority
;
78 HeapFree(GetProcessHeap(), 0, pcs_buffer
);
81 if (!spi
->NextEntryOffset
) break;
82 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ spi
->NextEntryOffset
);
85 HeapFree(GetProcessHeap(), 0, pcs_buffer
);
89 static void fetch_thread_stack(struct dump_context
* dc
, const void* teb_addr
,
90 const CONTEXT
* ctx
, MINIDUMP_MEMORY_DESCRIPTOR
* mmd
)
95 if (ReadProcessMemory(dc
->process
->handle
, teb_addr
, &tib
, sizeof(tib
), NULL
) &&
96 dbghelp_current_cpu
&&
97 dbghelp_current_cpu
->get_addr(NULL
/* FIXME */, ctx
, cpu_addr_stack
, &addr
) && addr
.Mode
== AddrModeFlat
)
101 addr
.Offset
-= dbghelp_current_cpu
->word_size
;
102 /* make sure stack pointer is within the established range of the stack. It could have
103 been clobbered by whatever caused the original exception. */
104 if (addr
.Offset
< (ULONG_PTR
)tib
.StackLimit
|| addr
.Offset
> (ULONG_PTR
)tib
.StackBase
)
105 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
108 mmd
->StartOfMemoryRange
= addr
.Offset
;
111 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
112 mmd
->Memory
.DataSize
= (ULONG_PTR
)tib
.StackBase
- mmd
->StartOfMemoryRange
;
116 /******************************************************************
119 * fetches some information about thread of id 'tid'
121 static BOOL
fetch_thread_info(struct dump_context
* dc
, int thd_idx
,
122 MINIDUMP_THREAD
* mdThd
, CONTEXT
* ctx
)
124 DWORD tid
= dc
->threads
[thd_idx
].tid
;
126 THREAD_BASIC_INFORMATION tbi
;
128 memset(ctx
, 0, sizeof(*ctx
));
130 mdThd
->ThreadId
= tid
;
131 mdThd
->SuspendCount
= 0;
133 mdThd
->Stack
.StartOfMemoryRange
= 0;
134 mdThd
->Stack
.Memory
.DataSize
= 0;
135 mdThd
->Stack
.Memory
.Rva
= 0;
136 mdThd
->ThreadContext
.DataSize
= 0;
137 mdThd
->ThreadContext
.Rva
= 0;
138 mdThd
->PriorityClass
= dc
->threads
[thd_idx
].prio_class
;
139 mdThd
->Priority
= dc
->threads
[thd_idx
].curr_prio
;
141 if ((hThread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, tid
)) == NULL
)
143 FIXME("Couldn't open thread %lu (%lu)\n", tid
, GetLastError());
147 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
,
148 &tbi
, sizeof(tbi
), NULL
) == STATUS_SUCCESS
)
150 mdThd
->Teb
= (ULONG_PTR
)tbi
.TebBaseAddress
;
151 if (tbi
.ExitStatus
== STILL_ACTIVE
)
153 mdThd
->SuspendCount
= SuspendThread(hThread
);
154 ctx
->ContextFlags
= CONTEXT_ALL
;
155 if (!GetThreadContext(hThread
, ctx
))
156 memset(ctx
, 0, sizeof(*ctx
));
157 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, ctx
, &mdThd
->Stack
);
158 ResumeThread(hThread
);
161 mdThd
->SuspendCount
= (DWORD
)-1;
163 CloseHandle(hThread
);
167 /******************************************************************
170 * Add a module to a dump context
172 static BOOL
add_module(struct dump_context
* dc
, const WCHAR
* name
,
173 DWORD64 base
, DWORD size
, DWORD timestamp
, DWORD checksum
,
178 dc
->alloc_modules
= 32;
179 dc
->modules
= HeapAlloc(GetProcessHeap(), 0,
180 dc
->alloc_modules
* sizeof(*dc
->modules
));
182 else if(dc
->num_modules
>= dc
->alloc_modules
)
184 dc
->alloc_modules
*= 2;
185 dc
->modules
= HeapReAlloc(GetProcessHeap(), 0, dc
->modules
,
186 dc
->alloc_modules
* sizeof(*dc
->modules
));
190 dc
->alloc_modules
= dc
->num_modules
= 0;
193 lstrcpynW(dc
->modules
[dc
->num_modules
].name
, name
,
194 ARRAY_SIZE(dc
->modules
[dc
->num_modules
].name
));
195 dc
->modules
[dc
->num_modules
].base
= base
;
196 dc
->modules
[dc
->num_modules
].size
= size
;
197 dc
->modules
[dc
->num_modules
].timestamp
= timestamp
;
198 dc
->modules
[dc
->num_modules
].checksum
= checksum
;
199 dc
->modules
[dc
->num_modules
].is_elf
= is_elf
;
205 /******************************************************************
206 * fetch_pe_module_info_cb
208 * Callback for accumulating in dump_context a PE modules set
210 static BOOL WINAPI
fetch_pe_module_info_cb(PCWSTR name
, DWORD64 base
, ULONG size
,
213 struct dump_context
* dc
= user
;
214 IMAGE_NT_HEADERS nth
;
216 if (!validate_addr64(base
)) return FALSE
;
218 if (pe_load_nt_header(dc
->process
->handle
, base
, &nth
, NULL
))
219 add_module(user
, name
, base
, size
,
220 nth
.FileHeader
.TimeDateStamp
, nth
.OptionalHeader
.CheckSum
,
225 /******************************************************************
226 * fetch_elf_module_info_cb
228 * Callback for accumulating in dump_context an host modules set
230 static BOOL
fetch_host_module_info_cb(const WCHAR
* name
, ULONG_PTR base
,
233 struct dump_context
* dc
= user
;
235 DWORD size
, checksum
;
237 /* FIXME: there's no relevant timestamp on ELF modules */
238 if (!dc
->process
->loader
->fetch_file_info(dc
->process
, name
, base
, &rbase
, &size
, &checksum
))
240 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
244 static void minidump_add_memory64_block(struct dump_context
* dc
, ULONG64 base
, ULONG64 size
)
248 dc
->alloc_mem64
= 32;
249 dc
->mem64
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem64
* sizeof(*dc
->mem64
));
251 else if (dc
->num_mem64
>= dc
->alloc_mem64
)
253 dc
->alloc_mem64
*= 2;
254 dc
->mem64
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem64
,
255 dc
->alloc_mem64
* sizeof(*dc
->mem64
));
259 dc
->mem64
[dc
->num_mem64
].base
= base
;
260 dc
->mem64
[dc
->num_mem64
].size
= size
;
263 else dc
->num_mem64
= dc
->alloc_mem64
= 0;
266 static void fetch_memory64_info(struct dump_context
* dc
)
269 MEMORY_BASIC_INFORMATION mbi
;
272 while (VirtualQueryEx(dc
->process
->handle
, (LPCVOID
)addr
, &mbi
, sizeof(mbi
)) != 0)
274 /* Memory regions with state MEM_COMMIT will be added to the dump */
275 if (mbi
.State
== MEM_COMMIT
)
277 minidump_add_memory64_block(dc
, (ULONG_PTR
)mbi
.BaseAddress
, mbi
.RegionSize
);
280 if ((addr
+ mbi
.RegionSize
) < addr
)
283 addr
= (ULONG_PTR
)mbi
.BaseAddress
+ mbi
.RegionSize
;
287 static void fetch_modules_info(struct dump_context
* dc
)
289 EnumerateLoadedModulesW64(dc
->process
->handle
, fetch_pe_module_info_cb
, dc
);
290 /* Since we include ELF modules in a separate stream from the regular PE ones,
291 * we can always include those ELF modules (they don't eat lots of space)
292 * And it's always a good idea to have a trace of the loaded ELF modules for
293 * a given application in a post mortem debugging condition.
295 dc
->process
->loader
->enum_modules(dc
->process
, fetch_host_module_info_cb
, dc
);
298 static void fetch_module_versioninfo(LPCWSTR filename
, VS_FIXEDFILEINFO
* ffi
)
303 memset(ffi
, 0, sizeof(*ffi
));
304 if ((sz
= GetFileVersionInfoSizeW(filename
, &handle
)))
306 void* info
= HeapAlloc(GetProcessHeap(), 0, sz
);
307 if (info
&& GetFileVersionInfoW(filename
, handle
, sz
, info
))
309 VS_FIXEDFILEINFO
* ptr
;
312 if (VerQueryValueW(info
, L
"\\", (void*)&ptr
, &len
))
313 memcpy(ffi
, ptr
, min(len
, sizeof(*ffi
)));
315 HeapFree(GetProcessHeap(), 0, info
);
319 /******************************************************************
320 * minidump_add_memory_block
322 * Add a memory block to be dumped in a minidump
323 * If rva is non 0, it's the rva in the minidump where has to be stored
324 * also the rva of the memory block when written (this allows us to reference
325 * a memory block from outside the list of memory blocks).
327 void minidump_add_memory_block(struct dump_context
* dc
, ULONG64 base
, ULONG size
, ULONG rva
)
332 dc
->mem
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem
* sizeof(*dc
->mem
));
334 else if (dc
->num_mem
>= dc
->alloc_mem
)
337 dc
->mem
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem
,
338 dc
->alloc_mem
* sizeof(*dc
->mem
));
342 dc
->mem
[dc
->num_mem
].base
= base
;
343 dc
->mem
[dc
->num_mem
].size
= size
;
344 dc
->mem
[dc
->num_mem
].rva
= rva
;
347 else dc
->num_mem
= dc
->alloc_mem
= 0;
350 /******************************************************************
353 * Writes a chunk of data at a given position in the minidump
355 static void writeat(struct dump_context
* dc
, RVA rva
, const void* data
, unsigned size
)
359 SetFilePointer(dc
->hFile
, rva
, NULL
, FILE_BEGIN
);
360 WriteFile(dc
->hFile
, data
, size
, &written
, NULL
);
363 /******************************************************************
366 * writes a new chunk of data to the minidump, increasing the current
369 static void append(struct dump_context
* dc
, const void* data
, unsigned size
)
371 writeat(dc
, dc
->rva
, data
, size
);
375 /******************************************************************
376 * dump_exception_info
378 * Write in File the exception information from pcs
380 static unsigned dump_exception_info(struct dump_context
* dc
)
382 MINIDUMP_EXCEPTION_STREAM mdExcpt
;
383 EXCEPTION_RECORD rec
, *prec
;
387 mdExcpt
.ThreadId
= dc
->except_param
->ThreadId
;
388 mdExcpt
.__alignment
= 0;
389 if (dc
->except_param
->ClientPointers
)
391 EXCEPTION_POINTERS ep
;
393 ReadProcessMemory(dc
->process
->handle
,
394 dc
->except_param
->ExceptionPointers
, &ep
, sizeof(ep
), NULL
);
395 ReadProcessMemory(dc
->process
->handle
,
396 ep
.ExceptionRecord
, &rec
, sizeof(rec
), NULL
);
397 ReadProcessMemory(dc
->process
->handle
,
398 ep
.ContextRecord
, &ctx
, sizeof(ctx
), NULL
);
404 prec
= dc
->except_param
->ExceptionPointers
->ExceptionRecord
;
405 pctx
= dc
->except_param
->ExceptionPointers
->ContextRecord
;
407 mdExcpt
.ExceptionRecord
.ExceptionCode
= prec
->ExceptionCode
;
408 mdExcpt
.ExceptionRecord
.ExceptionFlags
= prec
->ExceptionFlags
;
409 mdExcpt
.ExceptionRecord
.ExceptionRecord
= (DWORD_PTR
)prec
->ExceptionRecord
;
410 mdExcpt
.ExceptionRecord
.ExceptionAddress
= (DWORD_PTR
)prec
->ExceptionAddress
;
411 mdExcpt
.ExceptionRecord
.NumberParameters
= prec
->NumberParameters
;
412 mdExcpt
.ExceptionRecord
.__unusedAlignment
= 0;
413 for (i
= 0; i
< mdExcpt
.ExceptionRecord
.NumberParameters
; i
++)
414 mdExcpt
.ExceptionRecord
.ExceptionInformation
[i
] = prec
->ExceptionInformation
[i
];
415 mdExcpt
.ThreadContext
.DataSize
= sizeof(*pctx
);
416 mdExcpt
.ThreadContext
.Rva
= dc
->rva
+ sizeof(mdExcpt
);
418 append(dc
, &mdExcpt
, sizeof(mdExcpt
));
419 append(dc
, pctx
, sizeof(*pctx
));
420 return sizeof(mdExcpt
);
423 /******************************************************************
426 * Write in File the modules from pcs
428 static unsigned dump_modules(struct dump_context
* dc
, BOOL dump_elf
)
430 MINIDUMP_MODULE mdModule
;
431 MINIDUMP_MODULE_LIST mdModuleList
;
433 MINIDUMP_STRING
* ms
= (MINIDUMP_STRING
*)tmp
;
439 for (i
= nmod
= 0; i
< dc
->num_modules
; i
++)
441 if ((dc
->modules
[i
].is_elf
&& dump_elf
) ||
442 (!dc
->modules
[i
].is_elf
&& !dump_elf
))
446 mdModuleList
.NumberOfModules
= 0;
447 /* reserve space for mdModuleList
448 * FIXME: since we don't support 0 length arrays, we cannot use the
449 * size of mdModuleList
450 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
453 /* the stream size is just the size of the module index. It does not include the data for the
454 names of each module. *Technically* the names are supposed to go into the common string table
455 in the minidump file. Since each string is referenced by RVA they can all safely be located
456 anywhere between streams in the file, so the end of this stream is sufficient. */
458 dc
->rva
+= sz
= sizeof(mdModuleList
.NumberOfModules
) + sizeof(mdModule
) * nmod
;
459 for (i
= 0; i
< dc
->num_modules
; i
++)
461 if ((dc
->modules
[i
].is_elf
&& !dump_elf
) ||
462 (!dc
->modules
[i
].is_elf
&& dump_elf
))
465 flags_out
= ModuleWriteModule
| ModuleWriteMiscRecord
| ModuleWriteCvRecord
;
466 if (dc
->type
& MiniDumpWithDataSegs
)
467 flags_out
|= ModuleWriteDataSeg
;
468 if (dc
->type
& MiniDumpWithProcessThreadData
)
469 flags_out
|= ModuleWriteTlsData
;
470 if (dc
->type
& MiniDumpWithCodeSegs
)
471 flags_out
|= ModuleWriteCodeSegs
;
472 ms
->Length
= (lstrlenW(dc
->modules
[i
].name
) + 1) * sizeof(WCHAR
);
473 if (sizeof(ULONG
) + ms
->Length
> sizeof(tmp
))
474 FIXME("Buffer overflow!!!\n");
475 lstrcpyW(ms
->Buffer
, dc
->modules
[i
].name
);
479 MINIDUMP_CALLBACK_INPUT cbin
;
480 MINIDUMP_CALLBACK_OUTPUT cbout
;
482 cbin
.ProcessId
= dc
->pid
;
483 cbin
.ProcessHandle
= dc
->process
->handle
;
484 cbin
.CallbackType
= ModuleCallback
;
486 cbin
.Module
.FullPath
= ms
->Buffer
;
487 cbin
.Module
.BaseOfImage
= dc
->modules
[i
].base
;
488 cbin
.Module
.SizeOfImage
= dc
->modules
[i
].size
;
489 cbin
.Module
.CheckSum
= dc
->modules
[i
].checksum
;
490 cbin
.Module
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
491 memset(&cbin
.Module
.VersionInfo
, 0, sizeof(cbin
.Module
.VersionInfo
));
492 cbin
.Module
.CvRecord
= NULL
;
493 cbin
.Module
.SizeOfCvRecord
= 0;
494 cbin
.Module
.MiscRecord
= NULL
;
495 cbin
.Module
.SizeOfMiscRecord
= 0;
497 cbout
.ModuleWriteFlags
= flags_out
;
498 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
500 flags_out
&= cbout
.ModuleWriteFlags
;
502 if (flags_out
& ModuleWriteModule
)
504 /* fetch CPU dependent module info (like UNWIND_INFO) */
505 dbghelp_current_cpu
->fetch_minidump_module(dc
, i
, flags_out
);
507 mdModule
.BaseOfImage
= dc
->modules
[i
].base
;
508 mdModule
.SizeOfImage
= dc
->modules
[i
].size
;
509 mdModule
.CheckSum
= dc
->modules
[i
].checksum
;
510 mdModule
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
511 mdModule
.ModuleNameRva
= dc
->rva
;
512 ms
->Length
-= sizeof(WCHAR
);
513 append(dc
, ms
, sizeof(ULONG
) + ms
->Length
+ sizeof(WCHAR
));
514 if (!dump_elf
) fetch_module_versioninfo(ms
->Buffer
, &mdModule
.VersionInfo
);
515 else memset(&mdModule
.VersionInfo
, 0, sizeof(mdModule
.VersionInfo
));
516 mdModule
.CvRecord
.DataSize
= 0; /* FIXME */
517 mdModule
.CvRecord
.Rva
= 0; /* FIXME */
518 mdModule
.MiscRecord
.DataSize
= 0; /* FIXME */
519 mdModule
.MiscRecord
.Rva
= 0; /* FIXME */
520 mdModule
.Reserved0
= 0; /* FIXME */
521 mdModule
.Reserved1
= 0; /* FIXME */
523 rva_base
+ sizeof(mdModuleList
.NumberOfModules
) +
524 mdModuleList
.NumberOfModules
++ * sizeof(mdModule
),
525 &mdModule
, sizeof(mdModule
));
528 writeat(dc
, rva_base
, &mdModuleList
.NumberOfModules
,
529 sizeof(mdModuleList
.NumberOfModules
));
535 extern void do_x86cpuid(unsigned int ax
, unsigned int *p
);
536 __ASM_GLOBAL_FUNC( do_x86cpuid
,
539 "movl 12(%esp),%eax\n\t"
540 "movl 16(%esp),%esi\n\t"
542 "movl %eax,(%esi)\n\t"
543 "movl %ebx,4(%esi)\n\t"
544 "movl %ecx,8(%esi)\n\t"
545 "movl %edx,12(%esi)\n\t"
549 extern int have_x86cpuid(void);
550 __ASM_GLOBAL_FUNC( have_x86cpuid
,
553 "movl (%esp),%ecx\n\t"
554 "xorl $0x00200000,(%esp)\n\t"
560 "andl $0x00200000,%eax\n\t"
563 static void do_x86cpuid(unsigned int ax
, unsigned int *p
)
567 static int have_x86cpuid(void)
573 /******************************************************************
576 * Dumps into File the information about the system
578 static unsigned dump_system_info(struct dump_context
* dc
)
580 MINIDUMP_SYSTEM_INFO mdSysInfo
;
582 RTL_OSVERSIONINFOEXW osInfo
;
585 DWORD wine_extra
= 0;
587 const char *(CDECL
*wine_get_build_id
)(void);
588 void (CDECL
*wine_get_host_version
)(const char **sysname
, const char **release
);
589 const char* build_id
= NULL
;
590 const char* sys_name
= NULL
;
591 const char* release_name
= NULL
;
593 GetSystemInfo(&sysInfo
);
594 osInfo
.dwOSVersionInfoSize
= sizeof(osInfo
);
595 RtlGetVersion(&osInfo
);
597 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
598 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
599 if (wine_get_build_id
&& wine_get_host_version
)
601 /* cheat minidump system information by adding specific wine information */
602 wine_extra
= 4 + 4 * sizeof(slen
);
603 build_id
= wine_get_build_id();
604 wine_get_host_version(&sys_name
, &release_name
);
605 wine_extra
+= strlen(build_id
) + 1 + strlen(sys_name
) + 1 + strlen(release_name
) + 1;
608 mdSysInfo
.ProcessorArchitecture
= sysInfo
.wProcessorArchitecture
;
609 mdSysInfo
.ProcessorLevel
= sysInfo
.wProcessorLevel
;
610 mdSysInfo
.ProcessorRevision
= sysInfo
.wProcessorRevision
;
611 mdSysInfo
.NumberOfProcessors
= sysInfo
.dwNumberOfProcessors
;
612 mdSysInfo
.ProductType
= VER_NT_WORKSTATION
; /* FIXME */
613 mdSysInfo
.MajorVersion
= osInfo
.dwMajorVersion
;
614 mdSysInfo
.MinorVersion
= osInfo
.dwMinorVersion
;
615 mdSysInfo
.BuildNumber
= osInfo
.dwBuildNumber
;
616 mdSysInfo
.PlatformId
= osInfo
.dwPlatformId
;
618 mdSysInfo
.CSDVersionRva
= dc
->rva
+ sizeof(mdSysInfo
) + wine_extra
;
619 mdSysInfo
.Reserved1
= 0;
620 mdSysInfo
.SuiteMask
= VER_SUITE_TERMINAL
;
624 unsigned regs0
[4], regs1
[4];
626 do_x86cpuid(0, regs0
);
627 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[0] = regs0
[1];
628 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[1] = regs0
[3];
629 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[2] = regs0
[2];
630 do_x86cpuid(1, regs1
);
631 mdSysInfo
.Cpu
.X86CpuInfo
.VersionInformation
= regs1
[0];
632 mdSysInfo
.Cpu
.X86CpuInfo
.FeatureInformation
= regs1
[3];
633 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= 0;
634 if (regs0
[1] == 0x68747541 /* "Auth" */ &&
635 regs0
[3] == 0x69746e65 /* "enti" */ &&
636 regs0
[2] == 0x444d4163 /* "cAMD" */)
638 do_x86cpuid(0x80000000, regs1
); /* get vendor cpuid level */
639 if (regs1
[0] >= 0x80000001)
641 do_x86cpuid(0x80000001, regs1
); /* get vendor features */
642 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= regs1
[3];
651 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] = 0;
652 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[1] = 0;
654 for (i
= 0; i
< sizeof(mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0]) * 8; i
++)
655 if (IsProcessorFeaturePresent(i
))
656 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] |= one
<< i
;
658 append(dc
, &mdSysInfo
, sizeof(mdSysInfo
));
660 /* write Wine specific system information just behind the structure, and before any string */
663 static const char code
[] = {'W','I','N','E'};
665 WriteFile(dc
->hFile
, code
, 4, &written
, NULL
);
666 /* number of sub-info, so that we can extend structure if needed */
668 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
669 /* we store offsets from just after the WINE marker */
670 slen
= 4 * sizeof(DWORD
);
671 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
672 slen
+= strlen(build_id
) + 1;
673 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
674 slen
+= strlen(sys_name
) + 1;
675 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
676 WriteFile(dc
->hFile
, build_id
, strlen(build_id
) + 1, &written
, NULL
);
677 WriteFile(dc
->hFile
, sys_name
, strlen(sys_name
) + 1, &written
, NULL
);
678 WriteFile(dc
->hFile
, release_name
, strlen(release_name
) + 1, &written
, NULL
);
679 dc
->rva
+= wine_extra
;
682 /* write the service pack version string after this stream. It is referenced within the
683 stream by its RVA in the file. */
684 slen
= lstrlenW(osInfo
.szCSDVersion
) * sizeof(WCHAR
);
685 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
686 WriteFile(dc
->hFile
, osInfo
.szCSDVersion
, slen
, &written
, NULL
);
687 dc
->rva
+= sizeof(ULONG
) + slen
;
689 return sizeof(mdSysInfo
);
692 /******************************************************************
695 * Dumps into File the information about running threads
697 static unsigned dump_threads(struct dump_context
* dc
)
699 MINIDUMP_THREAD mdThd
;
700 MINIDUMP_THREAD_LIST mdThdList
;
706 mdThdList
.NumberOfThreads
= 0;
709 dc
->rva
+= sz
= sizeof(mdThdList
.NumberOfThreads
) + dc
->num_threads
* sizeof(mdThd
);
711 for (i
= 0; i
< dc
->num_threads
; i
++)
713 fetch_thread_info(dc
, i
, &mdThd
, &ctx
);
715 flags_out
= ThreadWriteThread
| ThreadWriteStack
| ThreadWriteContext
|
716 ThreadWriteInstructionWindow
;
717 if (dc
->type
& MiniDumpWithProcessThreadData
)
718 flags_out
|= ThreadWriteThreadData
;
719 if (dc
->type
& MiniDumpWithThreadInfo
)
720 flags_out
|= ThreadWriteThreadInfo
;
724 MINIDUMP_CALLBACK_INPUT cbin
;
725 MINIDUMP_CALLBACK_OUTPUT cbout
;
727 cbin
.ProcessId
= dc
->pid
;
728 cbin
.ProcessHandle
= dc
->process
->handle
;
729 cbin
.CallbackType
= ThreadCallback
;
730 cbin
.Thread
.ThreadId
= dc
->threads
[i
].tid
;
731 cbin
.Thread
.ThreadHandle
= 0; /* FIXME */
732 cbin
.Thread
.Context
= ctx
;
733 cbin
.Thread
.SizeOfContext
= sizeof(CONTEXT
);
734 cbin
.Thread
.StackBase
= mdThd
.Stack
.StartOfMemoryRange
;
735 cbin
.Thread
.StackEnd
= mdThd
.Stack
.StartOfMemoryRange
+
736 mdThd
.Stack
.Memory
.DataSize
;
738 cbout
.ThreadWriteFlags
= flags_out
;
739 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
741 flags_out
&= cbout
.ThreadWriteFlags
;
743 if (flags_out
& ThreadWriteThread
)
745 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteContext
))
747 mdThd
.ThreadContext
.Rva
= dc
->rva
;
748 mdThd
.ThreadContext
.DataSize
= sizeof(CONTEXT
);
749 append(dc
, &ctx
, sizeof(CONTEXT
));
751 if (mdThd
.Stack
.Memory
.DataSize
&& (flags_out
& ThreadWriteStack
))
753 minidump_add_memory_block(dc
, mdThd
.Stack
.StartOfMemoryRange
,
754 mdThd
.Stack
.Memory
.DataSize
,
755 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
756 mdThdList
.NumberOfThreads
* sizeof(mdThd
) +
757 FIELD_OFFSET(MINIDUMP_THREAD
, Stack
.Memory
.Rva
));
760 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
761 mdThdList
.NumberOfThreads
* sizeof(mdThd
),
762 &mdThd
, sizeof(mdThd
));
763 mdThdList
.NumberOfThreads
++;
765 /* fetch CPU dependent thread info (like 256 bytes around program counter */
766 dbghelp_current_cpu
->fetch_minidump_thread(dc
, i
, flags_out
, &ctx
);
768 writeat(dc
, rva_base
,
769 &mdThdList
.NumberOfThreads
, sizeof(mdThdList
.NumberOfThreads
));
774 /******************************************************************
777 * Dumps into File the information about threads's name
779 static unsigned dump_threads_names(struct dump_context
* dc
)
781 MINIDUMP_THREAD_NAME md_thread_name
;
782 MINIDUMP_THREAD_NAME_LIST md_thread_name_list
;
786 /* FIXME this could be optimized
787 * (we use dc->num_threads disk space, could be optimized to the number of threads with name)
791 dc
->rva
+= sz
= offsetof(MINIDUMP_THREAD_NAME_LIST
, ThreadNames
[dc
->num_threads
]);
793 md_thread_name_list
.NumberOfThreadNames
= 0;
795 for (i
= 0; i
< dc
->num_threads
; i
++)
800 if ((thread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, dc
->threads
[i
].tid
)) != NULL
)
802 if (GetThreadDescription(thread
, &thread_name
))
804 MINIDUMP_STRING md_string
;
806 md_thread_name
.ThreadId
= dc
->threads
[i
].tid
;
807 md_thread_name
.RvaOfThreadName
= dc
->rva
;
809 md_string
.Length
= wcslen(thread_name
) * sizeof(WCHAR
);
810 append(dc
, &md_string
.Length
, sizeof(md_string
.Length
));
811 append(dc
, thread_name
, md_string
.Length
);
814 rva_base
+ offsetof(MINIDUMP_THREAD_NAME_LIST
, ThreadNames
[md_thread_name_list
.NumberOfThreadNames
]),
815 &md_thread_name
, sizeof(md_thread_name
));
816 md_thread_name_list
.NumberOfThreadNames
++;
817 LocalFree(thread_name
);
822 if (!md_thread_name_list
.NumberOfThreadNames
) return 0;
824 writeat(dc
, rva_base
, &md_thread_name_list
.NumberOfThreadNames
, sizeof(md_thread_name_list
.NumberOfThreadNames
));
828 /******************************************************************
831 * dumps information about the memory of the process (stack of the threads)
833 static unsigned dump_memory_info(struct dump_context
* dc
)
835 MINIDUMP_MEMORY_LIST mdMemList
;
836 MINIDUMP_MEMORY_DESCRIPTOR mdMem
;
838 unsigned i
, pos
, len
, sz
;
842 mdMemList
.NumberOfMemoryRanges
= dc
->num_mem
;
843 append(dc
, &mdMemList
.NumberOfMemoryRanges
,
844 sizeof(mdMemList
.NumberOfMemoryRanges
));
846 sz
= mdMemList
.NumberOfMemoryRanges
* sizeof(mdMem
);
848 sz
+= sizeof(mdMemList
.NumberOfMemoryRanges
);
850 for (i
= 0; i
< dc
->num_mem
; i
++)
852 mdMem
.StartOfMemoryRange
= dc
->mem
[i
].base
;
853 mdMem
.Memory
.Rva
= dc
->rva
;
854 mdMem
.Memory
.DataSize
= dc
->mem
[i
].size
;
855 SetFilePointer(dc
->hFile
, dc
->rva
, NULL
, FILE_BEGIN
);
856 for (pos
= 0; pos
< dc
->mem
[i
].size
; pos
+= sizeof(tmp
))
858 len
= min(dc
->mem
[i
].size
- pos
, sizeof(tmp
));
859 if (read_process_memory(dc
->process
, dc
->mem
[i
].base
+ pos
, tmp
, len
))
860 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
862 dc
->rva
+= mdMem
.Memory
.DataSize
;
863 writeat(dc
, rva_base
+ i
* sizeof(mdMem
), &mdMem
, sizeof(mdMem
));
866 writeat(dc
, dc
->mem
[i
].rva
, &mdMem
.Memory
.Rva
, sizeof(mdMem
.Memory
.Rva
));
873 /******************************************************************
876 * dumps information about the memory of the process (virtual memory)
878 static unsigned dump_memory64_info(struct dump_context
* dc
)
880 MINIDUMP_MEMORY64_LIST mdMem64List
;
881 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64
;
887 LARGE_INTEGER filepos
;
889 sz
= sizeof(mdMem64List
.NumberOfMemoryRanges
) +
890 sizeof(mdMem64List
.BaseRva
) +
891 dc
->num_mem64
* sizeof(mdMem64
);
893 mdMem64List
.NumberOfMemoryRanges
= dc
->num_mem64
;
894 mdMem64List
.BaseRva
= dc
->rva
+ sz
;
896 append(dc
, &mdMem64List
.NumberOfMemoryRanges
,
897 sizeof(mdMem64List
.NumberOfMemoryRanges
));
898 append(dc
, &mdMem64List
.BaseRva
,
899 sizeof(mdMem64List
.BaseRva
));
902 dc
->rva
+= dc
->num_mem64
* sizeof(mdMem64
);
904 /* dc->rva is not updated past this point. The end of the dump
905 * is just the full memory data. */
906 filepos
.QuadPart
= dc
->rva
;
907 for (i
= 0; i
< dc
->num_mem64
; i
++)
909 mdMem64
.StartOfMemoryRange
= dc
->mem64
[i
].base
;
910 mdMem64
.DataSize
= dc
->mem64
[i
].size
;
911 SetFilePointerEx(dc
->hFile
, filepos
, NULL
, FILE_BEGIN
);
912 for (pos
= 0; pos
< dc
->mem64
[i
].size
; pos
+= sizeof(tmp
))
914 len
= min(dc
->mem64
[i
].size
- pos
, sizeof(tmp
));
915 if (read_process_memory(dc
->process
, dc
->mem64
[i
].base
+ pos
, tmp
, len
))
916 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
918 filepos
.QuadPart
+= mdMem64
.DataSize
;
919 writeat(dc
, rva_base
+ i
* sizeof(mdMem64
), &mdMem64
, sizeof(mdMem64
));
925 static unsigned dump_misc_info(struct dump_context
* dc
)
927 MINIDUMP_MISC_INFO mmi
;
929 mmi
.SizeOfInfo
= sizeof(mmi
);
930 mmi
.Flags1
= MINIDUMP_MISC1_PROCESS_ID
;
931 mmi
.ProcessId
= dc
->pid
;
932 /* FIXME: create/user/kernel time */
933 mmi
.ProcessCreateTime
= 0;
934 mmi
.ProcessKernelTime
= 0;
935 mmi
.ProcessUserTime
= 0;
937 append(dc
, &mmi
, sizeof(mmi
));
941 static DWORD CALLBACK
write_minidump(void *_args
)
943 struct dump_context
*dc
= _args
;
944 static const MINIDUMP_DIRECTORY emptyDir
= {UnusedStream
, {0, 0}};
945 MINIDUMP_HEADER mdHead
;
946 MINIDUMP_DIRECTORY mdDir
;
947 DWORD i
, nStreams
, idx_stream
;
949 if (!fetch_process_info(dc
)) return FALSE
;
950 fetch_modules_info(dc
);
953 nStreams
= 7 + (dc
->except_param
? 1 : 0) +
954 (dc
->user_stream
? dc
->user_stream
->UserStreamCount
: 0);
956 /* pad the directory size to a multiple of 4 for alignment purposes */
957 nStreams
= (nStreams
+ 3) & ~3;
959 /* 2) write header */
960 mdHead
.Signature
= MINIDUMP_SIGNATURE
;
961 mdHead
.Version
= MINIDUMP_VERSION
; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
962 mdHead
.NumberOfStreams
= nStreams
;
963 mdHead
.CheckSum
= 0; /* native sets a 0 checksum in its files */
964 mdHead
.StreamDirectoryRva
= sizeof(mdHead
);
965 mdHead
.TimeDateStamp
= time(NULL
);
966 mdHead
.Flags
= dc
->type
;
967 append(dc
, &mdHead
, sizeof(mdHead
));
969 /* 3) write stream directories */
970 dc
->rva
+= nStreams
* sizeof(mdDir
);
973 /* 3.1) write data stream directories */
975 /* must be first in minidump */
976 mdDir
.StreamType
= SystemInfoStream
;
977 mdDir
.Location
.Rva
= dc
->rva
;
978 mdDir
.Location
.DataSize
= dump_system_info(dc
);
979 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
980 &mdDir
, sizeof(mdDir
));
982 mdDir
.StreamType
= ThreadListStream
;
983 mdDir
.Location
.Rva
= dc
->rva
;
984 mdDir
.Location
.DataSize
= dump_threads(dc
);
985 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
986 &mdDir
, sizeof(mdDir
));
988 mdDir
.StreamType
= ThreadNamesStream
;
989 mdDir
.Location
.Rva
= dc
->rva
;
990 if ((mdDir
.Location
.DataSize
= dump_threads_names(dc
)))
991 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
992 &mdDir
, sizeof(mdDir
));
994 mdDir
.StreamType
= ModuleListStream
;
995 mdDir
.Location
.Rva
= dc
->rva
;
996 mdDir
.Location
.DataSize
= dump_modules(dc
, FALSE
);
997 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
998 &mdDir
, sizeof(mdDir
));
1000 mdDir
.StreamType
= 0xfff0; /* FIXME: this is part of MS reserved streams */
1001 mdDir
.Location
.Rva
= dc
->rva
;
1002 mdDir
.Location
.DataSize
= dump_modules(dc
, TRUE
);
1003 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1004 &mdDir
, sizeof(mdDir
));
1007 if (!(dc
->type
& MiniDumpWithFullMemory
))
1009 mdDir
.StreamType
= MemoryListStream
;
1010 mdDir
.Location
.Rva
= dc
->rva
;
1011 mdDir
.Location
.DataSize
= dump_memory_info(dc
);
1012 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1013 &mdDir
, sizeof(mdDir
));
1016 mdDir
.StreamType
= MiscInfoStream
;
1017 mdDir
.Location
.Rva
= dc
->rva
;
1018 mdDir
.Location
.DataSize
= dump_misc_info(dc
);
1019 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1020 &mdDir
, sizeof(mdDir
));
1022 /* 3.2) write exception information (if any) */
1023 if (dc
->except_param
)
1025 mdDir
.StreamType
= ExceptionStream
;
1026 mdDir
.Location
.Rva
= dc
->rva
;
1027 mdDir
.Location
.DataSize
= dump_exception_info(dc
);
1028 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1029 &mdDir
, sizeof(mdDir
));
1032 /* 3.3) write user defined streams (if any) */
1033 if (dc
->user_stream
)
1035 for (i
= 0; i
< dc
->user_stream
->UserStreamCount
; i
++)
1037 mdDir
.StreamType
= dc
->user_stream
->UserStreamArray
[i
].Type
;
1038 mdDir
.Location
.DataSize
= dc
->user_stream
->UserStreamArray
[i
].BufferSize
;
1039 mdDir
.Location
.Rva
= dc
->rva
;
1040 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1041 &mdDir
, sizeof(mdDir
));
1042 append(dc
, dc
->user_stream
->UserStreamArray
[i
].Buffer
,
1043 dc
->user_stream
->UserStreamArray
[i
].BufferSize
);
1047 /* 3.4) write full memory (if requested) */
1048 if (dc
->type
& MiniDumpWithFullMemory
)
1050 fetch_memory64_info(dc
);
1052 mdDir
.StreamType
= Memory64ListStream
;
1053 mdDir
.Location
.Rva
= dc
->rva
;
1054 mdDir
.Location
.DataSize
= dump_memory64_info(dc
);
1055 writeat(dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1056 &mdDir
, sizeof(mdDir
));
1059 /* fill the remaining directory entries with 0's (unused stream types) */
1060 /* NOTE: this should always come last in the dump! */
1061 for (i
= idx_stream
; i
< nStreams
; i
++)
1062 writeat(dc
, mdHead
.StreamDirectoryRva
+ i
* sizeof(emptyDir
), &emptyDir
, sizeof(emptyDir
));
1067 /******************************************************************
1068 * MiniDumpWriteDump (DEBUGHLP.@)
1071 BOOL WINAPI
MiniDumpWriteDump(HANDLE hProcess
, DWORD pid
, HANDLE hFile
,
1072 MINIDUMP_TYPE DumpType
,
1073 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
,
1074 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
,
1075 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
)
1077 struct dump_context dc
;
1078 BOOL sym_initialized
= FALSE
;
1081 if (!(dc
.process
= process_find_by_handle(hProcess
)))
1083 if (!(sym_initialized
= SymInitializeW(hProcess
, NULL
, TRUE
)))
1085 WARN("failed to initialize process\n");
1088 dc
.process
= process_find_by_handle(hProcess
);
1091 if (DumpType
& MiniDumpWithDataSegs
)
1092 FIXME("NIY MiniDumpWithDataSegs\n");
1093 if (DumpType
& MiniDumpWithHandleData
)
1094 FIXME("NIY MiniDumpWithHandleData\n");
1095 if (DumpType
& MiniDumpFilterMemory
)
1096 FIXME("NIY MiniDumpFilterMemory\n");
1097 if (DumpType
& MiniDumpScanMemory
)
1098 FIXME("NIY MiniDumpScanMemory\n");
1104 dc
.alloc_modules
= 0;
1107 dc
.cb
= CallbackParam
;
1116 dc
.except_param
= ExceptionParam
;
1117 dc
.user_stream
= UserStreamParam
;
1119 /* have a dedicated thread for fetching info on self */
1120 if (dc
.pid
!= GetCurrentProcessId())
1121 ret
= write_minidump(&dc
);
1125 HANDLE h
= CreateThread(NULL
, 0, write_minidump
, &dc
, 0, NULL
);
1128 if (WaitForSingleObject(h
, INFINITE
) == WAIT_OBJECT_0
&& GetExitCodeThread(h
, &exit_code
))
1131 TerminateThread(h
, 0);
1136 if (sym_initialized
)
1137 SymCleanup(hProcess
);
1139 HeapFree(GetProcessHeap(), 0, dc
.mem
);
1140 HeapFree(GetProcessHeap(), 0, dc
.mem64
);
1141 HeapFree(GetProcessHeap(), 0, dc
.modules
);
1142 HeapFree(GetProcessHeap(), 0, dc
.threads
);
1147 /******************************************************************
1148 * MiniDumpReadDumpStream (DEBUGHLP.@)
1152 BOOL WINAPI
MiniDumpReadDumpStream(PVOID base
, ULONG str_idx
,
1153 PMINIDUMP_DIRECTORY
* pdir
,
1154 PVOID
* stream
, ULONG
* size
)
1156 MINIDUMP_HEADER
* mdHead
= base
;
1158 if (mdHead
->Signature
== MINIDUMP_SIGNATURE
)
1160 MINIDUMP_DIRECTORY
* dir
;
1163 dir
= (MINIDUMP_DIRECTORY
*)((char*)base
+ mdHead
->StreamDirectoryRva
);
1164 for (i
= 0; i
< mdHead
->NumberOfStreams
; i
++, dir
++)
1166 if (dir
->StreamType
== str_idx
)
1168 if (pdir
) *pdir
= dir
;
1169 if (stream
) *stream
= (char*)base
+ dir
->Location
.Rva
;
1170 if (size
) *size
= dir
->Location
.DataSize
;
1175 SetLastError(ERROR_INVALID_PARAMETER
);