Files
SingularityViewer/indra/newview/llwindebug.cpp
Lirusaito 61beedd3d9 Changed style of comments with asterisks to avoid highlighting errors on //* with weak highlighters, change is to all files that could potentially break highlights
Most were needed, though some were just for possible problems with highlighting, should not affect performance whatsoever.
2012-01-09 05:40:03 -05:00

914 lines
24 KiB
C++

/**
* @file llwindebug.cpp
* @brief Windows debugging functions
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* 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
*
* 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
*
* 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.
*
* 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,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
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,CP_ACP);
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,CP_ACP);
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,CP_ACP);
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:
LLMemoryReserve();
~LLMemoryReserve();
void reserve();
void release();
protected:
unsigned char *mReserve;
static const size_t MEMORY_RESERVATION_SIZE;
};
LLMemoryReserve::LLMemoryReserve() :
mReserve(NULL)
{
};
LLMemoryReserve::~LLMemoryReserve()
{
release();
}
// I dunno - this just seemed like a pretty good value.
const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;
void LLMemoryReserve::reserve()
{
if(NULL == mReserve)
mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
};
void LLMemoryReserve::release()
{
delete [] mReserve;
mReserve = NULL;
};
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;
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()
{
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::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
if (s_first_run)
{
// First, try loading from the directory that the app resides in.
std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());
HMODULE hDll = NULL;
hDll = LoadLibraryA(local_dll_name.c_str());
if (!hDll)
{
hDll = LoadLibrary(L"dbghelp.dll");
}
if (!hDll)
{
LL_WARNS("AppInit") << "Couldn't find dbghelp.dll!" << LL_ENDL;
}
else
{
f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
if (!f_mdwp)
{
FreeLibrary(hDll);
hDll = NULL;
}
}
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();
if(prev_filter != gFilterFunc)
{
LL_WARNS("AppInit")
<< "Replacing unknown exception (" << (void *)prev_filter << ") with (" << (void *)filter_func << ") !" << LL_ENDL;
}
gFilterFunc = filter_func;
}
bool LLWinDebug::checkExceptionHandler()
{
bool ok = true;
LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
prev_filter = SetUnhandledExceptionFilter(gFilterFunc);
if (prev_filter != gFilterFunc)
{
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)
{
LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL;
}
else
{
LL_WARNS("AppInit") << "Our exception handler (" << (void *)gFilterFunc << ") replaced with NULL!" << LL_ENDL;
}
}
return ok;
}
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);
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::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();
LLControlVariable* save_minimap = gSavedSettings.getControl("SaveMinidump");
if(save_minimap && save_minimap->getValue().asBoolean())
{
_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");
}
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);
}