Breakpad3: WIP, minidump files created

TODO: re-work sending crash logs
get rid of standalone loggers
This commit is contained in:
Latif Khalifa
2013-10-01 13:43:45 +02:00
parent 6c45bf0353
commit c87f7b0576
29 changed files with 1745 additions and 1460 deletions

View File

@@ -16,7 +16,7 @@ set(CMAKE_CXX_FLAGS_RELEASE
set(CMAKE_C_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
"-DLL_RELEASE=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
"-DLL_RELEASE=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=1 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
# Don't bother with a MinSizeRel build.

View File

@@ -7,11 +7,13 @@ include(00-Common)
include(LLCommon)
include(APR)
include(Linking)
include(GoogleBreakpad)
include_directories(
${EXPAT_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${BREAKPAD_INCLUDE_DIRECTORIES}
)
set(llcommon_SOURCE_FILES
@@ -265,6 +267,7 @@ add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
add_dependencies(llcommon prepare)
target_link_libraries(
llcommon
${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}

View File

@@ -27,6 +27,14 @@
#include "linden_common.h"
#include "llapp.h"
#include <cstdlib>
#ifdef LL_DARWIN
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>
#endif
#include "llcommon.h"
#include "llapr.h"
#include "llerrorcontrol.h"
@@ -35,7 +43,9 @@
#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
@@ -43,13 +53,34 @@
// Windows uses structured exceptions, so it's handled a bit differently.
//
#if LL_WINDOWS
#include "windows.h"
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 *);
#if LL_LINUX
#include "google_breakpad/minidump_descriptor.h"
static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc,
void* context,
bool succeeded);
#else
// 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);
#endif
# if LL_DARWIN
/* OSX doesn't support SIGRT* */
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
@@ -79,7 +110,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;
@@ -100,14 +130,6 @@ void LLApp::commonCtor()
LLCommon::initClass();
#if !LL_WINDOWS
// This must be initialized before the error handler.
sSigChildCount = new LLAtomicU32(0);
#endif
// Setup error handling
setupErrorHandling();
// initialize the options structure. We need to make this an array
// because the structured data will not auto-allocate if we
// reference an invalid location with the [] operator.
@@ -118,8 +140,19 @@ void LLApp::commonCtor()
mOptions.append(sd);
}
// Make sure we clean up APR when we exit
// Don't need to do this if we're cleaning up APR in the destructor
//atexit(ll_cleanup_apr);
// 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(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
}
LLApp::LLApp(LLErrorThread *error_thread) :
@@ -131,10 +164,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :
LLApp::~LLApp()
{
#if !LL_WINDOWS
delete sSigChildCount;
sSigChildCount = NULL;
#endif
// reclaim live file memory
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
@@ -148,6 +177,8 @@ LLApp::~LLApp()
delete mThreadErrorp;
mThreadErrorp = NULL;
}
if(mExceptionHandler != 0) delete mExceptionHandler;
LLCommon::cleanupClass();
}
@@ -207,6 +238,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
}
++ii;
value.assign(argv[ii]);
#if LL_WINDOWS
//Windows changed command line parsing. Deal with it.
S32 slen = value.length() - 1;
S32 start = 0;
S32 end = slen;
if (argv[ii][start]=='"')start++;
if (argv[ii][end]=='"')end--;
if (start!=0 || end!=slen)
{
value = value.substr (start,end);
}
#endif
commands[name] = value;
}
setOptionData(PRIORITY_COMMAND_LINE, commands);
@@ -254,14 +299,60 @@ void LLApp::setupErrorHandling()
// 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);
#if LL_SEND_CRASH_REPORTS
// 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;
std::wostringstream ws;
ws << mCrashReportPipeStr << getPid();
std::wstring wpipe_name = ws.str();
std::string ptmp = std::string(wpipe_name.begin(), wpipe_name.end());
::Sleep(2000); //HACK hopefully a static wait won't blow up in my face before google fixes their implementation.
//HACK this for loop is ueless. Breakpad dumbly returns success when the OOP handler isn't initialized.
for (int retries=0;retries<5;++retries)
{
mExceptionHandler = new google_breakpad::ExceptionHandler(
L"",
NULL, //No filter
windows_post_minidump_callback,
0,
google_breakpad::ExceptionHandler::HANDLER_ALL,
MiniDumpNormal, //Generate a 'normal' minidump.
(WCHAR *)wpipe_name.c_str(),
NULL); //No custom client info.
if (mExceptionHandler)
{
break;
}
else
{
::Sleep(100); //Wait a tick and try again.
}
}
if (!mExceptionHandler)
{
llwarns << "Failed to initialize OOP exception handler. Defaulting to In Process handling" << llendl;
mExceptionHandler = new google_breakpad::ExceptionHandler(
std::wstring(mDumpPath.begin(),mDumpPath.end()), //Dump path
0, //dump filename
windows_post_minidump_callback,
0,
google_breakpad::ExceptionHandler::HANDLER_ALL);
}
if (mExceptionHandler)
{
mExceptionHandler->set_handle_debug_exceptions(true);
}
}
#endif
#else
//
// Start up signal handling.
@@ -271,9 +362,61 @@ void LLApp::setupErrorHandling()
//
setup_signals();
// Add google breakpad exception handler configured for Darwin/Linux.
bool installHandler = true;
#if LL_DARWIN
// For the special case of Darwin, we do not want to install the handler if
// the process is being debugged as the app will exit with value ABRT (6) if
// we do. Unfortunately, the code below which performs that test relies on
// the structure kinfo_proc which has been tagged by apple as an unstable
// API. We disable this test for shipping versions to avoid conflicts with
// future releases of Darwin. This test is really only needed for developers
// starting the app from a debugger anyway.
#ifndef LL_RELEASE_FOR_DOWNLOAD
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
struct kinfo_proc info;
memset(&info, 0, sizeof(info));
size_t size = sizeof(info);
int result = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
if((result == 0) || (errno == ENOMEM))
{
// P_TRACED flag is set, so this process is being debugged; do not install
// the handler
if(info.kp_proc.p_flag & P_TRACED) installHandler = false;
}
else
{
// Failed to discover if the process is being debugged; default to
// installing the handler.
installHandler = true;
}
#endif
if(installHandler && (mExceptionHandler == 0))
{
mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
}
#elif LL_LINUX
if(installHandler && (mExceptionHandler == 0))
{
if (mDumpPath.empty())
{
mDumpPath = "/tmp";
}
google_breakpad::MinidumpDescriptor desc(mDumpPath);
//mExceptionHandler = new google_breakpad::ExceptionHandler(desc, 0, unix_minidump_callback, 0, true, 0);
mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);
}
#endif
#endif
}
void LLApp::startErrorThread()
@@ -311,20 +454,6 @@ void LLApp::setErrorHandler(LLAppErrorHandler handler)
}
void LLApp::setSyncErrorHandler(LLAppErrorHandler handler)
{
LLApp::sSyncErrorHandler = handler;
}
// static
void LLApp::runSyncErrorHandler()
{
if (LLApp::sSyncErrorHandler)
{
LLApp::sSyncErrorHandler();
}
}
// static
void LLApp::runErrorHandler()
{
@@ -348,15 +477,46 @@ 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)
{
if (path.empty())
{
mDumpPath = "/tmp";
}
else
{
mDumpPath = path;
}
if(mExceptionHandler == 0) return;
#ifdef LL_WINDOWS
wchar_t buffer[MAX_MINDUMP_PATH_LENGTH];
mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH);
mExceptionHandler->set_dump_path(std::wstring(buffer));
#elif LL_LINUX
//google_breakpad::MinidumpDescriptor desc("/tmp"); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
google_breakpad::MinidumpDescriptor desc(mDumpPath); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
mExceptionHandler->set_minidump_descriptor(desc);
#else
mExceptionHandler->set_dump_path(mDumpPath);
#endif
}
void LLApp::setDebugFileNames(const std::string &path)
{
mStaticDebugFileName = path + "static_debug_info.log";
mDynamicDebugFileName = path + "dynamic_debug_info.log";
}
void LLApp::writeMiniDump()
{
if(mExceptionHandler == 0) return;
mExceptionHandler->WriteMinidump();
}
// static
void LLApp::setQuitting()
@@ -413,6 +573,12 @@ bool LLApp::isExiting()
void LLApp::disableCrashlogger()
{
// Disable Breakpad exception handler.
if (mExceptionHandler != 0)
{
delete mExceptionHandler;
mExceptionHandler = 0;
}
sDisableCrashlogger = TRUE;
}
@@ -787,4 +953,149 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
}
}
#if LL_LINUX
bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, 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
//HACK: *path points to the buffer in getMiniDumpFilename which has already allocated space
//to avoid doing allocation during crash.
char * path = LLApp::instance()->getMiniDumpFilename();
int dir_path_len = strlen(path);
// The path must not be truncated.
S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH - dir_path_len;
llassert( (remaining - strlen(minidump_desc.path())) > 5);
path += dir_path_len;
if (dir_path_len > 0 && path[-1] != '/')
{
*path++ = '/';
--remaining;
}
strncpy(path, minidump_desc.path(), remaining);
llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
LLApp::runErrorHandler();
#ifndef LL_RELEASE_FOR_DOWNLOAD
clear_signals();
return false;
#else
return true;
#endif
}
#endif
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: " << path << llendl;
LLApp::runErrorHandler();
#ifndef LL_RELEASE_FOR_DOWNLOAD
clear_signals();
return false;
#else
return true;
#endif
}
#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);
}
#ifndef LL_RELEASE_FOR_DOWNLOAD
return false;
#else
return true;
#endif
}
#endif

