| /* | |
| * File minidump.c - management of dumps (read & write) | |
| * | |
| * Copyright (C) 2004, Eric Pouech | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <time.h> | |
| #define NONAMELESSUNION | |
| #define NONAMELESSSTRUCT | |
| #include "dbghelp_private.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); | |
| #if 0 | |
| /* hard to see how we can generate this very easily (how to grab latest exception | |
| * in a process ?) | |
| */ | |
| static void DumpException(struct process* pcs, HANDLE hFile, RVA* rva) | |
| { | |
| MINIDUMP_EXCEPTION_STREAM mdExcpt; | |
| mdExcpt.ThreadId = DEBUG_CurrThread->tid; | |
| mdExcpt.__alignment = 0; | |
| mdExcpt.ExceptionRecord. | |
| ULONG ExceptionCode; | |
| ULONG ExceptionFlags; | |
| ULONGLONG ExceptionRecord; | |
| ULONGLONG ExceptionAddress; | |
| ULONG NumberParameters; | |
| ULONG __unusedAlignment; | |
| ULONGLONG ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; | |
| } | |
| #endif | |
| /****************************************************************** | |
| * dump_modules | |
| * | |
| * Write in File the modules from pcs | |
| */ | |
| static void dump_modules(struct process* pcs, HANDLE hFile, RVA* rva) | |
| { | |
| MINIDUMP_MODULE mdModule; | |
| MINIDUMP_MODULE_LIST mdModuleList; | |
| DWORD written; | |
| struct module* module = NULL; | |
| mdModuleList.NumberOfModules = 0; | |
| for (module = pcs->lmodules; module; module = module->next) | |
| mdModuleList.NumberOfModules++; | |
| WriteFile(hFile, &mdModuleList.NumberOfModules, | |
| sizeof(mdModuleList.NumberOfModules), &written, NULL); | |
| *rva += sizeof(mdModuleList.NumberOfModules) + | |
| sizeof(mdModule) * mdModuleList.NumberOfModules; | |
| for (module = pcs->lmodules; module; module = module->next) | |
| { | |
| mdModule.BaseOfImage = (DWORD)module->module.BaseOfImage; | |
| mdModule.SizeOfImage = module->module.ImageSize; | |
| mdModule.CheckSum = module->module.CheckSum; | |
| mdModule.TimeDateStamp = module->module.TimeDateStamp; | |
| mdModule.ModuleNameRva = *rva; | |
| *rva += strlen(module->module.ModuleName) + 1; | |
| memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */ | |
| mdModule.CvRecord.DataSize = 0; /* FIXME */ | |
| mdModule.CvRecord.Rva = 0; /* FIXME */ | |
| mdModule.MiscRecord.DataSize = 0; /* FIXME */ | |
| mdModule.MiscRecord.Rva = 0; /* FIXME */ | |
| mdModule.Reserved0 = 0; | |
| mdModule.Reserved1 = 0; | |
| WriteFile(hFile, &mdModule, sizeof(mdModule), &written, NULL); | |
| } | |
| for (module = pcs->lmodules; module; module = module->next) | |
| { | |
| WriteFile(hFile, module->module.ModuleName, | |
| strlen(module->module.ModuleName) + 1, &written, NULL); | |
| FIXME("CV and misc records not written\n"); | |
| } | |
| } | |
| /****************************************************************** | |
| * dump_system_info | |
| * | |
| * Dumps into File the information about the system | |
| */ | |
| static void dump_system_info(struct process* pcs, HANDLE hFile, RVA* rva) | |
| { | |
| MINIDUMP_SYSTEM_INFO mdSysInfo; | |
| SYSTEM_INFO sysInfo; | |
| OSVERSIONINFOA osInfo; | |
| DWORD written; | |
| GetSystemInfo(&sysInfo); | |
| GetVersionExA(&osInfo); | |
| mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture; | |
| mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel; | |
| mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision; | |
| mdSysInfo.Reserved0 = 0; | |
| mdSysInfo.MajorVersion = osInfo.dwMajorVersion; | |
| mdSysInfo.MinorVersion = osInfo.dwMinorVersion; | |
| mdSysInfo.BuildNumber = osInfo.dwBuildNumber; | |
| mdSysInfo.PlatformId = osInfo.dwPlatformId; | |
| mdSysInfo.CSDVersionRva = *rva + sizeof(mdSysInfo); | |
| mdSysInfo.Reserved1 = 0; | |
| WriteFile(hFile, &mdSysInfo, sizeof(mdSysInfo), &written, NULL); | |
| *rva += sizeof(mdSysInfo); | |
| WriteFile(hFile, osInfo.szCSDVersion, strlen(osInfo.szCSDVersion) + 1, | |
| &written, NULL); | |
| *rva += strlen(osInfo.szCSDVersion) + 1; | |
| } | |
| /****************************************************************** | |
| * dump_threads | |
| * | |
| * Dumps into File the information about running threads | |
| */ | |
| static void dump_threads(struct process* pcs, HANDLE hFile, RVA* rva) | |
| { | |
| #if 0 | |
| MINIDUMP_THREAD mdThd; | |
| MINIDUMP_THREAD_LIST mdThdList; | |
| DWORD written; | |
| DBG_THREAD* thd; | |
| mdThdList.NumberOfThreads = pcs->num_threads; | |
| WriteFile(hFile, &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads), | |
| &written, NULL); | |
| *rva += sizeof(mdThdList.NumberOfThreads) + | |
| mdThdList.NumberOfThreads * sizeof(mdThd); | |
| for (thd = pcs->threads; thd; thd = thd->next) | |
| { | |
| mdThd.ThreadId = thd->tid; | |
| mdThd.SuspendCount = 0; /* FIXME */ | |
| mdThd.PriorityClass = 0; /* FIXME */ | |
| mdThd.Priority = 0; /* FIXME */ | |
| mdThd.Teb = 0; /* FIXME */ | |
| mdThd.Stack.StartOfMemoryRange = 0; /* FIXME */ | |
| mdThd.Stack.Memory.DataSize = 0; /* FIXME */ | |
| mdThd.Stack.Memory.Rva = 0; /* FIXME */ | |
| mdThd.ThreadContext.DataSize = 0;/* FIXME */ | |
| mdThd.ThreadContext.Rva = 0; /* FIXME */ | |
| WriteFile(hFile, &mdThd, sizeof(mdThd), &written, NULL); | |
| FIXME("Stack & thread context not written\n"); | |
| } | |
| #endif | |
| } | |
| /****************************************************************** | |
| * MiniDumpWriteDump (DEBUGHLP.@) | |
| * | |
| * | |
| */ | |
| BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, | |
| MINIDUMP_TYPE DumpType, | |
| PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, | |
| PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, | |
| PMINIDUMP_CALLBACK_INFORMATION CallbackParam) | |
| { | |
| struct process* pcs; | |
| MINIDUMP_HEADER mdHead; | |
| MINIDUMP_DIRECTORY mdDir; | |
| DWORD currRva, written; | |
| DWORD i, nStream, addStream; | |
| RVA rva; | |
| pcs = process_find_by_handle(hProcess); | |
| if (!pcs) return FALSE; /* FIXME: should try to load it ??? */ | |
| /* 1) init */ | |
| nStream = UserStreamParam ? UserStreamParam->UserStreamCount : 0; | |
| addStream = 0; | |
| if (DumpType & MiniDumpNormal) | |
| addStream += 3; /* sure ? thread stack back trace */ | |
| if (DumpType & MiniDumpWithDataSegs) | |
| FIXME("NIY MiniDumpWithDataSegs\n"); | |
| if (DumpType & MiniDumpWithFullMemory) | |
| FIXME("NIY MiniDumpWithFullMemory\n"); | |
| if (DumpType & MiniDumpWithHandleData) | |
| FIXME("NIY MiniDumpWithHandleData\n"); | |
| if (DumpType & MiniDumpFilterMemory) | |
| FIXME("NIY MiniDumpFilterMemory\n"); | |
| if (DumpType & MiniDumpScanMemory) | |
| FIXME("NIY MiniDumpScanMemory\n"); | |
| /* 2) write header */ | |
| rva = sizeof(mdHead); | |
| mdHead.Signature = MINIDUMP_SIGNATURE; | |
| mdHead.Version = MINIDUMP_VERSION; | |
| mdHead.NumberOfStreams = nStream + addStream; | |
| mdHead.StreamDirectoryRva = rva; | |
| mdHead.u.TimeDateStamp = time(NULL); | |
| mdHead.Flags = DumpType; | |
| WriteFile(hFile, &mdHead, sizeof(mdHead), &written, NULL); | |
| /* 3) write stream directories */ | |
| rva += (nStream + addStream) * sizeof(mdDir); | |
| /* 3.1) write data stream directories */ | |
| currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); | |
| SetFilePointer(hFile, rva, NULL, FILE_BEGIN); | |
| mdDir.StreamType = ModuleListStream; | |
| mdDir.Location.Rva = rva; | |
| dump_modules(pcs, hFile, &rva); | |
| mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva; | |
| SetFilePointer(hFile, currRva, NULL, FILE_BEGIN); | |
| WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL); | |
| currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); | |
| SetFilePointer(hFile, rva, NULL, FILE_BEGIN); | |
| mdDir.StreamType = ThreadListStream; | |
| mdDir.Location.Rva = rva; | |
| dump_threads(pcs, hFile, &rva); | |
| mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva; | |
| SetFilePointer(hFile, currRva, NULL, FILE_BEGIN); | |
| WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL); | |
| currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); | |
| SetFilePointer(hFile, rva, NULL, FILE_BEGIN); | |
| mdDir.StreamType = SystemInfoStream; | |
| mdDir.Location.Rva = rva; | |
| dump_system_info(pcs, hFile, &rva); | |
| mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva; | |
| SetFilePointer(hFile, currRva, NULL, FILE_BEGIN); | |
| WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL); | |
| /* 3.2) write user define stream */ | |
| for (i = 0; i < nStream; i++) | |
| { | |
| mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type; | |
| mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize; | |
| mdDir.Location.Rva = rva; | |
| WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL); | |
| currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); | |
| SetFilePointer(hFile, rva, NULL, FILE_BEGIN); | |
| WriteFile(hFile, | |
| UserStreamParam->UserStreamArray[i].Buffer, | |
| UserStreamParam->UserStreamArray[i].BufferSize, | |
| &written, NULL); | |
| rva += UserStreamParam->UserStreamArray[i].BufferSize; | |
| SetFilePointer(hFile, currRva, NULL, FILE_BEGIN); | |
| } | |
| return TRUE; | |
| } |