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"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
54 /* process & thread information */
58 SYSTEM_PROCESS_INFORMATION
* spi
;
59 /* module information */
60 struct dump_module
* modules
;
62 unsigned alloc_modules
;
63 /* exception information */
64 /* output information */
68 struct dump_memory
* mem
;
71 /* callback information */
72 MINIDUMP_CALLBACK_INFORMATION
* cb
;
75 /******************************************************************
76 * fetch_processes_info
78 * reads system wide process information, and make spi point to the record
79 * for process of id 'pid'
81 static BOOL
fetch_processes_info(struct dump_context
* dc
)
83 ULONG buf_size
= 0x1000;
86 dc
->pcs_buffer
= NULL
;
87 if (!(dc
->pcs_buffer
= HeapAlloc(GetProcessHeap(), 0, buf_size
))) return FALSE
;
90 nts
= NtQuerySystemInformation(SystemProcessInformation
,
91 dc
->pcs_buffer
, buf_size
, NULL
);
92 if (nts
!= STATUS_INFO_LENGTH_MISMATCH
) break;
93 dc
->pcs_buffer
= HeapReAlloc(GetProcessHeap(), 0, dc
->pcs_buffer
,
95 if (!dc
->pcs_buffer
) return FALSE
;
98 if (nts
== STATUS_SUCCESS
)
100 dc
->spi
= dc
->pcs_buffer
;
103 if (HandleToUlong(dc
->spi
->UniqueProcessId
) == dc
->pid
) return TRUE
;
104 if (!dc
->spi
->NextEntryOffset
) break;
105 dc
->spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)dc
->spi
+ dc
->spi
->NextEntryOffset
);
108 HeapFree(GetProcessHeap(), 0, dc
->pcs_buffer
);
109 dc
->pcs_buffer
= NULL
;
114 static void fetch_thread_stack(struct dump_context
* dc
, const void* teb_addr
,
115 const CONTEXT
* ctx
, MINIDUMP_MEMORY_DESCRIPTOR
* mmd
)
120 if (ReadProcessMemory(dc
->hProcess
, teb_addr
, &tib
, sizeof(tib
), NULL
) &&
121 dbghelp_current_cpu
&&
122 dbghelp_current_cpu
->get_addr(NULL
/* FIXME */, ctx
, cpu_addr_stack
, &addr
) && addr
.Mode
== AddrModeFlat
)
126 addr
.Offset
-= dbghelp_current_cpu
->word_size
;
127 /* make sure stack pointer is within the established range of the stack. It could have
128 been clobbered by whatever caused the original exception. */
129 if (addr
.Offset
< (ULONG_PTR
)tib
.StackLimit
|| addr
.Offset
> (ULONG_PTR
)tib
.StackBase
)
130 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
133 mmd
->StartOfMemoryRange
= addr
.Offset
;
136 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
137 mmd
->Memory
.DataSize
= (ULONG_PTR
)tib
.StackBase
- mmd
->StartOfMemoryRange
;
141 /******************************************************************
144 * fetches some information about thread of id 'tid'
146 static BOOL
fetch_thread_info(struct dump_context
* dc
, int thd_idx
,
147 const MINIDUMP_EXCEPTION_INFORMATION
* except
,
148 MINIDUMP_THREAD
* mdThd
, CONTEXT
* ctx
)
150 DWORD tid
= HandleToUlong(dc
->spi
->ti
[thd_idx
].ClientId
.UniqueThread
);
152 THREAD_BASIC_INFORMATION tbi
;
154 memset(ctx
, 0, sizeof(*ctx
));
156 mdThd
->ThreadId
= tid
;
157 mdThd
->SuspendCount
= 0;
159 mdThd
->Stack
.StartOfMemoryRange
= 0;
160 mdThd
->Stack
.Memory
.DataSize
= 0;
161 mdThd
->Stack
.Memory
.Rva
= 0;
162 mdThd
->ThreadContext
.DataSize
= 0;
163 mdThd
->ThreadContext
.Rva
= 0;
164 mdThd
->PriorityClass
= dc
->spi
->ti
[thd_idx
].dwBasePriority
; /* FIXME */
165 mdThd
->Priority
= dc
->spi
->ti
[thd_idx
].dwCurrentPriority
;
167 if ((hThread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, tid
)) == NULL
)
169 FIXME("Couldn't open thread %u (%u)\n", tid
, GetLastError());
173 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
,
174 &tbi
, sizeof(tbi
), NULL
) == STATUS_SUCCESS
)
176 mdThd
->Teb
= (ULONG_PTR
)tbi
.TebBaseAddress
;
177 if (tbi
.ExitStatus
== STILL_ACTIVE
)
179 if (tid
!= GetCurrentThreadId() &&
180 (mdThd
->SuspendCount
= SuspendThread(hThread
)) != (DWORD
)-1)
182 ctx
->ContextFlags
= CONTEXT_FULL
;
183 if (!GetThreadContext(hThread
, ctx
))
184 memset(ctx
, 0, sizeof(*ctx
));
186 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, ctx
, &mdThd
->Stack
);
187 ResumeThread(hThread
);
189 else if (tid
== GetCurrentThreadId() && except
)
192 mdThd
->SuspendCount
= 1;
193 if (except
->ClientPointers
)
195 EXCEPTION_POINTERS ep
;
197 ReadProcessMemory(dc
->hProcess
, except
->ExceptionPointers
,
198 &ep
, sizeof(ep
), NULL
);
199 ReadProcessMemory(dc
->hProcess
, ep
.ContextRecord
,
200 &lctx
, sizeof(lctx
), NULL
);
203 else pctx
= except
->ExceptionPointers
->ContextRecord
;
206 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, pctx
, &mdThd
->Stack
);
208 else mdThd
->SuspendCount
= 0;
211 CloseHandle(hThread
);
215 /******************************************************************
218 * Add a module to a dump context
220 static BOOL
add_module(struct dump_context
* dc
, const WCHAR
* name
,
221 DWORD64 base
, DWORD size
, DWORD timestamp
, DWORD checksum
,
226 dc
->alloc_modules
= 32;
227 dc
->modules
= HeapAlloc(GetProcessHeap(), 0,
228 dc
->alloc_modules
* sizeof(*dc
->modules
));
230 else if(dc
->num_modules
>= dc
->alloc_modules
)
232 dc
->alloc_modules
*= 2;
233 dc
->modules
= HeapReAlloc(GetProcessHeap(), 0, dc
->modules
,
234 dc
->alloc_modules
* sizeof(*dc
->modules
));
238 dc
->alloc_modules
= dc
->num_modules
= 0;
242 !GetModuleFileNameExW(dc
->hProcess
, (HMODULE
)(DWORD_PTR
)base
,
243 dc
->modules
[dc
->num_modules
].name
,
244 sizeof(dc
->modules
[dc
->num_modules
].name
) / sizeof(WCHAR
)))
245 lstrcpynW(dc
->modules
[dc
->num_modules
].name
, name
,
246 sizeof(dc
->modules
[dc
->num_modules
].name
) / sizeof(WCHAR
));
247 dc
->modules
[dc
->num_modules
].base
= base
;
248 dc
->modules
[dc
->num_modules
].size
= size
;
249 dc
->modules
[dc
->num_modules
].timestamp
= timestamp
;
250 dc
->modules
[dc
->num_modules
].checksum
= checksum
;
251 dc
->modules
[dc
->num_modules
].is_elf
= is_elf
;
257 /******************************************************************
258 * fetch_pe_module_info_cb
260 * Callback for accumulating in dump_context a PE modules set
262 static BOOL WINAPI
fetch_pe_module_info_cb(PCWSTR name
, DWORD64 base
, ULONG size
,
265 struct dump_context
* dc
= user
;
266 IMAGE_NT_HEADERS nth
;
268 if (!validate_addr64(base
)) return FALSE
;
270 if (pe_load_nt_header(dc
->hProcess
, base
, &nth
))
271 add_module(user
, name
, base
, size
,
272 nth
.FileHeader
.TimeDateStamp
, nth
.OptionalHeader
.CheckSum
,
277 /******************************************************************
278 * fetch_elf_module_info_cb
280 * Callback for accumulating in dump_context an ELF modules set
282 static BOOL
fetch_elf_module_info_cb(const WCHAR
* name
, unsigned long base
,
285 struct dump_context
* dc
= user
;
287 DWORD size
, checksum
;
289 /* FIXME: there's no relevant timestamp on ELF modules */
290 /* NB: if we have a non-null base from the live-target use it (whenever
291 * the ELF module is relocatable or not). If we have a null base (ELF
292 * module isn't relocatable) then grab its base address from ELF file
294 if (!elf_fetch_file_info(name
, &rbase
, &size
, &checksum
))
296 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
300 /******************************************************************
301 * fetch_macho_module_info_cb
303 * Callback for accumulating in dump_context a Mach-O modules set
305 static BOOL
fetch_macho_module_info_cb(const WCHAR
* name
, unsigned long base
,
308 struct dump_context
* dc
= (struct dump_context
*)user
;
310 DWORD size
, checksum
;
312 /* FIXME: there's no relevant timestamp on Mach-O modules */
313 /* NB: if we have a non-null base from the live-target use it. If we have
314 * a null base, then grab its base address from Mach-O file.
316 if (!macho_fetch_file_info(name
, &rbase
, &size
, &checksum
))
318 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
322 static void fetch_modules_info(struct dump_context
* dc
)
324 EnumerateLoadedModulesW64(dc
->hProcess
, fetch_pe_module_info_cb
, dc
);
325 /* Since we include ELF modules in a separate stream from the regular PE ones,
326 * we can always include those ELF modules (they don't eat lots of space)
327 * And it's always a good idea to have a trace of the loaded ELF modules for
328 * a given application in a post mortem debugging condition.
330 elf_enum_modules(dc
->hProcess
, fetch_elf_module_info_cb
, dc
);
331 macho_enum_modules(dc
->hProcess
, fetch_macho_module_info_cb
, dc
);
334 static void fetch_module_versioninfo(LPCWSTR filename
, VS_FIXEDFILEINFO
* ffi
)
338 static const WCHAR backslashW
[] = {'\\', '\0'};
340 memset(ffi
, 0, sizeof(*ffi
));
341 if ((sz
= GetFileVersionInfoSizeW(filename
, &handle
)))
343 void* info
= HeapAlloc(GetProcessHeap(), 0, sz
);
344 if (info
&& GetFileVersionInfoW(filename
, handle
, sz
, info
))
346 VS_FIXEDFILEINFO
* ptr
;
349 if (VerQueryValueW(info
, backslashW
, (void*)&ptr
, &len
))
350 memcpy(ffi
, ptr
, min(len
, sizeof(*ffi
)));
352 HeapFree(GetProcessHeap(), 0, info
);
356 /******************************************************************
359 * Add a memory block to be dumped in a minidump
360 * If rva is non 0, it's the rva in the minidump where has to be stored
361 * also the rva of the memory block when written (this allows to reference
362 * a memory block from outside the list of memory blocks).
364 static void add_memory_block(struct dump_context
* dc
, ULONG64 base
, ULONG size
, ULONG rva
)
369 dc
->mem
= HeapAlloc(GetProcessHeap(), 0, dc
->alloc_mem
* sizeof(*dc
->mem
));
371 else if (dc
->num_mem
>= dc
->alloc_mem
)
374 dc
->mem
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem
,
375 dc
->alloc_mem
* sizeof(*dc
->mem
));
379 dc
->mem
[dc
->num_mem
].base
= base
;
380 dc
->mem
[dc
->num_mem
].size
= size
;
381 dc
->mem
[dc
->num_mem
].rva
= rva
;
384 else dc
->num_mem
= dc
->alloc_mem
= 0;
387 /******************************************************************
390 * Writes a chunk of data at a given position in the minidump
392 static void writeat(struct dump_context
* dc
, RVA rva
, const void* data
, unsigned size
)
396 SetFilePointer(dc
->hFile
, rva
, NULL
, FILE_BEGIN
);
397 WriteFile(dc
->hFile
, data
, size
, &written
, NULL
);
400 /******************************************************************
403 * writes a new chunk of data to the minidump, increasing the current
406 static void append(struct dump_context
* dc
, const void* data
, unsigned size
)
408 writeat(dc
, dc
->rva
, data
, size
);
412 /******************************************************************
413 * dump_exception_info
415 * Write in File the exception information from pcs
417 static unsigned dump_exception_info(struct dump_context
* dc
,
418 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
420 MINIDUMP_EXCEPTION_STREAM mdExcpt
;
421 EXCEPTION_RECORD rec
, *prec
;
425 mdExcpt
.ThreadId
= except
->ThreadId
;
426 mdExcpt
.__alignment
= 0;
427 if (except
->ClientPointers
)
429 EXCEPTION_POINTERS ep
;
431 ReadProcessMemory(dc
->hProcess
,
432 except
->ExceptionPointers
, &ep
, sizeof(ep
), NULL
);
433 ReadProcessMemory(dc
->hProcess
,
434 ep
.ExceptionRecord
, &rec
, sizeof(rec
), NULL
);
435 ReadProcessMemory(dc
->hProcess
,
436 ep
.ContextRecord
, &ctx
, sizeof(ctx
), NULL
);
442 prec
= except
->ExceptionPointers
->ExceptionRecord
;
443 pctx
= except
->ExceptionPointers
->ContextRecord
;
445 mdExcpt
.ExceptionRecord
.ExceptionCode
= prec
->ExceptionCode
;
446 mdExcpt
.ExceptionRecord
.ExceptionFlags
= prec
->ExceptionFlags
;
447 mdExcpt
.ExceptionRecord
.ExceptionRecord
= (DWORD_PTR
)prec
->ExceptionRecord
;
448 mdExcpt
.ExceptionRecord
.ExceptionAddress
= (DWORD_PTR
)prec
->ExceptionAddress
;
449 mdExcpt
.ExceptionRecord
.NumberParameters
= prec
->NumberParameters
;
450 mdExcpt
.ExceptionRecord
.__unusedAlignment
= 0;
451 for (i
= 0; i
< mdExcpt
.ExceptionRecord
.NumberParameters
; i
++)
452 mdExcpt
.ExceptionRecord
.ExceptionInformation
[i
] = prec
->ExceptionInformation
[i
];
453 mdExcpt
.ThreadContext
.DataSize
= sizeof(*pctx
);
454 mdExcpt
.ThreadContext
.Rva
= dc
->rva
+ sizeof(mdExcpt
);
456 append(dc
, &mdExcpt
, sizeof(mdExcpt
));
457 append(dc
, pctx
, sizeof(*pctx
));
458 return sizeof(mdExcpt
);
461 /******************************************************************
464 * Write in File the modules from pcs
466 static unsigned dump_modules(struct dump_context
* dc
, BOOL dump_elf
)
468 MINIDUMP_MODULE mdModule
;
469 MINIDUMP_MODULE_LIST mdModuleList
;
471 MINIDUMP_STRING
* ms
= (MINIDUMP_STRING
*)tmp
;
477 for (i
= nmod
= 0; i
< dc
->num_modules
; i
++)
479 if ((dc
->modules
[i
].is_elf
&& dump_elf
) ||
480 (!dc
->modules
[i
].is_elf
&& !dump_elf
))
484 mdModuleList
.NumberOfModules
= 0;
485 /* reserve space for mdModuleList
486 * FIXME: since we don't support 0 length arrays, we cannot use the
487 * size of mdModuleList
488 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
491 /* the stream size is just the size of the module index. It does not include the data for the
492 names of each module. *Technically* the names are supposed to go into the common string table
493 in the minidump file. Since each string is referenced by RVA they can all safely be located
494 anywhere between streams in the file, so the end of this stream is sufficient. */
496 dc
->rva
+= sz
= sizeof(mdModuleList
.NumberOfModules
) + sizeof(mdModule
) * nmod
;
497 for (i
= 0; i
< dc
->num_modules
; i
++)
499 if ((dc
->modules
[i
].is_elf
&& !dump_elf
) ||
500 (!dc
->modules
[i
].is_elf
&& dump_elf
))
503 flags_out
= ModuleWriteModule
| ModuleWriteMiscRecord
| ModuleWriteCvRecord
;
504 if (dc
->type
& MiniDumpWithDataSegs
)
505 flags_out
|= ModuleWriteDataSeg
;
506 if (dc
->type
& MiniDumpWithProcessThreadData
)
507 flags_out
|= ModuleWriteTlsData
;
508 if (dc
->type
& MiniDumpWithCodeSegs
)
509 flags_out
|= ModuleWriteCodeSegs
;
510 ms
->Length
= (lstrlenW(dc
->modules
[i
].name
) + 1) * sizeof(WCHAR
);
511 if (sizeof(ULONG
) + ms
->Length
> sizeof(tmp
))
512 FIXME("Buffer overflow!!!\n");
513 lstrcpyW(ms
->Buffer
, dc
->modules
[i
].name
);
517 MINIDUMP_CALLBACK_INPUT cbin
;
518 MINIDUMP_CALLBACK_OUTPUT cbout
;
520 cbin
.ProcessId
= dc
->pid
;
521 cbin
.ProcessHandle
= dc
->hProcess
;
522 cbin
.CallbackType
= ModuleCallback
;
524 cbin
.u
.Module
.FullPath
= ms
->Buffer
;
525 cbin
.u
.Module
.BaseOfImage
= dc
->modules
[i
].base
;
526 cbin
.u
.Module
.SizeOfImage
= dc
->modules
[i
].size
;
527 cbin
.u
.Module
.CheckSum
= dc
->modules
[i
].checksum
;
528 cbin
.u
.Module
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
529 memset(&cbin
.u
.Module
.VersionInfo
, 0, sizeof(cbin
.u
.Module
.VersionInfo
));
530 cbin
.u
.Module
.CvRecord
= NULL
;
531 cbin
.u
.Module
.SizeOfCvRecord
= 0;
532 cbin
.u
.Module
.MiscRecord
= NULL
;
533 cbin
.u
.Module
.SizeOfMiscRecord
= 0;
535 cbout
.u
.ModuleWriteFlags
= flags_out
;
536 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
538 flags_out
&= cbout
.u
.ModuleWriteFlags
;
540 if (flags_out
& ModuleWriteModule
)
542 mdModule
.BaseOfImage
= dc
->modules
[i
].base
;
543 mdModule
.SizeOfImage
= dc
->modules
[i
].size
;
544 mdModule
.CheckSum
= dc
->modules
[i
].checksum
;
545 mdModule
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
546 mdModule
.ModuleNameRva
= dc
->rva
;
547 ms
->Length
-= sizeof(WCHAR
);
548 append(dc
, ms
, sizeof(ULONG
) + ms
->Length
+ sizeof(WCHAR
));
549 fetch_module_versioninfo(ms
->Buffer
, &mdModule
.VersionInfo
);
550 mdModule
.CvRecord
.DataSize
= 0; /* FIXME */
551 mdModule
.CvRecord
.Rva
= 0; /* FIXME */
552 mdModule
.MiscRecord
.DataSize
= 0; /* FIXME */
553 mdModule
.MiscRecord
.Rva
= 0; /* FIXME */
554 mdModule
.Reserved0
= 0; /* FIXME */
555 mdModule
.Reserved1
= 0; /* FIXME */
557 rva_base
+ sizeof(mdModuleList
.NumberOfModules
) +
558 mdModuleList
.NumberOfModules
++ * sizeof(mdModule
),
559 &mdModule
, sizeof(mdModule
));
562 writeat(dc
, rva_base
, &mdModuleList
.NumberOfModules
,
563 sizeof(mdModuleList
.NumberOfModules
));
568 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
569 * We are compiled with -fPIC, so we can't clobber ebx.
571 static inline void do_x86cpuid(unsigned int ax
, unsigned int *p
)
573 #if defined(__GNUC__) && defined(__i386__)
574 __asm__("pushl %%ebx\n\t"
576 "movl %%ebx, %%esi\n\t"
578 : "=a" (p
[0]), "=S" (p
[1]), "=c" (p
[2]), "=d" (p
[3])
583 /* From xf86info havecpuid.c 1.11 */
584 static inline int have_x86cpuid(void)
586 #if defined(__GNUC__) && defined(__i386__)
598 : "=&r" (f1
), "=&r" (f2
)
599 : "ir" (0x00200000));
600 return ((f1
^f2
) & 0x00200000) != 0;
606 /******************************************************************
609 * Dumps into File the information about the system
611 static unsigned dump_system_info(struct dump_context
* dc
)
613 MINIDUMP_SYSTEM_INFO mdSysInfo
;
615 OSVERSIONINFOW osInfo
;
618 DWORD wine_extra
= 0;
620 const char *(CDECL
*wine_get_build_id
)(void);
621 void (CDECL
*wine_get_host_version
)(const char **sysname
, const char **release
);
622 const char* build_id
= NULL
;
623 const char* sys_name
= NULL
;
624 const char* release_name
= NULL
;
626 GetSystemInfo(&sysInfo
);
627 osInfo
.dwOSVersionInfoSize
= sizeof(osInfo
);
628 GetVersionExW(&osInfo
);
630 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
631 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
632 if (wine_get_build_id
&& wine_get_host_version
)
634 /* cheat minidump system information by adding specific wine information */
635 wine_extra
= 4 + 4 * sizeof(slen
);
636 build_id
= wine_get_build_id();
637 wine_get_host_version(&sys_name
, &release_name
);
638 wine_extra
+= strlen(build_id
) + 1 + strlen(sys_name
) + 1 + strlen(release_name
) + 1;
641 mdSysInfo
.ProcessorArchitecture
= sysInfo
.u
.s
.wProcessorArchitecture
;
642 mdSysInfo
.ProcessorLevel
= sysInfo
.wProcessorLevel
;
643 mdSysInfo
.ProcessorRevision
= sysInfo
.wProcessorRevision
;
644 mdSysInfo
.u
.s
.NumberOfProcessors
= sysInfo
.dwNumberOfProcessors
;
645 mdSysInfo
.u
.s
.ProductType
= VER_NT_WORKSTATION
; /* FIXME */
646 mdSysInfo
.MajorVersion
= osInfo
.dwMajorVersion
;
647 mdSysInfo
.MinorVersion
= osInfo
.dwMinorVersion
;
648 mdSysInfo
.BuildNumber
= osInfo
.dwBuildNumber
;
649 mdSysInfo
.PlatformId
= osInfo
.dwPlatformId
;
651 mdSysInfo
.CSDVersionRva
= dc
->rva
+ sizeof(mdSysInfo
) + wine_extra
;
652 mdSysInfo
.u1
.Reserved1
= 0;
653 mdSysInfo
.u1
.s
.SuiteMask
= VER_SUITE_TERMINAL
;
657 unsigned regs0
[4], regs1
[4];
659 do_x86cpuid(0, regs0
);
660 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[0] = regs0
[1];
661 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[1] = regs0
[2];
662 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[2] = regs0
[3];
663 do_x86cpuid(1, regs1
);
664 mdSysInfo
.Cpu
.X86CpuInfo
.VersionInformation
= regs1
[0];
665 mdSysInfo
.Cpu
.X86CpuInfo
.FeatureInformation
= regs1
[3];
666 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= 0;
667 if (regs0
[1] == 0x68747541 /* "Auth" */ &&
668 regs0
[3] == 0x69746e65 /* "enti" */ &&
669 regs0
[2] == 0x444d4163 /* "cAMD" */)
671 do_x86cpuid(0x80000000, regs1
); /* get vendor cpuid level */
672 if (regs1
[0] >= 0x80000001)
674 do_x86cpuid(0x80000001, regs1
); /* get vendor features */
675 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= regs1
[3];
684 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] = 0;
685 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[1] = 0;
687 for (i
= 0; i
< sizeof(mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0]) * 8; i
++)
688 if (IsProcessorFeaturePresent(i
))
689 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] |= one
<< i
;
691 append(dc
, &mdSysInfo
, sizeof(mdSysInfo
));
693 /* write Wine specific system information just behind the structure, and before any string */
696 char code
[] = {'W','I','N','E'};
698 WriteFile(dc
->hFile
, code
, 4, &written
, NULL
);
699 /* number of sub-info, so that we can extend structure if needed */
701 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
702 /* we store offsets from just after the WINE marker */
703 slen
= 4 * sizeof(DWORD
);
704 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
705 slen
+= strlen(build_id
) + 1;
706 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
707 slen
+= strlen(sys_name
) + 1;
708 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
709 WriteFile(dc
->hFile
, build_id
, strlen(build_id
) + 1, &written
, NULL
);
710 WriteFile(dc
->hFile
, sys_name
, strlen(sys_name
) + 1, &written
, NULL
);
711 WriteFile(dc
->hFile
, release_name
, strlen(release_name
) + 1, &written
, NULL
);
712 dc
->rva
+= wine_extra
;
715 /* write the service pack version string after this stream. It is referenced within the
716 stream by its RVA in the file. */
717 slen
= lstrlenW(osInfo
.szCSDVersion
) * sizeof(WCHAR
);
718 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
719 WriteFile(dc
->hFile
, osInfo
.szCSDVersion
, slen
, &written
, NULL
);
720 dc
->rva
+= sizeof(ULONG
) + slen
;
722 return sizeof(mdSysInfo
);
725 /******************************************************************
728 * Dumps into File the information about running threads
730 static unsigned dump_threads(struct dump_context
* dc
,
731 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
733 MINIDUMP_THREAD mdThd
;
734 MINIDUMP_THREAD_LIST mdThdList
;
740 mdThdList
.NumberOfThreads
= 0;
743 dc
->rva
+= sz
= sizeof(mdThdList
.NumberOfThreads
) + dc
->spi
->dwThreadCount
* sizeof(mdThd
);
745 for (i
= 0; i
< dc
->spi
->dwThreadCount
; i
++)
747 fetch_thread_info(dc
, i
, except
, &mdThd
, &ctx
);
749 flags_out
= ThreadWriteThread
| ThreadWriteStack
| ThreadWriteContext
|
750 ThreadWriteInstructionWindow
;
751 if (dc
->type
& MiniDumpWithProcessThreadData
)
752 flags_out
|= ThreadWriteThreadData
;
753 if (dc
->type
& MiniDumpWithThreadInfo
)
754 flags_out
|= ThreadWriteThreadInfo
;
758 MINIDUMP_CALLBACK_INPUT cbin
;
759 MINIDUMP_CALLBACK_OUTPUT cbout
;
761 cbin
.ProcessId
= dc
->pid
;
762 cbin
.ProcessHandle
= dc
->hProcess
;
763 cbin
.CallbackType
= ThreadCallback
;
764 cbin
.u
.Thread
.ThreadId
= HandleToUlong(dc
->spi
->ti
[i
].ClientId
.UniqueThread
);
765 cbin
.u
.Thread
.ThreadHandle
= 0; /* FIXME */
766 cbin
.u
.Thread
.Context
= ctx
;
767 cbin
.u
.Thread
.SizeOfContext
= sizeof(CONTEXT
);
768 cbin
.u
.Thread
.StackBase
= mdThd
.Stack
.StartOfMemoryRange
;
769 cbin
.u
.Thread
.StackEnd
= mdThd
.Stack
.StartOfMemoryRange
+
770 mdThd
.Stack
.Memory
.DataSize
;
772 cbout
.u
.ThreadWriteFlags
= flags_out
;
773 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
775 flags_out
&= cbout
.u
.ThreadWriteFlags
;
777 if (flags_out
& ThreadWriteThread
)
779 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteContext
))
781 mdThd
.ThreadContext
.Rva
= dc
->rva
;
782 mdThd
.ThreadContext
.DataSize
= sizeof(CONTEXT
);
783 append(dc
, &ctx
, sizeof(CONTEXT
));
785 if (mdThd
.Stack
.Memory
.DataSize
&& (flags_out
& ThreadWriteStack
))
787 add_memory_block(dc
, mdThd
.Stack
.StartOfMemoryRange
,
788 mdThd
.Stack
.Memory
.DataSize
,
789 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
790 mdThdList
.NumberOfThreads
* sizeof(mdThd
) +
791 FIELD_OFFSET(MINIDUMP_THREAD
, Stack
.Memory
.Rva
));
794 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
795 mdThdList
.NumberOfThreads
* sizeof(mdThd
),
796 &mdThd
, sizeof(mdThd
));
797 mdThdList
.NumberOfThreads
++;
799 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteInstructionWindow
))
801 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
802 * - also crop values across module boundaries,
803 * - and don't make it i386 dependent
805 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
808 writeat(dc
, rva_base
,
809 &mdThdList
.NumberOfThreads
, sizeof(mdThdList
.NumberOfThreads
));
814 /******************************************************************
817 * dumps information about the memory of the process (stack of the threads)
819 static unsigned dump_memory_info(struct dump_context
* dc
)
821 MINIDUMP_MEMORY_LIST mdMemList
;
822 MINIDUMP_MEMORY_DESCRIPTOR mdMem
;
824 unsigned i
, pos
, len
, sz
;
828 mdMemList
.NumberOfMemoryRanges
= dc
->num_mem
;
829 append(dc
, &mdMemList
.NumberOfMemoryRanges
,
830 sizeof(mdMemList
.NumberOfMemoryRanges
));
832 sz
= mdMemList
.NumberOfMemoryRanges
* sizeof(mdMem
);
834 sz
+= sizeof(mdMemList
.NumberOfMemoryRanges
);
836 for (i
= 0; i
< dc
->num_mem
; i
++)
838 mdMem
.StartOfMemoryRange
= dc
->mem
[i
].base
;
839 mdMem
.Memory
.Rva
= dc
->rva
;
840 mdMem
.Memory
.DataSize
= dc
->mem
[i
].size
;
841 SetFilePointer(dc
->hFile
, dc
->rva
, NULL
, FILE_BEGIN
);
842 for (pos
= 0; pos
< dc
->mem
[i
].size
; pos
+= sizeof(tmp
))
844 len
= min(dc
->mem
[i
].size
- pos
, sizeof(tmp
));
845 if (ReadProcessMemory(dc
->hProcess
,
846 (void*)(DWORD_PTR
)(dc
->mem
[i
].base
+ pos
),
848 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
850 dc
->rva
+= mdMem
.Memory
.DataSize
;
851 writeat(dc
, rva_base
+ i
* sizeof(mdMem
), &mdMem
, sizeof(mdMem
));
854 writeat(dc
, dc
->mem
[i
].rva
, &mdMem
.Memory
.Rva
, sizeof(mdMem
.Memory
.Rva
));
861 static unsigned dump_misc_info(struct dump_context
* dc
)
863 MINIDUMP_MISC_INFO mmi
;
865 mmi
.SizeOfInfo
= sizeof(mmi
);
866 mmi
.Flags1
= MINIDUMP_MISC1_PROCESS_ID
;
867 mmi
.ProcessId
= dc
->pid
;
868 /* FIXME: create/user/kernel time */
869 mmi
.ProcessCreateTime
= 0;
870 mmi
.ProcessKernelTime
= 0;
871 mmi
.ProcessUserTime
= 0;
873 append(dc
, &mmi
, sizeof(mmi
));
877 /******************************************************************
878 * MiniDumpWriteDump (DEBUGHLP.@)
881 BOOL WINAPI
MiniDumpWriteDump(HANDLE hProcess
, DWORD pid
, HANDLE hFile
,
882 MINIDUMP_TYPE DumpType
,
883 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
,
884 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
,
885 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
)
887 static const MINIDUMP_DIRECTORY emptyDir
= {UnusedStream
, {0, 0}};
888 MINIDUMP_HEADER mdHead
;
889 MINIDUMP_DIRECTORY mdDir
;
890 DWORD i
, nStreams
, idx_stream
;
891 struct dump_context dc
;
893 dc
.hProcess
= hProcess
;
898 dc
.alloc_modules
= 0;
899 dc
.cb
= CallbackParam
;
906 if (!fetch_processes_info(&dc
)) return FALSE
;
907 fetch_modules_info(&dc
);
910 nStreams
= 6 + (ExceptionParam
? 1 : 0) +
911 (UserStreamParam
? UserStreamParam
->UserStreamCount
: 0);
913 /* pad the directory size to a multiple of 4 for alignment purposes */
914 nStreams
= (nStreams
+ 3) & ~3;
916 if (DumpType
& MiniDumpWithDataSegs
)
917 FIXME("NIY MiniDumpWithDataSegs\n");
918 if (DumpType
& MiniDumpWithFullMemory
)
919 FIXME("NIY MiniDumpWithFullMemory\n");
920 if (DumpType
& MiniDumpWithHandleData
)
921 FIXME("NIY MiniDumpWithHandleData\n");
922 if (DumpType
& MiniDumpFilterMemory
)
923 FIXME("NIY MiniDumpFilterMemory\n");
924 if (DumpType
& MiniDumpScanMemory
)
925 FIXME("NIY MiniDumpScanMemory\n");
927 /* 2) write header */
928 mdHead
.Signature
= MINIDUMP_SIGNATURE
;
929 mdHead
.Version
= MINIDUMP_VERSION
; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
930 mdHead
.NumberOfStreams
= nStreams
;
931 mdHead
.CheckSum
= 0; /* native sets a 0 checksum in its files */
932 mdHead
.StreamDirectoryRva
= sizeof(mdHead
);
933 mdHead
.u
.TimeDateStamp
= time(NULL
);
934 mdHead
.Flags
= DumpType
;
935 append(&dc
, &mdHead
, sizeof(mdHead
));
937 /* 3) write stream directories */
938 dc
.rva
+= nStreams
* sizeof(mdDir
);
941 /* 3.1) write data stream directories */
943 /* must be first in minidump */
944 mdDir
.StreamType
= SystemInfoStream
;
945 mdDir
.Location
.Rva
= dc
.rva
;
946 mdDir
.Location
.DataSize
= dump_system_info(&dc
);
947 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
948 &mdDir
, sizeof(mdDir
));
950 mdDir
.StreamType
= ThreadListStream
;
951 mdDir
.Location
.Rva
= dc
.rva
;
952 mdDir
.Location
.DataSize
= dump_threads(&dc
, ExceptionParam
);
953 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
954 &mdDir
, sizeof(mdDir
));
956 mdDir
.StreamType
= ModuleListStream
;
957 mdDir
.Location
.Rva
= dc
.rva
;
958 mdDir
.Location
.DataSize
= dump_modules(&dc
, FALSE
);
959 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
960 &mdDir
, sizeof(mdDir
));
962 mdDir
.StreamType
= 0xfff0; /* FIXME: this is part of MS reserved streams */
963 mdDir
.Location
.Rva
= dc
.rva
;
964 mdDir
.Location
.DataSize
= dump_modules(&dc
, TRUE
);
965 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
966 &mdDir
, sizeof(mdDir
));
968 mdDir
.StreamType
= MemoryListStream
;
969 mdDir
.Location
.Rva
= dc
.rva
;
970 mdDir
.Location
.DataSize
= dump_memory_info(&dc
);
971 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
972 &mdDir
, sizeof(mdDir
));
974 mdDir
.StreamType
= MiscInfoStream
;
975 mdDir
.Location
.Rva
= dc
.rva
;
976 mdDir
.Location
.DataSize
= dump_misc_info(&dc
);
977 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
978 &mdDir
, sizeof(mdDir
));
980 /* 3.2) write exception information (if any) */
983 mdDir
.StreamType
= ExceptionStream
;
984 mdDir
.Location
.Rva
= dc
.rva
;
985 mdDir
.Location
.DataSize
= dump_exception_info(&dc
, ExceptionParam
);
986 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
987 &mdDir
, sizeof(mdDir
));
990 /* 3.3) write user defined streams (if any) */
993 for (i
= 0; i
< UserStreamParam
->UserStreamCount
; i
++)
995 mdDir
.StreamType
= UserStreamParam
->UserStreamArray
[i
].Type
;
996 mdDir
.Location
.DataSize
= UserStreamParam
->UserStreamArray
[i
].BufferSize
;
997 mdDir
.Location
.Rva
= dc
.rva
;
998 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
999 &mdDir
, sizeof(mdDir
));
1000 append(&dc
, UserStreamParam
->UserStreamArray
[i
].Buffer
,
1001 UserStreamParam
->UserStreamArray
[i
].BufferSize
);
1005 /* fill the remaining directory entries with 0's (unused stream types) */
1006 /* NOTE: this should always come last in the dump! */
1007 for (i
= idx_stream
; i
< nStreams
; i
++)
1008 writeat(&dc
, mdHead
.StreamDirectoryRva
+ i
* sizeof(emptyDir
), &emptyDir
, sizeof(emptyDir
));
1010 HeapFree(GetProcessHeap(), 0, dc
.pcs_buffer
);
1011 HeapFree(GetProcessHeap(), 0, dc
.mem
);
1012 HeapFree(GetProcessHeap(), 0, dc
.modules
);
1017 /******************************************************************
1018 * MiniDumpReadDumpStream (DEBUGHLP.@)
1022 BOOL WINAPI
MiniDumpReadDumpStream(PVOID base
, ULONG str_idx
,
1023 PMINIDUMP_DIRECTORY
* pdir
,
1024 PVOID
* stream
, ULONG
* size
)
1026 MINIDUMP_HEADER
* mdHead
= base
;
1028 if (mdHead
->Signature
== MINIDUMP_SIGNATURE
)
1030 MINIDUMP_DIRECTORY
* dir
;
1033 dir
= (MINIDUMP_DIRECTORY
*)((char*)base
+ mdHead
->StreamDirectoryRva
);
1034 for (i
= 0; i
< mdHead
->NumberOfStreams
; i
++, dir
++)
1036 if (dir
->StreamType
== str_idx
)
1038 if (pdir
) *pdir
= dir
;
1039 if (stream
) *stream
= (char*)base
+ dir
->Location
.Rva
;
1040 if (size
) *size
= dir
->Location
.DataSize
;
1045 SetLastError(ERROR_INVALID_PARAMETER
);