View File

@@ -37,14 +37,10 @@ template <typename Type> class LLAtomic32;
typedef LLAtomic32<U32> LLAtomicU32;
class LLErrorThread;
class LLLiveFile;
#if LL_LINUX
#include <signal.h>
//typedef struct siginfo siginfo_t; //Removed as per changes in glibc 2.16 - Drake Arconis
#include <signal.h>
#endif
typedef void (*LLAppErrorHandler)();
typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
@@ -64,6 +60,10 @@ public:
};
#endif
namespace google_breakpad {
class ExceptionHandler; // See exception_handler.h
}
class LL_COMMON_API LLApp : public LLOptionInterface
{
friend class LLErrorThread;
@@ -212,11 +212,43 @@ public:
#endif
static int getPid();
//
// Error handling methods
//
/** @name Error handling methods */
//@{
/**
* @brief Do our generic platform-specific error-handling setup --
* signals on unix, structured exceptions on windows.
*
* DO call this method if your app will either spawn children or be
* spawned by a launcher.
* Call just after app object construction.
* (Otherwise your app will crash when getting signals,
* and will not core dump.)
*
* DO NOT call this method if your application has specialized
* error handling code.
*/
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);
void setDebugFileNames(const std::string &path);
// Return the Google Breakpad minidump filename after a crash.
char *getMiniDumpFilename() { return mMinidumpPath; }
std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
// Write out a Google Breakpad minidump file.
void writeMiniDump();
#if !LL_WINDOWS
//
// Child process handling (Unix only for now)
@@ -236,6 +268,7 @@ public:
pid_t fork();
#endif
public:
typedef std::map<std::string, std::string> string_map;
string_map mOptionMap; // Contains all command-line options and arguments in a map
@@ -246,6 +279,9 @@ protected:
static EAppStatus sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.
std::string mDumpPath; //output path for google breakpad. Dependency workaround.
#if !LL_WINDOWS
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
@@ -254,26 +290,25 @@ protected:
static LLAppChildCallback sDefaultChildCallback;
#endif
/**
* @ brief This method is called once as soon as logging is initialized.
*/
void startErrorThread();
/**
* @brief This method is called at the end, just prior to deinitializing curl.
*/
void stopErrorThread();
private:
void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)
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 mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
std::string mStaticDebugFileName;
std::string mDynamicDebugFileName;
// *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
@@ -291,6 +326,7 @@ private:
private:
// the static application instance if it was created.
static LLApp* sApplication;
google_breakpad::ExceptionHandler * mExceptionHandler;
#if !LL_WINDOWS

View File

@@ -127,6 +127,7 @@ public:
mRunning = false;
}
void resetWithExpiry(F32 expiration) { reset(); setTimerExpirySec(expiration); }
void pause(); // Mark elapsed time so far.
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.

View File

@@ -19,12 +19,14 @@ include_directories(
set(llcrashlogger_SOURCE_FILES
llcrashlogger.cpp
llcrashlock.cpp
)
set(llcrashlogger_HEADER_FILES
CMakeLists.txt
llcrashlogger.h
llcrashlock.h
)
set_source_files_properties(${llcrashlogger_HEADER_FILES}

View File

@@ -0,0 +1,211 @@
/**
* @file llformat.cpp
* @date January 2007
* @brief string formatting utility
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llcrashlock.h"
#include "lldir.h"
#include "llsd.h"
#include "llsdserialize.h"
#include "llnametable.h"
#include "llframetimer.h"
#include <boost/filesystem.hpp>
#include <string>
#include <iostream>
#include <stdio.h>
#if LL_WINDOWS //For windows platform.
#include <windows.h>
#include <TlHelp32.h>
/*
namespace {
inline DWORD getpid() {
return GetCurrentProcessId();
}
}
*/
bool LLCrashLock::isProcessAlive(U32 pid, const std::string& pname)
{
std::wstring wpname;
wpname = std::wstring(pname.begin(), pname.end());
HANDLE snapshot;
PROCESSENTRY32 pe32;
bool matched = false;
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE)
{
return false;
}
else
{
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(snapshot, &pe32))
{
do {
std::wstring wexecname = pe32.szExeFile;
std::string execname = std::string(wexecname.begin(), wexecname.end());
if (!wpname.compare(pe32.szExeFile))
{
if (pid == (U32)pe32.th32ProcessID)
{
matched = true;
break;
}
}
} while (Process32Next(snapshot, &pe32));
}
}
CloseHandle(snapshot);
return matched;
}
#else //Everyone Else
bool LLCrashLock::isProcessAlive(U32 pid, const std::string& pname)
{
//Will boost.process ever become a reality?
std::stringstream cmd;
cmd << "pgrep '" << pname << "' | grep '^" << pid << "$'";
return (!system(cmd.str().c_str()));
}
#endif //Everyone else.
LLCrashLock::LLCrashLock() : mCleanUp(true), mWaitingPID(0)
{
}
void LLCrashLock::setCleanUp(bool cleanup)
{
mCleanUp = cleanup; //Allow cleanup to be disabled for debugging.
}
LLSD LLCrashLock::getLockFile(std::string filename)
{
LLSD lock_sd = LLSD::emptyMap();
llifstream ifile(filename);
if (ifile.is_open())
{
LLSDSerialize::fromXML(lock_sd, ifile);
ifile.close();
}
return lock_sd;
}
bool LLCrashLock::putLockFile(std::string filename, const LLSD& data)
{
bool result = true;
llofstream ofile(filename);
if (!LLSDSerialize::toXML(data,ofile))
{
result=false;
}
ofile.close();
return result;
}
bool LLCrashLock::requestMaster( F32 timeout )
{
if (mMaster.empty())
{
mMaster = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"crash_master.lock");
}
LLSD lock_sd=getLockFile(mMaster);
if (lock_sd.has("pid"))
{
mWaitingPID = lock_sd["pid"].asInteger();
if ( isProcessAlive(mWaitingPID, gDirUtilp->getExecutableFilename()) )
{
mTimer.resetWithExpiry(timeout);
return false;
}
}
U32 pid = getpid();
lock_sd["pid"] = (LLSD::Integer)pid;
return putLockFile(mMaster,lock_sd);
}
bool LLCrashLock::checkMaster()
{
if (mWaitingPID)
{
return (!isProcessAlive(mWaitingPID, gDirUtilp->getExecutableFilename()));
}
return false;
}
bool LLCrashLock::isWaiting()
{
return !mTimer.hasExpired();
}
void LLCrashLock::releaseMaster()
{
//Yeeeeeeehaw
unlink(mMaster.c_str());
}
LLSD LLCrashLock::getProcessList()
{
if (mDumpTable.empty())
{
mDumpTable= gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"crash_table.lock");
}
return getLockFile(mDumpTable);
}
//static
bool LLCrashLock::fileExists(std::string filename)
{
return boost::filesystem::exists(filename.c_str());
}
void LLCrashLock::cleanupProcess(std::string proc_dir)
{
boost::filesystem::remove_all(proc_dir);
}
bool LLCrashLock::putProcessList(const LLSD& proc_sd)
{
return putLockFile(mDumpTable,proc_sd);
}

View File

