Breakpad3: WIP, minidump files created
TODO: re-work sending crash logs get rid of standalone loggers
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
211
indra/llcrashlogger/llcrashlock.cpp
Normal file
211
indra/llcrashlogger/llcrashlock.cpp
Normal 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);
|
||||
}
|
||||
73
indra/llcrashlogger/llcrashlock.h
Normal file
73
indra/llcrashlogger/llcrashlock.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>2</integer>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>CurlMaxTotalConcurrentConnections</key>
|
||||
<map>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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$
|
||||
*/
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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$
|
||||
*/
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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$
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user