summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/llapp.cpp188
-rw-r--r--indra/llcommon/llapp.h25
3 files changed, 175 insertions, 40 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 527ab42fc9..2a036df06e 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -9,6 +9,7 @@ include(Linking)
include(Boost)
include(Pth)
include(LLSharedLibs)
+include(GoogleBreakpad)
include(GooglePerfTools)
include(Copy3rdPartyLibs)
@@ -258,6 +259,7 @@ endif(LLCOMMON_LINK_SHARED)
target_link_libraries(
llcommon
+ ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 6b2d1b7c20..eedec0b24e 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -30,6 +30,8 @@
* $/LicenseInfo$
*/
+#include <cstdlib>
+
#include "linden_common.h"
#include "llapp.h"
@@ -41,8 +43,11 @@
#include "lllivefile.h"
#include "llmemory.h"
#include "llstl.h" // for DeletePointer()
+#include "llstring.h"
#include "lleventtimer.h"
+#include "google_breakpad/exception_handler.h"
+
//
// Signal handling
//
@@ -51,11 +56,22 @@
#if LL_WINDOWS
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);
+bool windows_post_minidump_callback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded);
#else
# include <signal.h>
# include <unistd.h> // for fork()
void setup_signals();
void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+
+// Called by breakpad exception handler after the minidump has been generated.
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded);
# if LL_DARWIN
/* OSX doesn't support SIGRT* */
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
@@ -81,7 +97,6 @@ BOOL LLApp::sLogInSignal = FALSE;
// static
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
LLAppErrorHandler LLApp::sErrorHandler = NULL;
-LLAppErrorHandler LLApp::sSyncErrorHandler = NULL;
BOOL LLApp::sErrorThreadRunning = FALSE;
#if !LL_WINDOWS
LLApp::child_map LLApp::sChildMap;
@@ -123,7 +138,12 @@ void LLApp::commonCtor()
// Set the application to this instance.
sApplication = this;
-
+
+ mExceptionHandler = 0;
+
+ // initialize the buffer to write the minidump filename to
+ // (this is used to avoid allocating memory in the crash handler)
+ memset(minidump_path, 0, MAX_MINDUMP_PATH_LENGTH);
}
LLApp::LLApp(LLErrorThread *error_thread) :
@@ -152,6 +172,8 @@ LLApp::~LLApp()
delete mThreadErrorp;
mThreadErrorp = NULL;
}
+
+ if(mExceptionHandler != 0) delete mExceptionHandler;
LLCommon::cleanupClass();
}
@@ -262,19 +284,18 @@ void LLApp::setupErrorHandling()
// occasionally checks to see if the app is in an error state, and sees if it needs to be run.
#if LL_WINDOWS
- // Windows doesn't have the same signal handling mechanisms as UNIX, thus APR doesn't provide
- // a signal handling thread implementation.
- // What we do is install an unhandled exception handler, which will try to do the right thing
- // in the case of an error (generate a minidump)
-
- // Disable this until the viewer gets ported so server crashes can be JIT debugged.
- //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
- //prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
-
// This sets a callback to handle w32 signals to the console window.
// The viewer shouldn't be affected, sicne its a windowed app.
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
+ // Install the Google Breakpad crash handler for Windows
+ if(mExceptionHandler == 0)
+ {
+ llwarns << "adding breakpad exception handler" << llendl;
+ mExceptionHandler = new google_breakpad::ExceptionHandler(
+ L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL);
+ }
+
#else
//
// Start up signal handling.
@@ -282,9 +303,14 @@ void LLApp::setupErrorHandling()
// There are two different classes of signals. Synchronous signals are delivered to a specific
// thread, asynchronous signals can be delivered to any thread (in theory)
//
-
setup_signals();
-
+
+ // Add google breakpad exception handler configured for Darwin/Linux.
+ if(mExceptionHandler == 0)
+ {
+ std::string dumpPath = "/tmp/";
+ mExceptionHandler = new google_breakpad::ExceptionHandler(dumpPath, 0, &unix_post_minidump_callback, 0, true);
+ }
#endif
startErrorThread();
@@ -310,21 +336,6 @@ void LLApp::setErrorHandler(LLAppErrorHandler handler)
LLApp::sErrorHandler = handler;
}
-
-void LLApp::setSyncErrorHandler(LLAppErrorHandler handler)
-{
- LLApp::sSyncErrorHandler = handler;
-}
-
-// static
-void LLApp::runSyncErrorHandler()
-{
- if (LLApp::sSyncErrorHandler)
- {
- LLApp::sSyncErrorHandler();
- }
-}
-
// static
void LLApp::runErrorHandler()
{
@@ -337,7 +348,6 @@ void LLApp::runErrorHandler()
LLApp::setStopped();
}
-
// static
void LLApp::setStatus(EAppStatus status)
{
@@ -348,15 +358,27 @@ void LLApp::setStatus(EAppStatus status)
// static
void LLApp::setError()
{
- if (!isError())
- {
- // perform any needed synchronous error-handling
- runSyncErrorHandler();
- // set app status to ERROR so that the LLErrorThread notices
- setStatus(APP_STATUS_ERROR);
- }
+ // set app status to ERROR so that the LLErrorThread notices
+ setStatus(APP_STATUS_ERROR);
}
+void LLApp::setMiniDumpDir(const std::string &path)
+{
+ llassert(mExceptionHandler);
+#ifdef LL_WINDOWS
+ wchar_t buffer[MAX_MINDUMP_PATH_LENGTH];
+ mbstowcs(buffer, path.c_str(), MAX_MINDUMP_PATH_LENGTH);
+ mExceptionHandler->set_dump_path(std::wstring(buffer));
+#else
+ mExceptionHandler->set_dump_path(path);
+#endif
+}
+
+void LLApp::writeMiniDump()
+{
+ llassert(mExceptionHandler);
+ mExceptionHandler->WriteMinidump();
+}
// static
void LLApp::setQuitting()
@@ -587,6 +609,7 @@ void setup_signals()
// Asynchronous signals that result in core
sigaction(SIGQUIT, &act, NULL);
+
}
void clear_signals()
@@ -765,4 +788,97 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
}
}
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded)
+{
+ // Copy minidump file path into fixed buffer in the app instance to avoid
+ // heap allocations in a crash handler.
+
+ // path format: <dump_dir>/<minidump_id>.dmp
+ int dirPathLength = strlen(dump_dir);
+ int idLength = strlen(minidump_id);
+
+ // The path must not be truncated.
+ llassert((dirPathLength + idLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH);
+
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
+ strncpy(path, dump_dir, remaining);
+ remaining -= dirPathLength;
+ path += dirPathLength;
+ if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
+ {
+ *path++ = '/';
+ --remaining;
+ }
+ if (remaining > 0)
+ {
+ strncpy(path, minidump_id, remaining);
+ remaining -= idLength;
+ path += idLength;
+ strncpy(path, ".dmp", remaining);
+ }
+
+ llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
+ LLApp::runErrorHandler();
+ return true;
+}
#endif // !WINDOWS
+
+#ifdef LL_WINDOWS
+bool windows_post_minidump_callback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded)
+{
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
+ size_t bytesUsed;
+
+ bytesUsed = wcstombs(path, dump_path, static_cast<size_t>(remaining));
+ remaining -= bytesUsed;
+ path += bytesUsed;
+ if(remaining > 0 && bytesUsed > 0 && path[-1] != '\\')
+ {
+ *path++ = '\\';
+ --remaining;
+ }
+ if(remaining > 0)
+ {
+ bytesUsed = wcstombs(path, minidump_id, static_cast<size_t>(remaining));
+ remaining -= bytesUsed;
+ path += bytesUsed;
+ }
+ if(remaining > 0)
+ {
+ strncpy(path, ".dmp", remaining);
+ }
+
+ llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
+ // *NOTE:Mani - this code is stolen from LLApp, where its never actually used.
+ //OSMessageBox("Attach Debugger Now", "Error", OSMB_OK);
+ // *TODO: Translate the signals/exceptions into cross-platform stuff
+ // Windows implementation
+ llinfos << "Entering Windows Exception Handler..." << llendl;
+
+ if (LLApp::isError())
+ {
+ llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
+ }
+
+ // Flag status to error, so thread_error starts its work
+ LLApp::setError();
+
+ // Block in the exception handler until the app has stopped
+ // This is pretty sketchy, but appears to work just fine
+ while (!LLApp::isStopped())
+ {
+ ms_sleep(10);
+ }
+
+ return true;
+}
+#endif
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index e5b8edf9c3..fef05a7939 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -66,6 +66,10 @@ public:
};
#endif
+namespace google_breakpad {
+ class ExceptionHandler; // See exception_handler.h
+}
+
class LL_COMMON_API LLApp : public LLOptionInterface
{
friend class LLErrorThread;
@@ -227,8 +231,20 @@ public:
void setupErrorHandling();
void setErrorHandler(LLAppErrorHandler handler);
- void setSyncErrorHandler(LLAppErrorHandler handler);
+ static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
//@}
+
+ // the maximum length of the minidump filename returned by getMiniDumpFilename()
+ static const U32 MAX_MINDUMP_PATH_LENGTH = 256;
+
+ // change the directory where Breakpad minidump files are written to
+ void setMiniDumpDir(const std::string &path);
+
+ // Return the Google Breakpad minidump filename after a crash.
+ char *getMiniDumpFilename() { return minidump_path; }
+
+ // Write out a Google Breakpad minidump file.
+ void writeMiniDump();
#if !LL_WINDOWS
//
@@ -286,15 +302,14 @@ protected:
private:
void startErrorThread();
- static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
- static void runSyncErrorHandler(); // run IMMEDIATELY when we get an error, ran in the context of the faulting thread.
+ // Contains the filename of the minidump file after a crash.
+ char minidump_path[MAX_MINDUMP_PATH_LENGTH];
// *NOTE: On Windows, we need a routine to reset the structured
// exception handler when some evil driver has taken it over for
// their own purposes
typedef int(*signal_handler_func)(int signum);
static LLAppErrorHandler sErrorHandler;
- static LLAppErrorHandler sSyncErrorHandler;
// Default application threads
LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
@@ -315,6 +330,8 @@ private:
private:
// the static application instance if it was created.
static LLApp* sApplication;
+
+ google_breakpad::ExceptionHandler * mExceptionHandler;
#if !LL_WINDOWS