@@ -0,0 +1,73 @@
/**
* @file llpidlock.h
* @brief Maintainence of disk locking files for crash reporting
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_CRASHLOCK_H
#define LL_CRASHLOCK_H
#include "llframetimer.h"
class LLSD;
#if !LL_WINDOWS //For non-windows platforms.
#include <signal.h>
#endif
//Crash reporter will now be kicked off by the viewer but otherwise
//run independent of the viewer.
class LLCrashLock
{
public:
LLCrashLock();
bool requestMaster( F32 timeout=300.0); //Wait until timeout for master lock.
bool checkMaster(); //True if available. False if not.
void releaseMaster( ); //Release master lockfile.
bool isLockPresent(std::string filename); //Check if lockfile exists.
bool isProcessAlive(U32 pid, const std::string& pname); //Check if pid is alive.
bool isWaiting(); //Waiting for master lock to be released.
LLSD getProcessList(); //Get next process pid/dir pairs
void cleanupProcess(std::string proc_dir); //Remove from list, clean up working dir.
bool putProcessList(const LLSD& processlist); //Write pid/dir pairs back to disk.
static bool fileExists(std::string filename);
//getters
S32 getPID();
//setters
void setCleanUp(bool cleanup=true);
void setSaveName(std::string savename);
private:
LLSD getLockFile(std::string filename);
bool putLockFile(std::string filename, const LLSD& data);
bool mCleanUp;
std::string mMaster;
std::string mDumpTable;
U32 mWaitingPID; //The process we're waiting on if any.
LLFrameTimer mTimer;
};
#endif // LL_CRASHLOCK_H

View File

@@ -2,62 +2,58 @@
* @file llcrashlogger.cpp
* @brief Crash logger implementation
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <map>
#include "llcrashlogger.h"
#include "llcrashlock.h"
#include "linden_common.h"
#include "llstring.h"
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "indra_constants.h" // CRASH_BEHAVIOR_...
#include "llerror.h"
#include "llerrorcontrol.h"
#include "lltimer.h"
#include "lldir.h"
#include "llfile.h"
#include "llsdserialize.h"
#include "lliopipe.h"
#include "llpumpio.h"
#include "llhttpclient.h"
#include "llsdserialize.h"
#include "llcurl.h"
#include "llproxy.h"
#include "aistatemachine.h"
LLPumpIO* gServicePump;
BOOL gBreak = false;
BOOL gSent = false;
extern void startEngineThread(void);
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout;
extern void startEngineThread(void);
class LLCrashLoggerResponder : public LLHTTPClient::ResponderWithResult
{
@@ -66,42 +62,35 @@ public:
{
}
/*virtual*/ void error(U32 status, const std::string& reason)
virtual void error(U32 status, const std::string& reason)
{
gBreak = true;
gBreak = true;
}
/*virtual*/ void result(const LLSD& content)
{
virtual void result(const LLSD& content)
{
gBreak = true;
gSent = true;
}
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return crashLoggerResponder_timeout; }
/*virtual*/ char const* getName(void) const { return "LLCrashLoggerResponder"; }
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const
{
return crashLoggerResponder_timeout;
}
virtual char const* getName(void) const
{
return "LLCrashLoggerResponder";
}
};
bool LLCrashLoggerText::mainLoop()
{
std::cout << "Entering main loop" << std::endl;
sendCrashLogs();
return true;
}
void LLCrashLoggerText::updateApplication(const std::string& message)
{
LLCrashLogger::updateApplication(message);
std::cout << message << std::endl;
}
LLCrashLogger::LLCrashLogger() :
mCrashBehavior(CRASH_BEHAVIOR_ASK),
mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
mCrashInPreviousExec(false),
mCrashSettings("CrashSettings"),
mSentCrashLogs(false),
mCrashHost(""),
mCrashSettings("CrashSettings")
mCrashHost("")
{
}
LLCrashLogger::~LLCrashLogger()
@@ -163,38 +152,67 @@ std::string getStartupStateFromLog(std::string& sllog)
return startup_state;
}
void LLCrashLogger::gatherFiles()
bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename )
{
/*
//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
std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
std::ifstream debug_log_file(db_file_name.c_str());
std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename);
std::ifstream debug_log_file(db_file_name.c_str());
// Look for it in the debug_info.log file
if (debug_log_file.is_open())
{
LLSDSerialize::fromXML(mDebugLog, debug_log_file);
{
LLSDSerialize::fromXML(dest, debug_log_file);
debug_log_file.close();
return true;
}
return false;
}
void LLCrashLogger::mergeLogs( LLSD src_sd )
{
LLSD::map_iterator iter = src_sd.beginMap();
LLSD::map_iterator end = src_sd.endMap();
for( ; iter != end; ++iter)
{
mDebugLog[iter->first] = iter->second;
}
}
bool LLCrashLogger::readMinidump(std::string minidump_path)
{
size_t length=0;
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);
length = (size_t)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;
}
return (length>0?true:false);
}
void LLCrashLogger::gatherFiles()
{
updateApplication("Gathering logs...");
LLSD static_sd;
LLSD dynamic_sd;
bool has_logs = readDebugFromXML( static_sd, "static_debug_info.log" );
has_logs |= readDebugFromXML( dynamic_sd, "dynamic_debug_info.log" );
if ( has_logs )
{
mDebugLog = static_sd;
mergeLogs(dynamic_sd);
mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
@@ -219,11 +237,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
@@ -235,7 +251,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();
@@ -256,8 +277,7 @@ void LLCrashLogger::gatherFiles()
mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
mCrashInfo["DebugLog"] = mDebugLog;
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log");
updateApplication("Encoding files...");
@@ -282,52 +302,115 @@ 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));
}
std::string minidump_path;
// Add minidump as binary.
bool has_minidump = mDebugLog.has("MinidumpPath");
if (has_minidump)
minidump_path = mDebugLog["MinidumpPath"].asString();
if (has_minidump)
{
has_minidump = readMinidump(minidump_path);
}
if (!has_minidump) //Viewer was probably so hosed it couldn't write remaining data. Try brute force.
{
//Look for a filename at least 30 characters long in the dump dir which contains the characters MDMP as the first 4 characters in the file.
typedef std::vector<std::string> vec;
std::string pathname = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"");
vec file_vec = gDirUtilp->getFilesInDir(pathname);
for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter)
{
if ( ( iter->length() > 30 ) && (iter->rfind(".log") != (iter->length()-4) ) )
{
std::string fullname = pathname + *iter;
std::ifstream fdat( fullname.c_str(), std::ifstream::binary);
if (fdat)
{
char buf[5];
fdat.read(buf,4);
fdat.close();
if (!strncmp(buf,"MDMP",4))
{
minidump_path = *iter;
has_minidump = readMinidump(fullname);
mDebugLog["MinidumpPath"] = fullname;
}
}
}
}
}
}
LLSD LLCrashLogger::constructPostData()
{
LLSD ret;
return mCrashInfo;
}
// Singu Note, defiend in indra_constants.h # const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
S32 LLCrashLogger::loadCrashBehaviorSetting()
{
// First check user_settings (in the user's home dir)
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
if (! mCrashSettings.loadFromFile(filename))
{
// Next check app_settings (in the SL program dir)
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE);
mCrashSettings.loadFromFile(filename);
}
mCrashSettings.loadFromFile(filename);
S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
// If we didn't load any files above, this will return the default
S32 value = mCrashSettings.getS32("CrashSubmitBehavior");
return value;
// Whatever value we got, make sure it's valid
switch (value)
{
case CRASH_BEHAVIOR_NEVER_SEND:
return CRASH_BEHAVIOR_NEVER_SEND;
case CRASH_BEHAVIOR_ALWAYS_SEND:
return CRASH_BEHAVIOR_ALWAYS_SEND;
}
return CRASH_BEHAVIOR_ASK;
}
bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
{
if (crash_behavior != CRASH_BEHAVIOR_ASK && crash_behavior != CRASH_BEHAVIOR_ALWAYS_SEND) return false;
switch (crash_behavior)
{
case CRASH_BEHAVIOR_ASK:
case CRASH_BEHAVIOR_NEVER_SEND:
case CRASH_BEHAVIOR_ALWAYS_SEND:
break;
default:
return false;
}
mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior);
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
mCrashSettings.saveToFile(filename, FALSE);
return true;
}
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries)
bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
{
gBreak = false;
std::string status_message;
for(int i = 0; i < retries; ++i)
{
status_message = llformat("%s, try %d...", msg.c_str(), i+1);
LLHTTPClient::post(host, data, new LLCrashLoggerResponder);
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
LLHTTPClient::post(host, data, new LLCrashLoggerResponder());
while(!gBreak)
{
updateApplication(status_message);
updateApplication(); // No new message, just pump the IO
}
if(gSent)
{
@@ -337,89 +420,194 @@ bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg
return gSent;
}
bool LLCrashLogger::sendCrashLogs()
bool LLCrashLogger::sendCrashLog(std::string dump_dir)
{
gDirUtilp->setDumpDir( dump_dir );
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"SecondLifeCrashReport");
std::string report_file = dump_path + ".log";
gatherFiles();
LLSD post_data;
post_data = constructPostData();
updateApplication("Sending reports...");
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
"SecondLifeCrashReport");
std::string report_file = dump_path + ".log";
std::ofstream out_file(report_file.c_str());
LLSDSerialize::toPrettyXML(post_data, out_file);
out_file.close();
bool sent = false;
// *TODO: Translate
//*TODO: Translate
if(mCrashHost != "")
{
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3);
sent = runCrashLogPost(mCrashHost, post_data, std::string("Sending to server"), 3, 5);
}
if(!sent)
{
sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3);
sent = runCrashLogPost(mAltCrashHost, post_data, std::string("Sending to alternate server"), 3, 5);
}
mSentCrashLogs = sent;
return sent;
}
return true;
bool LLCrashLogger::sendCrashLogs()
{
//pertinent code from below moved into a subroutine.
LLSD locks = mKeyMaster.getProcessList();
LLSD newlocks = LLSD::emptyArray();
LLSD opts = getOptionData(PRIORITY_COMMAND_LINE);
LLSD rec;
if ( opts.has("pid") && opts.has("dumpdir") && opts.has("procname") )
{
rec["pid"]=opts["pid"];
rec["dumpdir"]=opts["dumpdir"];
rec["procname"]=opts["procname"];
#if LL_WINDOWS
locks.append(rec);
#endif
}
if (locks.isArray())
{
for (LLSD::array_iterator lock=locks.beginArray();
lock !=locks.endArray();
++lock)
{
if ( (*lock).has("pid") && (*lock).has("dumpdir") && (*lock).has("procname") )
{
if ( mKeyMaster.isProcessAlive( (*lock)["pid"].asInteger(), (*lock)["procname"].asString() ) )
{
newlocks.append(*lock);
}
else
{
//TODO: This is a hack but I didn't want to include boost in another file or retest everything related to lldir
if (LLCrashLock::fileExists((*lock)["dumpdir"].asString()))
{
//the viewer cleans up the log directory on clean shutdown
//but is ignorant of the locking table.
if (!sendCrashLog((*lock)["dumpdir"].asString()))
{
newlocks.append(*lock); //Failed to send log so don't delete it.
}
else
{
mCrashInfo["DebugLog"].erase("MinidumpPath");
mKeyMaster.cleanupProcess((*lock)["dumpdir"].asString());
}
}
}
}
else
{
llwarns << "Discarding corrupted entry from lock table." << llendl;
}
}
}
#if !LL_WINDOWS
if (rec)
{
newlocks.append(rec);
}
#endif
mKeyMaster.putProcessList(newlocks);
return true;
}
void LLCrashLogger::updateApplication(const std::string& message)
{
/* Sing TODO
gServicePump->pump();
gServicePump->callback();
gMainThreadEngine.mainloop();
*/
if (!message.empty()) llinfos << message << llendl;
}
bool LLCrashLogger::init()
{
// Initialize curl
AICurlInterface::initCurl();
// Initialize state machine engines.
AIEngine::setMaxCount(100); // StateMachineMaxTime
// Start state machine thread.
startEngineThread();
LLCurl::initCurl();
AIEngine::setMaxCount(100);
// We assume that all the logs we're looking for reside on the current drive
gDirUtilp->initAppDirs("SecondLife");
LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
// Default to the product name "Second Life" (this is overridden by the -name argument)
mProductName = "Second Life";
mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
"(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
// Handle locking
bool locked = mKeyMaster.requestMaster(); //Request maser locking file. wait time is defaulted to 300S
while (!locked && mKeyMaster.isWaiting())
{
#if LL_WINDOWS
Sleep(1000);
#else
sleep(1);
#endif
locked = mKeyMaster.checkMaster();
}
if (!locked)
{
llwarns << "Unable to get master lock. Another crash reporter may be hung." << llendl;
return false;
}
// Rename current log file to ".old"
std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
#if LL_WINDOWS
LLAPRFile::remove(old_log_file);
#endif
LLFile::rename(log_file.c_str(), old_log_file.c_str());
// Set the log file to crashreport.log
LLError::logToFile(log_file);
mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
"Controls behavior when viewer crashes "
"(0 = ask before sending crash report, "
"1 = always send crash report, "
"2 = never send crash report)");
llinfos << "Loading crash behavior setting" << llendl;
mCrashBehavior = loadCrashBehaviorSetting();
// If user doesn't want to send, bail out
if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
{
llinfos << "Crash behavior is never_send, quitting" << llendl;
return false;
}
// Start curl thread.
AICurlInterface::startCurlThread(&mCrashSettings);
gServicePump = new LLPumpIO;
//If we've opened the crash logger, assume we can delete the marker file if it exists
if( gDirUtilp )
{
std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
LLAPRFile::remove( marker_file );
}
startEngineThread();
/* Singu Note: not needed for AICurl
gServicePump = new LLPumpIO(gAPRPoolp);
gServicePump->prime(gAPRPoolp);
LLHTTPClient::setPump(*gServicePump);
*/
return true;
}
// For cleanup code common to all platforms.
void LLCrashLogger::commonCleanup()
{
LLProxy::cleanupClass();
}

