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
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
27 #define WIN32_NO_STATUS
28 #include "dbghelp_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
36 /******************************************************************
39 * reads system wide process information, and gather from it the threads information
40 * for process of id 'pid'
42 static BOOL
fetch_process_info(struct dump_context
* dc
)
44 ULONG buf_size
= 0x1000;
46 void* pcs_buffer
= NULL
;
48 if (!(pcs_buffer
= HeapAlloc(GetProcessHeap(), 0, buf_size
))) return FALSE
;
51 nts
= NtQuerySystemInformation(SystemProcessInformation
,
52 pcs_buffer
, buf_size
, NULL
);
53 if (nts
!= STATUS_INFO_LENGTH_MISMATCH
) break;
54 pcs_buffer
= HeapReAlloc(GetProcessHeap(), 0, pcs_buffer
, buf_size
*= 2);
55 if (!pcs_buffer
) return FALSE
;
58 if (nts
== STATUS_SUCCESS
)
60 SYSTEM_PROCESS_INFORMATION
* spi
= pcs_buffer
;
65 if (HandleToUlong(spi
->UniqueProcessId
) == dc
->pid
)
67 dc
->num_threads
= spi
->dwThreadCount
;
68 dc
->threads
= HeapAlloc(GetProcessHeap(), 0,
69 dc
->num_threads
* sizeof(dc
->threads
[0]));
70 if (!dc
->threads
) goto failed
;
71 for (i
= 0; i
< dc
->num_threads
; i
++)
73 dc
->threads
[i
].tid
= HandleToULong(spi
->ti
[i
].ClientId
.UniqueThread
);
74 dc
->threads
[i
].prio_class
= spi
->ti
[i
].dwBasePriority
; /* FIXME */
75 dc
->threads
[i
].curr_prio
= spi
->ti
[i
].dwCurrentPriority
;
77 HeapFree(GetProcessHeap(), 0, pcs_buffer
);
80 if (!spi
->NextEntryOffset
) break;
81 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 const MINIDUMP_EXCEPTION_INFORMATION
* except
,
123 MINIDUMP_THREAD
* mdThd
, CONTEXT
* ctx
)
125 DWORD tid
= dc
->threads
[thd_idx
].tid
;
127 THREAD_BASIC_INFORMATION tbi
;
129 memset(ctx
, 0, sizeof(*ctx
));
131 mdThd
->ThreadId
= tid
;
132 mdThd
->SuspendCount
= 0;
134 mdThd
->Stack
.StartOfMemoryRange
= 0;
135 mdThd
->Stack
.Memory
.DataSize
= 0;
136 mdThd
->Stack
.Memory
.Rva
= 0;
137 mdThd
->ThreadContext
.DataSize
= 0;
138 mdThd
->ThreadContext
.Rva
= 0;
139 mdThd
->PriorityClass
= dc
->threads
[thd_idx
].prio_class
;
140 mdThd
->Priority
= dc
->threads
[thd_idx
].curr_prio
;
142 if ((hThread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, tid
)) == NULL
)
144 FIXME("Couldn't open thread %u (%u)\n", tid
, GetLastError());
148 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
,
149 &tbi
, sizeof(tbi
), NULL
) == STATUS_SUCCESS
)
151 mdThd
->Teb
= (ULONG_PTR
)tbi
.TebBaseAddress
;
152 if (tbi
.ExitStatus
== STILL_ACTIVE
)
154 if (tid
!= GetCurrentThreadId() &&
155 (mdThd
->SuspendCount
= SuspendThread(hThread
)) != (DWORD
)-1)
157 ctx
->ContextFlags
= CONTEXT_FULL
;
158 if (!GetThreadContext(hThread
, ctx
))
159 memset(ctx
, 0, sizeof(*ctx
));
161 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, ctx
, &mdThd
->Stack
);
162 ResumeThread(hThread
);
164 else if (tid
== GetCurrentThreadId() && except
)
167 mdThd
->SuspendCount
= 1;
168 if (except
->ClientPointers
)
170 EXCEPTION_POINTERS ep
;
172 ReadProcessMemory(dc
->process
->handle
, except
->ExceptionPointers
,
173 &ep
, sizeof(ep
), NULL
);
174 ReadProcessMemory(dc
->process
->handle
, ep
.ContextRecord
,
175 &lctx
, sizeof(lctx
), NULL
);
178 else pctx
= except
->ExceptionPointers
->ContextRecord
;
181 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, pctx
, &mdThd
->Stack
);
183 else mdThd
->SuspendCount
= 0;
186 CloseHandle(hThread
);
190 /******************************************************************
193 * Add a module to a dump context
195 static BOOL
add_module(struct dump_context
* dc
, const WCHAR
* name
,
196 DWORD64 base
, DWORD size
, DWORD timestamp
, DWORD checksum
,
201 dc
->alloc_modules
= 32;
202 dc
->modules
= HeapAlloc(GetProcessHeap(), 0,
203 dc
->alloc_modules
* sizeof(*dc
->modules
));
205 else if(dc
->num_modules
>= dc
->alloc_modules
)
207 dc
->alloc_modules
*= 2;
208 dc
->modules
= HeapReAlloc(GetProcessHeap(), 0, dc
->modules
,
209 dc
->alloc_modules
* sizeof(*dc
->modules
));
213 dc
->alloc_modules
= dc
->num_modules
= 0;
217 !GetModuleFileNameExW(dc
->process
->handle
, (HMODULE
)(DWORD_PTR
)base
,
218 dc
->modules
[dc
->num_modules
].name
,
219 ARRAY_SIZE(dc
->modules
[dc
->num_modules
].name
)))
220 lstrcpynW(dc
->modules
[dc
->num_modules
].name
, name
,
221 ARRAY_SIZE(dc
->modules
[dc
->num_modules
].name
));
222 dc
->modules
[dc
->num_modules
].base
= base
;
223 dc
->modules
[dc
->num_modules
].size
= size
;
224 dc
->modules
[dc
->num_modules
].timestamp
= timestamp
;
225 dc
->modules
[dc
->num_modules
].checksum
= checksum
;
226 dc
->modules
[dc
->num_modules
].is_elf
= is_elf
;
232 /******************************************************************
233 * fetch_pe_module_info_cb
235 * Callback for accumulating in dump_context a PE modules set
237 static BOOL WINAPI
fetch_pe_module_info_cb(PCWSTR name
, DWORD64 base
, ULONG size
,
240 struct dump_context
* dc
= user
;
241 IMAGE_NT_HEADERS nth
;
243 if (!validate_addr64(base
)) return FALSE
;
245 if (pe_load_nt_header(dc
->process
->handle
, base
, &nth
))
246 add_module(user
, name
, base
, size
,
247 nth
.FileHeader
.TimeDateStamp
, nth
.OptionalHeader
.CheckSum
,
252 /******************************************************************
253 * fetch_elf_module_info_cb
255 * Callback for accumulating in dump_context an host modules set
257 static BOOL
fetch_host_module_info_cb(const WCHAR
* name
, ULONG_PTR base
,
260 struct dump_context
* dc
= user
;
262 DWORD size
, checksum
;
264 /* FIXME: there's no relevant timestamp on ELF modules */
265 if (!dc
->process
->loader
->fetch_file_info(dc
->process
, name
, base
, &rbase
, &size
, &checksum
))
267 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
271 static void minidump_add_memory64_block(struct dump_context
* dc
, ULONG64 base
, ULONG64 size
)
275 dc
->alloc_mem64
= 32;
276 dc
->mem64
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem64
* sizeof(*dc
->mem64
));
278 else if (dc
->num_mem64
>= dc
->alloc_mem64
)
280 dc
->alloc_mem64
*= 2;
281 dc
->mem64
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem64
,
282 dc
->alloc_mem64
* sizeof(*dc
->mem64
));
286 dc
->mem64
[dc
->num_mem64
].base
= base
;
287 dc
->mem64
[dc
->num_mem64
].size
= size
;
290 else dc
->num_mem64
= dc
->alloc_mem64
= 0;
293 static void fetch_memory64_info(struct dump_context
* dc
)
296 MEMORY_BASIC_INFORMATION mbi
;
299 while (VirtualQueryEx(dc
->process
->handle
, (LPCVOID
)addr
, &mbi
, sizeof(mbi
)) != 0)
301 /* Memory regions with state MEM_COMMIT will be added to the dump */
302 if (mbi
.State
== MEM_COMMIT
)
304 minidump_add_memory64_block(dc
, (ULONG_PTR
)mbi
.BaseAddress
, mbi
.RegionSize
);
307 if ((addr
+ mbi
.RegionSize
) < addr
)
310 addr
= (ULONG_PTR
)mbi
.BaseAddress
+ mbi
.RegionSize
;
314 static void fetch_modules_info(struct dump_context
* dc
)
316 EnumerateLoadedModulesW64(dc
->process
->handle
, fetch_pe_module_info_cb
, dc
);
317 /* Since we include ELF modules in a separate stream from the regular PE ones,
318 * we can always include those ELF modules (they don't eat lots of space)
319 * And it's always a good idea to have a trace of the loaded ELF modules for
320 * a given application in a post mortem debugging condition.
322 dc
->process
->loader
->enum_modules(dc
->process
, fetch_host_module_info_cb
, dc
);
325 static void fetch_module_versioninfo(LPCWSTR filename
, VS_FIXEDFILEINFO
* ffi
)
329 static const WCHAR backslashW
[] = {'\\', '\0'};
331 memset(ffi
, 0, sizeof(*ffi
));
332 if ((sz
= GetFileVersionInfoSizeW(filename
, &handle
)))
334 void* info
= HeapAlloc(GetProcessHeap(), 0, sz
);
335 if (info
&& GetFileVersionInfoW(filename
, handle
, sz
, info
))
337 VS_FIXEDFILEINFO
* ptr
;
340 if (VerQueryValueW(info
, backslashW
, (void*)&ptr
, &len
))
341 memcpy(ffi
, ptr
, min(len
, sizeof(*ffi
)));
343 HeapFree(GetProcessHeap(), 0, info
);
347 /******************************************************************
348 * minidump_add_memory_block
350 * Add a memory block to be dumped in a minidump
351 * If rva is non 0, it's the rva in the minidump where has to be stored
352 * also the rva of the memory block when written (this allows us to reference
353 * a memory block from outside the list of memory blocks).
355 void minidump_add_memory_block(struct dump_context
* dc
, ULONG64 base
, ULONG size
, ULONG rva
)
360 dc
->mem
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem
* sizeof(*dc
->mem
));
362 else if (dc
->num_mem
>= dc
->alloc_mem
)
365 dc
->mem
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem
,
366 dc
->alloc_mem
* sizeof(*dc
->mem
));
370 dc
->mem
[dc
->num_mem
].base
= base
;
371 dc
->mem
[dc
->num_mem
].size
= size
;
372 dc
->mem
[dc
->num_mem
].rva
= rva
;
375 else dc
->num_mem
= dc
->alloc_mem
= 0;
378 /******************************************************************
381 * Writes a chunk of data at a given position in the minidump
383 static void writeat(struct dump_context
* dc
, RVA rva
, const void* data
, unsigned size
)
387 SetFilePointer(dc
->hFile
, rva
, NULL
, FILE_BEGIN
);
388 WriteFile(dc
->hFile
, data
, size
, &written
, NULL
);
391 /******************************************************************
394 * writes a new chunk of data to the minidump, increasing the current
397 static void append(struct dump_context
* dc
, const void* data
, unsigned size
)
399 writeat(dc
, dc
->rva
, data
, size
);
403 /******************************************************************
404 * dump_exception_info
406 * Write in File the exception information from pcs
408 static unsigned dump_exception_info(struct dump_context
* dc
,
409 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
411 MINIDUMP_EXCEPTION_STREAM mdExcpt
;
412 EXCEPTION_RECORD rec
, *prec
;
416 mdExcpt
.ThreadId
= except
->ThreadId
;
417 mdExcpt
.__alignment
= 0;
418 if (except
->ClientPointers
)
420 EXCEPTION_POINTERS ep
;
422 ReadProcessMemory(dc
->process
->handle
,
423 except
->ExceptionPointers
, &ep
, sizeof(ep
), NULL
);
424 ReadProcessMemory(dc
->process
->handle
,
425 ep
.ExceptionRecord
, &rec
, sizeof(rec
), NULL
);
426 ReadProcessMemory(dc
->process
->handle
,
427 ep
.ContextRecord
, &ctx
, sizeof(ctx
), NULL
);
433 prec
= except
->ExceptionPointers
->ExceptionRecord
;
434 pctx
= except
->ExceptionPointers
->ContextRecord
;
436 mdExcpt
.ExceptionRecord
.ExceptionCode
= prec
->ExceptionCode
;
437 mdExcpt
.ExceptionRecord
.ExceptionFlags
= prec
->ExceptionFlags
;
438 mdExcpt
.ExceptionRecord
.ExceptionRecord
= (DWORD_PTR
)prec
->ExceptionRecord
;
439 mdExcpt
.ExceptionRecord
.ExceptionAddress
= (DWORD_PTR
)prec
->ExceptionAddress
;
440 mdExcpt
.ExceptionRecord
.NumberParameters
= prec
->NumberParameters
;
441 mdExcpt
.ExceptionRecord
.__unusedAlignment
= 0;
442 for (i
= 0; i
< mdExcpt
.ExceptionRecord
.NumberParameters
; i
++)
443 mdExcpt
.ExceptionRecord
.ExceptionInformation
[i
] = prec
->ExceptionInformation
[i
];
444 mdExcpt
.ThreadContext
.DataSize
= sizeof(*pctx
);
445 mdExcpt
.ThreadContext
.Rva
= dc
->rva
+ sizeof(mdExcpt
);
447 append(dc
, &mdExcpt
, sizeof(mdExcpt
));
448 append(dc
, pctx
, sizeof(*pctx
));
449 return sizeof(mdExcpt
);
452 /******************************************************************
455 * Write in File the modules from pcs
457 static unsigned dump_modules(struct dump_context
* dc
, BOOL dump_elf
)
459 MINIDUMP_MODULE mdModule
;
460 MINIDUMP_MODULE_LIST mdModuleList
;
462 MINIDUMP_STRING
* ms
= (MINIDUMP_STRING
*)tmp
;
468 for (i
= nmod
= 0; i
< dc
->num_modules
; i
++)
470 if ((dc
->modules
[i
].is_elf
&& dump_elf
) ||
471 (!dc
->modules
[i
].is_elf
&& !dump_elf
))
475 mdModuleList
.NumberOfModules
= 0;
476 /* reserve space for mdModuleList
477 * FIXME: since we don't support 0 length arrays, we cannot use the
478 * size of mdModuleList
479 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
482 /* the stream size is just the size of the module index. It does not include the data for the
483 names of each module. *Technically* the names are supposed to go into the common string table
484 in the minidump file. Since each string is referenced by RVA they can all safely be located
485 anywhere between streams in the file, so the end of this stream is sufficient. */
487 dc
->rva
+= sz
= sizeof(mdModuleList
.NumberOfModules
) + sizeof(mdModule
) * nmod
;
488 for (i
= 0; i
< dc
->num_modules
; i
++)
490 if ((dc
->modules
[i
].is_elf
&& !dump_elf
) ||
491 (!dc
->modules
[i
].is_elf
&& dump_elf
))
494 flags_out
= ModuleWriteModule
| ModuleWriteMiscRecord
| ModuleWriteCvRecord
;
495 if (dc
->type
& MiniDumpWithDataSegs
)
496 flags_out
|= ModuleWriteDataSeg
;
497 if (dc
->type
& MiniDumpWithProcessThreadData
)
498 flags_out
|= ModuleWriteTlsData
;
499 if (dc
->type
& MiniDumpWithCodeSegs
)
500 flags_out
|= ModuleWriteCodeSegs
;
501 ms
->Length
= (lstrlenW(dc
->modules
[i
].name
) + 1) * sizeof(WCHAR
);
502 if (sizeof(ULONG
) + ms
->Length
> sizeof(tmp
))
503 FIXME("Buffer overflow!!!\n");
504 lstrcpyW(ms
->Buffer
, dc
->modules
[i
].name
);
508 MINIDUMP_CALLBACK_INPUT cbin
;
509 MINIDUMP_CALLBACK_OUTPUT cbout
;
511 cbin
.ProcessId
= dc
->pid
;
512 cbin
.ProcessHandle
= dc
->process
->handle
;
513 cbin
.CallbackType
= ModuleCallback
;
515 cbin
.u
.Module
.FullPath
= ms
->Buffer
;
516 cbin
.u
.Module
.BaseOfImage
= dc
->modules
[i
].base
;
517 cbin
.u
.Module
.SizeOfImage
= dc
->modules
[i
].size
;
518 cbin
.u
.Module
.CheckSum
= dc
->modules
[i
].checksum
;
519 cbin
.u
.Module
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
520 memset(&cbin
.u
.Module
.VersionInfo
, 0, sizeof(cbin
.u
.Module
.VersionInfo
));
521 cbin
.u
.Module
.CvRecord
= NULL
;
522 cbin
.u
.Module
.SizeOfCvRecord
= 0;
523 cbin
.u
.Module
.MiscRecord
= NULL
;
524 cbin
.u
.Module
.SizeOfMiscRecord
= 0;
526 cbout
.u
.ModuleWriteFlags
= flags_out
;
527 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
529 flags_out
&= cbout
.u
.ModuleWriteFlags
;
531 if (flags_out
& ModuleWriteModule
)
533 /* fetch CPU dependent module info (like UNWIND_INFO) */
534 dbghelp_current_cpu
->fetch_minidump_module(dc
, i
, flags_out
);
536 mdModule
.BaseOfImage
= dc
->modules
[i
].base
;
537 mdModule
.SizeOfImage
= dc
->modules
[i
].size
;
538 mdModule
.CheckSum
= dc
->modules
[i
].checksum
;
539 mdModule
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
540 mdModule
.ModuleNameRva
= dc
->rva
;
541 ms
->Length
-= sizeof(WCHAR
);
542 append(dc
, ms
, sizeof(ULONG
) + ms
->Length
+ sizeof(WCHAR
));
543 fetch_module_versioninfo(ms
->Buffer
, &mdModule
.VersionInfo
);
544 mdModule
.CvRecord
.DataSize
= 0; /* FIXME */
545 mdModule
.CvRecord
.Rva
= 0; /* FIXME */
546 mdModule
.MiscRecord
.DataSize
= 0; /* FIXME */
547 mdModule
.MiscRecord
.Rva
= 0; /* FIXME */
548 mdModule
.Reserved0
= 0; /* FIXME */
549 mdModule
.Reserved1
= 0; /* FIXME */
551 rva_base
+ sizeof(mdModuleList
.NumberOfModules
) +
552 mdModuleList
.NumberOfModules
++ * sizeof(mdModule
),
553 &mdModule
, sizeof(mdModule
));
556 writeat(dc
, rva_base
, &mdModuleList
.NumberOfModules
,
557 sizeof(mdModuleList
.NumberOfModules
));
563 extern void do_x86cpuid(unsigned int ax
, unsigned int *p
);
564 __ASM_GLOBAL_FUNC( do_x86cpuid
,
567 "movl 12(%esp),%eax\n\t"
568 "movl 16(%esp),%esi\n\t"
570 "movl %eax,(%esi)\n\t"
571 "movl %ebx,4(%esi)\n\t"
572 "movl %ecx,8(%esi)\n\t"
573 "movl %edx,12(%esi)\n\t"
577 extern int have_x86cpuid(void);
578 __ASM_GLOBAL_FUNC( have_x86cpuid
,
581 "movl (%esp),%ecx\n\t"
582 "xorl $0x00200000,(%esp)\n\t"
588 "andl $0x00200000,%eax\n\t"
591 static void do_x86cpuid(unsigned int ax
, unsigned int *p
)
595 static int have_x86cpuid(void)
601 /******************************************************************
604 * Dumps into File the information about the system
606 static unsigned dump_system_info(struct dump_context
* dc
)
608 MINIDUMP_SYSTEM_INFO mdSysInfo
;
610 OSVERSIONINFOW osInfo
;
613 DWORD wine_extra
= 0;
615 const char *(CDECL
*wine_get_build_id
)(void);
616 void (CDECL
*wine_get_host_version
)(const char **sysname
, const char **release
);
617 const char* build_id
= NULL
;
618 const char* sys_name
= NULL
;
619 const char* release_name
= NULL
;
621 GetSystemInfo(&sysInfo
);
622 osInfo
.dwOSVersionInfoSize
= sizeof(osInfo
);
623 GetVersionExW(&osInfo
);
625 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
626 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
627 if (wine_get_build_id
&& wine_get_host_version
)
629 /* cheat minidump system information by adding specific wine information */
630 wine_extra
= 4 + 4 * sizeof(slen
);
631 build_id
= wine_get_build_id();
632 wine_get_host_version(&sys_name
, &release_name
);
633 wine_extra
+= strlen(build_id
) + 1 + strlen(sys_name
) + 1 + strlen(release_name
) + 1;
636 mdSysInfo
.ProcessorArchitecture
= sysInfo
.u
.s
.wProcessorArchitecture
;
637 mdSysInfo
.ProcessorLevel
= sysInfo
.wProcessorLevel
;
638 mdSysInfo
.ProcessorRevision
= sysInfo
.wProcessorRevision
;
639 mdSysInfo
.u
.s
.NumberOfProcessors
= sysInfo
.dwNumberOfProcessors
;
640 mdSysInfo
.u
.s
.ProductType
= VER_NT_WORKSTATION
; /* FIXME */
641 mdSysInfo
.MajorVersion
= osInfo
.dwMajorVersion
;
642 mdSysInfo
.MinorVersion
= osInfo
.dwMinorVersion
;
643 mdSysInfo
.BuildNumber
= osInfo
.dwBuildNumber
;
644 mdSysInfo
.PlatformId
= osInfo
.dwPlatformId
;
646 mdSysInfo
.CSDVersionRva
= dc
->rva
+ sizeof(mdSysInfo
) + wine_extra
;
647 mdSysInfo
.u1
.Reserved1
= 0;
648 mdSysInfo
.u1
.s
.SuiteMask
= VER_SUITE_TERMINAL
;
652 unsigned regs0
[4], regs1
[4];
654 do_x86cpuid(0, regs0
);
655 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[0] = regs0
[1];
656 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[1] = regs0
[3];
657 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[2] = regs0
[2];
658 do_x86cpuid(1, regs1
);
659 mdSysInfo
.Cpu
.X86CpuInfo
.VersionInformation
= regs1
[0];
660 mdSysInfo
.Cpu
.X86CpuInfo
.FeatureInformation
= regs1
[3];
661 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= 0;
662 if (regs0
[1] == 0x68747541 /* "Auth" */ &&
663 regs0
[3] == 0x69746e65 /* "enti" */ &&
664 regs0
[2] == 0x444d4163 /* "cAMD" */)
666 do_x86cpuid(0x80000000, regs1
); /* get vendor cpuid level */
667 if (regs1
[0] >= 0x80000001)
669 do_x86cpuid(0x80000001, regs1
); /* get vendor features */
670 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= regs1
[3];
679 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] = 0;
680 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[1] = 0;
682 for (i
= 0; i
< sizeof(mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0]) * 8; i
++)
683 if (IsProcessorFeaturePresent(i
))
684 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] |= one
<< i
;
686 append(dc
, &mdSysInfo
, sizeof(mdSysInfo
));
688 /* write Wine specific system information just behind the structure, and before any string */
691 static const char code
[] = {'W','I','N','E'};
693 WriteFile(dc
->hFile
, code
, 4, &written
, NULL
);
694 /* number of sub-info, so that we can extend structure if needed */
696 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
697 /* we store offsets from just after the WINE marker */
698 slen
= 4 * sizeof(DWORD
);
699 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
700 slen
+= strlen(build_id
) + 1;
701 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
702 slen
+= strlen(sys_name
) + 1;
703 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
704 WriteFile(dc
->hFile
, build_id
, strlen(build_id
) + 1, &written
, NULL
);
705 WriteFile(dc
->hFile
, sys_name
, strlen(sys_name
) + 1, &written
, NULL
);
706 WriteFile(dc
->hFile
, release_name
, strlen(release_name
) + 1, &written
, NULL
);
707 dc
->rva
+= wine_extra
;
710 /* write the service pack version string after this stream. It is referenced within the
711 stream by its RVA in the file. */
712 slen
= lstrlenW(osInfo
.szCSDVersion
) * sizeof(WCHAR
);
713 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
714 WriteFile(dc
->hFile
, osInfo
.szCSDVersion
, slen
, &written
, NULL
);
715 dc
->rva
+= sizeof(ULONG
) + slen
;
717 return sizeof(mdSysInfo
);
720 /******************************************************************
723 * Dumps into File the information about running threads
725 static unsigned dump_threads(struct dump_context
* dc
,
726 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
728 MINIDUMP_THREAD mdThd
;
729 MINIDUMP_THREAD_LIST mdThdList
;
735 mdThdList
.NumberOfThreads
= 0;
738 dc
->rva
+= sz
= sizeof(mdThdList
.NumberOfThreads
) + dc
->num_threads
* sizeof(mdThd
);
740 for (i
= 0; i
< dc
->num_threads
; i
++)
742 fetch_thread_info(dc
, i
, except
, &mdThd
, &ctx
);
744 flags_out
= ThreadWriteThread
| ThreadWriteStack
| ThreadWriteContext
|
745 ThreadWriteInstructionWindow
;
746 if (dc
->type
& MiniDumpWithProcessThreadData
)
747 flags_out
|= ThreadWriteThreadData
;
748 if (dc
->type
& MiniDumpWithThreadInfo
)
749 flags_out
|= ThreadWriteThreadInfo
;
753 MINIDUMP_CALLBACK_INPUT cbin
;
754 MINIDUMP_CALLBACK_OUTPUT cbout
;
756 cbin
.ProcessId
= dc
->pid
;
757 cbin
.ProcessHandle
= dc
->process
->handle
;
758 cbin
.CallbackType
= ThreadCallback
;
759 cbin
.u
.Thread
.ThreadId
= dc
->threads
[i
].tid
;
760 cbin
.u
.Thread
.ThreadHandle
= 0; /* FIXME */
761 cbin
.u
.Thread
.Context
= ctx
;
762 cbin
.u
.Thread
.SizeOfContext
= sizeof(CONTEXT
);
763 cbin
.u
.Thread
.StackBase
= mdThd
.Stack
.StartOfMemoryRange
;
764 cbin
.u
.Thread
.StackEnd
= mdThd
.Stack
.StartOfMemoryRange
+
765 mdThd
.Stack
.Memory
.DataSize
;
767 cbout
.u
.ThreadWriteFlags
= flags_out
;
768 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
770 flags_out
&= cbout
.u
.ThreadWriteFlags
;
772 if (flags_out
& ThreadWriteThread
)
774 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteContext
))
776 mdThd
.ThreadContext
.Rva
= dc
->rva
;
777 mdThd
.ThreadContext
.DataSize
= sizeof(CONTEXT
);
778 append(dc
, &ctx
, sizeof(CONTEXT
));
780 if (mdThd
.Stack
.Memory
.DataSize
&& (flags_out
& ThreadWriteStack
))
782 minidump_add_memory_block(dc
, mdThd
.Stack
.StartOfMemoryRange
,
783 mdThd
.Stack
.Memory
.DataSize
,
784 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
785 mdThdList
.NumberOfThreads
* sizeof(mdThd
) +
786 FIELD_OFFSET(MINIDUMP_THREAD
, Stack
.Memory
.Rva
));
789 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
790 mdThdList
.NumberOfThreads
* sizeof(mdThd
),
791 &mdThd
, sizeof(mdThd
));
792 mdThdList
.NumberOfThreads
++;
794 /* fetch CPU dependent thread info (like 256 bytes around program counter */
795 dbghelp_current_cpu
->fetch_minidump_thread(dc
, i
, flags_out
, &ctx
);
797 writeat(dc
, rva_base
,
798 &mdThdList
.NumberOfThreads
, sizeof(mdThdList
.NumberOfThreads
));
803 /******************************************************************
806 * dumps information about the memory of the process (stack of the threads)
808 static unsigned dump_memory_info(struct dump_context
* dc
)
810 MINIDUMP_MEMORY_LIST mdMemList
;
811 MINIDUMP_MEMORY_DESCRIPTOR mdMem
;
813 unsigned i
, pos
, len
, sz
;
817 mdMemList
.NumberOfMemoryRanges
= dc
->num_mem
;
818 append(dc
, &mdMemList
.NumberOfMemoryRanges
,
819 sizeof(mdMemList
.NumberOfMemoryRanges
));
821 sz
= mdMemList
.NumberOfMemoryRanges
* sizeof(mdMem
);
823 sz
+= sizeof(mdMemList
.NumberOfMemoryRanges
);
825 for (i
= 0; i
< dc
->num_mem
; i
++)
827 mdMem
.StartOfMemoryRange
= dc
->mem
[i
].base
;
828 mdMem
.Memory
.Rva
= dc
->rva
;
829 mdMem
.Memory
.DataSize
= dc
->mem
[i
].size
;
830 SetFilePointer(dc
->hFile
, dc
->rva
, NULL
, FILE_BEGIN
);
831 for (pos
= 0; pos
< dc
->mem
[i
].size
; pos
+= sizeof(tmp
))
833 len
= min(dc
->mem
[i
].size
- pos
, sizeof(tmp
));
834 if (read_process_memory(dc
->process
, dc
->mem
[i
].base
+ pos
, tmp
, len
))
835 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
837 dc
->rva
+= mdMem
.Memory
.DataSize
;
838 writeat(dc
, rva_base
+ i
* sizeof(mdMem
), &mdMem
, sizeof(mdMem
));
841 writeat(dc
, dc
->mem
[i
].rva
, &mdMem
.Memory
.Rva
, sizeof(mdMem
.Memory
.Rva
));
848 /******************************************************************
851 * dumps information about the memory of the process (virtual memory)
853 static unsigned dump_memory64_info(struct dump_context
* dc
)
855 MINIDUMP_MEMORY64_LIST mdMem64List
;
856 MINIDUMP_MEMORY_DESCRIPTOR64 mdMem64
;
862 LARGE_INTEGER filepos
;
864 sz
= sizeof(mdMem64List
.NumberOfMemoryRanges
) +
865 sizeof(mdMem64List
.BaseRva
) +
866 dc
->num_mem64
* sizeof(mdMem64
);
868 mdMem64List
.NumberOfMemoryRanges
= dc
->num_mem64
;
869 mdMem64List
.BaseRva
= dc
->rva
+ sz
;
871 append(dc
, &mdMem64List
.NumberOfMemoryRanges
,
872 sizeof(mdMem64List
.NumberOfMemoryRanges
));
873 append(dc
, &mdMem64List
.BaseRva
,
874 sizeof(mdMem64List
.BaseRva
));
877 dc
->rva
+= dc
->num_mem64
* sizeof(mdMem64
);
879 /* dc->rva is not updated past this point. The end of the dump
880 * is just the full memory data. */
881 filepos
.QuadPart
= dc
->rva
;
882 for (i
= 0; i
< dc
->num_mem64
; i
++)
884 mdMem64
.StartOfMemoryRange
= dc
->mem64
[i
].base
;
885 mdMem64
.DataSize
= dc
->mem64
[i
].size
;
886 SetFilePointerEx(dc
->hFile
, filepos
, NULL
, FILE_BEGIN
);
887 for (pos
= 0; pos
< dc
->mem64
[i
].size
; pos
+= sizeof(tmp
))
889 len
= min(dc
->mem64
[i
].size
- pos
, sizeof(tmp
));
890 if (read_process_memory(dc
->process
, dc
->mem64
[i
].base
+ pos
, tmp
, len
))
891 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
893 filepos
.QuadPart
+= mdMem64
.DataSize
;
894 writeat(dc
, rva_base
+ i
* sizeof(mdMem64
), &mdMem64
, sizeof(mdMem64
));
900 static unsigned dump_misc_info(struct dump_context
* dc
)
902 MINIDUMP_MISC_INFO mmi
;
904 mmi
.SizeOfInfo
= sizeof(mmi
);
905 mmi
.Flags1
= MINIDUMP_MISC1_PROCESS_ID
;
906 mmi
.ProcessId
= dc
->pid
;
907 /* FIXME: create/user/kernel time */
908 mmi
.ProcessCreateTime
= 0;
909 mmi
.ProcessKernelTime
= 0;
910 mmi
.ProcessUserTime
= 0;
912 append(dc
, &mmi
, sizeof(mmi
));
916 /******************************************************************
917 * MiniDumpWriteDump (DEBUGHLP.@)
920 BOOL WINAPI
MiniDumpWriteDump(HANDLE hProcess
, DWORD pid
, HANDLE hFile
,
921 MINIDUMP_TYPE DumpType
,
922 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
,
923 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
,
924 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
)
926 static const MINIDUMP_DIRECTORY emptyDir
= {UnusedStream
, {0, 0}};
927 MINIDUMP_HEADER mdHead
;
928 MINIDUMP_DIRECTORY mdDir
;
929 DWORD i
, nStreams
, idx_stream
;
930 struct dump_context dc
;
931 BOOL sym_initialized
= FALSE
;
933 if (!(dc
.process
= process_find_by_handle(hProcess
)))
935 if (!(sym_initialized
= SymInitializeW(hProcess
, NULL
, TRUE
)))
937 WARN("failed to initialize process\n");
940 dc
.process
= process_find_by_handle(hProcess
);
947 dc
.alloc_modules
= 0;
950 dc
.cb
= CallbackParam
;
960 if (!fetch_process_info(&dc
)) return FALSE
;
961 fetch_modules_info(&dc
);
964 nStreams
= 6 + (ExceptionParam
? 1 : 0) +
965 (UserStreamParam
? UserStreamParam
->UserStreamCount
: 0);
967 /* pad the directory size to a multiple of 4 for alignment purposes */
968 nStreams
= (nStreams
+ 3) & ~3;
970 if (DumpType
& MiniDumpWithDataSegs
)
971 FIXME("NIY MiniDumpWithDataSegs\n");
972 if (DumpType
& MiniDumpWithHandleData
)
973 FIXME("NIY MiniDumpWithHandleData\n");
974 if (DumpType
& MiniDumpFilterMemory
)
975 FIXME("NIY MiniDumpFilterMemory\n");
976 if (DumpType
& MiniDumpScanMemory
)
977 FIXME("NIY MiniDumpScanMemory\n");
979 /* 2) write header */
980 mdHead
.Signature
= MINIDUMP_SIGNATURE
;
981 mdHead
.Version
= MINIDUMP_VERSION
; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
982 mdHead
.NumberOfStreams
= nStreams
;
983 mdHead
.CheckSum
= 0; /* native sets a 0 checksum in its files */
984 mdHead
.StreamDirectoryRva
= sizeof(mdHead
);
985 mdHead
.u
.TimeDateStamp
= time(NULL
);
986 mdHead
.Flags
= DumpType
;
987 append(&dc
, &mdHead
, sizeof(mdHead
));
989 /* 3) write stream directories */
990 dc
.rva
+= nStreams
* sizeof(mdDir
);
993 /* 3.1) write data stream directories */
995 /* must be first in minidump */
996 mdDir
.StreamType
= SystemInfoStream
;
997 mdDir
.Location
.Rva
= dc
.rva
;
998 mdDir
.Location
.DataSize
= dump_system_info(&dc
);
999 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1000 &mdDir
, sizeof(mdDir
));
1002 mdDir
.StreamType
= ThreadListStream
;
1003 mdDir
.Location
.Rva
= dc
.rva
;
1004 mdDir
.Location
.DataSize
= dump_threads(&dc
, ExceptionParam
);
1005 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1006 &mdDir
, sizeof(mdDir
));
1008 mdDir
.StreamType
= ModuleListStream
;
1009 mdDir
.Location
.Rva
= dc
.rva
;
1010 mdDir
.Location
.DataSize
= dump_modules(&dc
, FALSE
);
1011 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1012 &mdDir
, sizeof(mdDir
));
1014 mdDir
.StreamType
= 0xfff0; /* FIXME: this is part of MS reserved streams */
1015 mdDir
.Location
.Rva
= dc
.rva
;
1016 mdDir
.Location
.DataSize
= dump_modules(&dc
, TRUE
);
1017 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1018 &mdDir
, sizeof(mdDir
));
1021 if (!(DumpType
& MiniDumpWithFullMemory
))
1023 mdDir
.StreamType
= MemoryListStream
;
1024 mdDir
.Location
.Rva
= dc
.rva
;
1025 mdDir
.Location
.DataSize
= dump_memory_info(&dc
);
1026 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1027 &mdDir
, sizeof(mdDir
));
1030 mdDir
.StreamType
= MiscInfoStream
;
1031 mdDir
.Location
.Rva
= dc
.rva
;
1032 mdDir
.Location
.DataSize
= dump_misc_info(&dc
);
1033 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1034 &mdDir
, sizeof(mdDir
));
1036 /* 3.2) write exception information (if any) */
1039 mdDir
.StreamType
= ExceptionStream
;
1040 mdDir
.Location
.Rva
= dc
.rva
;
1041 mdDir
.Location
.DataSize
= dump_exception_info(&dc
, ExceptionParam
);
1042 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1043 &mdDir
, sizeof(mdDir
));
1046 /* 3.3) write user defined streams (if any) */
1047 if (UserStreamParam
)
1049 for (i
= 0; i
< UserStreamParam
->UserStreamCount
; i
++)
1051 mdDir
.StreamType
= UserStreamParam
->UserStreamArray
[i
].Type
;
1052 mdDir
.Location
.DataSize
= UserStreamParam
->UserStreamArray
[i
].BufferSize
;
1053 mdDir
.Location
.Rva
= dc
.rva
;
1054 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1055 &mdDir
, sizeof(mdDir
));
1056 append(&dc
, UserStreamParam
->UserStreamArray
[i
].Buffer
,
1057 UserStreamParam
->UserStreamArray
[i
].BufferSize
);
1061 /* 3.4) write full memory (if requested) */
1062 if (DumpType
& MiniDumpWithFullMemory
)
1064 fetch_memory64_info(&dc
);
1066 mdDir
.StreamType
= Memory64ListStream
;
1067 mdDir
.Location
.Rva
= dc
.rva
;
1068 mdDir
.Location
.DataSize
= dump_memory64_info(&dc
);
1069 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
1070 &mdDir
, sizeof(mdDir
));
1073 /* fill the remaining directory entries with 0's (unused stream types) */
1074 /* NOTE: this should always come last in the dump! */
1075 for (i
= idx_stream
; i
< nStreams
; i
++)
1076 writeat(&dc
, mdHead
.StreamDirectoryRva
+ i
* sizeof(emptyDir
), &emptyDir
, sizeof(emptyDir
));
1078 if (sym_initialized
)
1079 SymCleanup(hProcess
);
1081 HeapFree(GetProcessHeap(), 0, dc
.mem
);
1082 HeapFree(GetProcessHeap(), 0, dc
.mem64
);
1083 HeapFree(GetProcessHeap(), 0, dc
.modules
);
1084 HeapFree(GetProcessHeap(), 0, dc
.threads
);
1089 /******************************************************************
1090 * MiniDumpReadDumpStream (DEBUGHLP.@)
1094 BOOL WINAPI
MiniDumpReadDumpStream(PVOID base
, ULONG str_idx
,
1095 PMINIDUMP_DIRECTORY
* pdir
,
1096 PVOID
* stream
, ULONG
* size
)
1098 MINIDUMP_HEADER
* mdHead
= base
;
1100 if (mdHead
->Signature
== MINIDUMP_SIGNATURE
)
1102 MINIDUMP_DIRECTORY
* dir
;
1105 dir
= (MINIDUMP_DIRECTORY
*)((char*)base
+ mdHead
->StreamDirectoryRva
);
1106 for (i
= 0; i
< mdHead
->NumberOfStreams
; i
++, dir
++)
1108 if (dir
->StreamType
== str_idx
)
1110 if (pdir
) *pdir
= dir
;
1111 if (stream
) *stream
= (char*)base
+ dir
->Location
.Rva
;
1112 if (size
) *size
= dir
->Location
.DataSize
;
1117 SetLastError(ERROR_INVALID_PARAMETER
);