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.cpp254
1 files changed, 254 insertions, 0 deletions
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
new file mode 100644
index 0000000000..428df0b45a
--- /dev/null
+++ b/indra/newview/llwindebug.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file llwindebug.cpp
+ * @brief Windows debugging functions
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#ifdef LL_WINDOWS
+
+#include "llwindebug.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+
+// 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,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+ );
+
+MINIDUMPWRITEDUMP f_mdwp = NULL;
+
+
+
+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;
+
+// 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.
+ // Less potential for stack mangling
+
+ BOOL ok = TRUE;
+ 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)
+ {
+ llwarns << "Couldn't find dbghelp.dll!" << llendl;
+
+ std::string msg = "Couldn't find dbghelp.dll at ";
+ msg += local_dll_name;
+ msg += "!\n";
+
+ write_debug(msg.c_str());
+
+ ok = FALSE;
+ }
+ else
+ {
+ f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
+
+ if (!f_mdwp)
+ {
+ write_debug("No MiniDumpWriteDump!\n");
+ ok = FALSE;
+ }
+ }
+
+ gEmergencyMemoryReserve.reserve();
+ }
+
+ 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;
+ }
+ 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);
+ }
+
+ }
+}
+
+// static
+LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
+{
+
+ //
+ // 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.
+ //
+ 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;
+
+ writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
+
+ if(alsoSaveMaxiDump)
+ writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
+ }
+ 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();
+ }
+
+ //
+ // At this point, we always want to exit the app. There's no graceful
+ // recovery for an unhandled exception.
+ //
+ // Just kill the process.
+ LONG retval = EXCEPTION_EXECUTE_HANDLER;
+
+ return retval;
+}
+
+#endif
+