View File

@@ -2,45 +2,38 @@
* @file llcrashlogger.h
* @brief Crash Logger Definition
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLCRASHLOGGER_H
#define LLCRASHLOGGER_H
#include "linden_common.h"
#include <vector>
#include "linden_common.h"
#include "llapp.h"
#include "llsd.h"
#include "llcontrol.h"
class AIHTTPTimeoutPolicy;
#include "llcrashlock.h"
class LLCrashLogger : public LLApp
{
@@ -48,18 +41,25 @@ public:
LLCrashLogger();
virtual ~LLCrashLogger();
S32 loadCrashBehaviorSetting();
bool readDebugFromXML(LLSD& dest, const std::string& filename );
void gatherFiles();
void mergeLogs( LLSD src_sd );
virtual void gatherPlatformSpecificFiles() {}
bool saveCrashBehaviorSetting(S32 crash_behavior);
bool sendCrashLog(std::string dump_dir);
bool sendCrashLogs();
LLSD constructPostData();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
virtual bool init();
virtual bool mainLoop() = 0;
virtual bool cleanup() { return true; }
virtual bool cleanup() = 0;
void commonCleanup();
void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
S32 getCrashBehavior() { return mCrashBehavior; }
bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries);
bool runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout);
bool readMinidump(std::string minidump_path);
protected:
S32 mCrashBehavior;
BOOL mCrashInPreviousExec;
@@ -72,17 +72,7 @@ protected:
std::string mAltCrashHost;
LLSD mDebugLog;
bool mSentCrashLogs;
LLCrashLock mKeyMaster;
};
class LLCrashLoggerText : public LLCrashLogger
{
public:
LLCrashLoggerText(void) {}
~LLCrashLoggerText(void) {}
virtual bool mainLoop();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
};
#endif //LLCRASHLOGGER_H

View File

@@ -922,7 +922,6 @@ P(charactersResponder);
P(checkAgentAppearanceServiceResponder);
P(classifiedStatsResponder);
P(consoleResponder);
P2(crashLoggerResponder, transfer_22s_connect_10s);
P(createInventoryCategoryResponder);
P(emeraldDicDownloader);
P(environmentApplyResponder);
@@ -993,4 +992,4 @@ P(webProfileResponders);
P(wholeModelFeeResponder);
P(wholeModelUploadResponder);
P2(XMLRPCResponder, connect_40s);
P2(crashLoggerResponder, transfer_300s);

View File

