summaryrefslogtreecommitdiff
path: root/indra/newview/llwindebug.cpp
diff options
context:
space:
mode:
authorKyle Machulis <qdot@lindenlab.com>2007-11-20 23:42:48 +0000
committerKyle Machulis <qdot@lindenlab.com>2007-11-20 23:42:48 +0000
commit2e7c0d973ed28d732ea19762099ed3c55123780e (patch)
tree36b04301d325e07075880a8a64166c5284d1d61f /indra/newview/llwindebug.cpp
parent5356b917545d43df5537128245ee5b786b705b90 (diff)
svn merge -r74104:74124 svn+ssh://svn/svn/linden/branches/crash-logger-cleanup-merge-6
Diffstat (limited to 'indra/newview/llwindebug.cpp')
-rw-r--r--indra/newview/llwindebug.cpp441
1 files changed, 350 insertions, 91 deletions
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
index 88ba5822eb..13c5ac3bbb 100644
--- a/indra/newview/llwindebug.cpp
+++ b/indra/newview/llwindebug.cpp
@@ -33,12 +33,70 @@
#ifdef LL_WINDOWS
+#include <tchar.h>
+#include <tlhelp32.h>
+#include <atlbase.h>
+#include "llappviewer.h"
#include "llwindebug.h"
#include "llviewercontrol.h"
#include "lldir.h"
-
-#include "llappviewer.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>Date Modified</key>
+ <string></string>
+ <key>Exception Code</key>
+ <string></string>
+ <key>Exception Read/Write Address</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>Module Name</key>
+ <string></string>
+ <key>Module Base Address</key>
+ <string></string>
+ <key>Module Offset Address</key>
+ <string></string>
+ <key>Parameters</key>
+ <array>
+ <string></string>
+ </array>
+ </map>
+ <!-- ... -->
+ </array>
+ </map>
+</llsd>
+
+*/
+
+// From viewer.h
+extern BOOL gInProductionGrid;
+
+extern void (*gCrashCallback)(void);
// based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
@@ -49,6 +107,255 @@ 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
+
+//****************************************************************************************
+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.
+{
+
+ USES_CONVERSION;
+
+ 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["Call Stack"][i]["Module Name"] = W2A(Module_Name);
+ info["Call Stack"][i]["Module Address"] = (int)Module_Addr;
+ info["Call Stack"][i]["Call Offset"] = (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["Call Stack"][i]["Parameters"] = params;
+ }
+ info["Call Stack"][i]["Return Address"] = (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.
+{
+ USES_CONVERSION;
+
+ 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"] = W2A(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"] = W2A(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["Date Modified"] = llformat("%02d/%02d/%d", T.wMonth, T.wDay, T.wYear);
+ }
+ CloseHandle(hFile);
+ }
+ }
+ else
+ {
+ info["Exception Addr"] = (int)E.ExceptionAddress;
+ }
+
+ info["Exception Code"] = (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.
+ Str_Len = 0;
+ for (i = 0; i < 16; i++)
+ Str_Len += wsprintf(Str + Str_Len, L" %02X", PBYTE(E.ExceptionAddress)[i]);
+ info["Instruction"] = W2A(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);
+
+ if (Str[0] == NL[0])
+ lstrcpy(Str, Str + sizeof(NL) - 1);
+
+
+ return info;
+} //Get_Exception_Info
+
+#define UNICODE
class LLMemoryReserve {
@@ -92,7 +399,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.
@@ -119,7 +425,7 @@ BOOL LLWinDebug::setupExceptionHandler()
msg += local_dll_name;
msg += "!\n";
- LLAppViewer::instance()->writeDebug(msg.c_str());
+ //write_debug(msg.c_str());
ok = FALSE;
}
@@ -129,7 +435,7 @@ BOOL LLWinDebug::setupExceptionHandler()
if (!f_mdwp)
{
- LLAppViewer::instance()->writeDebug("No MiniDumpWriteDump!\n");
+ //write_debug("No MiniDumpWriteDump!\n");
FreeLibrary(hDll);
hDll = NULL;
ok = FALSE;
@@ -139,76 +445,37 @@ BOOL LLWinDebug::setupExceptionHandler()
gEmergencyMemoryReserve.reserve();
}
- // *REMOVE: LLApp now handles the exception handing.
- // LLAppViewerWin32 calls SetUnhandledExceptionFilter()
-
- //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
- //prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
-
- //if (s_first_run)
- //{
- // // We're fine, this is the first run.
- // s_first_run = FALSE;
- // return ok;
- //}
- //if (!prev_filter)
- //{
- // llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
- // ok = FALSE;
- //}
- //if (prev_filter != LLWinDebug::handleException)
- //{
- // llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
- // ok = FALSE;
- //}
+ LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+ prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
- return ok;
-#else
- // Internal builds don't mess with exception handling.
- return TRUE;
-#endif
-}
+ // 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");
-void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
-{
- if(f_mdwp == NULL)
+ if (s_first_run)
{
- LLAppViewer::instance()->writeDebug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
+ // We're fine, this is the first run.
+ s_first_run = FALSE;
+ return ok;
}
- else if(gDirUtilp == NULL)
+ if (!prev_filter)
{
- LLAppViewer::instance()->writeDebug("No way to generate a minidump, no gDirUtilp!\n");
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
+ ok = FALSE;
}
- else
+ if (prev_filter != LLWinDebug::handleException)
{
- 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);
- }
-
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
+ ok = FALSE;
}
-}
+ return ok;
+ // Internal builds don't mess with exception handling.
+ //return TRUE;
+}
// static
LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
{
@@ -222,42 +489,35 @@ 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;
}
+ //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
// recovery for an unhandled exception.
@@ -269,4 +529,3 @@ LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
}
#endif
-