Merging latest dessie/viewer-release into brad/viewer-public

master
Lynx Linden 2010-06-10 18:57:18 +01:00
commit 5f8cc41e79
27 changed files with 535 additions and 1534 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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.

View File

@ -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>

View File

@ -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:]))

View File

@ -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.

View File

@ -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

View File

@ -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);

View 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);

View File

@ -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()

View File

@ -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);

View File

@ -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)

View File

@ -57,7 +57,6 @@ protected:
virtual bool restoreErrorTrap();
virtual void handleCrashReporting(bool reportFreeze);
virtual void handleSyncCrashTrace();
virtual bool sendURLToOtherInstance(const std::string& url);

View File

@ -199,7 +199,6 @@
#include "llstartuplistener.h"
#if LL_WINDOWS
#include "llwindebug.h"
#include "lldxhardware.h"
#endif

View File

@ -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

View File

@ -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()

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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>