@@ -1,3 +1,4 @@
/**
* @file lldir.cpp
* @brief implementation of directory utilities base class
@@ -42,6 +43,7 @@
#include "lldiriterator.h"
#include "stringize.h"
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
@@ -69,6 +71,8 @@ LLDir_Linux gDirUtil;
LLDir *gDirUtilp = (LLDir *)&gDirUtil;
static const char* const empty = "";
std::string LLDir::sDumpDir = "";
LLDir::LLDir()
: mAppName(""),
mExecutablePathAndName(""),
@@ -89,7 +93,32 @@ LLDir::~LLDir()
{
}
std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
{
//Returns a vector of fullpath filenames.
boost::filesystem::path p (dirname);
std::vector<std::string> v;
if (exists(p))
{
if (is_directory(p))
{
boost::filesystem::directory_iterator end_iter;
for (boost::filesystem::directory_iterator dir_itr(p);
dir_itr != end_iter;
++dir_itr)
{
if (boost::filesystem::is_regular_file(dir_itr->status()))
{
v.push_back(dir_itr->path().filename().c_str());
}
}
}
}
return v;
}
S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
{
S32 count = 0;
@@ -148,6 +177,12 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
return count;
}
U32 LLDir::deleteDirAndContents(const std::string& dir_name)
{
//Removes the directory and its contents. Returns number of files removed.
return boost::filesystem::remove_all(dir_name);
}
const std::string LLDir::findFile(const std::string &filename,
const std::string& searchPath1,
const std::string& searchPath2,
@@ -235,6 +270,31 @@ const std::string &LLDir::getChatLogsDir() const
return mChatLogsDir;
}
void LLDir::setDumpDir( const std::string& path )
{
LLDir::sDumpDir = path;
if (! sDumpDir.empty() && sDumpDir.rbegin() == mDirDelimiter.rbegin() )
{
sDumpDir.erase(sDumpDir.size() -1);
}
}
const std::string &LLDir::getDumpDir() const
{
if (sDumpDir.empty() )
{
LLUUID uid;
uid.generate();
sDumpDir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "")
+ "dump-" + uid.asString();
dir_exists_or_crash(sDumpDir);
}
return LLDir::sDumpDir;
}
const std::string &LLDir::getPerAccountChatLogsDir() const
{
return mPerAccountChatLogsDir;
@@ -396,12 +456,29 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd
prefix = getCacheDir();
break;
case LL_PATH_DUMP:
prefix=getDumpDir();
break;
case LL_PATH_USER_SETTINGS:
prefix = add(getOSUserAppDir(), "user_settings");
break;
case LL_PATH_PER_SL_ACCOUNT:
prefix = getLindenUserDir();
if (prefix.empty())
{
// if we're asking for the per-SL-account directory but we haven't
// logged in yet (or otherwise don't know the account name from
// which to build this string), then intentionally return a blank
// string to the caller and skip the below warning about a blank
// prefix.
LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: "
<< ELLPathToString(location)
<< ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
<< "' => ''" << LL_ENDL;
return std::string();
}
break;
case LL_PATH_CHAT_LOGS:

View File

@@ -53,6 +53,7 @@ typedef enum ELLPath
LL_PATH_EXECUTABLE = 16,
LL_PATH_DEFAULT_SKIN = 17,
LL_PATH_FONTS = 18,
LL_PATH_DUMP = 19,
LL_PATH_LAST
} ELLPath;
@@ -71,7 +72,8 @@ class LLDir
const std::string& app_read_only_data_dir = "") = 0;
virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
U32 deleteDirAndContents(const std::string& dir_name);
std::vector<std::string> getFilesInDir(const std::string &dirname);
// pure virtual functions
virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0;
@@ -94,6 +96,7 @@ class LLDir
const std::string &getOSUserAppDir() const; // Location of the os-specific user app dir
const std::string &getLindenUserDir(bool empty_ok = false) const; // Location of the Linden user dir.
const std::string &getChatLogsDir() const; // Location of the chat logs dir.
const std::string &getDumpDir() const; // Location of the per-run dump dir.
const std::string &getPerAccountChatLogsDir() const; // Location of the per account chat logs dir.
const std::string &getTempDir() const; // Common temporary directory
const std::string getCacheDir(bool get_default = false) const; // Location of the cache.
@@ -128,6 +131,7 @@ class LLDir
// For producing safe download file names from potentially unsafe ones
static std::string getScrubbedFileName(const std::string uncleanFileName);
static std::string getForbiddenFileChars();
void setDumpDir( const std::string& path );
virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir
virtual void setPerAccountChatLogsDir(const std::string &grid, const std::string &first, const std::string &last); // Set the per user chat log directory.
@@ -173,6 +177,7 @@ protected:
std::string mDefaultSkinDir; // Location for default skin info.
std::string mUserSkinDir; // Location for user-modified skin info.
std::string mLLPluginDir; // Location for plugins and plugin shell
static std::string sDumpDir; // Per-run crash report subdir of log directory.
};
void dir_exists_or_crash(const std::string &dir_name);

View File

@@ -2190,6 +2190,17 @@ This should be as low as possible, but too low may break functionality</string>
<integer>1</integer>
</map>
<!-- Standard SL options (To my knowledge) -->
<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

@@ -9,7 +9,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>2</integer>
<integer>1</integer>
</map>
<key>CurlMaxTotalConcurrentConnections</key>
<map>

View File

@@ -558,6 +558,11 @@ LLAppViewer::LLAppViewer() :
llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl;
}
mDumpPath ="";
// Need to do this initialization before we do anything else, since anything
// that touches files should really go through the lldir API
gDirUtilp->initAppDirs("SecondLife");
sInstance = this;
}
@@ -580,7 +585,9 @@ public:
};
bool LLAppViewer::init()
{
{
setupErrorHandling();
//
// Start of the application
//
@@ -605,9 +612,6 @@ bool LLAppViewer::init()
//initialize particle index pool
LLVOPartGroup::initClass();
// Need to do this initialization before we do anything else, since anything
// that touches files should really go through the lldir API
gDirUtilp->initAppDirs("SecondLife");
// set skin search path to default, will be overridden later
// this allows simple skinned file lookups to work
gDirUtilp->setSkinFolder("default");
@@ -636,6 +640,12 @@ bool LLAppViewer::init()
initMaxHeapSize() ;
LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ;
// write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues.
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
mDumpPath = logdir;
setMiniDumpDir(logdir);
logdir += gDirUtilp->getDirDelimiter();
setDebugFileNames(logdir);
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
@@ -1448,6 +1458,14 @@ bool LLAppViewer::cleanup()
{
//ditch LLVOAvatarSelf instance
gAgentAvatarp = NULL;
// remove any old breakpad minidump files from the log directory
if (! isError())
{
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
}
cleanup_pose_stand();
//flag all elements as needing to be destroyed immediately
@@ -1725,6 +1743,7 @@ bool LLAppViewer::cleanup()
}
// </edit>
removeDumpDir();
writeDebugInfo();
if(!gDirUtilp->getLindenUserDir(true).empty())
@@ -2449,7 +2468,6 @@ bool LLAppViewer::initConfiguration()
initMarkerFile();
checkForCrash();
}
else
{
@@ -2469,10 +2487,6 @@ bool LLAppViewer::initConfiguration()
initMarkerFile();
if(!mSecondInstance)
{
checkForCrash();
}
}
// need to do this here - need to have initialized global settings first
@@ -2488,62 +2502,6 @@ bool LLAppViewer::initConfiguration()
return true; // Config was successful.
}
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;
//
// Pop up a freeze or crash warning dialog
//
S32 choice;
const S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
if(cb == CRASH_BEHAVIOR_ASK)
{
std::ostringstream msg;
msg << LLTrans::getString("MBFrozenCrashed");
std::string alert = LLTrans::getString("APP_NAME") + " " + LLTrans::getString("MBAlert");
choice = OSMessageBox(msg.str(),
alert,
OSMB_YESNO);
}
else if(cb == CRASH_BEHAVIOR_NEVER_SEND)
{
choice = OSBTN_NO;
}
else
{
choice = OSBTN_YES;
}
if (OSBTN_YES == choice)
{
llinfos << "Sending crash report." << llendl;
bool report_freeze = true;
handleCrashReporting(report_freeze);
}
else
{
llinfos << "Not sending crash report." << llendl;
}
}
#endif // LL_SEND_CRASH_REPORTS
}
bool LLAppViewer::initWindow()
{
LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL;
@@ -2613,12 +2571,21 @@ bool LLAppViewer::initWindow()
return true;
}
void LLAppViewer::writeDebugInfo()
void LLAppViewer::writeDebugInfo(bool isStatic)
{
std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
llinfos << "Opening debug file " << debug_filename << llendl;
llofstream out_file(debug_filename);
LLSDSerialize::toPrettyXML(gDebugInfo, out_file);
//Try to do the minimum when writing data during a crash.
std::string* debug_filename;
debug_filename = ( isStatic
? getStaticDebugFile()
: getDynamicDebugFile() );
llinfos << "Opening debug file " << *debug_filename << llendl;
llofstream out_file(*debug_filename);
isStatic ? LLSDSerialize::toPrettyXML(gDebugInfo, out_file)
: LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file);
out_file.close();
}
@@ -2669,6 +2636,10 @@ void LLAppViewer::removeCacheFiles(const std::string& file_mask)
void LLAppViewer::writeSystemInfo()
{
if (! gDebugInfo.has("Dynamic") )
gDebugInfo["Dynamic"] = LLSD::emptyMap();
gDebugInfo["SLLog"] = LLError::logFileName();
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
@@ -2706,6 +2677,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")
@@ -2730,13 +2710,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;
@@ -2768,8 +2741,12 @@ void LLAppViewer::handleViewerCrash()
}
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["Dynamic"]["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
@@ -2783,11 +2760,11 @@ void LLAppViewer::handleViewerCrash()
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
if ( parcel && parcel->getMusicURL()[0])
{
gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL();
gDebugInfo["Dynamic"]["ParcelMusicURL"] = parcel->getMusicURL();
}
if ( parcel && parcel->getMediaURL()[0])
{
gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL();
gDebugInfo["Dynamic"]["ParcelMediaURL"] = parcel->getMediaURL();
}
@@ -2795,15 +2772,15 @@ void LLAppViewer::handleViewerCrash()
gDebugInfo["CAFilename"] = gDirUtilp->getCAFile();
gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
gDebugInfo["Dynamic"]["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10;
gDebugInfo["Dynamic"]["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10;
gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin();
gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
if(gLogoutInProgress)
{
gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
gDebugInfo["Dynamic"]["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
}
else
{
@@ -2812,23 +2789,23 @@ void LLAppViewer::handleViewerCrash()
if(gAgent.getRegion())
{
gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName();
gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName();
const LLVector3& loc = gAgent.getPositionAgent();
gDebugInfo["CurrentLocationX"] = loc.mV[0];
gDebugInfo["CurrentLocationY"] = loc.mV[1];
gDebugInfo["CurrentLocationZ"] = loc.mV[2];
gDebugInfo["Dynamic"]["CurrentLocationX"] = loc.mV[0];
gDebugInfo["Dynamic"]["CurrentLocationY"] = loc.mV[1];
gDebugInfo["Dynamic"]["CurrentLocationZ"] = loc.mV[2];
}
if(LLAppViewer::instance()->mMainloopTimeout)
{
gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState();
gDebugInfo["Dynamic"]["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState();
}
// The crash is being handled here so set this value to false.
// Otherwise the crash logger will think this crash was a freeze.
gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)false;
gDebugInfo["Dynamic"]["CrashNotHandled"] = (LLSD::Boolean)false;
//Write out the crash status file
//Use marker file style setup, as that's the simplest, especially since
@@ -2850,11 +2827,18 @@ void LLAppViewer::handleViewerCrash()
LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_file_name << LL_ENDL;
}
}
char *minidump_file = pApp->getMiniDumpFilename();
if(minidump_file && minidump_file[0] != 0)
{
gDebugInfo["Dynamic"]["MinidumpPath"] = minidump_file;
}
gDebugInfo["Dynamic"]["CrashType"]="crash";
if (gMessageSystem && gDirUtilp)
{
std::string filename;
filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log");
filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log");
llofstream file(filename, llofstream::binary);
if(file.good())
{
@@ -2870,19 +2854,15 @@ void LLAppViewer::handleViewerCrash()
gMessageSystem->stopLogging();
}
if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo);
if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo["Dynamic"]);
// Close the debug file
pApp->writeDebugInfo();
pApp->writeDebugInfo(false); //false answers the isStatic question with the least overhead.
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)
if(gDebugInfo["Dynamic"]["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH)
{
pApp->removeMarkerFile(true);
}
@@ -2891,11 +2871,6 @@ void LLAppViewer::handleViewerCrash()
pApp->removeMarkerFile(false);
}
// Call to pure virtual, handled by platform specific llappviewer instance.
pApp->handleCrashReporting();
#endif //!LL_DARWIN
return;
}
@@ -3020,6 +2995,18 @@ void LLAppViewer::removeMarkerFile(bool leave_logout_marker)
LLAPRFile::remove( mLogoutMarkerFileName );
mLogoutMarkerFile = NULL;
}
else
{
LL_WARNS("MarkerFile") << "leaving markers because this is a second instance" << LL_ENDL;
}
}
void LLAppViewer::removeDumpDir()
{
//Call this routine only on clean exit. Crash reporter will clean up
//its locking table for us.
std::string dump_dir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
gDirUtilp->deleteDirAndContents(dump_dir);
}
void LLAppViewer::forceQuit()
@@ -3592,13 +3579,6 @@ void LLAppViewer::badNetworkHandler()
mPurgeOnExit = TRUE;
#if LL_WINDOWS
// Generates the minidump.
LLWinDebug::generateCrashStacks(NULL);
#endif
LLAppViewer::handleSyncViewerCrash();
LLAppViewer::handleViewerCrash();
std::string grid_support_msg = "";
if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty())
{
@@ -3617,6 +3597,8 @@ void LLAppViewer::badNetworkHandler()
"If the problem continues, please report the issue at: \n"
"http://www.singularityviewer.org" << grid_support_msg;
forceDisconnect(message.str());
LLApp::instance()->writeMiniDump();
}
// This routine may get called more than once during the shutdown process.

View File

@@ -77,7 +77,7 @@ public:
bool quitRequested() { return mQuitRequested; }
bool logoutRequestSent() { return mLogoutRequestSent; }
void writeDebugInfo();
void writeDebugInfo(bool isStatic=true);
const LLOSInfo& getOSInfo() const { return mSysOSInfo; }
@@ -86,11 +86,8 @@ 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.
virtual void initCrashReporting(bool reportFreeze = false) = 0; // What to do with crash report?
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
static LLTextureCache* getTextureCache() { return sTextureCache; }
@@ -118,6 +115,7 @@ public:
void removeMarkerFile(bool leave_logout_marker = false);
void removeDumpDir();
// LLAppViewer testing helpers.
// *NOTE: These will potentially crash the viewer. Only for debugging.
virtual void forceErrorLLError();

View File

@@ -79,50 +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);
// 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
@@ -187,8 +143,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);
@@ -407,10 +361,25 @@ bool LLAppViewerWin32::init()
// (Don't send our data to Microsoft--at least until we are Logo approved and have a way
// of getting the data back from them.)
//
llinfos << "Turning off Windows error reporting." << llendl;
// llinfos << "Turning off Windows error reporting." << llendl;
disableWinErrorReporting();
return LLAppViewer::init();
#ifndef LL_RELEASE_FOR_DOWNLOAD
LLWinDebug::instance().init();
#endif
#if LL_WINDOWS
#if LL_SEND_CRASH_REPORTS
LLAppViewer* pApp = LLAppViewer::instance();
pApp->initCrashReporting();
#endif
#endif
bool success = LLAppViewer::init();
return success;
}
bool LLAppViewerWin32::cleanup()
@@ -424,12 +393,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();
}
@@ -552,39 +515,66 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
}
bool LLAppViewerWin32::restoreErrorTrap()
{
return LLWinDebug::checkExceptionHandler();
{
return true;
//return LLWinDebug::checkExceptionHandler();
}
void LLAppViewerWin32::handleSyncCrashTrace()
{
// do nothing
}
void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
void LLAppViewerWin32::initCrashReporting(bool reportFreeze)
{
const char* logger_name = "win_crash_logger.exe";
std::string exe_path = gDirUtilp->getExecutableDir();
exe_path += gDirUtilp->getDirDelimiter();
exe_path += logger_name;
const char* arg_str = logger_name;
std::stringstream pid_str;
pid_str << LLApp::getPid();
std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "");
std::string appname = gDirUtilp->getExecutableFilename();
// *NOTE:Mani - win_crash_logger.exe no longer parses command line options.
if(reportFreeze)
S32 slen = logdir.length() -1;
S32 end = slen;
while (logdir.at(end) == '/' || logdir.at(end) == '\\') end--;
if (slen !=end)
{
// Spawn crash logger.
// NEEDS to wait until completion, otherwise log files will get smashed.
_spawnl(_P_WAIT, exe_path.c_str(), arg_str, NULL);
logdir = logdir.substr(0,end+1);
}
else
std::string arg_str = "\"" + exe_path + "\" -dumpdir \"" + logdir + "\" -procname \"" + appname + "\" -pid " + pid_str.str();
llinfos << "spawning " << arg_str << llendl;
_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str.c_str(), NULL);
/* STARTUPINFO siStartupInfo;
std::string arg_str = "-dumpdir \"" + logdir + "\" -procname \"" + appname + "\" -pid " + pid_str.str();
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&mCrashReporterProcessInfo, 0, sizeof(mCrashReporterProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
std::wstring exe_wstr;
exe_wstr.assign(exe_path.begin(), exe_path.end());
std::wstring arg_wstr;
arg_wstr.assign(arg_str.begin(), arg_str.end());
if(CreateProcess(&exe_wstr[0],
&arg_wstr[0], // Application arguments
0,
0,
FALSE,
CREATE_DEFAULT_ERROR_MODE,
0,
0, // Working directory
&siStartupInfo,
&mCrashReporterProcessInfo) == FALSE)
// Could not start application -> call 'GetLastError()'
{
S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
if(cb != CRASH_BEHAVIOR_NEVER_SEND)
{
_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
}
}
//llinfos << "CreateProcess failed " << GetLastError() << llendl;
return;
}
*/
}
//virtual

