summaryrefslogtreecommitdiff
path: root/indra/newview/llwindebug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llwindebug.cpp')
-rw-r--r--indra/newview/llwindebug.cpp837
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);
}