diff options
Diffstat (limited to 'indra/newview/llwindebug.cpp')
-rw-r--r-- | indra/newview/llwindebug.cpp | 837 |
1 files changed, 779 insertions, 58 deletions
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp index 551d0be8d7..59bc9dc62b 100644 --- a/indra/newview/llwindebug.cpp +++ b/indra/newview/llwindebug.cpp @@ -1,33 +1,98 @@ -/** +/** * @file llwindebug.cpp * @brief Windows debugging functions * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. * - * 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; - * version 2.1 of the License only. + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * - * 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. + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ + #include "llviewerprecompiledheaders.h" +#include <tchar.h> +#include <tlhelp32.h> #include "llwindebug.h" +#include "llviewercontrol.h" #include "lldir.h" +#include "llsd.h" +#include "llsdserialize.h" + +#pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable: 4100) //unreferenced formal parameter + + +/* +LLSD Block for Windows Dump Information +<llsd> + <map> + <key>Platform</key> + <string></string> + <key>Process</key> + <string></string> + <key>Module</key> + <string></string> + <key>DateModified</key> + <string></string> + <key>ExceptionCode</key> + <string></string> + <key>ExceptionRead/WriteAddress</key> + <string></string> + <key>Instruction</key> + <string></string> + <key>Registers</key> + <map> + <!-- Continued for all registers --> + <key>EIP</key> + <string>...</string> + <!-- ... --> + </map> + <key>Call Stack</key> + <array> + <!-- One map per stack frame --> + <map> + <key>ModuleName</key> + <string></string> + <key>ModuleBaseAddress</key> + <string></string> + <key>ModuleOffsetAddress</key> + <string></string> + <key>Parameters</key> + <array> + <string></string> + </array> + </map> + <!-- ... --> + </array> + </map> +</llsd> +*/ + + +extern void (*gCrashCallback)(void); // based on dbghelp.h typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, @@ -38,6 +103,527 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF MINIDUMPWRITEDUMP f_mdwp = NULL; +#undef UNICODE + +static LPTOP_LEVEL_EXCEPTION_FILTER gFilterFunc = NULL; + +HMODULE hDbgHelp; + +// Tool Help functions. +typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID); +typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); +typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + +CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; +MODULE32_FIRST Module32First_; +MODULE32_NEST Module32Next_; + +#define DUMP_SIZE_MAX 8000 //max size of our dump +#define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls +#define NL L"\r\n" //new line + + +typedef struct STACK +{ + STACK * Ebp; + PBYTE Ret_Addr; + DWORD Param[0]; +} STACK, * PSTACK; + +BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr); +void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, + const CONTEXT* context_record, + LLSD& info); + +void printError( CHAR* msg ) +{ + DWORD eNum; + TCHAR sysMsg[256]; + TCHAR* p; + + eNum = GetLastError( ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, eNum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + sysMsg, 256, NULL ); + + // Trim the end of the line and terminate it with a null + p = sysMsg; + while( ( *p > 31 ) || ( *p == 9 ) ) + ++p; + do { *p-- = 0; } while( ( p >= sysMsg ) && + ( ( *p == '.' ) || ( *p < 33 ) ) ); + + // Display the message + printf( "\n WARNING: %s failed with error %d (%s)", msg, eNum, sysMsg ); +} + +BOOL GetProcessThreadIDs(DWORD process_id, std::vector<DWORD>& thread_ids) +{ + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + // Take a snapshot of all running threads + hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( hThreadSnap == INVALID_HANDLE_VALUE ) + return( FALSE ); + + // Fill in the size of the structure before using it. + te32.dwSize = sizeof(THREADENTRY32 ); + + // Retrieve information about the first thread, + // and exit if unsuccessful + if( !Thread32First( hThreadSnap, &te32 ) ) + { + printError( "Thread32First" ); // Show cause of failure + CloseHandle( hThreadSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the thread list of the system, + // and display information about each thread + // associated with the specified process + do + { + if( te32.th32OwnerProcessID == process_id ) + { + thread_ids.push_back(te32.th32ThreadID); + } + } while( Thread32Next(hThreadSnap, &te32 ) ); + +// Don't forget to clean up the snapshot object. + CloseHandle( hThreadSnap ); + return( TRUE ); +} + +BOOL GetThreadCallStack(DWORD thread_id, LLSD& info) +{ + if(GetCurrentThreadId() == thread_id) + { + // Early exit for the current thread. + // Suspending the current thread would be a bad idea. + // Plus you can't retrieve a valid current thread context. + return false; + } + + HANDLE thread_handle = INVALID_HANDLE_VALUE; + thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id); + if(INVALID_HANDLE_VALUE == thread_handle) + { + return FALSE; + } + + BOOL result = false; + if(-1 != SuspendThread(thread_handle)) + { + CONTEXT context_struct; + context_struct.ContextFlags = CONTEXT_FULL; + if(GetThreadContext(thread_handle, &context_struct)) + { + Get_Call_Stack(NULL, &context_struct, info); + result = true; + } + ResumeThread(thread_handle); + } + else + { + // Couldn't suspend thread. + } + + CloseHandle(thread_handle); + return result; +} + + +//Windows Call Stack Construction idea from +//http://www.codeproject.com/tools/minidump.asp + +//**************************************************************************************** +BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr) +//**************************************************************************************** +// Find module by Ret_Addr (address in the module). +// Return Module_Name (full path) and Module_Addr (start address). +// Return TRUE if found. +{ + MODULEENTRY32 M = {sizeof(M)}; + HANDLE hSnapshot; + + bool found = false; + + if (CreateToolhelp32Snapshot_) + { + hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0); + + if ((hSnapshot != INVALID_HANDLE_VALUE) && + Module32First_(hSnapshot, &M)) + { + do + { + if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize) + { + lstrcpyn(Module_Name, M.szExePath, MAX_PATH); + Module_Addr = M.modBaseAddr; + found = true; + break; + } + } while (Module32Next_(hSnapshot, &M)); + } + + CloseHandle(hSnapshot); + } + + return found; +} //Get_Module_By_Ret_Addr + +bool has_valid_call_before(PDWORD cur_stack_loc) +{ + PBYTE p_first_byte = (PBYTE)(*cur_stack_loc - 1); + PBYTE p_second_byte = (PBYTE)(*cur_stack_loc -2); + PBYTE p_fifth_byte = (PBYTE)(*cur_stack_loc - 5); + PBYTE p_sixth_byte = (PBYTE)(*cur_stack_loc - 6); + + // make sure we can read it + if(IsBadReadPtr(p_sixth_byte, 6 * sizeof(BYTE))) + { + return false; + } + + // check for 9a + 4 bytes + if(*p_fifth_byte == 0x9A) + { + return true; + } + + // Check for E8 + 4 bytes and last byte is 00 or FF + if(*p_fifth_byte == 0xE8 && (*p_first_byte == 0x00 || *p_first_byte == 0xFF)) + { + return true; + } + + // the other is six bytes + if(*p_sixth_byte == 0xFF || *p_second_byte == 0xFF) + { + return true; + } + + return false; +} + +PBYTE get_valid_frame(PBYTE esp) +{ + PDWORD cur_stack_loc = NULL; + const int max_search = 400; + WCHAR module_name[MAX_PATH]; + PBYTE module_addr = 0; + + // round to highest multiple of four + esp = (esp + (4 - ((int)esp % 4)) % 4); + + // scroll through stack a few hundred places. + for (cur_stack_loc = (PDWORD) esp; cur_stack_loc < (PDWORD)esp + max_search; cur_stack_loc += 1) + { + // if you can read the pointer, + if (IsBadReadPtr(cur_stack_loc, sizeof(PDWORD))) + { + continue; + } + + // check if it's in a module + if (!Get_Module_By_Ret_Addr((PBYTE)*cur_stack_loc, module_name, module_addr)) + { + continue; + } + + // check if the code before the instruction ptr is a call + if(!has_valid_call_before(cur_stack_loc)) + { + continue; + } + + // if these all pass, return that ebp, otherwise continue till we're dead + return (PBYTE)(cur_stack_loc - 1); + } + + return NULL; +} + +bool shouldUseStackWalker(PSTACK Ebp, int max_depth) +{ + WCHAR Module_Name[MAX_PATH]; + PBYTE Module_Addr = 0; + int depth = 0; + + while (depth < max_depth) + { + if (IsBadReadPtr(Ebp, sizeof(PSTACK)) || + IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) || + Ebp->Ebp < Ebp || + Ebp->Ebp - Ebp > 0xFFFFFF || + IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) || + !Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr)) + { + return true; + } + depth++; + Ebp = Ebp->Ebp; + } + + return false; +} + +//****************************************************************** +void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, + const CONTEXT* context_record, + LLSD& info) +//****************************************************************** +// Fill Str with call stack info. +// pException can be either GetExceptionInformation() or NULL. +// If pException = NULL - get current call stack. +{ + LPWSTR Module_Name = new WCHAR[MAX_PATH]; + PBYTE Module_Addr = 0; + LLSD params; + PBYTE Esp = NULL; + LLSD tmp_info; + + bool fake_frame = false; + bool ebp_used = false; + const int HEURISTIC_MAX_WALK = 20; + int heuristic_walk_i = 0; + int Ret_Addr_I = 0; + + STACK Stack = {0, 0}; + PSTACK Ebp; + + if (exception_record && context_record) //fake frame for exception address + { + Stack.Ebp = (PSTACK)(context_record->Ebp); + Stack.Ret_Addr = (PBYTE)exception_record->ExceptionAddress; + Ebp = &Stack; + Esp = (PBYTE) context_record->Esp; + fake_frame = true; + } + else if(context_record) + { + Ebp = (PSTACK)(context_record->Ebp); + Esp = (PBYTE)(context_record->Esp); + } + else + { + Ebp = (PSTACK)&exception_record - 1; //frame addr of Get_Call_Stack() + Esp = (PBYTE)&exception_record; + + // Skip frame of Get_Call_Stack(). + if (!IsBadReadPtr(Ebp, sizeof(PSTACK))) + Ebp = Ebp->Ebp; //caller ebp + } + + // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX. + // Break trace on wrong stack frame. + for (Ret_Addr_I = 0; + heuristic_walk_i < HEURISTIC_MAX_WALK && + Ret_Addr_I < CALL_TRACE_MAX && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); + Ret_Addr_I++) + { + // If module with Ebp->Ret_Addr found. + if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr)) + { + // Save module's address and full path. + tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name); + tmp_info["CallStack"][Ret_Addr_I]["ModuleAddress"] = (int)Module_Addr; + tmp_info["CallStack"][Ret_Addr_I]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr); + + // Save 5 params of the call. We don't know the real number of params. + if (fake_frame && !Ret_Addr_I) //fake frame for exception address + params[0] = "Exception Offset"; + else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD))) + { + for(int j = 0; j < 5; ++j) + { + params[j] = (int)Ebp->Param[j]; + } + } + tmp_info["CallStack"][Ret_Addr_I]["Parameters"] = params; + } + + tmp_info["CallStack"][Ret_Addr_I]["ReturnAddress"] = (int)Ebp->Ret_Addr; + + // get ready for next frame + // Set ESP to just after return address. Not the real esp, but just enough after the return address + if(!fake_frame) { + Esp = (PBYTE)Ebp + 8; + } + else + { + fake_frame = false; + } + + // is next ebp valid? + // only run if we've never found a good ebp + // and make sure the one after is valid as well + if( !ebp_used && + shouldUseStackWalker(Ebp, 2)) + { + heuristic_walk_i++; + PBYTE new_ebp = get_valid_frame(Esp); + if (new_ebp != NULL) + { + Ebp = (PSTACK)new_ebp; + } + } + else + { + ebp_used = true; + Ebp = Ebp->Ebp; + } + } +/* TODO remove or turn this code back on to edit the stack after i see a few raw ones. -Palmer + // Now go back through and edit out heuristic stacks that could very well be bogus. + // Leave the top and the last 3 stack chosen by the heuristic, however. + if(heuristic_walk_i > 2) + { + info["CallStack"][0] = tmp_info["CallStack"][0]; + std::string ttest = info["CallStack"][0]["ModuleName"]; + for(int cur_frame = 1; + (cur_frame + heuristic_walk_i - 2 < Ret_Addr_I); + ++cur_frame) + { + // edit out the middle heuristic found frames + info["CallStack"][cur_frame] = tmp_info["CallStack"][cur_frame + heuristic_walk_i - 2]; + } + } + else + { + info = tmp_info; + } +*/ + info = tmp_info; + info["HeuristicWalkI"] = heuristic_walk_i; + info["EbpUsed"] = ebp_used; + +} //Get_Call_Stack + +//*********************************** +void WINAPI Get_Version_Str(LLSD& info) +//*********************************** +// Fill Str with Windows version. +{ + OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later + + if (!GetVersionEx((POSVERSIONINFO)&V)) + { + ZeroMemory(&V, sizeof(V)); + V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx((POSVERSIONINFO)&V); + } + + if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) + V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx + + info["Platform"] = llformat("Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,... + V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor, V.wProductType); +} //Get_Version_Str + +//************************************************************* +LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException) +//************************************************************* +// Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str. +{ + LLSD info; + LPWSTR Str; + int Str_Len; +// int i; + LPWSTR Module_Name = new WCHAR[MAX_PATH]; + PBYTE Module_Addr; + HANDLE hFile; + FILETIME Last_Write_Time; + FILETIME Local_File_Time; + SYSTEMTIME T; + + Str = new WCHAR[DUMP_SIZE_MAX]; + Str_Len = 0; + if (!Str) + return NULL; + + Get_Version_Str(info); + + GetModuleFileName(NULL, Str, MAX_PATH); + info["Process"] = ll_convert_wide_to_string(Str); + info["ThreadID"] = (S32)GetCurrentThreadId(); + + // If exception occurred. + if (pException) + { + EXCEPTION_RECORD & E = *pException->ExceptionRecord; + CONTEXT & C = *pException->ContextRecord; + + // If module with E.ExceptionAddress found - save its path and date. + if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr)) + { + info["Module"] = ll_convert_wide_to_string(Module_Name); + + if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) + { + if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time)) + { + FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time); + FileTimeToSystemTime(&Local_File_Time, &T); + + info["DateModified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear); + } + CloseHandle(hFile); + } + } + else + { + info["ExceptionAddr"] = (int)E.ExceptionAddress; + } + + info["ExceptionCode"] = (int)E.ExceptionCode; + + /* + //TODO: Fix this + if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) + { + // Access violation type - Write/Read. + LLSD exception_info; + exception_info["Type"] = E.ExceptionInformation[0] ? "Write" : "Read"; + exception_info["Address"] = llformat("%08x", E.ExceptionInformation[1]); + info["Exception Information"] = exception_info; + } + */ + + + // Save instruction that caused exception. + /* + std::string str; + for (i = 0; i < 16; i++) + str += llformat(" %02X", PBYTE(E.ExceptionAddress)[i]); + info["Instruction"] = str; + */ + LLSD registers; + registers["EAX"] = (int)C.Eax; + registers["EBX"] = (int)C.Ebx; + registers["ECX"] = (int)C.Ecx; + registers["EDX"] = (int)C.Edx; + registers["ESI"] = (int)C.Esi; + registers["EDI"] = (int)C.Edi; + registers["ESP"] = (int)C.Esp; + registers["EBP"] = (int)C.Ebp; + registers["EIP"] = (int)C.Eip; + registers["EFlags"] = (int)C.EFlags; + info["Registers"] = registers; + } //if (pException) + + // Save call stack info. + Get_Call_Stack(pException->ExceptionRecord, pException->ContextRecord, info); + + return info; +} //Get_Exception_Info + +#define UNICODE + class LLMemoryReserve { public: @@ -77,23 +663,66 @@ void LLMemoryReserve::release() static LLMemoryReserve gEmergencyMemoryReserve; +#ifndef _M_IX86 + #error "The following code only works for x86!" +#endif +LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter( + LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) +{ + if(lpTopLevelExceptionFilter == gFilterFunc) + return gFilterFunc; -LONG NTAPI vectoredHandler(PEXCEPTION_POINTERS exception_infop) + llinfos << "Someone tried to set the exception filter. Listing call stack modules" << llendl; + LLSD cs_info; + Get_Call_Stack(NULL, NULL, cs_info); + + if(cs_info.has("CallStack") && cs_info["CallStack"].isArray()) + { + LLSD cs = cs_info["CallStack"]; + for(LLSD::array_iterator i = cs.beginArray(); + i != cs.endArray(); + ++i) + { + llinfos << "Module: " << (*i)["ModuleName"] << llendl; + } + } + + return gFilterFunc; +} + +BOOL PreventSetUnhandledExceptionFilter() { - LLWinDebug::instance().generateMinidump(exception_infop); - return EXCEPTION_CONTINUE_SEARCH; + HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll")); + if (hKernel32 == NULL) + return FALSE; + + void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter"); + if(pOrgEntry == NULL) + return FALSE; + + unsigned char newJump[ 100 ]; + DWORD dwOrgEntryAddr = (DWORD)pOrgEntry; + dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far + void *pNewFunc = &MyDummySetUnhandledExceptionFilter; + DWORD dwNewEntryAddr = (DWORD) pNewFunc; + DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr; + + newJump[ 0 ] = 0xE9; // JMP absolute + memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc)); + SIZE_T bytesWritten; + BOOL bRet = WriteProcessMemory(GetCurrentProcess(), + pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten); + return bRet; } // static -void LLWinDebug::init() +void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func) { + static bool s_first_run = true; // Load the dbghelp dll now, instead of waiting for the crash. // Less potential for stack mangling - // Don't install vectored exception handler if being debugged. - if(IsDebuggerPresent()) return; - if (s_first_run) { // First, try loading from the directory that the app resides in. @@ -124,68 +753,160 @@ void LLWinDebug::init() gEmergencyMemoryReserve.reserve(); s_first_run = false; + } + + // Try to get Tool Help library functions. + HMODULE hKernel32; + hKernel32 = GetModuleHandle(_T("KERNEL32")); + CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot"); + Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32FirstW"); + Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32NextW"); + + LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; + prev_filter = SetUnhandledExceptionFilter(filter_func); + + // *REMOVE:Mani + //PreventSetUnhandledExceptionFilter(); - // Add this exeption hanlder to save windows style minidump. - AddVectoredExceptionHandler(0, &vectoredHandler); + if(prev_filter != gFilterFunc) + { + LL_WARNS("AppInit") + << "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL; } + + gFilterFunc = filter_func; } -void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename) +bool LLWinDebug::checkExceptionHandler() { - // Temporary fix to switch out the code that writes the DMP file. - // Fix coming that doesn't write a mini dump file for regular C++ exceptions. - const bool enable_write_dump_file = false; - if ( enable_write_dump_file ) + bool ok = true; + LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; + prev_filter = SetUnhandledExceptionFilter(gFilterFunc); + + if (prev_filter != gFilterFunc) { - if(f_mdwp == NULL || gDirUtilp == NULL) + LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with " << prev_filter << "!" << LL_ENDL; + ok = false; + } + + if (prev_filter == NULL) + { + ok = FALSE; + if (gFilterFunc == NULL) { - return; + LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL; } else { - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename); + LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL; + } + } - HANDLE hFile = CreateFileA(dump_path.c_str(), - GENERIC_WRITE, - FILE_SHARE_WRITE, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); + return ok; +} - if (hFile != INVALID_HANDLE_VALUE) - { - // Write the dump, ignoring the return value - f_mdwp(GetCurrentProcess(), - GetCurrentProcessId(), - hFile, - type, - ExInfop, - NULL, - NULL); +void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename) +{ + if(f_mdwp == NULL || gDirUtilp == NULL) + { + return; + //write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n"); + } + else + { + std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename); - CloseHandle(hFile); - } + HANDLE hFile = CreateFileA(dump_path.c_str(), + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + // Write the dump, ignoring the return value + f_mdwp(GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + type, + ExInfop, + NULL, + NULL); + CloseHandle(hFile); } + } } // static -void LLWinDebug::generateMinidump(struct _EXCEPTION_POINTERS *exception_infop) +void LLWinDebug::generateCrashStacks(struct _EXCEPTION_POINTERS *exception_infop) { + // *NOTE:Mani - This method is no longer the exception handler. + // Its called from viewer_windows_exception_handler() and other places. + + // + // Let go of a bunch of reserved memory to give library calls etc + // a chance to execute normally in the case that we ran out of + // memory. + // + LLSD info; std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException"); + std::string log_path = dump_path + ".log"; + if (exception_infop) { // Since there is exception info... Release the hounds. gEmergencyMemoryReserve.release(); - _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + if(gSavedSettings.getControl("SaveMinidump").notNull() && gSavedSettings.getBOOL("SaveMinidump")) + { + _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + + ExInfo.ThreadId = ::GetCurrentThreadId(); + ExInfo.ExceptionPointers = exception_infop; + ExInfo.ClientPointers = NULL; + + writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp"); + writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp"); + } - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = exception_infop; - ExInfo.ClientPointers = NULL; - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLife.dmp"); + info = Get_Exception_Info(exception_infop); } + + LLSD threads; + std::vector<DWORD> thread_ids; + GetProcessThreadIDs(GetCurrentProcessId(), thread_ids); + + for(std::vector<DWORD>::iterator th_itr = thread_ids.begin(); + th_itr != thread_ids.end(); + ++th_itr) + { + LLSD thread_info; + if(*th_itr != GetCurrentThreadId()) + { + GetThreadCallStack(*th_itr, thread_info); + } + + if(thread_info) + { + threads[llformat("ID %d", *th_itr)] = thread_info; + } + } + + info["Threads"] = threads; + + llofstream out_file(log_path); + LLSDSerialize::toPrettyXML(info, out_file); + out_file.close(); +} + +void LLWinDebug::clearCrashStacks() +{ + LLSD info; + std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException.log"); + LLFile::remove(dump_path); } |