View File

@@ -56,13 +56,13 @@ protected:
virtual bool initParseCommandLine(LLCommandLineParser& clp);
virtual bool restoreErrorTrap();
virtual void handleCrashReporting(bool reportFreeze);
virtual void handleSyncCrashTrace();
virtual void initCrashReporting(bool reportFreeze);
virtual bool sendURLToOtherInstance(const std::string& url);
std::string generateSerialNumber();
static const std::string sWindowClass;
private:

View File

@@ -2,98 +2,35 @@
* @file llwindebug.cpp
* @brief Windows debugging functions
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/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,
@@ -103,527 +40,6 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF
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,CP_ACP);
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,CP_ACP);
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,CP_ACP);
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:
@@ -663,66 +79,23 @@ void LLMemoryReserve::release()
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)
LONG NTAPI vectoredHandler(PEXCEPTION_POINTERS exception_infop)
{
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;
LLWinDebug::instance().generateMinidump(exception_infop);
return EXCEPTION_CONTINUE_SEARCH;
}
// static
void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
void LLWinDebug::init()
{
static bool s_first_run = true;
// Load the dbghelp dll now, instead of waiting for the crash.
// Less potential for stack mangling
// Don't install vectored exception handler if being debugged.
if(IsDebuggerPresent()) return;
if (s_first_run)
{
// First, try loading from the directory that the app resides in.
@@ -753,161 +126,68 @@ void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
gEmergencyMemoryReserve.reserve();
s_first_run = false;
// Add this exeption hanlder to save windows style minidump.
AddVectoredExceptionHandler(0, &vectoredHandler);
}
// 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)
// Temporary fix to switch out the code that writes the DMP file.
// Fix coming that doesn't write a mini dump file for regular C++ exceptions.
const bool enable_write_dump_file = false;
if ( enable_write_dump_file )
{
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)
if(f_mdwp == NULL || gDirUtilp == NULL)
{
// Write the dump, ignoring the return value
f_mdwp(GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
type,
ExInfop,
NULL,
NULL);
CloseHandle(hFile);
return;
}
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)
void LLWinDebug::generateMinidump(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();
LLControlVariable* save_minimap = gSavedSettings.getControl("SaveMinidump");
if(save_minimap && save_minimap->getValue().asBoolean())
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
_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);
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = exception_infop;
ExInfo.ClientPointers = NULL;
writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLife.dmp");
}
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

