Merging latest dessie/viewer-release into brad/viewer-public
commit
5f8cc41e79
|
|
@ -19,7 +19,7 @@ if(WINDOWS)
|
|||
set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-win32")
|
||||
set(vivox_files
|
||||
SLVoice.exe
|
||||
libsndfile-1.dll
|
||||
libsndfile-1.dll
|
||||
vivoxplatform.dll
|
||||
vivoxsdk.dll
|
||||
ortp.dll
|
||||
|
|
@ -167,6 +167,7 @@ elseif(DARWIN)
|
|||
libexpat.dylib
|
||||
libllqtwebkit.dylib
|
||||
libndofdev.dylib
|
||||
libexception_handler.dylib
|
||||
)
|
||||
|
||||
# fmod is statically linked on darwin
|
||||
|
|
@ -216,6 +217,7 @@ elseif(LINUX)
|
|||
libapr-1.so.0
|
||||
libaprutil-1.so.0
|
||||
libatk-1.0.so
|
||||
libbreakpad_client.so.0
|
||||
libcrypto.so.0.9.7
|
||||
libdb-4.2.so
|
||||
libexpat.so
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# -*- cmake -*-
|
||||
include(Prebuilt)
|
||||
|
||||
if (STANDALONE)
|
||||
MESSAGE(FATAL_ERROR "*TODO standalone support for google breakad is unimplemented")
|
||||
# *TODO - implement this include(FindGoogleBreakpad)
|
||||
else (STANDALONE)
|
||||
use_prebuilt_binary(google_breakpad)
|
||||
if (DARWIN)
|
||||
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler)
|
||||
endif (DARWIN)
|
||||
if (LINUX)
|
||||
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client)
|
||||
endif (LINUX)
|
||||
if (WINDOWS)
|
||||
set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client common)
|
||||
endif (WINDOWS)
|
||||
endif (STANDALONE)
|
||||
|
||||
|
|
@ -120,7 +120,6 @@ LLCrashLoggerLinux::~LLCrashLoggerLinux(void)
|
|||
|
||||
void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
|
||||
{
|
||||
mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
|
||||
}
|
||||
|
||||
bool LLCrashLoggerLinux::mainLoop()
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -155,25 +155,6 @@ std::string getStartupStateFromLog(std::string& sllog)
|
|||
|
||||
void LLCrashLogger::gatherFiles()
|
||||
{
|
||||
|
||||
/*
|
||||
//TODO:This function needs to be reimplemented somewhere in here...
|
||||
if(!previous_crash && is_crash_log)
|
||||
{
|
||||
// Make sure the file isn't too old.
|
||||
double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
|
||||
|
||||
// llinfos << "age is " << age << llendl;
|
||||
|
||||
if(age > 60.0)
|
||||
{
|
||||
// The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
|
||||
llwarns << "File " << mFilename << " is too old!" << llendl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
updateApplication("Gathering logs...");
|
||||
|
||||
// Figure out the filename of the debug log
|
||||
|
|
@ -209,11 +190,9 @@ void LLCrashLogger::gatherFiles()
|
|||
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
|
||||
}
|
||||
|
||||
#if !LL_DARWIN
|
||||
if(mCrashInPreviousExec)
|
||||
#else
|
||||
#endif
|
||||
{
|
||||
// Restarting after freeze.
|
||||
// Replace the log file ext with .old, since the
|
||||
// instance that launched this process has overwritten
|
||||
// SecondLife.log
|
||||
|
|
@ -225,7 +204,12 @@ void LLCrashLogger::gatherFiles()
|
|||
gatherPlatformSpecificFiles();
|
||||
|
||||
//Use the debug log to reconstruct the URL to send the crash report to
|
||||
if(mDebugLog.has("CurrentSimHost"))
|
||||
if(mDebugLog.has("CrashHostUrl"))
|
||||
{
|
||||
// Crash log receiver has been manually configured.
|
||||
mCrashHost = mDebugLog["CrashHostUrl"].asString();
|
||||
}
|
||||
else if(mDebugLog.has("CurrentSimHost"))
|
||||
{
|
||||
mCrashHost = "https://";
|
||||
mCrashHost += mDebugLog["CurrentSimHost"].asString();
|
||||
|
|
@ -247,7 +231,6 @@ void LLCrashLogger::gatherFiles()
|
|||
|
||||
mCrashInfo["DebugLog"] = mDebugLog;
|
||||
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
|
||||
mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
||||
|
||||
updateApplication("Encoding files...");
|
||||
|
||||
|
|
@ -272,8 +255,30 @@ void LLCrashLogger::gatherFiles()
|
|||
trimSLLog(crash_info);
|
||||
}
|
||||
|
||||
mCrashInfo[(*itr).first] = rawstr_to_utf8(crash_info);
|
||||
mCrashInfo[(*itr).first] = LLStringFn::strip_invalid_xml(rawstr_to_utf8(crash_info));
|
||||
}
|
||||
|
||||
// Add minidump as binary.
|
||||
std::string minidump_path = mDebugLog["MinidumpPath"];
|
||||
if(minidump_path != "")
|
||||
{
|
||||
std::ifstream minidump_stream(minidump_path.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
if(minidump_stream.is_open())
|
||||
{
|
||||
minidump_stream.seekg(0, std::ios::end);
|
||||
size_t length = minidump_stream.tellg();
|
||||
minidump_stream.seekg(0, std::ios::beg);
|
||||
|
||||
LLSD::Binary data;
|
||||
data.resize(length);
|
||||
|
||||
minidump_stream.read(reinterpret_cast<char *>(&(data[0])),length);
|
||||
minidump_stream.close();
|
||||
|
||||
mCrashInfo["Minidump"] = data;
|
||||
}
|
||||
}
|
||||
mCrashInfo["DebugLog"].erase("MinidumpPath");
|
||||
}
|
||||
|
||||
LLSD LLCrashLogger::constructPostData()
|
||||
|
|
|
|||
|
|
@ -211,89 +211,6 @@ bool LLCrashLoggerMac::init(void)
|
|||
void LLCrashLoggerMac::gatherPlatformSpecificFiles()
|
||||
{
|
||||
updateApplication("Gathering hardware information...");
|
||||
char path[MAX_PATH];
|
||||
FSRef folder;
|
||||
|
||||
if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
|
||||
{
|
||||
// folder is an FSRef to ~/Library/Logs/
|
||||
if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
|
||||
{
|
||||
struct stat dw_stat;
|
||||
std::string mBuf;
|
||||
bool isLeopard = false;
|
||||
// Try the 10.3 path first...
|
||||
std::string dw_file_name = std::string(path) + std::string("/CrashReporter/Second Life.crash.log");
|
||||
int res = stat(dw_file_name.c_str(), &dw_stat);
|
||||
|
||||
if (res)
|
||||
{
|
||||
// Try the 10.2 one next...
|
||||
dw_file_name = std::string(path) + std::string("/Second Life.crash.log");
|
||||
res = stat(dw_file_name.c_str(), &dw_stat);
|
||||
}
|
||||
|
||||
if(res)
|
||||
{
|
||||
//10.5: Like 10.3+, except it puts the crash time in the file instead of dividing it up
|
||||
//using asterisks. Get a directory listing, search for files starting with second life,
|
||||
//use the last one found.
|
||||
std::string old_file_name, current_file_name, pathname, mask;
|
||||
pathname = std::string(path) + std::string("/CrashReporter/");
|
||||
mask = "Second Life*";
|
||||
while(gDirUtilp->getNextFileInDir(pathname, mask, current_file_name, false))
|
||||
{
|
||||
old_file_name = current_file_name;
|
||||
}
|
||||
if(old_file_name != "")
|
||||
{
|
||||
dw_file_name = pathname + old_file_name;
|
||||
res=stat(dw_file_name.c_str(), &dw_stat);
|
||||
isLeopard = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!res)
|
||||
{
|
||||
std::ifstream fp(dw_file_name.c_str());
|
||||
std::stringstream str;
|
||||
if(!fp.is_open()) return;
|
||||
str << fp.rdbuf();
|
||||
mBuf = str.str();
|
||||
|
||||
if(!isLeopard)
|
||||
{
|
||||
// Crash logs consist of a number of entries, one per crash.
|
||||
// Each entry is preceeded by "**********" on a line by itself.
|
||||
// We want only the most recent (i.e. last) one.
|
||||
const char *sep = "**********";
|
||||
const char *start = mBuf.c_str();
|
||||
const char *cur = start;
|
||||
const char *temp = strstr(cur, sep);
|
||||
|
||||
while(temp != NULL)
|
||||
{
|
||||
// Skip past the marker we just found
|
||||
cur = temp + strlen(sep); /* Flawfinder: ignore */
|
||||
|
||||
// and try to find another
|
||||
temp = strstr(cur, sep);
|
||||
}
|
||||
|
||||
// If there's more than one entry in the log file, strip all but the last one.
|
||||
if(cur != start)
|
||||
{
|
||||
mBuf.erase(0, cur - start);
|
||||
}
|
||||
}
|
||||
mCrashInfo["CrashLog"] = mBuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
llwarns << "Couldn't find any CrashReporter files..." << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LLCrashLoggerMac::mainLoop()
|
||||
|
|
|
|||
|
|
@ -1072,7 +1072,6 @@ set(viewer_HEADER_FILES
|
|||
llwearabletype.h
|
||||
llweb.h
|
||||
llwind.h
|
||||
llwindebug.h
|
||||
llwlanimator.h
|
||||
llwldaycycle.h
|
||||
llwlparammanager.h
|
||||
|
|
@ -1146,12 +1145,10 @@ endif (LINUX)
|
|||
if (WINDOWS)
|
||||
list(APPEND viewer_SOURCE_FILES
|
||||
llappviewerwin32.cpp
|
||||
llwindebug.cpp
|
||||
)
|
||||
|
||||
list(APPEND viewer_HEADER_FILES
|
||||
llappviewerwin32.h
|
||||
llwindebug.h
|
||||
)
|
||||
|
||||
# precompiled header configuration
|
||||
|
|
@ -1708,6 +1705,29 @@ if (LINUX)
|
|||
add_dependencies(package linux-updater-target)
|
||||
check_message_template(package)
|
||||
endif (NOT INSTALL)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched
|
||||
COMMAND ${PYTHON_EXECUTABLE}
|
||||
ARGS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
|
||||
--arch=${ARCH}
|
||||
--actions=copy
|
||||
--artwork=${ARTWORK_DIR}
|
||||
--build=${CMAKE_CURRENT_BINARY_DIR}
|
||||
--buildtype=${CMAKE_BUILD_TYPE}
|
||||
--configuration=${CMAKE_CFG_INTDIR}
|
||||
--dest=${CMAKE_CURRENT_BINARY_DIR}/packaged
|
||||
--grid=${GRID}
|
||||
--source=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py
|
||||
${COPY_INPUT_DEPENDENCIES}
|
||||
COMMENT "Performing viewer_manifest copy"
|
||||
)
|
||||
|
||||
add_custom_target(copy_l_viewer_manifest ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched)
|
||||
add_dependencies(copy_l_viewer_manifest "${VIEWER_BINARY_NAME}" linux-crash-logger-target linux-updater-target)
|
||||
endif (LINUX)
|
||||
|
||||
if (DARWIN)
|
||||
|
|
@ -1795,6 +1815,45 @@ if (INSTALL)
|
|||
include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake)
|
||||
endif (INSTALL)
|
||||
|
||||
if (PACKAGE)
|
||||
if (WINDOWS)
|
||||
set(VIEWER_DIST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
|
||||
set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-windows.tar.bz2")
|
||||
set(VIEWER_EXE_GLOBS "${VIEWER_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX} slplugin.exe")
|
||||
set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}")
|
||||
set(VIEWER_COPY_MANIFEST copy_w_viewer_manifest)
|
||||
endif (WINDOWS)
|
||||
if (DARWIN)
|
||||
set(VIEWER_DIST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${product}.app")
|
||||
set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin.tar.bz2")
|
||||
set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin")
|
||||
set(VIEWER_LIB_GLOB "*.dylib")
|
||||
endif (DARWIN)
|
||||
if (LINUX)
|
||||
set(VIEWER_DIST_DIR "${CMAKE_CURRENT_BINARY_DIR}/packaged")
|
||||
set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-linux.tar.bz2")
|
||||
set(VIEWER_EXE_GLOBS "do-not-directly-run-secondlife-bin SLPlugin")
|
||||
set(VIEWER_LIB_GLOB "*${CMAKE_SHARED_MODULE_SUFFIX}*")
|
||||
set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
|
||||
endif (LINUX)
|
||||
|
||||
add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
|
||||
COMMAND "${PYTHON_EXECUTABLE}"
|
||||
ARGS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
|
||||
"${VIEWER_DIST_DIR}"
|
||||
"${VIEWER_EXE_GLOBS}"
|
||||
"${VIEWER_LIB_GLOB}"
|
||||
"${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/bin/dump_syms"
|
||||
"${VIEWER_SYMBOL_FILE}"
|
||||
DEPENDS generate_breakpad_symbols.py
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(generate_breakpad_symbols ALL DEPENDS "${VIEWER_SYMBOL_FILE}")
|
||||
add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")
|
||||
add_dependencies(package generate_breakpad_symbols)
|
||||
endif (PACKAGE)
|
||||
|
||||
if (LL_TESTS)
|
||||
# To add a viewer unit test, just add the test .cpp file below
|
||||
# This creates a separate test project per file listed.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
<?xml version="1.0" ?>
|
||||
<llsd>
|
||||
<map>
|
||||
<key>CrashHostUrl</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>A URL pointing to a crash report handler; overrides cluster negotiation to locate crash handler.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string />
|
||||
</map>
|
||||
<key>AFKTimeout</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python
|
||||
# @file generate_breakpad_symbols.py
|
||||
# @author Brad Kittenbrink <brad@lindenlab.com>
|
||||
# @brief Simple tool for generating google_breakpad symbol information
|
||||
# for the crash reporter.
|
||||
#
|
||||
# $LicenseInfo:firstyear=2010&license=viewergpl$
|
||||
#
|
||||
# Copyright (c) 2010-2010, 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$
|
||||
|
||||
|
||||
import collections
|
||||
import fnmatch
|
||||
import itertools
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
import shlex
|
||||
import subprocess
|
||||
import tarfile
|
||||
import StringIO
|
||||
|
||||
def usage():
|
||||
print >>sys.stderr, "usage: %s viewer_dir viewer_exes libs_suffix dump_syms_tool viewer_symbol_file" % sys.argv[0]
|
||||
|
||||
class MissingModuleError(Exception):
|
||||
def __init__(self, modules):
|
||||
Exception.__init__(self, "Failed to find required modules: %r" % modules)
|
||||
self.modules = modules
|
||||
|
||||
def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file):
|
||||
print "generate_breakpad_symbols run with args: %s" % str((viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file))
|
||||
|
||||
# split up list of viewer_exes
|
||||
# "'Second Life' SLPlugin" becomes ['Second Life', 'SLPlugin']
|
||||
viewer_exes = shlex.split(viewer_exes)
|
||||
|
||||
found_required = dict([(module, False) for module in viewer_exes])
|
||||
|
||||
def matches(f):
|
||||
if f in viewer_exes:
|
||||
found_required[f] = True
|
||||
return True
|
||||
return fnmatch.fnmatch(f, libs_suffix)
|
||||
|
||||
def list_files():
|
||||
for (dirname, subdirs, filenames) in os.walk(viewer_dir):
|
||||
#print "scanning '%s' for modules..." % dirname
|
||||
for f in itertools.ifilter(matches, filenames):
|
||||
yield os.path.join(dirname, f)
|
||||
|
||||
def dump_module(m):
|
||||
print "dumping module '%s' with '%s'..." % (m, dump_syms_tool)
|
||||
child = subprocess.Popen([dump_syms_tool, m] , stdout=subprocess.PIPE)
|
||||
out, err = child.communicate()
|
||||
return (m,child.returncode, out, err)
|
||||
|
||||
out = tarfile.open(viewer_symbol_file, 'w:bz2')
|
||||
|
||||
for (filename,status,symbols,err) in itertools.imap(dump_module, list_files()):
|
||||
if status == 0:
|
||||
module_line = symbols[:symbols.index('\n')]
|
||||
module_line = module_line.split()
|
||||
hash_id = module_line[3]
|
||||
module = ' '.join(module_line[4:])
|
||||
if sys.platform in ['win32', 'cygwin']:
|
||||
mod_name = module[:module.rindex('.pdb')]
|
||||
else:
|
||||
mod_name = module
|
||||
symbolfile = StringIO.StringIO(symbols)
|
||||
info = tarfile.TarInfo("%(module)s/%(hash_id)s/%(mod_name)s.sym" % dict(module=module, hash_id=hash_id, mod_name=mod_name))
|
||||
info.size = symbolfile.len
|
||||
out.addfile(info, symbolfile)
|
||||
else:
|
||||
print >>sys.stderr, "warning: failed to dump symbols for '%s': %s" % (filename, err)
|
||||
|
||||
out.close()
|
||||
|
||||
missing_modules = [m for (m,_) in
|
||||
itertools.ifilter(lambda (k,v): not v, found_required.iteritems())
|
||||
]
|
||||
if missing_modules:
|
||||
print >> sys.stderr, "failed to generate %s" % viewer_symbol_file
|
||||
os.remove(viewer_symbol_file)
|
||||
raise MissingModuleError(missing_modules)
|
||||
|
||||
symbols = tarfile.open(viewer_symbol_file, 'r:bz2')
|
||||
tarfile_members = symbols.getnames()
|
||||
symbols.close()
|
||||
|
||||
for required_module in viewer_exes:
|
||||
def match_module_basename(m):
|
||||
return os.path.splitext(required_module)[0].lower() \
|
||||
== os.path.splitext(os.path.basename(m))[0].lower()
|
||||
# there must be at least one .sym file in tarfile_members that matches
|
||||
# each required module (ignoring file extensions)
|
||||
if not reduce(operator.or_, itertools.imap(match_module_basename, tarfile_members)):
|
||||
print >> sys.stderr, "failed to find required %s in generated %s" \
|
||||
% (required_module, viewer_symbol_file)
|
||||
os.remove(viewer_symbol_file)
|
||||
raise MissingModuleError([required_module])
|
||||
|
||||
print "successfully generated %s including required modules '%s'" % (viewer_symbol_file, viewer_exes)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 6:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
sys.exit(main(*sys.argv[1:]))
|
||||
|
||||
|
|
@ -103,8 +103,8 @@
|
|||
// Third party library includes
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include "llwindebug.h"
|
||||
# include <share.h> // For _SH_DENYWR in initMarkerFile
|
||||
#else
|
||||
# include <sys/file.h> // For initMarkerFile support
|
||||
|
|
@ -599,6 +599,11 @@ bool LLAppViewer::init()
|
|||
if (!initConfiguration())
|
||||
return false;
|
||||
|
||||
// write Google Breakpad minidump files to our log directory
|
||||
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
|
||||
logdir += gDirUtilp->getDirDelimiter();
|
||||
setMiniDumpDir(logdir);
|
||||
|
||||
// Although initLogging() is the right place to mess with
|
||||
// setFatalFunction(), we can't query gSavedSettings until after
|
||||
// initConfiguration().
|
||||
|
|
@ -1239,6 +1244,14 @@ bool LLAppViewer::cleanup()
|
|||
// workaround for DEV-35406 crash on shutdown
|
||||
LLEventPumps::instance().reset();
|
||||
|
||||
// remove any old breakpad minidump files from the log directory
|
||||
if (! isError())
|
||||
{
|
||||
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
|
||||
logdir += gDirUtilp->getDirDelimiter();
|
||||
gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
|
||||
}
|
||||
|
||||
// *TODO - generalize this and move DSO wrangling to a helper class -brad
|
||||
std::set<struct apr_dso_handle_t *>::const_iterator i;
|
||||
for(i = mPlugins.begin(); i != mPlugins.end(); ++i)
|
||||
|
|
@ -2295,17 +2308,7 @@ void LLAppViewer::checkForCrash(void)
|
|||
{
|
||||
|
||||
#if LL_SEND_CRASH_REPORTS
|
||||
//*NOTE:Mani The current state of the crash handler has the MacOSX
|
||||
// sending all crash reports as freezes, in order to let
|
||||
// the MacOSX CrashRepoter generate stacks before spawning the
|
||||
// SL crash logger.
|
||||
// The Linux and Windows clients generate their own stacks and
|
||||
// spawn the SL crash logger immediately. This may change in the future.
|
||||
#if LL_DARWIN
|
||||
if(gLastExecEvent != LAST_EXEC_NORMAL)
|
||||
#else
|
||||
if (gLastExecEvent == LAST_EXEC_FROZE)
|
||||
#endif
|
||||
{
|
||||
llinfos << "Last execution froze, requesting to send crash report." << llendl;
|
||||
//
|
||||
|
|
@ -2515,6 +2518,15 @@ void LLAppViewer::writeSystemInfo()
|
|||
// If the crash is handled by LLAppViewer::handleViewerCrash, ie not a freeze,
|
||||
// then the value of "CrashNotHandled" will be set to true.
|
||||
gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)true;
|
||||
|
||||
// Insert crash host url (url to post crash log to) if configured. This insures
|
||||
// that the crash report will go to the proper location in the case of a
|
||||
// prior freeze.
|
||||
std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
|
||||
if(crashHostUrl != "")
|
||||
{
|
||||
gDebugInfo["CrashHostUrl"] = crashHostUrl;
|
||||
}
|
||||
|
||||
// Dump some debugging info
|
||||
LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME")
|
||||
|
|
@ -2536,13 +2548,6 @@ void LLAppViewer::writeSystemInfo()
|
|||
writeDebugInfo(); // Save out debug_info.log early, in case of crash.
|
||||
}
|
||||
|
||||
void LLAppViewer::handleSyncViewerCrash()
|
||||
{
|
||||
LLAppViewer* pApp = LLAppViewer::instance();
|
||||
// Call to pure virtual, handled by platform specific llappviewer instance.
|
||||
pApp->handleSyncCrashTrace();
|
||||
}
|
||||
|
||||
void LLAppViewer::handleViewerCrash()
|
||||
{
|
||||
llinfos << "Handle viewer crash entry." << llendl;
|
||||
|
|
@ -2566,9 +2571,13 @@ void LLAppViewer::handleViewerCrash()
|
|||
return;
|
||||
}
|
||||
pApp->mReportedCrash = TRUE;
|
||||
|
||||
// Make sure the watchdog gets turned off...
|
||||
// pApp->destroyMainloopTimeout(); // SJB: Bah. This causes the crash handler to hang, not sure why.
|
||||
|
||||
// Insert crash host url (url to post crash log to) if configured.
|
||||
std::string crashHostUrl = gSavedSettings.get<std::string>("CrashHostUrl");
|
||||
if(crashHostUrl != "")
|
||||
{
|
||||
gDebugInfo["CrashHostUrl"] = crashHostUrl;
|
||||
}
|
||||
|
||||
//We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version
|
||||
//to check against no matter what
|
||||
|
|
@ -2600,6 +2609,12 @@ void LLAppViewer::handleViewerCrash()
|
|||
gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin();
|
||||
gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
|
||||
|
||||
char *minidump_file = pApp->getMiniDumpFilename();
|
||||
if(minidump_file && minidump_file[0] != 0)
|
||||
{
|
||||
gDebugInfo["MinidumpPath"] = minidump_file;
|
||||
}
|
||||
|
||||
if(gLogoutInProgress)
|
||||
{
|
||||
gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
|
||||
|
|
@ -2677,10 +2692,6 @@ void LLAppViewer::handleViewerCrash()
|
|||
|
||||
LLError::logToFile("");
|
||||
|
||||
// On Mac, we send the report on the next run, since we need macs crash report
|
||||
// for a stack trace, so we have to let it the app fail.
|
||||
#if !LL_DARWIN
|
||||
|
||||
// Remove the marker file, since otherwise we'll spawn a process that'll keep it locked
|
||||
if(gDebugInfo["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH)
|
||||
{
|
||||
|
|
@ -2693,8 +2704,6 @@ void LLAppViewer::handleViewerCrash()
|
|||
|
||||
// Call to pure virtual, handled by platform specific llappviewer instance.
|
||||
pApp->handleCrashReporting();
|
||||
|
||||
#endif //!LL_DARWIN
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -3338,13 +3347,6 @@ void LLAppViewer::badNetworkHandler()
|
|||
|
||||
mPurgeOnExit = TRUE;
|
||||
|
||||
#if LL_WINDOWS
|
||||
// Generates the minidump.
|
||||
LLWinDebug::generateCrashStacks(NULL);
|
||||
#endif
|
||||
LLAppViewer::handleSyncViewerCrash();
|
||||
LLAppViewer::handleViewerCrash();
|
||||
|
||||
std::ostringstream message;
|
||||
message <<
|
||||
"The viewer has detected mangled network data indicative\n"
|
||||
|
|
@ -3357,6 +3359,8 @@ void LLAppViewer::badNetworkHandler()
|
|||
"If the problem continues, see the Tech Support FAQ at: \n"
|
||||
"www.secondlife.com/support";
|
||||
forceDisconnect(message.str());
|
||||
|
||||
LLApp::instance()->writeMiniDump();
|
||||
}
|
||||
|
||||
// This routine may get called more than once during the shutdown process.
|
||||
|
|
|
|||
|
|
@ -92,9 +92,7 @@ public:
|
|||
virtual bool restoreErrorTrap() = 0; // Require platform specific override to reset error handling mechanism.
|
||||
// return false if the error trap needed restoration.
|
||||
virtual void handleCrashReporting(bool reportFreeze = false) = 0; // What to do with crash report?
|
||||
virtual void handleSyncCrashTrace() = 0; // any low-level crash-prep that has to happen in the context of the crashing thread before the crash report is delivered.
|
||||
static void handleViewerCrash(); // Hey! The viewer crashed. Do this, soon.
|
||||
static void handleSyncViewerCrash(); // Hey! The viewer crashed. Do this right NOW in the context of the crashing thread.
|
||||
void checkForCrash();
|
||||
|
||||
// Thread accessors
|
||||
|
|
|
|||
|
|
@ -46,23 +46,6 @@
|
|||
|
||||
#include <exception>
|
||||
|
||||
#if LL_LINUX
|
||||
# include <dlfcn.h> // RTLD_LAZY
|
||||
# include <execinfo.h> // backtrace - glibc only
|
||||
#elif LL_SOLARIS
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
# include <fcntl.h>
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
|
||||
#ifdef LL_ELFBIN
|
||||
# ifdef __GNUC__
|
||||
# include <cxxabi.h> // for symbol demangling
|
||||
# endif
|
||||
# include "ELFIO/ELFIO.h" // for better backtraces
|
||||
#endif
|
||||
|
||||
#if LL_DBUS_ENABLED
|
||||
# include "llappviewerlinux_api_dbus.h"
|
||||
|
||||
|
|
@ -86,7 +69,6 @@ static void exceptionTerminateHandler()
|
|||
// reinstall default terminate() handler in case we re-terminate.
|
||||
if (gOldTerminateHandler) std::set_terminate(gOldTerminateHandler);
|
||||
// treat this like a regular viewer crash, with nice stacktrace etc.
|
||||
LLAppViewer::handleSyncViewerCrash();
|
||||
LLAppViewer::handleViewerCrash();
|
||||
// we've probably been killed-off before now, but...
|
||||
gOldTerminateHandler(); // call old terminate() handler
|
||||
|
|
@ -109,7 +91,6 @@ int main( int argc, char **argv )
|
|||
gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler);
|
||||
// install crash handlers
|
||||
viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
|
||||
viewer_app_ptr->setSyncErrorHandler(LLAppViewer::handleSyncViewerCrash);
|
||||
|
||||
bool ok = viewer_app_ptr->init();
|
||||
if(!ok)
|
||||
|
|
@ -138,201 +119,6 @@ int main( int argc, char **argv )
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LL_SOLARIS
|
||||
static inline BOOL do_basic_glibc_backtrace()
|
||||
{
|
||||
BOOL success = FALSE;
|
||||
|
||||
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
||||
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
||||
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w");
|
||||
if (!StraceFile)
|
||||
{
|
||||
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
||||
StraceFile = stderr;
|
||||
}
|
||||
|
||||
printstack(fileno(StraceFile));
|
||||
|
||||
if (StraceFile != stderr)
|
||||
fclose(StraceFile);
|
||||
|
||||
return success;
|
||||
}
|
||||
#else
|
||||
#define MAX_STACK_TRACE_DEPTH 40
|
||||
// This uses glibc's basic built-in stack-trace functions for a not very
|
||||
// amazing backtrace.
|
||||
static inline BOOL do_basic_glibc_backtrace()
|
||||
{
|
||||
void *stackarray[MAX_STACK_TRACE_DEPTH];
|
||||
size_t size;
|
||||
char **strings;
|
||||
size_t i;
|
||||
BOOL success = FALSE;
|
||||
|
||||
size = backtrace(stackarray, MAX_STACK_TRACE_DEPTH);
|
||||
strings = backtrace_symbols(stackarray, size);
|
||||
|
||||
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
||||
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
||||
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w"); // Flawfinder: ignore
|
||||
if (!StraceFile)
|
||||
{
|
||||
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
||||
StraceFile = stderr;
|
||||
}
|
||||
|
||||
if (size)
|
||||
{
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
// the format of the StraceFile is very specific, to allow (kludgy) machine-parsing
|
||||
fprintf(StraceFile, "%-3lu ", (unsigned long)i);
|
||||
fprintf(StraceFile, "%-32s\t", "unknown");
|
||||
fprintf(StraceFile, "%p ", stackarray[i]);
|
||||
fprintf(StraceFile, "%s\n", strings[i]);
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
if (StraceFile != stderr)
|
||||
fclose(StraceFile);
|
||||
|
||||
free (strings);
|
||||
return success;
|
||||
}
|
||||
|
||||
#if LL_ELFBIN
|
||||
// This uses glibc's basic built-in stack-trace functions together with
|
||||
// ELFIO's ability to parse the .symtab ELF section for better symbol
|
||||
// extraction without exporting symbols (which'd cause subtle, fatal bugs).
|
||||
static inline BOOL do_elfio_glibc_backtrace()
|
||||
{
|
||||
void *stackarray[MAX_STACK_TRACE_DEPTH];
|
||||
size_t btsize;
|
||||
char **strings;
|
||||
BOOL success = FALSE;
|
||||
|
||||
std::string appfilename = gDirUtilp->getExecutablePathAndName();
|
||||
|
||||
std::string strace_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
||||
llinfos << "Opening stack trace file " << strace_filename << llendl;
|
||||
LLFILE* StraceFile = LLFile::fopen(strace_filename, "w"); // Flawfinder: ignore
|
||||
if (!StraceFile)
|
||||
{
|
||||
llinfos << "Opening stack trace file " << strace_filename << " failed. Using stderr." << llendl;
|
||||
StraceFile = stderr;
|
||||
}
|
||||
|
||||
// get backtrace address list and basic symbol info
|
||||
btsize = backtrace(stackarray, MAX_STACK_TRACE_DEPTH);
|
||||
strings = backtrace_symbols(stackarray, btsize);
|
||||
|
||||
// create ELF reader for our app binary
|
||||
IELFI* pReader;
|
||||
const IELFISection* pSec = NULL;
|
||||
IELFISymbolTable* pSymTbl = 0;
|
||||
if (ERR_ELFIO_NO_ERROR != ELFIO::GetInstance()->CreateELFI(&pReader) ||
|
||||
ERR_ELFIO_NO_ERROR != pReader->Load(appfilename.c_str()) ||
|
||||
// find symbol table, create reader-object
|
||||
NULL == (pSec = pReader->GetSection( ".symtab" )) ||
|
||||
ERR_ELFIO_NO_ERROR != pReader->CreateSectionReader(IELFI::ELFI_SYMBOL, pSec, (void**)&pSymTbl) )
|
||||
{
|
||||
// Failed to open our binary and read its symbol table somehow
|
||||
llinfos << "Could not initialize ELF symbol reading - doing basic backtrace." << llendl;
|
||||
if (StraceFile != stderr)
|
||||
fclose(StraceFile);
|
||||
// note that we may be leaking some of the above ELFIO
|
||||
// objects now, but it's expected that we'll be dead soon
|
||||
// and we want to tread delicately until we get *some* kind
|
||||
// of useful backtrace.
|
||||
return do_basic_glibc_backtrace();
|
||||
}
|
||||
|
||||
// iterate over trace and symtab, looking for plausible symbols
|
||||
std::string name;
|
||||
Elf32_Addr value;
|
||||
Elf32_Word ssize;
|
||||
unsigned char bind;
|
||||
unsigned char type;
|
||||
Elf32_Half section;
|
||||
int nSymNo = pSymTbl->GetSymbolNum();
|
||||
size_t btpos;
|
||||
for (btpos = 0; btpos < btsize; ++btpos)
|
||||
{
|
||||
// the format of the StraceFile is very specific, to allow (kludgy) machine-parsing
|
||||
fprintf(StraceFile, "%-3ld ", (long)btpos);
|
||||
int symidx;
|
||||
for (symidx = 0; symidx < nSymNo; ++symidx)
|
||||
{
|
||||
if (ERR_ELFIO_NO_ERROR ==
|
||||
pSymTbl->GetSymbol(symidx, name, value, ssize,
|
||||
bind, type, section))
|
||||
{
|
||||
// check if trace address within symbol range
|
||||
if (uintptr_t(stackarray[btpos]) >= value &&
|
||||
uintptr_t(stackarray[btpos]) < value+ssize)
|
||||
{
|
||||
// symbol is inside viewer
|
||||
fprintf(StraceFile, "%-32s\t", "com.secondlife.indra.viewer");
|
||||
fprintf(StraceFile, "%p ", stackarray[btpos]);
|
||||
|
||||
char *demangled_str = NULL;
|
||||
int demangle_result = 1;
|
||||
demangled_str =
|
||||
abi::__cxa_demangle
|
||||
(name.c_str(), NULL, NULL,
|
||||
&demangle_result);
|
||||
if (0 == demangle_result &&
|
||||
NULL != demangled_str) {
|
||||
fprintf(StraceFile,
|
||||
"%s", demangled_str);
|
||||
free(demangled_str);
|
||||
}
|
||||
else // failed demangle; print it raw
|
||||
{
|
||||
fprintf(StraceFile,
|
||||
"%s", name.c_str());
|
||||
}
|
||||
// print offset from symbol start
|
||||
fprintf(StraceFile,
|
||||
" + %lu\n",
|
||||
uintptr_t(stackarray[btpos]) -
|
||||
value);
|
||||
goto got_sym; // early escape
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback:
|
||||
// Didn't find a suitable symbol in the binary - it's probably
|
||||
// a symbol in a DSO; use glibc's idea of what it should be.
|
||||
fprintf(StraceFile, "%-32s\t", "unknown");
|
||||
fprintf(StraceFile, "%p ", stackarray[btpos]);
|
||||
fprintf(StraceFile, "%s\n", strings[btpos]);
|
||||
got_sym:;
|
||||
}
|
||||
|
||||
if (StraceFile != stderr)
|
||||
fclose(StraceFile);
|
||||
|
||||
pSymTbl->Release();
|
||||
pSec->Release();
|
||||
pReader->Release();
|
||||
|
||||
free(strings);
|
||||
|
||||
llinfos << "Finished generating stack trace." << llendl;
|
||||
|
||||
success = TRUE;
|
||||
return success;
|
||||
}
|
||||
#endif // LL_ELFBIN
|
||||
|
||||
#endif // LL_SOLARIS
|
||||
|
||||
|
||||
LLAppViewerLinux::LLAppViewerLinux()
|
||||
{
|
||||
}
|
||||
|
|
@ -541,16 +327,6 @@ bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url)
|
|||
}
|
||||
#endif // LL_DBUS_ENABLED
|
||||
|
||||
void LLAppViewerLinux::handleSyncCrashTrace()
|
||||
{
|
||||
// This backtrace writes into stack_trace.log
|
||||
# if LL_ELFBIN
|
||||
do_elfio_glibc_backtrace(); // more useful backtrace
|
||||
# else
|
||||
do_basic_glibc_backtrace(); // only slightly useful backtrace
|
||||
# endif // LL_ELFBIN
|
||||
}
|
||||
|
||||
void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
|
||||
{
|
||||
std::string cmd =gDirUtilp->getExecutableDir();
|
||||
|
|
@ -686,6 +462,8 @@ bool LLAppViewerLinux::beingDebugged()
|
|||
bool LLAppViewerLinux::initLogging()
|
||||
{
|
||||
// Remove the last stack trace, if any
|
||||
// This file is no longer created, since the move to Google Breakpad
|
||||
// The code is left here to clean out any old state in the log dir
|
||||
std::string old_stack_file =
|
||||
gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
||||
LLFile::remove(old_stack_file);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ protected:
|
|||
|
||||
virtual bool restoreErrorTrap();
|
||||
virtual void handleCrashReporting(bool reportFreeze);
|
||||
virtual void handleSyncCrashTrace();
|
||||
|
||||
virtual bool initLogging();
|
||||
virtual bool initParseCommandLine(LLCommandLineParser& clp);
|
||||
|
|
|
|||
|
|
@ -264,11 +264,6 @@ bool LLAppViewerMacOSX::restoreErrorTrap()
|
|||
return reset_count == 0;
|
||||
}
|
||||
|
||||
void LLAppViewerMacOSX::handleSyncCrashTrace()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
static OSStatus CarbonEventHandler(EventHandlerCallRef inHandlerCallRef,
|
||||
EventRef inEvent,
|
||||
void* inUserData)
|
||||
|
|
@ -384,38 +379,6 @@ void LLAppViewerMacOSX::handleCrashReporting(bool reportFreeze)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if(!reportFreeze)
|
||||
{
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// TODO from palmer: Find a better way to handle managing old crash logs
|
||||
// when this is a separate imbedable module. Ideally just sort crash stack
|
||||
// logs based on date, and grab the latest one as opposed to deleting them
|
||||
// for thoughts on what the module would look like.
|
||||
// See: https://wiki.lindenlab.com/wiki/Viewer_Crash_Reporter_Round_4
|
||||
|
||||
// Remove the crash stack log from previous executions.
|
||||
// Since we've started logging a new instance of the app, we can assume
|
||||
// The old crash stack is invalid for the next crash report.
|
||||
char path[MAX_PATH];
|
||||
FSRef folder;
|
||||
if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
|
||||
{
|
||||
// folder is an FSRef to ~/Library/Logs/
|
||||
if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
|
||||
{
|
||||
std::string pathname = std::string(path) + std::string("/CrashReporter/");
|
||||
std::string mask = "Second Life*";
|
||||
std::string file_name;
|
||||
while(gDirUtilp->getNextFileInDir(pathname, mask, file_name, false))
|
||||
{
|
||||
LLFile::remove(pathname + file_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string LLAppViewerMacOSX::generateSerialNumber()
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ public:
|
|||
protected:
|
||||
virtual bool restoreErrorTrap();
|
||||
virtual void handleCrashReporting(bool reportFreeze);
|
||||
virtual void handleSyncCrashTrace();
|
||||
|
||||
std::string generateSerialNumber();
|
||||
virtual bool initParseCommandLine(LLCommandLineParser& clp);
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@
|
|||
#include "llweb.h"
|
||||
#include "llsecondlifeurls.h"
|
||||
|
||||
#include "llwindebug.h"
|
||||
|
||||
#include "llviewernetwork.h"
|
||||
#include "llmd5.h"
|
||||
#include "llfindlocale.h"
|
||||
|
|
@ -81,51 +79,6 @@ extern "C" {
|
|||
|
||||
const std::string LLAppViewerWin32::sWindowClass = "Second Life";
|
||||
|
||||
LONG WINAPI viewer_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop)
|
||||
{
|
||||
// *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
|
||||
_tprintf( _T("Entering Windows Exception Handler...\n") );
|
||||
llinfos << "Entering Windows Exception Handler..." << llendl;
|
||||
|
||||
// Make sure the user sees something to indicate that the app crashed.
|
||||
LONG retval;
|
||||
|
||||
if (LLApp::isError())
|
||||
{
|
||||
_tprintf( _T("Got another fatal signal while in the error handler, die now!\n") );
|
||||
llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
|
||||
|
||||
retval = EXCEPTION_EXECUTE_HANDLER;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Generate a minidump if we can.
|
||||
// Before we wake the error thread...
|
||||
// Which will start the crash reporting.
|
||||
LLWinDebug::generateCrashStacks(exception_infop);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
//
|
||||
// At this point, we always want to exit the app. There's no graceful
|
||||
// recovery for an unhandled exception.
|
||||
//
|
||||
// Just kill the process.
|
||||
retval = EXCEPTION_EXECUTE_HANDLER;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Create app mutex creates a unique global windows object.
|
||||
// If the object can be created it returns true, otherwise
|
||||
// it returns false. The false result can be used to determine
|
||||
|
|
@ -191,8 +144,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
|
|||
gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
|
||||
|
||||
LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine);
|
||||
|
||||
LLWinDebug::initExceptionHandler(viewer_windows_exception_handler);
|
||||
|
||||
viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
|
||||
|
||||
|
|
@ -405,12 +356,6 @@ bool LLAppViewerWin32::cleanup()
|
|||
|
||||
bool LLAppViewerWin32::initLogging()
|
||||
{
|
||||
// Remove the crash stack log from previous executions.
|
||||
// Since we've started logging a new instance of the app, we can assume
|
||||
// *NOTE: This should happen before the we send a 'previous instance froze'
|
||||
// crash report, but it must happen after we initialize the DirUtil.
|
||||
LLWinDebug::clearCrashStacks();
|
||||
|
||||
return LLAppViewer::initLogging();
|
||||
}
|
||||
|
||||
|
|
@ -529,13 +474,9 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
|
|||
}
|
||||
|
||||
bool LLAppViewerWin32::restoreErrorTrap()
|
||||
{
|
||||
return LLWinDebug::checkExceptionHandler();
|
||||
}
|
||||
|
||||
void LLAppViewerWin32::handleSyncCrashTrace()
|
||||
{
|
||||
// do nothing
|
||||
{
|
||||
return true;
|
||||
//return LLWinDebug::checkExceptionHandler();
|
||||
}
|
||||
|
||||
void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ protected:
|
|||
|
||||
virtual bool restoreErrorTrap();
|
||||
virtual void handleCrashReporting(bool reportFreeze);
|
||||
virtual void handleSyncCrashTrace();
|
||||
|
||||
virtual bool sendURLToOtherInstance(const std::string& url);
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,6 @@
|
|||
#include "llstartuplistener.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include "llwindebug.h"
|
||||
#include "lldxhardware.h"
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -109,10 +109,6 @@
|
|||
#include <boost/algorithm/string/split.hpp> //
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#if LL_WINDOWS // For Windows specific error handler
|
||||
#include "llwindebug.h" // For the invalid message handler
|
||||
#endif
|
||||
|
||||
#include "llnotificationmanager.h" //
|
||||
|
||||
#if LL_MSVC
|
||||
|
|
|
|||
|
|
@ -402,19 +402,16 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump)
|
|||
|
||||
void LLVivoxVoiceClient::terminate()
|
||||
{
|
||||
|
||||
// leaveAudioSession();
|
||||
logout();
|
||||
// As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to.
|
||||
// ms_sleep(2000);
|
||||
connectorShutdown();
|
||||
closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
|
||||
|
||||
// This will do unpleasant things on windows.
|
||||
// killGateway();
|
||||
|
||||
|
||||
|
||||
if(mConnected)
|
||||
{
|
||||
logout();
|
||||
connectorShutdown();
|
||||
closeSocket(); // Need to do this now -- bad things happen if the destructor does it later.
|
||||
}
|
||||
else
|
||||
{
|
||||
killGateway();
|
||||
}
|
||||
}
|
||||
|
||||
const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion()
|
||||
|
|
|
|||
|
|
@ -1,912 +0,0 @@
|
|||
/**
|
||||
* @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);
|
||||
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);
|
||||
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);
|
||||
|
||||
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();
|
||||
|
||||
if(gSavedSettings.getControl("SaveMinidump").notNull() && gSavedSettings.getBOOL("SaveMinidump"))
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* @file llwindebug.h
|
||||
* @brief LLWinDebug class header file
|
||||
*
|
||||
* $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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLWINDEBUG_H
|
||||
#define LL_LLWINDEBUG_H
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include <dbghelp.h>
|
||||
|
||||
class LLWinDebug
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief initialize the llwindebug exception filter callback
|
||||
*
|
||||
* Hand a windows unhandled exception filter to LLWinDebug
|
||||
* This method should only be called to change the
|
||||
* exception filter used by llwindebug.
|
||||
*
|
||||
* Setting filter_func to NULL will clear any custom filters.
|
||||
**/
|
||||
static void initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func);
|
||||
|
||||
/**
|
||||
* @brief check the status of the exception filter.
|
||||
*
|
||||
* Resets unhandled exception filter to the filter specified
|
||||
* w/ initExceptionFilter).
|
||||
* Returns false if the exception filter was modified.
|
||||
*
|
||||
* *NOTE:Mani In the past mozlib has been accused of
|
||||
* overriding the exception filter. If the mozlib filter
|
||||
* is required, perhaps we can chain calls from our
|
||||
* filter to mozlib's.
|
||||
**/
|
||||
static bool checkExceptionHandler();
|
||||
|
||||
static void generateCrashStacks(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL);
|
||||
static void clearCrashStacks(); // Delete the crash stack file(s).
|
||||
|
||||
static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // LL_LLWINDEBUG_H
|
||||
|
|
@ -41,9 +41,12 @@ from llmanifest import LLManifest, main, proper_windows_path, path_ancestors
|
|||
|
||||
class ViewerManifest(LLManifest):
|
||||
def is_packaging_viewer(self):
|
||||
# This is overridden by the WindowsManifest sub-class,
|
||||
# which has different behavior if it is not packaging the viewer.
|
||||
return True
|
||||
# Some commands, files will only be included
|
||||
# if we are packaging the viewer on windows.
|
||||
# This manifest is also used to copy
|
||||
# files during the build (see copy_w_viewer_manifest
|
||||
# and copy_l_viewer_manifest targets)
|
||||
return 'package' in self.args['actions']
|
||||
|
||||
def construct(self):
|
||||
super(ViewerManifest, self).construct()
|
||||
|
|
@ -175,13 +178,6 @@ class WindowsManifest(ViewerManifest):
|
|||
else:
|
||||
return ''.join(self.channel().split()) + '.exe'
|
||||
|
||||
def is_packaging_viewer(self):
|
||||
# Some commands, files will only be included
|
||||
# if we are packaging the viewer on windows.
|
||||
# This manifest is also used to copy
|
||||
# files during the build.
|
||||
return 'package' in self.args['actions']
|
||||
|
||||
def test_msvcrt_and_copy_action(self, src, dst):
|
||||
# This is used to test a dll manifest.
|
||||
# It is used as a temporary override during the construct method
|
||||
|
|
@ -641,7 +637,9 @@ class DarwinManifest(ViewerManifest):
|
|||
if dylibs["llcommon"]:
|
||||
for libfile in ("libapr-1.0.3.7.dylib",
|
||||
"libaprutil-1.0.3.8.dylib",
|
||||
"libexpat.0.5.0.dylib"):
|
||||
"libexpat.0.5.0.dylib",
|
||||
"libexception_handler.dylib",
|
||||
):
|
||||
self.path(os.path.join(libdir, libfile), libfile)
|
||||
|
||||
#libfmodwrapper.dylib
|
||||
|
|
@ -662,7 +660,9 @@ class DarwinManifest(ViewerManifest):
|
|||
for libfile in ("libllcommon.dylib",
|
||||
"libapr-1.0.3.7.dylib",
|
||||
"libaprutil-1.0.3.8.dylib",
|
||||
"libexpat.0.5.0.dylib"):
|
||||
"libexpat.0.5.0.dylib",
|
||||
"libexception_handler.dylib",
|
||||
):
|
||||
target_lib = os.path.join('../../..', libfile)
|
||||
self.run_command("ln -sf %(target)r %(link)r" %
|
||||
{'target': target_lib,
|
||||
|
|
@ -893,6 +893,7 @@ class Linux_i686Manifest(LinuxManifest):
|
|||
if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
|
||||
self.path("libapr-1.so.0")
|
||||
self.path("libaprutil-1.so.0")
|
||||
self.path("libbreakpad_client.so.0.0.0", "libbreakpad_client.so.0")
|
||||
self.path("libdb-4.2.so")
|
||||
self.path("libcrypto.so.0.9.7")
|
||||
self.path("libexpat.so.1")
|
||||
|
|
@ -930,7 +931,7 @@ class Linux_i686Manifest(LinuxManifest):
|
|||
self.path("libvivoxplatform.so")
|
||||
self.end_prefix("lib")
|
||||
|
||||
if self.args['buildtype'].lower() == 'release':
|
||||
if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
|
||||
print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
|
||||
self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
|
||||
|
||||
|
|
|
|||
|
|
@ -299,7 +299,6 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
|
|||
// At this point we're responsive enough the user could click the close button
|
||||
SetCursor(gCursorArrow);
|
||||
mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo();
|
||||
mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLifeException.log");
|
||||
}
|
||||
|
||||
bool LLCrashLoggerWindows::mainLoop()
|
||||
|
|
|
|||
33
install.xml
33
install.xml
|
|
@ -583,6 +583,39 @@
|
|||
</map>
|
||||
</map>
|
||||
</map>
|
||||
<key>google_breakpad</key>
|
||||
<map>
|
||||
<key>copyright</key>
|
||||
<string>Copyright (c) 2006, Google Inc.</string>
|
||||
<key>description</key>
|
||||
<string>An open-source multi-platform crash reporting system </string>
|
||||
<key>license</key>
|
||||
<string>bsd</string>
|
||||
<key>packages</key>
|
||||
<map>
|
||||
<key>darwin</key>
|
||||
<map>
|
||||
<key>md5sum</key>
|
||||
<string>ced4010b59f1a579caa7fe3c18512499</string>
|
||||
<key>url</key>
|
||||
<uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google_breakpad-0.0.0-rev599-darwin-20100528a.tar.bz2</uri>
|
||||
</map>
|
||||
<key>linux</key>
|
||||
<map>
|
||||
<key>md5sum</key>
|
||||
<string>29c3e7dad60bbf02c811786436d99523</string>
|
||||
<key>url</key>
|
||||
<uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/google_breakpad-0.0.0-rev599-linux-20100521b.tar.bz2</uri>
|
||||
</map>
|
||||
<key>windows</key>
|
||||
<map>
|
||||
<key>md5sum</key>
|
||||
<string>0859d47242990125f17eaab30bece2ff</string>
|
||||
<key>url</key>
|
||||
<uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/google_breakpad-0.0.0-rev599-windows-20100524.tar.bz2</uri>
|
||||
</map>
|
||||
</map>
|
||||
</map>
|
||||
<key>googlemock</key>
|
||||
<map>
|
||||
<key>copyright</key>
|
||||
|
|
|
|||
Loading…
Reference in New Issue