Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenminidump.c
Go to the documentation of this file.
00001 /* 00002 * File minidump.c - management of dumps (read & write) 00003 * 00004 * Copyright (C) 2004-2005, Eric Pouech 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00019 */ 00020 00021 #include <time.h> 00022 00023 #define NONAMELESSUNION 00024 #define NONAMELESSSTRUCT 00025 00026 #include "ntstatus.h" 00027 #define WIN32_NO_STATUS 00028 #include "dbghelp_private.h" 00029 #include "winternl.h" 00030 #include "psapi.h" 00031 #include "wine/debug.h" 00032 00033 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); 00034 00035 struct dump_memory 00036 { 00037 ULONG64 base; 00038 ULONG size; 00039 ULONG rva; 00040 }; 00041 00042 struct dump_module 00043 { 00044 unsigned is_elf; 00045 ULONG64 base; 00046 ULONG size; 00047 DWORD timestamp; 00048 DWORD checksum; 00049 WCHAR name[MAX_PATH]; 00050 }; 00051 00052 struct dump_context 00053 { 00054 /* process & thread information */ 00055 HANDLE hProcess; 00056 DWORD pid; 00057 void* pcs_buffer; 00058 SYSTEM_PROCESS_INFORMATION* spi; 00059 /* module information */ 00060 struct dump_module* modules; 00061 unsigned num_modules; 00062 unsigned alloc_modules; 00063 /* exception information */ 00064 /* output information */ 00065 MINIDUMP_TYPE type; 00066 HANDLE hFile; 00067 RVA rva; 00068 struct dump_memory* mem; 00069 unsigned num_mem; 00070 unsigned alloc_mem; 00071 /* callback information */ 00072 MINIDUMP_CALLBACK_INFORMATION* cb; 00073 }; 00074 00075 /****************************************************************** 00076 * fetch_processes_info 00077 * 00078 * reads system wide process information, and make spi point to the record 00079 * for process of id 'pid' 00080 */ 00081 static BOOL fetch_processes_info(struct dump_context* dc) 00082 { 00083 ULONG buf_size = 0x1000; 00084 NTSTATUS nts; 00085 00086 dc->pcs_buffer = NULL; 00087 if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE; 00088 for (;;) 00089 { 00090 nts = NtQuerySystemInformation(SystemProcessInformation, 00091 dc->pcs_buffer, buf_size, NULL); 00092 if (nts != STATUS_INFO_LENGTH_MISMATCH) break; 00093 dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer, 00094 buf_size *= 2); 00095 if (!dc->pcs_buffer) return FALSE; 00096 } 00097 00098 if (nts == STATUS_SUCCESS) 00099 { 00100 dc->spi = dc->pcs_buffer; 00101 for (;;) 00102 { 00103 if (HandleToUlong(dc->spi->UniqueProcessId) == dc->pid) return TRUE; 00104 if (!dc->spi->NextEntryOffset) break; 00105 dc->spi = (SYSTEM_PROCESS_INFORMATION*)((char*)dc->spi + dc->spi->NextEntryOffset); 00106 } 00107 } 00108 HeapFree(GetProcessHeap(), 0, dc->pcs_buffer); 00109 dc->pcs_buffer = NULL; 00110 dc->spi = NULL; 00111 return FALSE; 00112 } 00113 00114 static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr, 00115 const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd) 00116 { 00117 NT_TIB tib; 00118 ADDRESS64 addr; 00119 00120 if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL) && 00121 dbghelp_current_cpu && 00122 dbghelp_current_cpu->get_addr(NULL /* FIXME */, ctx, cpu_addr_stack, &addr) && addr.Mode == AddrModeFlat) 00123 { 00124 if (addr.Offset) 00125 { 00126 addr.Offset -= dbghelp_current_cpu->word_size; 00127 /* make sure stack pointer is within the established range of the stack. It could have 00128 been clobbered by whatever caused the original exception. */ 00129 if (addr.Offset < (ULONG_PTR)tib.StackLimit || addr.Offset > (ULONG_PTR)tib.StackBase) 00130 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 00131 00132 else 00133 mmd->StartOfMemoryRange = addr.Offset; 00134 } 00135 else 00136 mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit; 00137 mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange; 00138 } 00139 } 00140 00141 /****************************************************************** 00142 * fetch_thread_info 00143 * 00144 * fetches some information about thread of id 'tid' 00145 */ 00146 static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx, 00147 const MINIDUMP_EXCEPTION_INFORMATION* except, 00148 MINIDUMP_THREAD* mdThd, CONTEXT* ctx) 00149 { 00150 DWORD tid = HandleToUlong(dc->spi->ti[thd_idx].ClientId.UniqueThread); 00151 HANDLE hThread; 00152 THREAD_BASIC_INFORMATION tbi; 00153 00154 memset(ctx, 0, sizeof(*ctx)); 00155 00156 mdThd->ThreadId = tid; 00157 mdThd->SuspendCount = 0; 00158 mdThd->Teb = 0; 00159 mdThd->Stack.StartOfMemoryRange = 0; 00160 mdThd->Stack.Memory.DataSize = 0; 00161 mdThd->Stack.Memory.Rva = 0; 00162 mdThd->ThreadContext.DataSize = 0; 00163 mdThd->ThreadContext.Rva = 0; 00164 mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */ 00165 mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority; 00166 00167 if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL) 00168 { 00169 FIXME("Couldn't open thread %u (%u)\n", tid, GetLastError()); 00170 return FALSE; 00171 } 00172 00173 if (NtQueryInformationThread(hThread, ThreadBasicInformation, 00174 &tbi, sizeof(tbi), NULL) == STATUS_SUCCESS) 00175 { 00176 mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress; 00177 if (tbi.ExitStatus == STILL_ACTIVE) 00178 { 00179 if (tid != GetCurrentThreadId() && 00180 (mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1) 00181 { 00182 ctx->ContextFlags = CONTEXT_FULL; 00183 if (!GetThreadContext(hThread, ctx)) 00184 memset(ctx, 0, sizeof(*ctx)); 00185 00186 fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack); 00187 ResumeThread(hThread); 00188 } 00189 else if (tid == GetCurrentThreadId() && except) 00190 { 00191 CONTEXT lctx, *pctx; 00192 mdThd->SuspendCount = 1; 00193 if (except->ClientPointers) 00194 { 00195 EXCEPTION_POINTERS ep; 00196 00197 ReadProcessMemory(dc->hProcess, except->ExceptionPointers, 00198 &ep, sizeof(ep), NULL); 00199 ReadProcessMemory(dc->hProcess, ep.ContextRecord, 00200 &lctx, sizeof(lctx), NULL); 00201 pctx = &lctx; 00202 } 00203 else pctx = except->ExceptionPointers->ContextRecord; 00204 00205 *ctx = *pctx; 00206 fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack); 00207 } 00208 else mdThd->SuspendCount = 0; 00209 } 00210 } 00211 CloseHandle(hThread); 00212 return TRUE; 00213 } 00214 00215 /****************************************************************** 00216 * add_module 00217 * 00218 * Add a module to a dump context 00219 */ 00220 static BOOL add_module(struct dump_context* dc, const WCHAR* name, 00221 DWORD64 base, DWORD size, DWORD timestamp, DWORD checksum, 00222 BOOL is_elf) 00223 { 00224 if (!dc->modules) 00225 { 00226 dc->alloc_modules = 32; 00227 dc->modules = HeapAlloc(GetProcessHeap(), 0, 00228 dc->alloc_modules * sizeof(*dc->modules)); 00229 } 00230 else if(dc->num_modules >= dc->alloc_modules) 00231 { 00232 dc->alloc_modules *= 2; 00233 dc->modules = HeapReAlloc(GetProcessHeap(), 0, dc->modules, 00234 dc->alloc_modules * sizeof(*dc->modules)); 00235 } 00236 if (!dc->modules) 00237 { 00238 dc->alloc_modules = dc->num_modules = 0; 00239 return FALSE; 00240 } 00241 if (is_elf || 00242 !GetModuleFileNameExW(dc->hProcess, (HMODULE)(DWORD_PTR)base, 00243 dc->modules[dc->num_modules].name, 00244 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR))) 00245 lstrcpynW(dc->modules[dc->num_modules].name, name, 00246 sizeof(dc->modules[dc->num_modules].name) / sizeof(WCHAR)); 00247 dc->modules[dc->num_modules].base = base; 00248 dc->modules[dc->num_modules].size = size; 00249 dc->modules[dc->num_modules].timestamp = timestamp; 00250 dc->modules[dc->num_modules].checksum = checksum; 00251 dc->modules[dc->num_modules].is_elf = is_elf; 00252 dc->num_modules++; 00253 00254 return TRUE; 00255 } 00256 00257 /****************************************************************** 00258 * fetch_pe_module_info_cb 00259 * 00260 * Callback for accumulating in dump_context a PE modules set 00261 */ 00262 static BOOL WINAPI fetch_pe_module_info_cb(PCWSTR name, DWORD64 base, ULONG size, 00263 PVOID user) 00264 { 00265 struct dump_context* dc = user; 00266 IMAGE_NT_HEADERS nth; 00267 00268 if (!validate_addr64(base)) return FALSE; 00269 00270 if (pe_load_nt_header(dc->hProcess, base, &nth)) 00271 add_module(user, name, base, size, 00272 nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum, 00273 FALSE); 00274 return TRUE; 00275 } 00276 00277 /****************************************************************** 00278 * fetch_elf_module_info_cb 00279 * 00280 * Callback for accumulating in dump_context an ELF modules set 00281 */ 00282 static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base, 00283 void* user) 00284 { 00285 struct dump_context* dc = user; 00286 DWORD_PTR rbase; 00287 DWORD size, checksum; 00288 00289 /* FIXME: there's no relevant timestamp on ELF modules */ 00290 /* NB: if we have a non-null base from the live-target use it (whenever 00291 * the ELF module is relocatable or not). If we have a null base (ELF 00292 * module isn't relocatable) then grab its base address from ELF file 00293 */ 00294 if (!elf_fetch_file_info(name, &rbase, &size, &checksum)) 00295 size = checksum = 0; 00296 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE); 00297 return TRUE; 00298 } 00299 00300 /****************************************************************** 00301 * fetch_macho_module_info_cb 00302 * 00303 * Callback for accumulating in dump_context a Mach-O modules set 00304 */ 00305 static BOOL fetch_macho_module_info_cb(const WCHAR* name, unsigned long base, 00306 void* user) 00307 { 00308 struct dump_context* dc = (struct dump_context*)user; 00309 DWORD_PTR rbase; 00310 DWORD size, checksum; 00311 00312 /* FIXME: there's no relevant timestamp on Mach-O modules */ 00313 /* NB: if we have a non-null base from the live-target use it. If we have 00314 * a null base, then grab its base address from Mach-O file. 00315 */ 00316 if (!macho_fetch_file_info(name, &rbase, &size, &checksum)) 00317 size = checksum = 0; 00318 add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE); 00319 return TRUE; 00320 } 00321 00322 static void fetch_modules_info(struct dump_context* dc) 00323 { 00324 EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc); 00325 /* Since we include ELF modules in a separate stream from the regular PE ones, 00326 * we can always include those ELF modules (they don't eat lots of space) 00327 * And it's always a good idea to have a trace of the loaded ELF modules for 00328 * a given application in a post mortem debugging condition. 00329 */ 00330 elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc); 00331 macho_enum_modules(dc->hProcess, fetch_macho_module_info_cb, dc); 00332 } 00333 00334 static void fetch_module_versioninfo(LPCWSTR filename, VS_FIXEDFILEINFO* ffi) 00335 { 00336 DWORD handle; 00337 DWORD sz; 00338 static const WCHAR backslashW[] = {'\\', '\0'}; 00339 00340 memset(ffi, 0, sizeof(*ffi)); 00341 if ((sz = GetFileVersionInfoSizeW(filename, &handle))) 00342 { 00343 void* info = HeapAlloc(GetProcessHeap(), 0, sz); 00344 if (info && GetFileVersionInfoW(filename, handle, sz, info)) 00345 { 00346 VS_FIXEDFILEINFO* ptr; 00347 UINT len; 00348 00349 if (VerQueryValueW(info, backslashW, (void*)&ptr, &len)) 00350 memcpy(ffi, ptr, min(len, sizeof(*ffi))); 00351 } 00352 HeapFree(GetProcessHeap(), 0, info); 00353 } 00354 } 00355 00356 /****************************************************************** 00357 * add_memory_block 00358 * 00359 * Add a memory block to be dumped in a minidump 00360 * If rva is non 0, it's the rva in the minidump where has to be stored 00361 * also the rva of the memory block when written (this allows to reference 00362 * a memory block from outside the list of memory blocks). 00363 */ 00364 static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva) 00365 { 00366 if (!dc->mem) 00367 { 00368 dc->alloc_mem = 32; 00369 dc->mem = HeapAlloc(GetProcessHeap(), 0, dc->alloc_mem * sizeof(*dc->mem)); 00370 } 00371 else if (dc->num_mem >= dc->alloc_mem) 00372 { 00373 dc->alloc_mem *= 2; 00374 dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem, 00375 dc->alloc_mem * sizeof(*dc->mem)); 00376 } 00377 if (dc->mem) 00378 { 00379 dc->mem[dc->num_mem].base = base; 00380 dc->mem[dc->num_mem].size = size; 00381 dc->mem[dc->num_mem].rva = rva; 00382 dc->num_mem++; 00383 } 00384 else dc->num_mem = dc->alloc_mem = 0; 00385 } 00386 00387 /****************************************************************** 00388 * writeat 00389 * 00390 * Writes a chunk of data at a given position in the minidump 00391 */ 00392 static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size) 00393 { 00394 DWORD written; 00395 00396 SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN); 00397 WriteFile(dc->hFile, data, size, &written, NULL); 00398 } 00399 00400 /****************************************************************** 00401 * append 00402 * 00403 * writes a new chunk of data to the minidump, increasing the current 00404 * rva in dc 00405 */ 00406 static void append(struct dump_context* dc, const void* data, unsigned size) 00407 { 00408 writeat(dc, dc->rva, data, size); 00409 dc->rva += size; 00410 } 00411 00412 /****************************************************************** 00413 * dump_exception_info 00414 * 00415 * Write in File the exception information from pcs 00416 */ 00417 static unsigned dump_exception_info(struct dump_context* dc, 00418 const MINIDUMP_EXCEPTION_INFORMATION* except) 00419 { 00420 MINIDUMP_EXCEPTION_STREAM mdExcpt; 00421 EXCEPTION_RECORD rec, *prec; 00422 CONTEXT ctx, *pctx; 00423 DWORD i; 00424 00425 mdExcpt.ThreadId = except->ThreadId; 00426 mdExcpt.__alignment = 0; 00427 if (except->ClientPointers) 00428 { 00429 EXCEPTION_POINTERS ep; 00430 00431 ReadProcessMemory(dc->hProcess, 00432 except->ExceptionPointers, &ep, sizeof(ep), NULL); 00433 ReadProcessMemory(dc->hProcess, 00434 ep.ExceptionRecord, &rec, sizeof(rec), NULL); 00435 ReadProcessMemory(dc->hProcess, 00436 ep.ContextRecord, &ctx, sizeof(ctx), NULL); 00437 prec = &rec; 00438 pctx = &ctx; 00439 } 00440 else 00441 { 00442 prec = except->ExceptionPointers->ExceptionRecord; 00443 pctx = except->ExceptionPointers->ContextRecord; 00444 } 00445 mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode; 00446 mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags; 00447 mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord; 00448 mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress; 00449 mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters; 00450 mdExcpt.ExceptionRecord.__unusedAlignment = 0; 00451 for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++) 00452 mdExcpt.ExceptionRecord.ExceptionInformation[i] = prec->ExceptionInformation[i]; 00453 mdExcpt.ThreadContext.DataSize = sizeof(*pctx); 00454 mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt); 00455 00456 append(dc, &mdExcpt, sizeof(mdExcpt)); 00457 append(dc, pctx, sizeof(*pctx)); 00458 return sizeof(mdExcpt); 00459 } 00460 00461 /****************************************************************** 00462 * dump_modules 00463 * 00464 * Write in File the modules from pcs 00465 */ 00466 static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf) 00467 { 00468 MINIDUMP_MODULE mdModule; 00469 MINIDUMP_MODULE_LIST mdModuleList; 00470 char tmp[1024]; 00471 MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp; 00472 ULONG i, nmod; 00473 RVA rva_base; 00474 DWORD flags_out; 00475 unsigned sz; 00476 00477 for (i = nmod = 0; i < dc->num_modules; i++) 00478 { 00479 if ((dc->modules[i].is_elf && dump_elf) || 00480 (!dc->modules[i].is_elf && !dump_elf)) 00481 nmod++; 00482 } 00483 00484 mdModuleList.NumberOfModules = 0; 00485 /* reserve space for mdModuleList 00486 * FIXME: since we don't support 0 length arrays, we cannot use the 00487 * size of mdModuleList 00488 * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file 00489 */ 00490 00491 /* the stream size is just the size of the module index. It does not include the data for the 00492 names of each module. *Technically* the names are supposed to go into the common string table 00493 in the minidump file. Since each string is referenced by RVA they can all safely be located 00494 anywhere between streams in the file, so the end of this stream is sufficient. */ 00495 rva_base = dc->rva; 00496 dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod; 00497 for (i = 0; i < dc->num_modules; i++) 00498 { 00499 if ((dc->modules[i].is_elf && !dump_elf) || 00500 (!dc->modules[i].is_elf && dump_elf)) 00501 continue; 00502 00503 flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord; 00504 if (dc->type & MiniDumpWithDataSegs) 00505 flags_out |= ModuleWriteDataSeg; 00506 if (dc->type & MiniDumpWithProcessThreadData) 00507 flags_out |= ModuleWriteTlsData; 00508 if (dc->type & MiniDumpWithCodeSegs) 00509 flags_out |= ModuleWriteCodeSegs; 00510 ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR); 00511 if (sizeof(ULONG) + ms->Length > sizeof(tmp)) 00512 FIXME("Buffer overflow!!!\n"); 00513 lstrcpyW(ms->Buffer, dc->modules[i].name); 00514 00515 if (dc->cb) 00516 { 00517 MINIDUMP_CALLBACK_INPUT cbin; 00518 MINIDUMP_CALLBACK_OUTPUT cbout; 00519 00520 cbin.ProcessId = dc->pid; 00521 cbin.ProcessHandle = dc->hProcess; 00522 cbin.CallbackType = ModuleCallback; 00523 00524 cbin.u.Module.FullPath = ms->Buffer; 00525 cbin.u.Module.BaseOfImage = dc->modules[i].base; 00526 cbin.u.Module.SizeOfImage = dc->modules[i].size; 00527 cbin.u.Module.CheckSum = dc->modules[i].checksum; 00528 cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp; 00529 memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo)); 00530 cbin.u.Module.CvRecord = NULL; 00531 cbin.u.Module.SizeOfCvRecord = 0; 00532 cbin.u.Module.MiscRecord = NULL; 00533 cbin.u.Module.SizeOfMiscRecord = 0; 00534 00535 cbout.u.ModuleWriteFlags = flags_out; 00536 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) 00537 continue; 00538 flags_out &= cbout.u.ModuleWriteFlags; 00539 } 00540 if (flags_out & ModuleWriteModule) 00541 { 00542 mdModule.BaseOfImage = dc->modules[i].base; 00543 mdModule.SizeOfImage = dc->modules[i].size; 00544 mdModule.CheckSum = dc->modules[i].checksum; 00545 mdModule.TimeDateStamp = dc->modules[i].timestamp; 00546 mdModule.ModuleNameRva = dc->rva; 00547 ms->Length -= sizeof(WCHAR); 00548 append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR)); 00549 fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo); 00550 mdModule.CvRecord.DataSize = 0; /* FIXME */ 00551 mdModule.CvRecord.Rva = 0; /* FIXME */ 00552 mdModule.MiscRecord.DataSize = 0; /* FIXME */ 00553 mdModule.MiscRecord.Rva = 0; /* FIXME */ 00554 mdModule.Reserved0 = 0; /* FIXME */ 00555 mdModule.Reserved1 = 0; /* FIXME */ 00556 writeat(dc, 00557 rva_base + sizeof(mdModuleList.NumberOfModules) + 00558 mdModuleList.NumberOfModules++ * sizeof(mdModule), 00559 &mdModule, sizeof(mdModule)); 00560 } 00561 } 00562 writeat(dc, rva_base, &mdModuleList.NumberOfModules, 00563 sizeof(mdModuleList.NumberOfModules)); 00564 00565 return sz; 00566 } 00567 00568 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p 00569 * We are compiled with -fPIC, so we can't clobber ebx. 00570 */ 00571 static inline void do_x86cpuid(unsigned int ax, unsigned int *p) 00572 { 00573 #if defined(__GNUC__) && defined(__i386__) 00574 __asm__("pushl %%ebx\n\t" 00575 "cpuid\n\t" 00576 "movl %%ebx, %%esi\n\t" 00577 "popl %%ebx" 00578 : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3]) 00579 : "0" (ax)); 00580 #endif 00581 } 00582 00583 /* From xf86info havecpuid.c 1.11 */ 00584 static inline int have_x86cpuid(void) 00585 { 00586 #if defined(__GNUC__) && defined(__i386__) 00587 unsigned int f1, f2; 00588 __asm__("pushfl\n\t" 00589 "pushfl\n\t" 00590 "popl %0\n\t" 00591 "movl %0,%1\n\t" 00592 "xorl %2,%0\n\t" 00593 "pushl %0\n\t" 00594 "popfl\n\t" 00595 "pushfl\n\t" 00596 "popl %0\n\t" 00597 "popfl" 00598 : "=&r" (f1), "=&r" (f2) 00599 : "ir" (0x00200000)); 00600 return ((f1^f2) & 0x00200000) != 0; 00601 #else 00602 return 0; 00603 #endif 00604 } 00605 00606 /****************************************************************** 00607 * dump_system_info 00608 * 00609 * Dumps into File the information about the system 00610 */ 00611 static unsigned dump_system_info(struct dump_context* dc) 00612 { 00613 MINIDUMP_SYSTEM_INFO mdSysInfo; 00614 SYSTEM_INFO sysInfo; 00615 OSVERSIONINFOW osInfo; 00616 DWORD written; 00617 ULONG slen; 00618 00619 GetSystemInfo(&sysInfo); 00620 osInfo.dwOSVersionInfoSize = sizeof(osInfo); 00621 GetVersionExW(&osInfo); 00622 00623 mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture; 00624 mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel; 00625 mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision; 00626 mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors; 00627 mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */ 00628 mdSysInfo.MajorVersion = osInfo.dwMajorVersion; 00629 mdSysInfo.MinorVersion = osInfo.dwMinorVersion; 00630 mdSysInfo.BuildNumber = osInfo.dwBuildNumber; 00631 mdSysInfo.PlatformId = osInfo.dwPlatformId; 00632 00633 mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo); 00634 mdSysInfo.u1.Reserved1 = 0; 00635 mdSysInfo.u1.s.SuiteMask = VER_SUITE_TERMINAL; 00636 00637 if (have_x86cpuid()) 00638 { 00639 unsigned regs0[4], regs1[4]; 00640 00641 do_x86cpuid(0, regs0); 00642 mdSysInfo.Cpu.X86CpuInfo.VendorId[0] = regs0[1]; 00643 mdSysInfo.Cpu.X86CpuInfo.VendorId[1] = regs0[2]; 00644 mdSysInfo.Cpu.X86CpuInfo.VendorId[2] = regs0[3]; 00645 do_x86cpuid(1, regs1); 00646 mdSysInfo.Cpu.X86CpuInfo.VersionInformation = regs1[0]; 00647 mdSysInfo.Cpu.X86CpuInfo.FeatureInformation = regs1[3]; 00648 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = 0; 00649 if (regs0[1] == 0x68747541 /* "Auth" */ && 00650 regs0[3] == 0x69746e65 /* "enti" */ && 00651 regs0[2] == 0x444d4163 /* "cAMD" */) 00652 { 00653 do_x86cpuid(0x80000000, regs1); /* get vendor cpuid level */ 00654 if (regs1[0] >= 0x80000001) 00655 { 00656 do_x86cpuid(0x80000001, regs1); /* get vendor features */ 00657 mdSysInfo.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = regs1[3]; 00658 } 00659 } 00660 } 00661 else 00662 { 00663 unsigned i; 00664 ULONG64 one = 1; 00665 00666 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0; 00667 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; 00668 00669 for (i = 0; i < sizeof(mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++) 00670 if (IsProcessorFeaturePresent(i)) 00671 mdSysInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= one << i; 00672 } 00673 append(dc, &mdSysInfo, sizeof(mdSysInfo)); 00674 00675 /* write the service pack version string after this stream. It is referenced within the 00676 stream by its RVA in the file. */ 00677 slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR); 00678 WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL); 00679 WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL); 00680 dc->rva += sizeof(ULONG) + slen; 00681 00682 return sizeof(mdSysInfo); 00683 } 00684 00685 /****************************************************************** 00686 * dump_threads 00687 * 00688 * Dumps into File the information about running threads 00689 */ 00690 static unsigned dump_threads(struct dump_context* dc, 00691 const MINIDUMP_EXCEPTION_INFORMATION* except) 00692 { 00693 MINIDUMP_THREAD mdThd; 00694 MINIDUMP_THREAD_LIST mdThdList; 00695 unsigned i, sz; 00696 RVA rva_base; 00697 DWORD flags_out; 00698 CONTEXT ctx; 00699 00700 mdThdList.NumberOfThreads = 0; 00701 00702 rva_base = dc->rva; 00703 dc->rva += sz = sizeof(mdThdList.NumberOfThreads) + dc->spi->dwThreadCount * sizeof(mdThd); 00704 00705 for (i = 0; i < dc->spi->dwThreadCount; i++) 00706 { 00707 fetch_thread_info(dc, i, except, &mdThd, &ctx); 00708 00709 flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | 00710 ThreadWriteInstructionWindow; 00711 if (dc->type & MiniDumpWithProcessThreadData) 00712 flags_out |= ThreadWriteThreadData; 00713 if (dc->type & MiniDumpWithThreadInfo) 00714 flags_out |= ThreadWriteThreadInfo; 00715 00716 if (dc->cb) 00717 { 00718 MINIDUMP_CALLBACK_INPUT cbin; 00719 MINIDUMP_CALLBACK_OUTPUT cbout; 00720 00721 cbin.ProcessId = dc->pid; 00722 cbin.ProcessHandle = dc->hProcess; 00723 cbin.CallbackType = ThreadCallback; 00724 cbin.u.Thread.ThreadId = HandleToUlong(dc->spi->ti[i].ClientId.UniqueThread); 00725 cbin.u.Thread.ThreadHandle = 0; /* FIXME */ 00726 cbin.u.Thread.Context = ctx; 00727 cbin.u.Thread.SizeOfContext = sizeof(CONTEXT); 00728 cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange; 00729 cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange + 00730 mdThd.Stack.Memory.DataSize; 00731 00732 cbout.u.ThreadWriteFlags = flags_out; 00733 if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) 00734 continue; 00735 flags_out &= cbout.u.ThreadWriteFlags; 00736 } 00737 if (flags_out & ThreadWriteThread) 00738 { 00739 if (ctx.ContextFlags && (flags_out & ThreadWriteContext)) 00740 { 00741 mdThd.ThreadContext.Rva = dc->rva; 00742 mdThd.ThreadContext.DataSize = sizeof(CONTEXT); 00743 append(dc, &ctx, sizeof(CONTEXT)); 00744 } 00745 if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack)) 00746 { 00747 add_memory_block(dc, mdThd.Stack.StartOfMemoryRange, 00748 mdThd.Stack.Memory.DataSize, 00749 rva_base + sizeof(mdThdList.NumberOfThreads) + 00750 mdThdList.NumberOfThreads * sizeof(mdThd) + 00751 FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva)); 00752 } 00753 writeat(dc, 00754 rva_base + sizeof(mdThdList.NumberOfThreads) + 00755 mdThdList.NumberOfThreads * sizeof(mdThd), 00756 &mdThd, sizeof(mdThd)); 00757 mdThdList.NumberOfThreads++; 00758 } 00759 if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow)) 00760 { 00761 /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP 00762 * - also crop values across module boundaries, 00763 * - and don't make it i386 dependent 00764 */ 00765 /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */ 00766 } 00767 } 00768 writeat(dc, rva_base, 00769 &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads)); 00770 00771 return sz; 00772 } 00773 00774 /****************************************************************** 00775 * dump_memory_info 00776 * 00777 * dumps information about the memory of the process (stack of the threads) 00778 */ 00779 static unsigned dump_memory_info(struct dump_context* dc) 00780 { 00781 MINIDUMP_MEMORY_LIST mdMemList; 00782 MINIDUMP_MEMORY_DESCRIPTOR mdMem; 00783 DWORD written; 00784 unsigned i, pos, len, sz; 00785 RVA rva_base; 00786 char tmp[1024]; 00787 00788 mdMemList.NumberOfMemoryRanges = dc->num_mem; 00789 append(dc, &mdMemList.NumberOfMemoryRanges, 00790 sizeof(mdMemList.NumberOfMemoryRanges)); 00791 rva_base = dc->rva; 00792 sz = mdMemList.NumberOfMemoryRanges * sizeof(mdMem); 00793 dc->rva += sz; 00794 sz += sizeof(mdMemList.NumberOfMemoryRanges); 00795 00796 for (i = 0; i < dc->num_mem; i++) 00797 { 00798 mdMem.StartOfMemoryRange = dc->mem[i].base; 00799 mdMem.Memory.Rva = dc->rva; 00800 mdMem.Memory.DataSize = dc->mem[i].size; 00801 SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN); 00802 for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp)) 00803 { 00804 len = min(dc->mem[i].size - pos, sizeof(tmp)); 00805 if (ReadProcessMemory(dc->hProcess, 00806 (void*)(DWORD_PTR)(dc->mem[i].base + pos), 00807 tmp, len, NULL)) 00808 WriteFile(dc->hFile, tmp, len, &written, NULL); 00809 } 00810 dc->rva += mdMem.Memory.DataSize; 00811 writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem)); 00812 if (dc->mem[i].rva) 00813 { 00814 writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva)); 00815 } 00816 } 00817 00818 return sz; 00819 } 00820 00821 static unsigned dump_misc_info(struct dump_context* dc) 00822 { 00823 MINIDUMP_MISC_INFO mmi; 00824 00825 mmi.SizeOfInfo = sizeof(mmi); 00826 mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID; 00827 mmi.ProcessId = dc->pid; 00828 /* FIXME: create/user/kernel time */ 00829 mmi.ProcessCreateTime = 0; 00830 mmi.ProcessKernelTime = 0; 00831 mmi.ProcessUserTime = 0; 00832 00833 append(dc, &mmi, sizeof(mmi)); 00834 return sizeof(mmi); 00835 } 00836 00837 /****************************************************************** 00838 * MiniDumpWriteDump (DEBUGHLP.@) 00839 * 00840 */ 00841 BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, 00842 MINIDUMP_TYPE DumpType, 00843 PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 00844 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 00845 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) 00846 { 00847 static const MINIDUMP_DIRECTORY emptyDir = {UnusedStream, {0, 0}}; 00848 MINIDUMP_HEADER mdHead; 00849 MINIDUMP_DIRECTORY mdDir; 00850 DWORD i, nStreams, idx_stream; 00851 struct dump_context dc; 00852 00853 dc.hProcess = hProcess; 00854 dc.hFile = hFile; 00855 dc.pid = pid; 00856 dc.modules = NULL; 00857 dc.num_modules = 0; 00858 dc.alloc_modules = 0; 00859 dc.cb = CallbackParam; 00860 dc.type = DumpType; 00861 dc.mem = NULL; 00862 dc.num_mem = 0; 00863 dc.alloc_mem = 0; 00864 dc.rva = 0; 00865 00866 if (!fetch_processes_info(&dc)) return FALSE; 00867 fetch_modules_info(&dc); 00868 00869 /* 1) init */ 00870 nStreams = 6 + (ExceptionParam ? 1 : 0) + 00871 (UserStreamParam ? UserStreamParam->UserStreamCount : 0); 00872 00873 /* pad the directory size to a multiple of 4 for alignment purposes */ 00874 nStreams = (nStreams + 3) & ~3; 00875 00876 if (DumpType & MiniDumpWithDataSegs) 00877 FIXME("NIY MiniDumpWithDataSegs\n"); 00878 if (DumpType & MiniDumpWithFullMemory) 00879 FIXME("NIY MiniDumpWithFullMemory\n"); 00880 if (DumpType & MiniDumpWithHandleData) 00881 FIXME("NIY MiniDumpWithHandleData\n"); 00882 if (DumpType & MiniDumpFilterMemory) 00883 FIXME("NIY MiniDumpFilterMemory\n"); 00884 if (DumpType & MiniDumpScanMemory) 00885 FIXME("NIY MiniDumpScanMemory\n"); 00886 00887 /* 2) write header */ 00888 mdHead.Signature = MINIDUMP_SIGNATURE; 00889 mdHead.Version = MINIDUMP_VERSION; /* NOTE: native puts in an 'implementation specific' value in the high order word of this member */ 00890 mdHead.NumberOfStreams = nStreams; 00891 mdHead.CheckSum = 0; /* native sets a 0 checksum in its files */ 00892 mdHead.StreamDirectoryRva = sizeof(mdHead); 00893 mdHead.u.TimeDateStamp = time(NULL); 00894 mdHead.Flags = DumpType; 00895 append(&dc, &mdHead, sizeof(mdHead)); 00896 00897 /* 3) write stream directories */ 00898 dc.rva += nStreams * sizeof(mdDir); 00899 idx_stream = 0; 00900 00901 /* 3.1) write data stream directories */ 00902 00903 /* must be first in minidump */ 00904 mdDir.StreamType = SystemInfoStream; 00905 mdDir.Location.Rva = dc.rva; 00906 mdDir.Location.DataSize = dump_system_info(&dc); 00907 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00908 &mdDir, sizeof(mdDir)); 00909 00910 mdDir.StreamType = ThreadListStream; 00911 mdDir.Location.Rva = dc.rva; 00912 mdDir.Location.DataSize = dump_threads(&dc, ExceptionParam); 00913 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00914 &mdDir, sizeof(mdDir)); 00915 00916 mdDir.StreamType = ModuleListStream; 00917 mdDir.Location.Rva = dc.rva; 00918 mdDir.Location.DataSize = dump_modules(&dc, FALSE); 00919 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00920 &mdDir, sizeof(mdDir)); 00921 00922 mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */ 00923 mdDir.Location.Rva = dc.rva; 00924 mdDir.Location.DataSize = dump_modules(&dc, TRUE); 00925 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00926 &mdDir, sizeof(mdDir)); 00927 00928 mdDir.StreamType = MemoryListStream; 00929 mdDir.Location.Rva = dc.rva; 00930 mdDir.Location.DataSize = dump_memory_info(&dc); 00931 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00932 &mdDir, sizeof(mdDir)); 00933 00934 mdDir.StreamType = MiscInfoStream; 00935 mdDir.Location.Rva = dc.rva; 00936 mdDir.Location.DataSize = dump_misc_info(&dc); 00937 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00938 &mdDir, sizeof(mdDir)); 00939 00940 /* 3.2) write exception information (if any) */ 00941 if (ExceptionParam) 00942 { 00943 mdDir.StreamType = ExceptionStream; 00944 mdDir.Location.Rva = dc.rva; 00945 mdDir.Location.DataSize = dump_exception_info(&dc, ExceptionParam); 00946 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00947 &mdDir, sizeof(mdDir)); 00948 } 00949 00950 /* 3.3) write user defined streams (if any) */ 00951 if (UserStreamParam) 00952 { 00953 for (i = 0; i < UserStreamParam->UserStreamCount; i++) 00954 { 00955 mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type; 00956 mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize; 00957 mdDir.Location.Rva = dc.rva; 00958 writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), 00959 &mdDir, sizeof(mdDir)); 00960 append(&dc, UserStreamParam->UserStreamArray[i].Buffer, 00961 UserStreamParam->UserStreamArray[i].BufferSize); 00962 } 00963 } 00964 00965 /* fill the remaining directory entries with 0's (unused stream types) */ 00966 /* NOTE: this should always come last in the dump! */ 00967 for (i = idx_stream; i < nStreams; i++) 00968 writeat(&dc, mdHead.StreamDirectoryRva + i * sizeof(emptyDir), &emptyDir, sizeof(emptyDir)); 00969 00970 HeapFree(GetProcessHeap(), 0, dc.pcs_buffer); 00971 HeapFree(GetProcessHeap(), 0, dc.mem); 00972 HeapFree(GetProcessHeap(), 0, dc.modules); 00973 00974 return TRUE; 00975 } 00976 00977 /****************************************************************** 00978 * MiniDumpReadDumpStream (DEBUGHLP.@) 00979 * 00980 * 00981 */ 00982 BOOL WINAPI MiniDumpReadDumpStream(PVOID base, ULONG str_idx, 00983 PMINIDUMP_DIRECTORY* pdir, 00984 PVOID* stream, ULONG* size) 00985 { 00986 MINIDUMP_HEADER* mdHead = base; 00987 00988 if (mdHead->Signature == MINIDUMP_SIGNATURE) 00989 { 00990 MINIDUMP_DIRECTORY* dir; 00991 DWORD i; 00992 00993 dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva); 00994 for (i = 0; i < mdHead->NumberOfStreams; i++, dir++) 00995 { 00996 if (dir->StreamType == str_idx) 00997 { 00998 if (pdir) *pdir = dir; 00999 if (stream) *stream = (char*)base + dir->Location.Rva; 01000 if (size) *size = dir->Location.DataSize; 01001 return TRUE; 01002 } 01003 } 01004 } 01005 SetLastError(ERROR_INVALID_PARAMETER); 01006 return FALSE; 01007 } Generated on Sat May 26 2012 04:21:59 for ReactOS by
1.7.6.1
|