@@ -2,31 +2,25 @@
* @file llwindebug.h
* @brief LLWinDebug class header file
*
* $LicenseInfo:firstyear=2004&license=viewergpl$
*
* Copyright (c) 2004-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,40 +30,14 @@
#include "stdtypes.h"
#include <dbghelp.h>
class LLWinDebug
class LLWinDebug:
public LLSingleton<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);
static void init();
static void generateMinidump(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL);
private:
static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);
};
#endif // LL_LLWINDEBUG_H

View File

@@ -3,7 +3,6 @@
project(win_crash_logger)
include(00-Common)
include(DirectX)
include(LLCommon)
include(LLCrashLogger)
include(LLMath)
@@ -12,6 +11,8 @@ include(LLVFS)
include(LLWindow)
include(LLXML)
include(Linking)
include(LLSharedLibs)
include(GoogleBreakpad)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
@@ -20,6 +21,11 @@ include_directories(
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${BREAKPAD_INCLUDE_DIRECTORIES}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
${LLXML_SYSTEM_INCLUDE_DIRS}
)
set(win_crash_logger_SOURCE_FILES
@@ -40,7 +46,7 @@ set_source_files_properties(${win_crash_logger_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
set(win_crash_logger_RESOURCE_FILES
snowglobe_icon.ico
ll_icon.ico
)
set_source_files_properties(${win_crash_logger_RESOURCE_FILES}
@@ -59,11 +65,12 @@ list(APPEND
${win_crash_logger_RESOURCE_FILES}
)
find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR})
# find_library(DXGUID_LIBRARY dxguid ${DIRECTX_LIBRARY_DIR})
add_executable(windows-crash-logger WIN32 ${win_crash_logger_SOURCE_FILES})
target_link_libraries(windows-crash-logger
${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
${LLCRASHLOGGER_LIBRARIES}
${LLWINDOW_LIBRARIES}
${LLVFS_LIBRARIES}
@@ -72,11 +79,13 @@ target_link_libraries(windows-crash-logger
${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES}
${WINDOWS_LIBRARIES}
${DXGUID_LIBRARY}
# ${DXGUID_LIBRARY}
${GOOGLE_PERFTOOLS_LIBRARIES}
user32
gdi32
ole32
oleaut32
wininet
Wldap32
)
@@ -87,3 +96,6 @@ if (WINDOWS)
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\""
)
endif (WINDOWS)
# Singu Note: not used by our build
# ll_deploy_sharedlibs_command(windows-crash-logger)

View File

@@ -2,31 +2,25 @@
* @file StdAfx.cpp
* @brief windows crash logger source file for includes
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

View File

@@ -2,31 +2,25 @@
* @file StdAfx.h
* @brief standard system includes
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,7 +38,6 @@
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>

View File

@@ -2,31 +2,25 @@
* @file llcrashloggerwindows.cpp
* @brief Windows crash logger implementation
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -40,7 +34,6 @@
#include "boost/tokenizer.hpp"
#include "dbghelp.h"
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
#include "llerror.h"
#include "llfile.h"
@@ -49,6 +42,10 @@
#include "lldxhardware.h"
#include "lldir.h"
#include "llsdserialize.h"
#include "llsdutil.h"
#include <client/windows/crash_generation/crash_generation_server.h>
#include <client/windows/crash_generation/client_info.h>
#define MAX_LOADSTRING 100
#define MAX_STRING 2048
@@ -71,6 +68,7 @@ BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialo
std::stringstream gDXInfo;
bool gSendLogs = false;
LLCrashLoggerWindows* LLCrashLoggerWindows::sInstance = NULL;
//Conversion from char* to wchar*
//Replacement for ATL macros, doesn't allocate memory
@@ -145,7 +143,7 @@ void LLCrashLoggerWindows::ProcessCaption(HWND hWnd)
TCHAR header[MAX_STRING];
std::string final;
GetWindowText(hWnd, templateText, sizeof(templateText));
final = llformat(ll_convert_wide_to_string(templateText,CP_ACP).c_str(), gProductName.c_str());
final = llformat(ll_convert_wide_to_string(templateText, CP_ACP).c_str(), gProductName.c_str());
ConvertLPCSTRToLPWSTR(final.c_str(), header);
SetWindowText(hWnd, header);
}
@@ -158,7 +156,7 @@ void LLCrashLoggerWindows::ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
TCHAR header[MAX_STRING];
std::string final;
GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
final = llformat(ll_convert_wide_to_string(templateText,CP_ACP).c_str(), gProductName.c_str());
final = llformat(ll_convert_wide_to_string(templateText, CP_ACP).c_str(), gProductName.c_str());
ConvertLPCSTRToLPWSTR(final.c_str(), header);
SetDlgItemText(hWnd, nIDDlgItem, header);
}
@@ -201,7 +199,7 @@ bool handle_button_click(WORD button_id)
wbuffer, // pointer to buffer for text
20000 // maximum size of string
);
std::string user_text(ll_convert_wide_to_string(wbuffer,CP_ACP));
std::string user_text(ll_convert_wide_to_string(wbuffer, CP_ACP));
// Activate and show the window.
ShowWindow(gHwndProgress, SW_SHOW);
// Try doing this second to make the progress window go frontmost.
@@ -247,14 +245,182 @@ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam
LLCrashLoggerWindows::LLCrashLoggerWindows(void)
{
if (LLCrashLoggerWindows::sInstance==NULL)
{
sInstance = this;
}
}
LLCrashLoggerWindows::~LLCrashLoggerWindows(void)
{
sInstance = NULL;
}
bool LLCrashLoggerWindows::getMessageWithTimeout(MSG *msg, UINT to)
{
bool res;
const int timerID=37;
SetTimer(NULL, timerID, to, NULL);
res = GetMessage(msg, NULL, 0, 0);
KillTimer(NULL, timerID);
if (!res)
return false;
if (msg->message == WM_TIMER && msg->hwnd == NULL && msg->wParam == 1)
return false; //TIMEOUT! You could call SetLastError() or something...
return true;
}
int LLCrashLoggerWindows::processingLoop() {
const int millisecs=1000;
static int first_connect = 1;
LLSD options = getOptionData( LLApp::PRIORITY_COMMAND_LINE );
MSG msg;
bool result;
while (1)
{
result = getMessageWithTimeout(&msg, millisecs);
if ( result )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (first_connect )
{
if ( mClientsConnected > 0)
{
first_connect = 0;
}
}
else
{
if (mClientsConnected == 0)
{
break;
}
if (!mKeyMaster.isProcessAlive(mPID, mProcName) )
{
break;
}
}
}
llinfos << "session ending.." << llendl;
llinfos << "clients connected :" << mClientsConnected << llendl;
return 0;
}
void LLCrashLoggerWindows::OnClientConnected(void* context,
const google_breakpad::ClientInfo* client_info)
{
llinfos << "client start. pid = " << client_info->pid() << llendl;
sInstance->mClientsConnected++;
}
void LLCrashLoggerWindows::OnClientExited(void* context,
const google_breakpad::ClientInfo* client_info)
{
llinfos << "client end. pid = " << client_info->pid() << llendl;
sInstance->mClientsConnected--;
}
/*
void LLCrashLoggerWindows::OnClientDumpRequest(void* context,
const google_breakpad::ClientInfo* client_info,
const std::wstring* file_path)
{
ProcessingLock lock;
if (!file_path)
{
llwarns << "dump with no file path" << llendl;
return;
}
if (!client_info)
{
llwarns << "dump with no client info" << llendl;
return;
}
LLCrashLoggerWindows* self = static_cast<LLCrashLoggerWindows*>(context);
if (!self)
{
llwarns << "dump with no context" << llendl;
return;
}
DWORD pid = client_info->pid();
// Send the crash dump using a worker thread. This operation has retry
// logic in case there is no internet connection at the time.
DumpJobInfo* dump_job = new DumpJobInfo(pid, self, map,
dump_location.value());
if (!::QueueUserWorkItem(&CrashService::AsyncSendDump,
dump_job, WT_EXECUTELONGFUNCTION)) {
LOG(ERROR) << "could not queue job";
}
}
*/
bool LLCrashLoggerWindows::initCrashServer()
{
//For Breakpad on Windows we need a full Out of Process service to get good data.
//This routine starts up the service on a named pipe that the viewer will then
//communicate with.
using namespace google_breakpad;
LLSD options = getOptionData( LLApp::PRIORITY_COMMAND_LINE );
std::string dump_path = options["dumpdir"].asString();
mClientsConnected = 0;
mPID = options["pid"].asInteger();
mProcName = options["procname"].asString();
std::wostringstream ws;
//Generate a quasi-uniq name for the named pipe. For our purposes
//this is unique-enough with least hassle. Worst case for duplicate name
//is a second instance of the viewer will not do crash reporting.
ws << mCrashReportPipeStr << mPID;
std::wstring wpipe_name = ws.str();
std::wstring wdump_path;
wdump_path.assign(dump_path.begin(), dump_path.end());
//Pipe naming conventions: http://msdn.microsoft.com/en-us/library/aa365783%28v=vs.85%29.aspx
mCrashHandler = new CrashGenerationServer( (WCHAR *)wpipe_name.c_str(),
NULL,
&LLCrashLoggerWindows::OnClientConnected, this,
NULL, NULL, // &LLCrashLoggerWindows::OnClientDumpRequest, this,
&LLCrashLoggerWindows::OnClientExited, this,
NULL, NULL,
true, &wdump_path);
if (!mCrashHandler) {
//Failed to start the crash server.
llwarns << "Failed to init crash server." << llendl;
return false;
}
// Start servicing clients.
if (!mCrashHandler->Start()) {
llwarns << "Failed to start crash server." << llendl;
return false;
}
return true;
}
bool LLCrashLoggerWindows::init(void)
{
initCrashServer();
bool ok = LLCrashLogger::init();
if(!ok) return false;
@@ -298,24 +464,23 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
SetCursor(gCursorWait);
// 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");
//mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); //Not initialized.
}
bool LLCrashLoggerWindows::mainLoop()
{
llinfos << "CrashSubmitBehavior is " << mCrashBehavior << llendl;
// Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529
// win_crash_logger.rc has been edited by hand.
// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
gProductName = mProductName;
gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
ProcessCaption(gHwndProgress);
ShowWindow(gHwndProgress, SW_HIDE );
if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
{
llinfos << "Showing crash report submit progress window." << llendl;
ShowWindow(gHwndProgress, SW_SHOW );
sendCrashLogs();
}
@@ -362,7 +527,7 @@ bool LLCrashLoggerWindows::mainLoop()
void LLCrashLoggerWindows::updateApplication(const std::string& message)
{
LLCrashLogger::updateApplication();
LLCrashLogger::updateApplication(message);
if(!message.empty()) show_progress(message);
update_messages();
}
@@ -376,8 +541,8 @@ bool LLCrashLoggerWindows::cleanup()
sleep_and_pump_messages(3);
}
PostQuitMessage(0);
commonCleanup();
mKeyMaster.releaseMaster();
return true;
}

