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 /* exception information */
63 /* output information */
67 struct dump_memory
* mem
;
69 /* callback information */
70 MINIDUMP_CALLBACK_INFORMATION
* cb
;
73 /******************************************************************
74 * fetch_processes_info
76 * reads system wide process information, and make spi point to the record
77 * for process of id 'pid'
79 static BOOL
fetch_processes_info(struct dump_context
* dc
)
81 ULONG buf_size
= 0x1000;
84 dc
->pcs_buffer
= NULL
;
85 if (!(dc
->pcs_buffer
= HeapAlloc(GetProcessHeap(), 0, buf_size
))) return FALSE
;
88 nts
= NtQuerySystemInformation(SystemProcessInformation
,
89 dc
->pcs_buffer
, buf_size
, NULL
);
90 if (nts
!= STATUS_INFO_LENGTH_MISMATCH
) break;
91 dc
->pcs_buffer
= HeapReAlloc(GetProcessHeap(), 0, dc
->pcs_buffer
,
93 if (!dc
->pcs_buffer
) return FALSE
;
96 if (nts
== STATUS_SUCCESS
)
98 dc
->spi
= dc
->pcs_buffer
;
101 if (HandleToUlong(dc
->spi
->UniqueProcessId
) == dc
->pid
) return TRUE
;
102 if (!dc
->spi
->NextEntryOffset
) break;
103 dc
->spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)dc
->spi
+ dc
->spi
->NextEntryOffset
);
106 HeapFree(GetProcessHeap(), 0, dc
->pcs_buffer
);
107 dc
->pcs_buffer
= NULL
;
112 static void fetch_thread_stack(struct dump_context
* dc
, const void* teb_addr
,
113 const CONTEXT
* ctx
, MINIDUMP_MEMORY_DESCRIPTOR
* mmd
)
117 if (ReadProcessMemory(dc
->hProcess
, teb_addr
, &tib
, sizeof(tib
), NULL
))
120 /* limiting the stack dumping to the size actually used */
123 /* make sure ESP is within the established range of the stack. It could have
124 been clobbered by whatever caused the original exception. */
125 if (ctx
->Esp
- 4 < (ULONG_PTR
)tib
.StackLimit
|| ctx
->Esp
- 4 > (ULONG_PTR
)tib
.StackBase
)
126 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
129 mmd
->StartOfMemoryRange
= (ctx
->Esp
- 4);
133 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
135 #elif defined(__powerpc__)
138 /* make sure IAR is within the established range of the stack. It could have
139 been clobbered by whatever caused the original exception. */
140 if (ctx
->Iar
- 4 < (ULONG_PTR
)tib
.StackLimit
|| ctx
->Iar
- 4 > (ULONG_PTR
)tib
.StackBase
)
141 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
144 mmd
->StartOfMemoryRange
= (ctx
->Iar
- 4);
148 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
150 #elif defined(__x86_64__)
153 /* make sure RSP is within the established range of the stack. It could have
154 been clobbered by whatever caused the original exception. */
155 if (ctx
->Rsp
- 8 < (ULONG_PTR
)tib
.StackLimit
|| ctx
->Rsp
- 8 > (ULONG_PTR
)tib
.StackBase
)
156 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
159 mmd
->StartOfMemoryRange
= (ctx
->Rsp
- 8);
163 mmd
->StartOfMemoryRange
= (ULONG_PTR
)tib
.StackLimit
;
166 #error unsupported CPU
168 mmd
->Memory
.DataSize
= (ULONG_PTR
)tib
.StackBase
- mmd
->StartOfMemoryRange
;
172 /******************************************************************
175 * fetches some information about thread of id 'tid'
177 static BOOL
fetch_thread_info(struct dump_context
* dc
, int thd_idx
,
178 const MINIDUMP_EXCEPTION_INFORMATION
* except
,
179 MINIDUMP_THREAD
* mdThd
, CONTEXT
* ctx
)
181 DWORD tid
= HandleToUlong(dc
->spi
->ti
[thd_idx
].ClientId
.UniqueThread
);
183 THREAD_BASIC_INFORMATION tbi
;
185 memset(ctx
, 0, sizeof(*ctx
));
187 mdThd
->ThreadId
= tid
;
188 mdThd
->SuspendCount
= 0;
190 mdThd
->Stack
.StartOfMemoryRange
= 0;
191 mdThd
->Stack
.Memory
.DataSize
= 0;
192 mdThd
->Stack
.Memory
.Rva
= 0;
193 mdThd
->ThreadContext
.DataSize
= 0;
194 mdThd
->ThreadContext
.Rva
= 0;
195 mdThd
->PriorityClass
= dc
->spi
->ti
[thd_idx
].dwBasePriority
; /* FIXME */
196 mdThd
->Priority
= dc
->spi
->ti
[thd_idx
].dwCurrentPriority
;
198 if ((hThread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, tid
)) == NULL
)
200 FIXME("Couldn't open thread %u (%u)\n", tid
, GetLastError());
204 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
,
205 &tbi
, sizeof(tbi
), NULL
) == STATUS_SUCCESS
)
207 mdThd
->Teb
= (ULONG_PTR
)tbi
.TebBaseAddress
;
208 if (tbi
.ExitStatus
== STILL_ACTIVE
)
210 if (tid
!= GetCurrentThreadId() &&
211 (mdThd
->SuspendCount
= SuspendThread(hThread
)) != (DWORD
)-1)
213 ctx
->ContextFlags
= CONTEXT_FULL
;
214 if (!GetThreadContext(hThread
, ctx
))
215 memset(ctx
, 0, sizeof(*ctx
));
217 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, ctx
, &mdThd
->Stack
);
218 ResumeThread(hThread
);
220 else if (tid
== GetCurrentThreadId() && except
)
223 mdThd
->SuspendCount
= 1;
224 if (except
->ClientPointers
)
226 EXCEPTION_POINTERS ep
;
228 ReadProcessMemory(dc
->hProcess
, except
->ExceptionPointers
,
229 &ep
, sizeof(ep
), NULL
);
230 ReadProcessMemory(dc
->hProcess
, ep
.ContextRecord
,
231 &ctx
, sizeof(ctx
), NULL
);
234 else pctx
= except
->ExceptionPointers
->ContextRecord
;
237 fetch_thread_stack(dc
, tbi
.TebBaseAddress
, pctx
, &mdThd
->Stack
);
239 else mdThd
->SuspendCount
= 0;
242 CloseHandle(hThread
);
246 /******************************************************************
249 * Add a module to a dump context
251 static BOOL
add_module(struct dump_context
* dc
, const WCHAR
* name
,
252 DWORD base
, DWORD size
, DWORD timestamp
, DWORD checksum
,
256 dc
->modules
= HeapAlloc(GetProcessHeap(), 0,
257 ++dc
->num_modules
* sizeof(*dc
->modules
));
259 dc
->modules
= HeapReAlloc(GetProcessHeap(), 0, dc
->modules
,
260 ++dc
->num_modules
* sizeof(*dc
->modules
));
261 if (!dc
->modules
) return FALSE
;
263 !GetModuleFileNameExW(dc
->hProcess
, (HMODULE
)base
,
264 dc
->modules
[dc
->num_modules
- 1].name
,
265 sizeof(dc
->modules
[dc
->num_modules
- 1].name
) / sizeof(WCHAR
)))
266 lstrcpynW(dc
->modules
[dc
->num_modules
- 1].name
, name
,
267 sizeof(dc
->modules
[dc
->num_modules
- 1].name
) / sizeof(WCHAR
));
268 dc
->modules
[dc
->num_modules
- 1].base
= base
;
269 dc
->modules
[dc
->num_modules
- 1].size
= size
;
270 dc
->modules
[dc
->num_modules
- 1].timestamp
= timestamp
;
271 dc
->modules
[dc
->num_modules
- 1].checksum
= checksum
;
272 dc
->modules
[dc
->num_modules
- 1].is_elf
= is_elf
;
277 /******************************************************************
278 * fetch_pe_module_info_cb
280 * Callback for accumulating in dump_context a PE modules set
282 static BOOL WINAPI
fetch_pe_module_info_cb(PCWSTR name
, DWORD64 base
, ULONG size
,
285 struct dump_context
* dc
= user
;
286 IMAGE_NT_HEADERS nth
;
288 if (!validate_addr64(base
)) return FALSE
;
290 if (pe_load_nt_header(dc
->hProcess
, base
, &nth
))
291 add_module(user
, name
, base
, size
,
292 nth
.FileHeader
.TimeDateStamp
, nth
.OptionalHeader
.CheckSum
,
297 /******************************************************************
298 * fetch_elf_module_info_cb
300 * Callback for accumulating in dump_context an ELF modules set
302 static BOOL
fetch_elf_module_info_cb(const WCHAR
* name
, unsigned long base
,
305 struct dump_context
* dc
= user
;
306 DWORD rbase
, size
, checksum
;
308 /* FIXME: there's no relevant timestamp on ELF modules */
309 /* NB: if we have a non-null base from the live-target use it (whenever
310 * the ELF module is relocatable or not). If we have a null base (ELF
311 * module isn't relocatable) then grab its base address from ELF file
313 if (!elf_fetch_file_info(name
, &rbase
, &size
, &checksum
))
315 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
319 /******************************************************************
320 * fetch_macho_module_info_cb
322 * Callback for accumulating in dump_context a Mach-O modules set
324 static BOOL
fetch_macho_module_info_cb(const WCHAR
* name
, unsigned long base
,
327 struct dump_context
* dc
= (struct dump_context
*)user
;
328 DWORD rbase
, size
, checksum
;
330 /* FIXME: there's no relevant timestamp on Mach-O modules */
331 /* NB: if we have a non-null base from the live-target use it. If we have
332 * a null base, then grab its base address from Mach-O file.
334 if (!macho_fetch_file_info(name
, &rbase
, &size
, &checksum
))
336 add_module(dc
, name
, base
? base
: rbase
, size
, 0 /* FIXME */, checksum
, TRUE
);
340 static void fetch_modules_info(struct dump_context
* dc
)
342 EnumerateLoadedModulesW64(dc
->hProcess
, fetch_pe_module_info_cb
, dc
);
343 /* Since we include ELF modules in a separate stream from the regular PE ones,
344 * we can always include those ELF modules (they don't eat lots of space)
345 * And it's always a good idea to have a trace of the loaded ELF modules for
346 * a given application in a post mortem debugging condition.
348 elf_enum_modules(dc
->hProcess
, fetch_elf_module_info_cb
, dc
);
349 macho_enum_modules(dc
->hProcess
, fetch_macho_module_info_cb
, dc
);
352 static void fetch_module_versioninfo(LPCWSTR filename
, VS_FIXEDFILEINFO
* ffi
)
356 static const WCHAR backslashW
[] = {'\\', '\0'};
358 memset(ffi
, 0, sizeof(*ffi
));
359 if ((sz
= GetFileVersionInfoSizeW(filename
, &handle
)))
361 void* info
= HeapAlloc(GetProcessHeap(), 0, sz
);
362 if (info
&& GetFileVersionInfoW(filename
, handle
, sz
, info
))
364 VS_FIXEDFILEINFO
* ptr
;
367 if (VerQueryValueW(info
, backslashW
, (void*)&ptr
, &len
))
368 memcpy(ffi
, ptr
, min(len
, sizeof(*ffi
)));
370 HeapFree(GetProcessHeap(), 0, info
);
374 /******************************************************************
377 * Add a memory block to be dumped in a minidump
378 * If rva is non 0, it's the rva in the minidump where has to be stored
379 * also the rva of the memory block when written (this allows to reference
380 * a memory block from outside the list of memory blocks).
382 static void add_memory_block(struct dump_context
* dc
, ULONG64 base
, ULONG size
, ULONG rva
)
385 dc
->mem
= HeapReAlloc(GetProcessHeap(), 0, dc
->mem
,
386 ++dc
->num_mem
* sizeof(*dc
->mem
));
388 dc
->mem
= HeapAlloc(GetProcessHeap(), 0, ++dc
->num_mem
* sizeof(*dc
->mem
));
391 dc
->mem
[dc
->num_mem
- 1].base
= base
;
392 dc
->mem
[dc
->num_mem
- 1].size
= size
;
393 dc
->mem
[dc
->num_mem
- 1].rva
= rva
;
395 else dc
->num_mem
= 0;
398 /******************************************************************
401 * Writes a chunk of data at a given position in the minidump
403 static void writeat(struct dump_context
* dc
, RVA rva
, const void* data
, unsigned size
)
407 SetFilePointer(dc
->hFile
, rva
, NULL
, FILE_BEGIN
);
408 WriteFile(dc
->hFile
, data
, size
, &written
, NULL
);
411 /******************************************************************
414 * writes a new chunk of data to the minidump, increasing the current
417 static void append(struct dump_context
* dc
, void* data
, unsigned size
)
419 writeat(dc
, dc
->rva
, data
, size
);
423 /******************************************************************
424 * dump_exception_info
426 * Write in File the exception information from pcs
428 static unsigned dump_exception_info(struct dump_context
* dc
,
429 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
431 MINIDUMP_EXCEPTION_STREAM mdExcpt
;
432 EXCEPTION_RECORD rec
, *prec
;
436 mdExcpt
.ThreadId
= except
->ThreadId
;
437 mdExcpt
.__alignment
= 0;
438 if (except
->ClientPointers
)
440 EXCEPTION_POINTERS ep
;
442 ReadProcessMemory(dc
->hProcess
,
443 except
->ExceptionPointers
, &ep
, sizeof(ep
), NULL
);
444 ReadProcessMemory(dc
->hProcess
,
445 ep
.ExceptionRecord
, &rec
, sizeof(rec
), NULL
);
446 ReadProcessMemory(dc
->hProcess
,
447 ep
.ContextRecord
, &ctx
, sizeof(ctx
), NULL
);
453 prec
= except
->ExceptionPointers
->ExceptionRecord
;
454 pctx
= except
->ExceptionPointers
->ContextRecord
;
456 mdExcpt
.ExceptionRecord
.ExceptionCode
= prec
->ExceptionCode
;
457 mdExcpt
.ExceptionRecord
.ExceptionFlags
= prec
->ExceptionFlags
;
458 mdExcpt
.ExceptionRecord
.ExceptionRecord
= (DWORD_PTR
)prec
->ExceptionRecord
;
459 mdExcpt
.ExceptionRecord
.ExceptionAddress
= (DWORD_PTR
)prec
->ExceptionAddress
;
460 mdExcpt
.ExceptionRecord
.NumberParameters
= prec
->NumberParameters
;
461 mdExcpt
.ExceptionRecord
.__unusedAlignment
= 0;
462 for (i
= 0; i
< mdExcpt
.ExceptionRecord
.NumberParameters
; i
++)
463 mdExcpt
.ExceptionRecord
.ExceptionInformation
[i
] = prec
->ExceptionInformation
[i
];
464 mdExcpt
.ThreadContext
.DataSize
= sizeof(*pctx
);
465 mdExcpt
.ThreadContext
.Rva
= dc
->rva
+ sizeof(mdExcpt
);
467 append(dc
, &mdExcpt
, sizeof(mdExcpt
));
468 append(dc
, pctx
, sizeof(*pctx
));
469 return sizeof(mdExcpt
);
472 /******************************************************************
475 * Write in File the modules from pcs
477 static unsigned dump_modules(struct dump_context
* dc
, BOOL dump_elf
)
479 MINIDUMP_MODULE mdModule
;
480 MINIDUMP_MODULE_LIST mdModuleList
;
482 MINIDUMP_STRING
* ms
= (MINIDUMP_STRING
*)tmp
;
488 for (i
= nmod
= 0; i
< dc
->num_modules
; i
++)
490 if ((dc
->modules
[i
].is_elf
&& dump_elf
) ||
491 (!dc
->modules
[i
].is_elf
&& !dump_elf
))
495 mdModuleList
.NumberOfModules
= 0;
496 /* reserve space for mdModuleList
497 * FIXME: since we don't support 0 length arrays, we cannot use the
498 * size of mdModuleList
499 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
502 /* the stream size is just the size of the module index. It does not include the data for the
503 names of each module. *Technically* the names are supposed to go into the common string table
504 in the minidump file. Since each string is referenced by RVA they can all safely be located
505 anywhere between streams in the file, so the end of this stream is sufficient. */
507 dc
->rva
+= sz
= sizeof(mdModuleList
.NumberOfModules
) + sizeof(mdModule
) * nmod
;
508 for (i
= 0; i
< dc
->num_modules
; i
++)
510 if ((dc
->modules
[i
].is_elf
&& !dump_elf
) ||
511 (!dc
->modules
[i
].is_elf
&& dump_elf
))
514 flags_out
= ModuleWriteModule
| ModuleWriteMiscRecord
| ModuleWriteCvRecord
;
515 if (dc
->type
& MiniDumpWithDataSegs
)
516 flags_out
|= ModuleWriteDataSeg
;
517 if (dc
->type
& MiniDumpWithProcessThreadData
)
518 flags_out
|= ModuleWriteTlsData
;
519 if (dc
->type
& MiniDumpWithCodeSegs
)
520 flags_out
|= ModuleWriteCodeSegs
;
521 ms
->Length
= (lstrlenW(dc
->modules
[i
].name
) + 1) * sizeof(WCHAR
);
522 if (sizeof(ULONG
) + ms
->Length
> sizeof(tmp
))
523 FIXME("Buffer overflow!!!\n");
524 lstrcpyW(ms
->Buffer
, dc
->modules
[i
].name
);
528 MINIDUMP_CALLBACK_INPUT cbin
;
529 MINIDUMP_CALLBACK_OUTPUT cbout
;
531 cbin
.ProcessId
= dc
->pid
;
532 cbin
.ProcessHandle
= dc
->hProcess
;
533 cbin
.CallbackType
= ModuleCallback
;
535 cbin
.u
.Module
.FullPath
= ms
->Buffer
;
536 cbin
.u
.Module
.BaseOfImage
= dc
->modules
[i
].base
;
537 cbin
.u
.Module
.SizeOfImage
= dc
->modules
[i
].size
;
538 cbin
.u
.Module
.CheckSum
= dc
->modules
[i
].checksum
;
539 cbin
.u
.Module
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
540 memset(&cbin
.u
.Module
.VersionInfo
, 0, sizeof(cbin
.u
.Module
.VersionInfo
));
541 cbin
.u
.Module
.CvRecord
= NULL
;
542 cbin
.u
.Module
.SizeOfCvRecord
= 0;
543 cbin
.u
.Module
.MiscRecord
= NULL
;
544 cbin
.u
.Module
.SizeOfMiscRecord
= 0;
546 cbout
.u
.ModuleWriteFlags
= flags_out
;
547 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
549 flags_out
&= cbout
.u
.ModuleWriteFlags
;
551 if (flags_out
& ModuleWriteModule
)
553 mdModule
.BaseOfImage
= dc
->modules
[i
].base
;
554 mdModule
.SizeOfImage
= dc
->modules
[i
].size
;
555 mdModule
.CheckSum
= dc
->modules
[i
].checksum
;
556 mdModule
.TimeDateStamp
= dc
->modules
[i
].timestamp
;
557 mdModule
.ModuleNameRva
= dc
->rva
;
558 ms
->Length
-= sizeof(WCHAR
);
559 append(dc
, ms
, sizeof(ULONG
) + ms
->Length
+ sizeof(WCHAR
));
560 fetch_module_versioninfo(ms
->Buffer
, &mdModule
.VersionInfo
);
561 mdModule
.CvRecord
.DataSize
= 0; /* FIXME */
562 mdModule
.CvRecord
.Rva
= 0; /* FIXME */
563 mdModule
.MiscRecord
.DataSize
= 0; /* FIXME */
564 mdModule
.MiscRecord
.Rva
= 0; /* FIXME */
565 mdModule
.Reserved0
= 0; /* FIXME */
566 mdModule
.Reserved1
= 0; /* FIXME */
568 rva_base
+ sizeof(mdModuleList
.NumberOfModules
) +
569 mdModuleList
.NumberOfModules
++ * sizeof(mdModule
),
570 &mdModule
, sizeof(mdModule
));
573 writeat(dc
, rva_base
, &mdModuleList
.NumberOfModules
,
574 sizeof(mdModuleList
.NumberOfModules
));
579 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
580 * We are compiled with -fPIC, so we can't clobber ebx.
582 static inline void do_x86cpuid(unsigned int ax
, unsigned int *p
)
584 #if defined(__GNUC__) && defined(__i386__)
585 __asm__("pushl %%ebx\n\t"
587 "movl %%ebx, %%esi\n\t"
589 : "=a" (p
[0]), "=S" (p
[1]), "=c" (p
[2]), "=d" (p
[3])
594 /* From xf86info havecpuid.c 1.11 */
595 static inline int have_x86cpuid(void)
597 #if defined(__GNUC__) && defined(__i386__)
609 : "=&r" (f1
), "=&r" (f2
)
610 : "ir" (0x00200000));
611 return ((f1
^f2
) & 0x00200000) != 0;
617 /******************************************************************
620 * Dumps into File the information about the system
622 static unsigned dump_system_info(struct dump_context
* dc
)
624 MINIDUMP_SYSTEM_INFO mdSysInfo
;
626 OSVERSIONINFOW osInfo
;
630 GetSystemInfo(&sysInfo
);
631 osInfo
.dwOSVersionInfoSize
= sizeof(osInfo
);
632 GetVersionExW(&osInfo
);
634 mdSysInfo
.ProcessorArchitecture
= sysInfo
.u
.s
.wProcessorArchitecture
;
635 mdSysInfo
.ProcessorLevel
= sysInfo
.wProcessorLevel
;
636 mdSysInfo
.ProcessorRevision
= sysInfo
.wProcessorRevision
;
637 mdSysInfo
.u
.s
.NumberOfProcessors
= sysInfo
.dwNumberOfProcessors
;
638 mdSysInfo
.u
.s
.ProductType
= VER_NT_WORKSTATION
; /* FIXME */
639 mdSysInfo
.MajorVersion
= osInfo
.dwMajorVersion
;
640 mdSysInfo
.MinorVersion
= osInfo
.dwMinorVersion
;
641 mdSysInfo
.BuildNumber
= osInfo
.dwBuildNumber
;
642 mdSysInfo
.PlatformId
= osInfo
.dwPlatformId
;
644 mdSysInfo
.CSDVersionRva
= dc
->rva
+ sizeof(mdSysInfo
);
645 mdSysInfo
.u1
.Reserved1
= 0;
646 mdSysInfo
.u1
.s
.SuiteMask
= VER_SUITE_TERMINAL
;
650 unsigned regs0
[4], regs1
[4];
652 do_x86cpuid(0, regs0
);
653 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[0] = regs0
[1];
654 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[1] = regs0
[2];
655 mdSysInfo
.Cpu
.X86CpuInfo
.VendorId
[2] = regs0
[3];
656 do_x86cpuid(1, regs1
);
657 mdSysInfo
.Cpu
.X86CpuInfo
.VersionInformation
= regs1
[0];
658 mdSysInfo
.Cpu
.X86CpuInfo
.FeatureInformation
= regs1
[3];
659 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= 0;
660 if (regs0
[1] == 0x68747541 /* "Auth" */ &&
661 regs0
[3] == 0x69746e65 /* "enti" */ &&
662 regs0
[2] == 0x444d4163 /* "cAMD" */)
664 do_x86cpuid(0x80000000, regs1
); /* get vendor cpuid level */
665 if (regs1
[0] >= 0x80000001)
667 do_x86cpuid(0x80000001, regs1
); /* get vendor features */
668 mdSysInfo
.Cpu
.X86CpuInfo
.AMDExtendedCpuFeatures
= regs1
[3];
677 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] = 0;
678 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[1] = 0;
680 for (i
= 0; i
< sizeof(mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0]) * 8; i
++)
681 if (IsProcessorFeaturePresent(i
))
682 mdSysInfo
.Cpu
.OtherCpuInfo
.ProcessorFeatures
[0] |= one
<< i
;
684 append(dc
, &mdSysInfo
, sizeof(mdSysInfo
));
686 /* write the service pack version string after this stream. It is referenced within the
687 stream by its RVA in the file. */
688 slen
= lstrlenW(osInfo
.szCSDVersion
) * sizeof(WCHAR
);
689 WriteFile(dc
->hFile
, &slen
, sizeof(slen
), &written
, NULL
);
690 WriteFile(dc
->hFile
, osInfo
.szCSDVersion
, slen
, &written
, NULL
);
691 dc
->rva
+= sizeof(ULONG
) + slen
;
693 return sizeof(mdSysInfo
);
696 /******************************************************************
699 * Dumps into File the information about running threads
701 static unsigned dump_threads(struct dump_context
* dc
,
702 const MINIDUMP_EXCEPTION_INFORMATION
* except
)
704 MINIDUMP_THREAD mdThd
;
705 MINIDUMP_THREAD_LIST mdThdList
;
711 mdThdList
.NumberOfThreads
= 0;
714 dc
->rva
+= sizeof(mdThdList
.NumberOfThreads
) +
715 dc
->spi
->dwThreadCount
* sizeof(mdThd
);
717 for (i
= 0; i
< dc
->spi
->dwThreadCount
; i
++)
719 fetch_thread_info(dc
, i
, except
, &mdThd
, &ctx
);
721 flags_out
= ThreadWriteThread
| ThreadWriteStack
| ThreadWriteContext
|
722 ThreadWriteInstructionWindow
;
723 if (dc
->type
& MiniDumpWithProcessThreadData
)
724 flags_out
|= ThreadWriteThreadData
;
725 if (dc
->type
& MiniDumpWithThreadInfo
)
726 flags_out
|= ThreadWriteThreadInfo
;
730 MINIDUMP_CALLBACK_INPUT cbin
;
731 MINIDUMP_CALLBACK_OUTPUT cbout
;
733 cbin
.ProcessId
= dc
->pid
;
734 cbin
.ProcessHandle
= dc
->hProcess
;
735 cbin
.CallbackType
= ThreadCallback
;
736 cbin
.u
.Thread
.ThreadId
= HandleToUlong(dc
->spi
->ti
[i
].ClientId
.UniqueThread
);
737 cbin
.u
.Thread
.ThreadHandle
= 0; /* FIXME */
738 cbin
.u
.Thread
.Context
= ctx
;
739 cbin
.u
.Thread
.SizeOfContext
= sizeof(CONTEXT
);
740 cbin
.u
.Thread
.StackBase
= mdThd
.Stack
.StartOfMemoryRange
;
741 cbin
.u
.Thread
.StackEnd
= mdThd
.Stack
.StartOfMemoryRange
+
742 mdThd
.Stack
.Memory
.DataSize
;
744 cbout
.u
.ThreadWriteFlags
= flags_out
;
745 if (!dc
->cb
->CallbackRoutine(dc
->cb
->CallbackParam
, &cbin
, &cbout
))
747 flags_out
&= cbout
.u
.ThreadWriteFlags
;
749 if (flags_out
& ThreadWriteThread
)
751 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteContext
))
753 mdThd
.ThreadContext
.Rva
= dc
->rva
;
754 mdThd
.ThreadContext
.DataSize
= sizeof(CONTEXT
);
755 append(dc
, &ctx
, sizeof(CONTEXT
));
757 if (mdThd
.Stack
.Memory
.DataSize
&& (flags_out
& ThreadWriteStack
))
759 add_memory_block(dc
, mdThd
.Stack
.StartOfMemoryRange
,
760 mdThd
.Stack
.Memory
.DataSize
,
761 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
762 mdThdList
.NumberOfThreads
* sizeof(mdThd
) +
763 FIELD_OFFSET(MINIDUMP_THREAD
, Stack
.Memory
.Rva
));
766 rva_base
+ sizeof(mdThdList
.NumberOfThreads
) +
767 mdThdList
.NumberOfThreads
* sizeof(mdThd
),
768 &mdThd
, sizeof(mdThd
));
769 mdThdList
.NumberOfThreads
++;
771 if (ctx
.ContextFlags
&& (flags_out
& ThreadWriteInstructionWindow
))
773 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
774 * - also crop values across module boundaries,
775 * - and don't make it i386 dependent
777 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
780 writeat(dc
, rva_base
,
781 &mdThdList
.NumberOfThreads
, sizeof(mdThdList
.NumberOfThreads
));
783 return dc
->rva
- rva_base
;
786 /******************************************************************
789 * dumps information about the memory of the process (stack of the threads)
791 static unsigned dump_memory_info(struct dump_context
* dc
)
793 MINIDUMP_MEMORY_LIST mdMemList
;
794 MINIDUMP_MEMORY_DESCRIPTOR mdMem
;
796 unsigned i
, pos
, len
, sz
;
800 mdMemList
.NumberOfMemoryRanges
= dc
->num_mem
;
801 append(dc
, &mdMemList
.NumberOfMemoryRanges
,
802 sizeof(mdMemList
.NumberOfMemoryRanges
));
804 sz
= mdMemList
.NumberOfMemoryRanges
* sizeof(mdMem
);
806 sz
+= sizeof(mdMemList
.NumberOfMemoryRanges
);
808 for (i
= 0; i
< dc
->num_mem
; i
++)
810 mdMem
.StartOfMemoryRange
= dc
->mem
[i
].base
;
811 mdMem
.Memory
.Rva
= dc
->rva
;
812 mdMem
.Memory
.DataSize
= dc
->mem
[i
].size
;
813 SetFilePointer(dc
->hFile
, dc
->rva
, NULL
, FILE_BEGIN
);
814 for (pos
= 0; pos
< dc
->mem
[i
].size
; pos
+= sizeof(tmp
))
816 len
= min(dc
->mem
[i
].size
- pos
, sizeof(tmp
));
817 if (ReadProcessMemory(dc
->hProcess
,
818 (void*)(dc
->mem
[i
].base
+ pos
),
820 WriteFile(dc
->hFile
, tmp
, len
, &written
, NULL
);
822 dc
->rva
+= mdMem
.Memory
.DataSize
;
823 writeat(dc
, rva_base
+ i
* sizeof(mdMem
), &mdMem
, sizeof(mdMem
));
826 writeat(dc
, dc
->mem
[i
].rva
, &mdMem
.Memory
.Rva
, sizeof(mdMem
.Memory
.Rva
));
833 static unsigned dump_misc_info(struct dump_context
* dc
)
835 MINIDUMP_MISC_INFO mmi
;
837 mmi
.SizeOfInfo
= sizeof(mmi
);
838 mmi
.Flags1
= MINIDUMP_MISC1_PROCESS_ID
;
839 mmi
.ProcessId
= dc
->pid
;
840 /* FIXME: create/user/kernel time */
841 mmi
.ProcessCreateTime
= 0;
842 mmi
.ProcessKernelTime
= 0;
843 mmi
.ProcessUserTime
= 0;
845 append(dc
, &mmi
, sizeof(mmi
));
849 /******************************************************************
850 * MiniDumpWriteDump (DEBUGHLP.@)
853 BOOL WINAPI
MiniDumpWriteDump(HANDLE hProcess
, DWORD pid
, HANDLE hFile
,
854 MINIDUMP_TYPE DumpType
,
855 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam
,
856 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam
,
857 PMINIDUMP_CALLBACK_INFORMATION CallbackParam
)
859 static const MINIDUMP_DIRECTORY emptyDir
= {UnusedStream
, {0, 0}};
860 MINIDUMP_HEADER mdHead
;
861 MINIDUMP_DIRECTORY mdDir
;
862 DWORD i
, nStreams
, idx_stream
;
863 struct dump_context dc
;
865 dc
.hProcess
= hProcess
;
870 dc
.cb
= CallbackParam
;
876 if (!fetch_processes_info(&dc
)) return FALSE
;
877 fetch_modules_info(&dc
);
880 nStreams
= 6 + (ExceptionParam
? 1 : 0) +
881 (UserStreamParam
? UserStreamParam
->UserStreamCount
: 0);
883 /* pad the directory size to a multiple of 4 for alignment purposes */
884 nStreams
= (nStreams
+ 3) & ~3;
886 if (DumpType
& MiniDumpWithDataSegs
)
887 FIXME("NIY MiniDumpWithDataSegs\n");
888 if (DumpType
& MiniDumpWithFullMemory
)
889 FIXME("NIY MiniDumpWithFullMemory\n");
890 if (DumpType
& MiniDumpWithHandleData
)
891 FIXME("NIY MiniDumpWithHandleData\n");
892 if (DumpType
& MiniDumpFilterMemory
)
893 FIXME("NIY MiniDumpFilterMemory\n");
894 if (DumpType
& MiniDumpScanMemory
)
895 FIXME("NIY MiniDumpScanMemory\n");
897 /* 2) write header */
898 mdHead
.Signature
= MINIDUMP_SIGNATURE
;
899 mdHead
.Version
= MINIDUMP_VERSION
; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */
900 mdHead
.NumberOfStreams
= nStreams
;
901 mdHead
.CheckSum
= 0; /* native sets a 0 checksum in its files */
902 mdHead
.StreamDirectoryRva
= sizeof(mdHead
);
903 mdHead
.u
.TimeDateStamp
= time(NULL
);
904 mdHead
.Flags
= DumpType
;
905 append(&dc
, &mdHead
, sizeof(mdHead
));
907 /* 3) write stream directories */
908 dc
.rva
+= nStreams
* sizeof(mdDir
);
911 /* 3.1) write data stream directories */
913 /* must be first in minidump */
914 mdDir
.StreamType
= SystemInfoStream
;
915 mdDir
.Location
.Rva
= dc
.rva
;
916 mdDir
.Location
.DataSize
= dump_system_info(&dc
);
917 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
918 &mdDir
, sizeof(mdDir
));
920 mdDir
.StreamType
= ThreadListStream
;
921 mdDir
.Location
.Rva
= dc
.rva
;
922 mdDir
.Location
.DataSize
= dump_threads(&dc
, ExceptionParam
);
923 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
924 &mdDir
, sizeof(mdDir
));
926 mdDir
.StreamType
= ModuleListStream
;
927 mdDir
.Location
.Rva
= dc
.rva
;
928 mdDir
.Location
.DataSize
= dump_modules(&dc
, FALSE
);
929 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
930 &mdDir
, sizeof(mdDir
));
932 mdDir
.StreamType
= 0xfff0; /* FIXME: this is part of MS reserved streams */
933 mdDir
.Location
.Rva
= dc
.rva
;
934 mdDir
.Location
.DataSize
= dump_modules(&dc
, TRUE
);
935 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
936 &mdDir
, sizeof(mdDir
));
938 mdDir
.StreamType
= MemoryListStream
;
939 mdDir
.Location
.Rva
= dc
.rva
;
940 mdDir
.Location
.DataSize
= dump_memory_info(&dc
);
941 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
942 &mdDir
, sizeof(mdDir
));
944 mdDir
.StreamType
= MiscInfoStream
;
945 mdDir
.Location
.Rva
= dc
.rva
;
946 mdDir
.Location
.DataSize
= dump_misc_info(&dc
);
947 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
948 &mdDir
, sizeof(mdDir
));
950 /* 3.2) write exception information (if any) */
953 mdDir
.StreamType
= ExceptionStream
;
954 mdDir
.Location
.Rva
= dc
.rva
;
955 mdDir
.Location
.DataSize
= dump_exception_info(&dc
, ExceptionParam
);
956 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
957 &mdDir
, sizeof(mdDir
));
960 /* 3.3) write user defined streams (if any) */
963 for (i
= 0; i
< UserStreamParam
->UserStreamCount
; i
++)
965 mdDir
.StreamType
= UserStreamParam
->UserStreamArray
[i
].Type
;
966 mdDir
.Location
.DataSize
= UserStreamParam
->UserStreamArray
[i
].BufferSize
;
967 mdDir
.Location
.Rva
= dc
.rva
;
968 writeat(&dc
, mdHead
.StreamDirectoryRva
+ idx_stream
++ * sizeof(mdDir
),
969 &mdDir
, sizeof(mdDir
));
970 append(&dc
, UserStreamParam
->UserStreamArray
[i
].Buffer
,
971 UserStreamParam
->UserStreamArray
[i
].BufferSize
);
975 /* fill the remaining directory entries with 0's (unused stream types) */
976 /* NOTE: this should always come last in the dump! */
977 for (i
= idx_stream
; i
< nStreams
; i
++)
978 writeat(&dc
, mdHead
.StreamDirectoryRva
+ i
* sizeof(emptyDir
), &emptyDir
, sizeof(emptyDir
));
980 HeapFree(GetProcessHeap(), 0, dc
.pcs_buffer
);
981 HeapFree(GetProcessHeap(), 0, dc
.mem
);
982 HeapFree(GetProcessHeap(), 0, dc
.modules
);
987 /******************************************************************
988 * MiniDumpReadDumpStream (DEBUGHLP.@)
992 BOOL WINAPI
MiniDumpReadDumpStream(PVOID base
, ULONG str_idx
,
993 PMINIDUMP_DIRECTORY
* pdir
,
994 PVOID
* stream
, ULONG
* size
)
996 MINIDUMP_HEADER
* mdHead
= base
;
998 if (mdHead
->Signature
== MINIDUMP_SIGNATURE
)
1000 MINIDUMP_DIRECTORY
* dir
;
1003 dir
= (MINIDUMP_DIRECTORY
*)((char*)base
+ mdHead
->StreamDirectoryRva
);
1004 for (i
= 0; i
< mdHead
->NumberOfStreams
; i
++, dir
++)
1006 if (dir
->StreamType
== str_idx
)
1008 if (pdir
) *pdir
= dir
;
1009 if (stream
) *stream
= (char*)base
+ dir
->Location
.Rva
;
1010 if (size
) *size
= dir
->Location
.DataSize
;
1015 SetLastError(ERROR_INVALID_PARAMETER
);