From b235c59d60472f818a9142c0886b95a0ff4191d7 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:19 -0500 Subject: Second Life viewer sources 1.18.6.0-RC --- linden/indra/newview/llwindebug.cpp | 406 +++++++++++++++++++++++++++++------- 1 file changed, 325 insertions(+), 81 deletions(-) (limited to 'linden/indra/newview/llwindebug.cpp') diff --git a/linden/indra/newview/llwindebug.cpp b/linden/indra/newview/llwindebug.cpp index b65262c..d0129c8 100644 --- a/linden/indra/newview/llwindebug.cpp +++ b/linden/indra/newview/llwindebug.cpp @@ -33,16 +33,69 @@ #ifdef LL_WINDOWS +#include +#include +#include "llappviewer.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 + + + Platform + + Process + + Module + + DateModified + + ExceptionCode + + ExceptionRead/WriteAddress + + Instruction + + Registers + + + EIP + ... + + + Call Stack + + + + ModuleName + + ModuleBaseAddress + + ModuleOffsetAddress + + Parameters + + + + + + + + + +*/ // From viewer.h extern BOOL gInProductionGrid; extern void (*gCrashCallback)(void); -extern void write_debug(const char *str); -extern void write_debug(const std::string &str); // based on dbghelp.h typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, @@ -53,6 +106,248 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF MINIDUMPWRITEDUMP f_mdwp = NULL; +#undef UNICODE + +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 + +//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 + +//****************************************************************** +void WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, 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; + + typedef struct STACK + { + STACK * Ebp; + PBYTE Ret_Addr; + DWORD Param[0]; + } STACK, * PSTACK; + + STACK Stack = {0, 0}; + PSTACK Ebp; + + if (pException) //fake frame for exception address + { + Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp; + Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress; + Ebp = &Stack; + } + else + { + Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack() + + // 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 (int Ret_Addr_I = 0, i = 0; + (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); + Ret_Addr_I++, Ebp = Ebp->Ebp, ++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. + info["CallStack"][i]["ModuleName"] = ll_convert_wide_to_string(Module_Name); + info["CallStack"][i]["ModuleAddress"] = (int)Module_Addr; + info["CallStack"][i]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr); + + LLSD params; + // Save 5 params of the call. We don't know the real number of params. + if (pException && !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]; + } + } + info["CallStack"][i]["Parameters"] = params; + } + info["CallStack"][i]["ReturnAddress"] = (int)Ebp->Ret_Addr; + } +} //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); + + // 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. + LLString 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, info); + + return info; +} //Get_Exception_Info + +#define UNICODE class LLMemoryReserve { @@ -96,7 +391,6 @@ static LLMemoryReserve gEmergencyMemoryReserve; // static BOOL LLWinDebug::setupExceptionHandler() { -#ifdef LL_RELEASE_FOR_DOWNLOAD static BOOL s_first_run = TRUE; // Load the dbghelp dll now, instead of waiting for the crash. @@ -123,7 +417,7 @@ BOOL LLWinDebug::setupExceptionHandler() msg += local_dll_name; msg += "!\n"; - write_debug(msg.c_str()); + //write_debug(msg.c_str()); ok = FALSE; } @@ -133,7 +427,7 @@ BOOL LLWinDebug::setupExceptionHandler() if (!f_mdwp) { - write_debug("No MiniDumpWriteDump!\n"); + //write_debug("No MiniDumpWriteDump!\n"); FreeLibrary(hDll); hDll = NULL; ok = FALSE; @@ -146,6 +440,13 @@ BOOL LLWinDebug::setupExceptionHandler() LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException); + // 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"); + if (s_first_run) { // We're fine, this is the first run. @@ -162,56 +463,16 @@ BOOL LLWinDebug::setupExceptionHandler() llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl; ok = FALSE; } + return ok; -#else // Internal builds don't mess with exception handling. - return TRUE; -#endif -} - -void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename) -{ - if(f_mdwp == NULL) - { - write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n"); - } - else if(gDirUtilp == NULL) - { - write_debug("No way to generate a minidump, no gDirUtilp!\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); - } - - } + //return TRUE; } - // static LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) { + // *NOTE:Mani - This method is no longer the initial exception handler. + // It is called from viewer_windows_exception_handler() and other places. // // Let go of a bunch of reserved memory to give library calls etc @@ -220,50 +481,34 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) // gEmergencyMemoryReserve.release(); - BOOL userWantsMaxiDump = - (stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0) - || (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0); - - BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid; - - /* Calculate alsoSaveMaxiDump here */ - if (exception_infop) { - _MINIDUMP_EXCEPTION_INFORMATION ExInfo; - ExInfo.ThreadId = ::GetCurrentThreadId(); - ExInfo.ExceptionPointers = exception_infop; - ExInfo.ClientPointers = NULL; + std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLifeException"); - writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp"); + std::string log_path = dump_path + ".log"; - if(alsoSaveMaxiDump) - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp"); + LLSD info; + info = Get_Exception_Info(exception_infop); + if (info) + { + std::ofstream out_file(log_path.c_str()); + LLSDSerialize::toPrettyXML(info, out_file); + out_file.close(); + } } else { - writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp"); - - if(alsoSaveMaxiDump) - writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp"); - } - - if (!exception_infop) - { // We're calling this due to a network error, not due to an actual exception. // It doesn't realy matter what we return. return EXCEPTION_CONTINUE_SEARCH; } - // - // Call the newview crash callback, which will spawn the crash - // reporter. It may or may not spawn a dialog. - // - if (gCrashCallback) - { - gCrashCallback(); - } + //handle viewer crash must be called here since + //we don't return handling of the application + //back to the process. + LLAppViewer::handleViewerCrash(); // // At this point, we always want to exit the app. There's no graceful @@ -276,4 +521,3 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop) } #endif - -- cgit v1.1