View File

@@ -2,31 +2,25 @@
* @file llcrashloggerwindows.h
* @brief Windows crash logger definition
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,21 +31,54 @@
#include "windows.h"
#include "llstring.h"
class LLSD;
namespace google_breakpad {
class CrashGenerationServer;
class ClientInfo;
}
class LLCrashLoggerWindows : public LLCrashLogger
{
public:
LLCrashLoggerWindows(void);
~LLCrashLoggerWindows(void);
static LLCrashLoggerWindows* sInstance;
virtual bool init();
virtual bool mainLoop();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
virtual bool cleanup();
virtual void gatherPlatformSpecificFiles();
//void annotateCallStack();
void setHandle(HINSTANCE hInst) { mhInst = hInst; }
int clients_connected() const {
return mClientsConnected;
}
bool getMessageWithTimeout(MSG *msg, UINT to);
// Starts the processing loop. This function does not return unless the
// user is logging off or the user closes the crash service window. The
// return value is a good number to pass in ExitProcess().
int processingLoop();
private:
void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
void ProcessCaption(HWND hWnd);
bool initCrashServer();
google_breakpad::CrashGenerationServer* mCrashHandler;
static void OnClientConnected(void* context,
const google_breakpad::ClientInfo* client_info);
/*static void OnClientDumpRequest(
void* context,
const google_breakpad::ClientInfo* client_info,
const std::wstring* file_path);*/
static void OnClientExited(void* context,
const google_breakpad::ClientInfo* client_info);
int mClientsConnected;
int mPID;
std::string mProcName;
HINSTANCE mhInst;
};

View File

@@ -2,31 +2,25 @@
* @file resource.h
* @brief Windows crash logger windows resources
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/

View File

@@ -2,79 +2,60 @@
* @file win_crash_logger.cpp
* @brief Windows crash logger implementation
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// win_crash_logger.cpp : Defines the entry point for the application.
//
// Must be first include, precompiled headers.
#include "linden_common.h"
#include "stdafx.h"
#include <stdlib.h>
#include "llcrashloggerwindows.h"
#include <iostream>
//
// Implementation
//
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
llinfos << "Starting crash reporter" << llendl;
llinfos << "Starting crash reporter." << llendl;
LLCrashLoggerWindows app;
app.setHandle(hInstance);
bool ok = app.init();
if(!ok)
app.parseCommandOptions(__argc, __argv);
LLSD options = LLApp::instance()->getOptionData(
LLApp::PRIORITY_COMMAND_LINE);
if (!(options.has("pid") && options.has("dumpdir")))
{
llwarns << "Insufficient parameters to crash report." << llendl;
}
if (! app.init())
{
llwarns << "Unable to initialize application." << llendl;
return -1;
return 1;
}
// Run the application main loop
if(!LLApp::isQuitting()) app.mainLoop();
if (!app.isError())
{
//
// We don't want to do cleanup here if the error handler got called -
// the assumption is that the error handler is responsible for doing
// app cleanup if there was a problem.
//
app.cleanup();
}
app.processingLoop();
app.mainLoop();
app.cleanup();
llinfos << "Crash reporter finished normally." << llendl;
return 0;
}

View File

@@ -2,31 +2,25 @@
* @file win_crash_logger.h
* @brief Windows crash logger project includes
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* 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
* Copyright (C) 2010, Linden Research, Inc.
*
* 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
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/