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
|
set(CMAKE_C_FLAGS_RELEASE
|
||||||
"${CMAKE_CXX_FLAGS_RELEASE}")
|
"${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
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.
|
# Don't bother with a MinSizeRel build.
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ include(00-Common)
|
|||||||
include(LLCommon)
|
include(LLCommon)
|
||||||
include(APR)
|
include(APR)
|
||||||
include(Linking)
|
include(Linking)
|
||||||
|
include(GoogleBreakpad)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${EXPAT_INCLUDE_DIRS}
|
${EXPAT_INCLUDE_DIRS}
|
||||||
${LLCOMMON_INCLUDE_DIRS}
|
${LLCOMMON_INCLUDE_DIRS}
|
||||||
${ZLIB_INCLUDE_DIRS}
|
${ZLIB_INCLUDE_DIRS}
|
||||||
|
${BREAKPAD_INCLUDE_DIRECTORIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(llcommon_SOURCE_FILES
|
set(llcommon_SOURCE_FILES
|
||||||
@@ -265,6 +267,7 @@ add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
|
|||||||
add_dependencies(llcommon prepare)
|
add_dependencies(llcommon prepare)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
llcommon
|
llcommon
|
||||||
|
${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
|
||||||
${APRUTIL_LIBRARIES}
|
${APRUTIL_LIBRARIES}
|
||||||
${APR_LIBRARIES}
|
${APR_LIBRARIES}
|
||||||
${EXPAT_LIBRARIES}
|
${EXPAT_LIBRARIES}
|
||||||
|
|||||||
@@ -27,6 +27,14 @@
|
|||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
#include "llapp.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 "llcommon.h"
|
||||||
#include "llapr.h"
|
#include "llapr.h"
|
||||||
#include "llerrorcontrol.h"
|
#include "llerrorcontrol.h"
|
||||||
@@ -35,7 +43,9 @@
|
|||||||
#include "lllivefile.h"
|
#include "lllivefile.h"
|
||||||
#include "llmemory.h"
|
#include "llmemory.h"
|
||||||
#include "llstl.h" // for DeletePointer()
|
#include "llstl.h" // for DeletePointer()
|
||||||
|
#include "llstring.h"
|
||||||
#include "lleventtimer.h"
|
#include "lleventtimer.h"
|
||||||
|
#include "google_breakpad/exception_handler.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Signal handling
|
// Signal handling
|
||||||
@@ -43,13 +53,34 @@
|
|||||||
// Windows uses structured exceptions, so it's handled a bit differently.
|
// Windows uses structured exceptions, so it's handled a bit differently.
|
||||||
//
|
//
|
||||||
#if LL_WINDOWS
|
#if LL_WINDOWS
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
|
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
|
||||||
BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);
|
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
|
#else
|
||||||
# include <signal.h>
|
# include <signal.h>
|
||||||
# include <unistd.h> // for fork()
|
# include <unistd.h> // for fork()
|
||||||
void setup_signals();
|
void setup_signals();
|
||||||
void default_unix_signal_handler(int signum, siginfo_t *info, void *);
|
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
|
# if LL_DARWIN
|
||||||
/* OSX doesn't support SIGRT* */
|
/* OSX doesn't support SIGRT* */
|
||||||
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
|
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
|
||||||
@@ -79,7 +110,6 @@ BOOL LLApp::sLogInSignal = FALSE;
|
|||||||
// static
|
// static
|
||||||
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
|
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
|
||||||
LLAppErrorHandler LLApp::sErrorHandler = NULL;
|
LLAppErrorHandler LLApp::sErrorHandler = NULL;
|
||||||
LLAppErrorHandler LLApp::sSyncErrorHandler = NULL;
|
|
||||||
BOOL LLApp::sErrorThreadRunning = FALSE;
|
BOOL LLApp::sErrorThreadRunning = FALSE;
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
LLApp::child_map LLApp::sChildMap;
|
LLApp::child_map LLApp::sChildMap;
|
||||||
@@ -100,14 +130,6 @@ void LLApp::commonCtor()
|
|||||||
|
|
||||||
LLCommon::initClass();
|
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
|
// initialize the options structure. We need to make this an array
|
||||||
// because the structured data will not auto-allocate if we
|
// because the structured data will not auto-allocate if we
|
||||||
// reference an invalid location with the [] operator.
|
// reference an invalid location with the [] operator.
|
||||||
@@ -118,8 +140,19 @@ void LLApp::commonCtor()
|
|||||||
mOptions.append(sd);
|
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.
|
// Set the application to this instance.
|
||||||
sApplication = this;
|
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) :
|
LLApp::LLApp(LLErrorThread *error_thread) :
|
||||||
@@ -131,10 +164,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :
|
|||||||
|
|
||||||
LLApp::~LLApp()
|
LLApp::~LLApp()
|
||||||
{
|
{
|
||||||
#if !LL_WINDOWS
|
|
||||||
delete sSigChildCount;
|
|
||||||
sSigChildCount = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// reclaim live file memory
|
// reclaim live file memory
|
||||||
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
|
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
|
||||||
@@ -149,6 +178,8 @@ LLApp::~LLApp()
|
|||||||
mThreadErrorp = NULL;
|
mThreadErrorp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mExceptionHandler != 0) delete mExceptionHandler;
|
||||||
|
|
||||||
LLCommon::cleanupClass();
|
LLCommon::cleanupClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +238,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
++ii;
|
++ii;
|
||||||
value.assign(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;
|
commands[name] = value;
|
||||||
}
|
}
|
||||||
setOptionData(PRIORITY_COMMAND_LINE, commands);
|
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
|
// 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)
|
// in the case of an error (generate a minidump)
|
||||||
|
|
||||||
// Disable this until the viewer gets ported so server crashes can be JIT debugged.
|
#if LL_SEND_CRASH_REPORTS
|
||||||
//LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
|
|
||||||
//prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
|
|
||||||
|
|
||||||
// This sets a callback to handle w32 signals to the console window.
|
// This sets a callback to handle w32 signals to the console window.
|
||||||
// The viewer shouldn't be affected, sicne its a windowed app.
|
// The viewer shouldn't be affected, sicne its a windowed app.
|
||||||
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
|
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
|
#else
|
||||||
//
|
//
|
||||||
// Start up signal handling.
|
// Start up signal handling.
|
||||||
@@ -272,8 +363,60 @@ void LLApp::setupErrorHandling()
|
|||||||
|
|
||||||
setup_signals();
|
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
|
#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()
|
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
|
// static
|
||||||
void LLApp::runErrorHandler()
|
void LLApp::runErrorHandler()
|
||||||
{
|
{
|
||||||
@@ -348,15 +477,46 @@ void LLApp::setStatus(EAppStatus status)
|
|||||||
// static
|
// static
|
||||||
void LLApp::setError()
|
void LLApp::setError()
|
||||||
{
|
{
|
||||||
if (!isError())
|
|
||||||
{
|
|
||||||
// perform any needed synchronous error-handling
|
|
||||||
runSyncErrorHandler();
|
|
||||||
// set app status to ERROR so that the LLErrorThread notices
|
// set app status to ERROR so that the LLErrorThread notices
|
||||||
setStatus(APP_STATUS_ERROR);
|
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
|
// static
|
||||||
void LLApp::setQuitting()
|
void LLApp::setQuitting()
|
||||||
@@ -413,6 +573,12 @@ bool LLApp::isExiting()
|
|||||||
void LLApp::disableCrashlogger()
|
void LLApp::disableCrashlogger()
|
||||||
{
|
{
|
||||||
// Disable Breakpad exception handler.
|
// Disable Breakpad exception handler.
|
||||||
|
if (mExceptionHandler != 0)
|
||||||
|
{
|
||||||
|
delete mExceptionHandler;
|
||||||
|
mExceptionHandler = 0;
|
||||||
|
}
|
||||||
|
|
||||||
sDisableCrashlogger = TRUE;
|
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
|
#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;
|
typedef LLAtomic32<U32> LLAtomicU32;
|
||||||
class LLErrorThread;
|
class LLErrorThread;
|
||||||
class LLLiveFile;
|
class LLLiveFile;
|
||||||
|
|
||||||
|
|
||||||
#if LL_LINUX
|
#if LL_LINUX
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
//typedef struct siginfo siginfo_t; //Removed as per changes in glibc 2.16 - Drake Arconis
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef void (*LLAppErrorHandler)();
|
typedef void (*LLAppErrorHandler)();
|
||||||
typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
|
typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
|
||||||
|
|
||||||
@@ -64,6 +60,10 @@ public:
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
class ExceptionHandler; // See exception_handler.h
|
||||||
|
}
|
||||||
|
|
||||||
class LL_COMMON_API LLApp : public LLOptionInterface
|
class LL_COMMON_API LLApp : public LLOptionInterface
|
||||||
{
|
{
|
||||||
friend class LLErrorThread;
|
friend class LLErrorThread;
|
||||||
@@ -212,11 +212,43 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
static int getPid();
|
static int getPid();
|
||||||
|
|
||||||
//
|
/** @name Error handling methods */
|
||||||
// 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 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
|
#if !LL_WINDOWS
|
||||||
//
|
//
|
||||||
// Child process handling (Unix only for now)
|
// Child process handling (Unix only for now)
|
||||||
@@ -236,6 +268,7 @@ public:
|
|||||||
pid_t fork();
|
pid_t fork();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> string_map;
|
typedef std::map<std::string, std::string> string_map;
|
||||||
string_map mOptionMap; // Contains all command-line options and arguments in a 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 EAppStatus sStatus; // Reflects current application status
|
||||||
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
static BOOL sErrorThreadRunning; // Set while the error thread is running
|
||||||
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
|
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
|
#if !LL_WINDOWS
|
||||||
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
|
||||||
@@ -254,26 +290,25 @@ protected:
|
|||||||
static LLAppChildCallback sDefaultChildCallback;
|
static LLAppChildCallback sDefaultChildCallback;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* @ brief This method is called once as soon as logging is initialized.
|
|
||||||
*/
|
|
||||||
void startErrorThread();
|
void startErrorThread();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This method is called at the end, just prior to deinitializing curl.
|
* @brief This method is called at the end, just prior to deinitializing curl.
|
||||||
*/
|
*/
|
||||||
void stopErrorThread();
|
void stopErrorThread();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)
|
// Contains the filename of the minidump file after a crash.
|
||||||
static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
|
char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
|
||||||
static void runSyncErrorHandler(); // run IMMEDIATELY when we get an error, ran in the context of the faulting thread.
|
|
||||||
|
std::string mStaticDebugFileName;
|
||||||
|
std::string mDynamicDebugFileName;
|
||||||
|
|
||||||
// *NOTE: On Windows, we need a routine to reset the structured
|
// *NOTE: On Windows, we need a routine to reset the structured
|
||||||
// exception handler when some evil driver has taken it over for
|
// exception handler when some evil driver has taken it over for
|
||||||
// their own purposes
|
// their own purposes
|
||||||
typedef int(*signal_handler_func)(int signum);
|
typedef int(*signal_handler_func)(int signum);
|
||||||
static LLAppErrorHandler sErrorHandler;
|
static LLAppErrorHandler sErrorHandler;
|
||||||
static LLAppErrorHandler sSyncErrorHandler;
|
|
||||||
|
|
||||||
// Default application threads
|
// Default application threads
|
||||||
LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
|
LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
|
||||||
@@ -291,6 +326,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
// the static application instance if it was created.
|
// the static application instance if it was created.
|
||||||
static LLApp* sApplication;
|
static LLApp* sApplication;
|
||||||
|
google_breakpad::ExceptionHandler * mExceptionHandler;
|
||||||
|
|
||||||
|
|
||||||
#if !LL_WINDOWS
|
#if !LL_WINDOWS
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ public:
|
|||||||
mRunning = false;
|
mRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resetWithExpiry(F32 expiration) { reset(); setTimerExpirySec(expiration); }
|
||||||
void pause(); // Mark elapsed time so far.
|
void pause(); // Mark elapsed time so far.
|
||||||
void unpause(); // Move 'start' time in order to decrement time between pause and unpause from ElapsedTime.
|
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
|
set(llcrashlogger_SOURCE_FILES
|
||||||
llcrashlogger.cpp
|
llcrashlogger.cpp
|
||||||
|
llcrashlock.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(llcrashlogger_HEADER_FILES
|
set(llcrashlogger_HEADER_FILES
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
|
||||||
llcrashlogger.h
|
llcrashlogger.h
|
||||||
|
llcrashlock.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(${llcrashlogger_HEADER_FILES}
|
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
|
* @file llcrashlogger.cpp
|
||||||
* @brief Crash logger implementation
|
* @brief Crash logger implementation
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "linden_common.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "llcrashlogger.h"
|
#include "llcrashlogger.h"
|
||||||
|
#include "llcrashlock.h"
|
||||||
|
#include "linden_common.h"
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
|
#include "indra_constants.h" // CRASH_BEHAVIOR_...
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
|
#include "llerrorcontrol.h"
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
#include "lldir.h"
|
#include "lldir.h"
|
||||||
|
#include "llfile.h"
|
||||||
#include "llsdserialize.h"
|
#include "llsdserialize.h"
|
||||||
#include "lliopipe.h"
|
#include "lliopipe.h"
|
||||||
#include "llpumpio.h"
|
#include "llpumpio.h"
|
||||||
#include "llhttpclient.h"
|
#include "llhttpclient.h"
|
||||||
#include "llsdserialize.h"
|
#include "llsdserialize.h"
|
||||||
#include "llcurl.h"
|
#include "llproxy.h"
|
||||||
#include "aistatemachine.h"
|
#include "aistatemachine.h"
|
||||||
|
|
||||||
LLPumpIO* gServicePump;
|
LLPumpIO* gServicePump;
|
||||||
BOOL gBreak = false;
|
BOOL gBreak = false;
|
||||||
BOOL gSent = false;
|
BOOL gSent = false;
|
||||||
|
|
||||||
|
extern void startEngineThread(void);
|
||||||
|
|
||||||
class AIHTTPTimeoutPolicy;
|
class AIHTTPTimeoutPolicy;
|
||||||
extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout;
|
extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout;
|
||||||
extern void startEngineThread(void);
|
|
||||||
|
|
||||||
class LLCrashLoggerResponder : public LLHTTPClient::ResponderWithResult
|
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;
|
gBreak = true;
|
||||||
gSent = true;
|
gSent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return crashLoggerResponder_timeout; }
|
virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const
|
||||||
/*virtual*/ char const* getName(void) const { return "LLCrashLoggerResponder"; }
|
{
|
||||||
|
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() :
|
LLCrashLogger::LLCrashLogger() :
|
||||||
mCrashBehavior(CRASH_BEHAVIOR_ASK),
|
mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
|
||||||
mCrashInPreviousExec(false),
|
mCrashInPreviousExec(false),
|
||||||
|
mCrashSettings("CrashSettings"),
|
||||||
mSentCrashLogs(false),
|
mSentCrashLogs(false),
|
||||||
mCrashHost(""),
|
mCrashHost("")
|
||||||
mCrashSettings("CrashSettings")
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LLCrashLogger::~LLCrashLogger()
|
LLCrashLogger::~LLCrashLogger()
|
||||||
@@ -163,38 +152,67 @@ std::string getStartupStateFromLog(std::string& sllog)
|
|||||||
return startup_state;
|
return startup_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCrashLogger::gatherFiles()
|
bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename )
|
||||||
{
|
{
|
||||||
|
std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,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::ifstream debug_log_file(db_file_name.c_str());
|
||||||
|
|
||||||
// Look for it in the debug_info.log file
|
// Look for it in the debug_info.log file
|
||||||
if (debug_log_file.is_open())
|
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();
|
mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean();
|
||||||
|
|
||||||
mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
|
mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString();
|
||||||
@@ -219,11 +237,9 @@ void LLCrashLogger::gatherFiles()
|
|||||||
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
|
mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !LL_DARWIN
|
|
||||||
if(mCrashInPreviousExec)
|
if(mCrashInPreviousExec)
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
|
// Restarting after freeze.
|
||||||
// Replace the log file ext with .old, since the
|
// Replace the log file ext with .old, since the
|
||||||
// instance that launched this process has overwritten
|
// instance that launched this process has overwritten
|
||||||
// SecondLife.log
|
// SecondLife.log
|
||||||
@@ -235,7 +251,12 @@ void LLCrashLogger::gatherFiles()
|
|||||||
gatherPlatformSpecificFiles();
|
gatherPlatformSpecificFiles();
|
||||||
|
|
||||||
//Use the debug log to reconstruct the URL to send the crash report to
|
//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 = "https://";
|
||||||
mCrashHost += mDebugLog["CurrentSimHost"].asString();
|
mCrashHost += mDebugLog["CurrentSimHost"].asString();
|
||||||
@@ -256,8 +277,7 @@ void LLCrashLogger::gatherFiles()
|
|||||||
mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
|
mAltCrashHost = "https://login.agni.lindenlab.com:12043/crash/report";
|
||||||
|
|
||||||
mCrashInfo["DebugLog"] = mDebugLog;
|
mCrashInfo["DebugLog"] = mDebugLog;
|
||||||
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
|
mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log");
|
||||||
mFileMap["StackTrace"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log");
|
|
||||||
|
|
||||||
updateApplication("Encoding files...");
|
updateApplication("Encoding files...");
|
||||||
|
|
||||||
@@ -282,52 +302,115 @@ void LLCrashLogger::gatherFiles()
|
|||||||
trimSLLog(crash_info);
|
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 LLCrashLogger::constructPostData()
|
||||||
{
|
{
|
||||||
LLSD ret;
|
|
||||||
return mCrashInfo;
|
return mCrashInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Singu Note, defiend in indra_constants.h # const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
|
||||||
|
|
||||||
S32 LLCrashLogger::loadCrashBehaviorSetting()
|
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);
|
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 we didn't load any files above, this will return the default
|
||||||
|
S32 value = mCrashSettings.getS32("CrashSubmitBehavior");
|
||||||
|
|
||||||
if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
|
// 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 value;
|
return CRASH_BEHAVIOR_ASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
|
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);
|
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
|
||||||
|
|
||||||
mCrashSettings.saveToFile(filename, FALSE);
|
mCrashSettings.saveToFile(filename, FALSE);
|
||||||
|
|
||||||
return true;
|
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;
|
gBreak = false;
|
||||||
std::string status_message;
|
|
||||||
for(int i = 0; i < retries; ++i)
|
for(int i = 0; i < retries; ++i)
|
||||||
{
|
{
|
||||||
status_message = llformat("%s, try %d...", msg.c_str(), i+1);
|
updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
|
||||||
LLHTTPClient::post(host, data, new LLCrashLoggerResponder);
|
LLHTTPClient::post(host, data, new LLCrashLoggerResponder());
|
||||||
while(!gBreak)
|
while(!gBreak)
|
||||||
{
|
{
|
||||||
updateApplication(status_message);
|
updateApplication(); // No new message, just pump the IO
|
||||||
}
|
}
|
||||||
if(gSent)
|
if(gSent)
|
||||||
{
|
{
|
||||||
@@ -337,8 +420,14 @@ bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg
|
|||||||
return gSent;
|
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();
|
gatherFiles();
|
||||||
|
|
||||||
LLSD post_data;
|
LLSD post_data;
|
||||||
@@ -346,10 +435,6 @@ bool LLCrashLogger::sendCrashLogs()
|
|||||||
|
|
||||||
updateApplication("Sending reports...");
|
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());
|
std::ofstream out_file(report_file.c_str());
|
||||||
LLSDSerialize::toPrettyXML(post_data, out_file);
|
LLSDSerialize::toPrettyXML(post_data, out_file);
|
||||||
out_file.close();
|
out_file.close();
|
||||||
@@ -359,45 +444,147 @@ bool LLCrashLogger::sendCrashLogs()
|
|||||||
//*TODO: Translate
|
//*TODO: Translate
|
||||||
if(mCrashHost != "")
|
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)
|
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;
|
mSentCrashLogs = sent;
|
||||||
|
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCrashLogger::updateApplication(const std::string& message)
|
void LLCrashLogger::updateApplication(const std::string& message)
|
||||||
{
|
{
|
||||||
|
/* Sing TODO
|
||||||
gServicePump->pump();
|
gServicePump->pump();
|
||||||
gServicePump->callback();
|
gServicePump->callback();
|
||||||
gMainThreadEngine.mainloop();
|
*/
|
||||||
|
if (!message.empty()) llinfos << message << llendl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLCrashLogger::init()
|
bool LLCrashLogger::init()
|
||||||
{
|
{
|
||||||
// Initialize curl
|
LLCurl::initCurl();
|
||||||
AICurlInterface::initCurl();
|
AIEngine::setMaxCount(100);
|
||||||
|
|
||||||
// Initialize state machine engines.
|
|
||||||
AIEngine::setMaxCount(100); // StateMachineMaxTime
|
|
||||||
|
|
||||||
// Start state machine thread.
|
|
||||||
startEngineThread();
|
|
||||||
|
|
||||||
// We assume that all the logs we're looking for reside on the current drive
|
// We assume that all the logs we're looking for reside on the current drive
|
||||||
gDirUtilp->initAppDirs("SecondLife");
|
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)
|
// Default to the product name "Second Life" (this is overridden by the -name argument)
|
||||||
mProductName = "Second Life";
|
mProductName = "Second Life";
|
||||||
|
|
||||||
mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
|
// Handle locking
|
||||||
"(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
|
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;
|
llinfos << "Loading crash behavior setting" << llendl;
|
||||||
mCrashBehavior = loadCrashBehaviorSetting();
|
mCrashBehavior = loadCrashBehaviorSetting();
|
||||||
@@ -409,17 +596,18 @@ bool LLCrashLogger::init()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start curl thread.
|
|
||||||
AICurlInterface::startCurlThread(&mCrashSettings);
|
AICurlInterface::startCurlThread(&mCrashSettings);
|
||||||
|
startEngineThread();
|
||||||
gServicePump = new LLPumpIO;
|
/* Singu Note: not needed for AICurl
|
||||||
|
gServicePump = new LLPumpIO(gAPRPoolp);
|
||||||
//If we've opened the crash logger, assume we can delete the marker file if it exists
|
gServicePump->prime(gAPRPoolp);
|
||||||
if( gDirUtilp )
|
LLHTTPClient::setPump(*gServicePump);
|
||||||
{
|
*/
|
||||||
std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
|
|
||||||
LLAPRFile::remove( marker_file );
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For cleanup code common to all platforms.
|
||||||
|
void LLCrashLogger::commonCleanup()
|
||||||
|
{
|
||||||
|
LLProxy::cleanupClass();
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,45 +2,38 @@
|
|||||||
* @file llcrashlogger.h
|
* @file llcrashlogger.h
|
||||||
* @brief Crash Logger Definition
|
* @brief Crash Logger Definition
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
#ifndef LLCRASHLOGGER_H
|
#ifndef LLCRASHLOGGER_H
|
||||||
#define LLCRASHLOGGER_H
|
#define LLCRASHLOGGER_H
|
||||||
|
|
||||||
#include "linden_common.h"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "linden_common.h"
|
||||||
|
|
||||||
#include "llapp.h"
|
#include "llapp.h"
|
||||||
#include "llsd.h"
|
#include "llsd.h"
|
||||||
#include "llcontrol.h"
|
#include "llcontrol.h"
|
||||||
|
#include "llcrashlock.h"
|
||||||
class AIHTTPTimeoutPolicy;
|
|
||||||
|
|
||||||
class LLCrashLogger : public LLApp
|
class LLCrashLogger : public LLApp
|
||||||
{
|
{
|
||||||
@@ -48,18 +41,25 @@ public:
|
|||||||
LLCrashLogger();
|
LLCrashLogger();
|
||||||
virtual ~LLCrashLogger();
|
virtual ~LLCrashLogger();
|
||||||
S32 loadCrashBehaviorSetting();
|
S32 loadCrashBehaviorSetting();
|
||||||
|
bool readDebugFromXML(LLSD& dest, const std::string& filename );
|
||||||
void gatherFiles();
|
void gatherFiles();
|
||||||
|
void mergeLogs( LLSD src_sd );
|
||||||
|
|
||||||
virtual void gatherPlatformSpecificFiles() {}
|
virtual void gatherPlatformSpecificFiles() {}
|
||||||
bool saveCrashBehaviorSetting(S32 crash_behavior);
|
bool saveCrashBehaviorSetting(S32 crash_behavior);
|
||||||
|
bool sendCrashLog(std::string dump_dir);
|
||||||
bool sendCrashLogs();
|
bool sendCrashLogs();
|
||||||
LLSD constructPostData();
|
LLSD constructPostData();
|
||||||
virtual void updateApplication(const std::string& message = LLStringUtil::null);
|
virtual void updateApplication(const std::string& message = LLStringUtil::null);
|
||||||
virtual bool init();
|
virtual bool init();
|
||||||
virtual bool mainLoop() = 0;
|
virtual bool mainLoop() = 0;
|
||||||
virtual bool cleanup() { return true; }
|
virtual bool cleanup() = 0;
|
||||||
|
void commonCleanup();
|
||||||
void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
|
void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
|
||||||
S32 getCrashBehavior() { return mCrashBehavior; }
|
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:
|
protected:
|
||||||
S32 mCrashBehavior;
|
S32 mCrashBehavior;
|
||||||
BOOL mCrashInPreviousExec;
|
BOOL mCrashInPreviousExec;
|
||||||
@@ -72,17 +72,7 @@ protected:
|
|||||||
std::string mAltCrashHost;
|
std::string mAltCrashHost;
|
||||||
LLSD mDebugLog;
|
LLSD mDebugLog;
|
||||||
bool mSentCrashLogs;
|
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
|
#endif //LLCRASHLOGGER_H
|
||||||
|
|||||||
@@ -922,7 +922,6 @@ P(charactersResponder);
|
|||||||
P(checkAgentAppearanceServiceResponder);
|
P(checkAgentAppearanceServiceResponder);
|
||||||
P(classifiedStatsResponder);
|
P(classifiedStatsResponder);
|
||||||
P(consoleResponder);
|
P(consoleResponder);
|
||||||
P2(crashLoggerResponder, transfer_22s_connect_10s);
|
|
||||||
P(createInventoryCategoryResponder);
|
P(createInventoryCategoryResponder);
|
||||||
P(emeraldDicDownloader);
|
P(emeraldDicDownloader);
|
||||||
P(environmentApplyResponder);
|
P(environmentApplyResponder);
|
||||||
@@ -993,4 +992,4 @@ P(webProfileResponders);
|
|||||||
P(wholeModelFeeResponder);
|
P(wholeModelFeeResponder);
|
||||||
P(wholeModelUploadResponder);
|
P(wholeModelUploadResponder);
|
||||||
P2(XMLRPCResponder, connect_40s);
|
P2(XMLRPCResponder, connect_40s);
|
||||||
|
P2(crashLoggerResponder, transfer_300s);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @file lldir.cpp
|
* @file lldir.cpp
|
||||||
* @brief implementation of directory utilities base class
|
* @brief implementation of directory utilities base class
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
|
|
||||||
#include "lldiriterator.h"
|
#include "lldiriterator.h"
|
||||||
#include "stringize.h"
|
#include "stringize.h"
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/range/begin.hpp>
|
#include <boost/range/begin.hpp>
|
||||||
#include <boost/range/end.hpp>
|
#include <boost/range/end.hpp>
|
||||||
@@ -69,6 +71,8 @@ LLDir_Linux gDirUtil;
|
|||||||
|
|
||||||
LLDir *gDirUtilp = (LLDir *)&gDirUtil;
|
LLDir *gDirUtilp = (LLDir *)&gDirUtil;
|
||||||
|
|
||||||
|
static const char* const empty = "";
|
||||||
|
std::string LLDir::sDumpDir = "";
|
||||||
LLDir::LLDir()
|
LLDir::LLDir()
|
||||||
: mAppName(""),
|
: mAppName(""),
|
||||||
mExecutablePathAndName(""),
|
mExecutablePathAndName(""),
|
||||||
@@ -89,6 +93,31 @@ 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 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
|
||||||
{
|
{
|
||||||
@@ -148,6 +177,12 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
|
|||||||
return count;
|
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 LLDir::findFile(const std::string &filename,
|
||||||
const std::string& searchPath1,
|
const std::string& searchPath1,
|
||||||
const std::string& searchPath2,
|
const std::string& searchPath2,
|
||||||
@@ -235,6 +270,31 @@ const std::string &LLDir::getChatLogsDir() const
|
|||||||
return mChatLogsDir;
|
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
|
const std::string &LLDir::getPerAccountChatLogsDir() const
|
||||||
{
|
{
|
||||||
return mPerAccountChatLogsDir;
|
return mPerAccountChatLogsDir;
|
||||||
@@ -396,12 +456,29 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd
|
|||||||
prefix = getCacheDir();
|
prefix = getCacheDir();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LL_PATH_DUMP:
|
||||||
|
prefix=getDumpDir();
|
||||||
|
break;
|
||||||
|
|
||||||
case LL_PATH_USER_SETTINGS:
|
case LL_PATH_USER_SETTINGS:
|
||||||
prefix = add(getOSUserAppDir(), "user_settings");
|
prefix = add(getOSUserAppDir(), "user_settings");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LL_PATH_PER_SL_ACCOUNT:
|
case LL_PATH_PER_SL_ACCOUNT:
|
||||||
prefix = getLindenUserDir();
|
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;
|
break;
|
||||||
|
|
||||||
case LL_PATH_CHAT_LOGS:
|
case LL_PATH_CHAT_LOGS:
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ typedef enum ELLPath
|
|||||||
LL_PATH_EXECUTABLE = 16,
|
LL_PATH_EXECUTABLE = 16,
|
||||||
LL_PATH_DEFAULT_SKIN = 17,
|
LL_PATH_DEFAULT_SKIN = 17,
|
||||||
LL_PATH_FONTS = 18,
|
LL_PATH_FONTS = 18,
|
||||||
|
LL_PATH_DUMP = 19,
|
||||||
LL_PATH_LAST
|
LL_PATH_LAST
|
||||||
} ELLPath;
|
} ELLPath;
|
||||||
|
|
||||||
@@ -71,7 +72,8 @@ class LLDir
|
|||||||
const std::string& app_read_only_data_dir = "") = 0;
|
const std::string& app_read_only_data_dir = "") = 0;
|
||||||
|
|
||||||
virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
|
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
|
// pure virtual functions
|
||||||
virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0;
|
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 &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 &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 &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 &getPerAccountChatLogsDir() const; // Location of the per account chat logs dir.
|
||||||
const std::string &getTempDir() const; // Common temporary directory
|
const std::string &getTempDir() const; // Common temporary directory
|
||||||
const std::string getCacheDir(bool get_default = false) const; // Location of the cache.
|
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
|
// For producing safe download file names from potentially unsafe ones
|
||||||
static std::string getScrubbedFileName(const std::string uncleanFileName);
|
static std::string getScrubbedFileName(const std::string uncleanFileName);
|
||||||
static std::string getForbiddenFileChars();
|
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 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.
|
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 mDefaultSkinDir; // Location for default skin info.
|
||||||
std::string mUserSkinDir; // Location for user-modified skin info.
|
std::string mUserSkinDir; // Location for user-modified skin info.
|
||||||
std::string mLLPluginDir; // Location for plugins and plugin shell
|
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);
|
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>
|
<integer>1</integer>
|
||||||
</map>
|
</map>
|
||||||
<!-- Standard SL options (To my knowledge) -->
|
<!-- 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>
|
<key>AFKTimeout</key>
|
||||||
<map>
|
<map>
|
||||||
<key>Comment</key>
|
<key>Comment</key>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>S32</string>
|
<string>S32</string>
|
||||||
<key>Value</key>
|
<key>Value</key>
|
||||||
<integer>2</integer>
|
<integer>1</integer>
|
||||||
</map>
|
</map>
|
||||||
<key>CurlMaxTotalConcurrentConnections</key>
|
<key>CurlMaxTotalConcurrentConnections</key>
|
||||||
<map>
|
<map>
|
||||||
|
|||||||
@@ -558,6 +558,11 @@ LLAppViewer::LLAppViewer() :
|
|||||||
llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl;
|
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;
|
sInstance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,6 +586,8 @@ public:
|
|||||||
|
|
||||||
bool LLAppViewer::init()
|
bool LLAppViewer::init()
|
||||||
{
|
{
|
||||||
|
setupErrorHandling();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Start of the application
|
// Start of the application
|
||||||
//
|
//
|
||||||
@@ -605,9 +612,6 @@ bool LLAppViewer::init()
|
|||||||
//initialize particle index pool
|
//initialize particle index pool
|
||||||
LLVOPartGroup::initClass();
|
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
|
// set skin search path to default, will be overridden later
|
||||||
// this allows simple skinned file lookups to work
|
// this allows simple skinned file lookups to work
|
||||||
gDirUtilp->setSkinFolder("default");
|
gDirUtilp->setSkinFolder("default");
|
||||||
@@ -636,6 +640,12 @@ bool LLAppViewer::init()
|
|||||||
initMaxHeapSize() ;
|
initMaxHeapSize() ;
|
||||||
|
|
||||||
LLPrivateMemoryPoolManager::initClass((BOOL)gSavedSettings.getBOOL("MemoryPrivatePoolEnabled"), (U32)gSavedSettings.getU32("MemoryPrivatePoolSize")) ;
|
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"));
|
mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling"));
|
||||||
|
|
||||||
@@ -1448,6 +1458,14 @@ bool LLAppViewer::cleanup()
|
|||||||
{
|
{
|
||||||
//ditch LLVOAvatarSelf instance
|
//ditch LLVOAvatarSelf instance
|
||||||
gAgentAvatarp = NULL;
|
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();
|
cleanup_pose_stand();
|
||||||
|
|
||||||
//flag all elements as needing to be destroyed immediately
|
//flag all elements as needing to be destroyed immediately
|
||||||
@@ -1725,6 +1743,7 @@ bool LLAppViewer::cleanup()
|
|||||||
}
|
}
|
||||||
// </edit>
|
// </edit>
|
||||||
|
|
||||||
|
removeDumpDir();
|
||||||
writeDebugInfo();
|
writeDebugInfo();
|
||||||
|
|
||||||
if(!gDirUtilp->getLindenUserDir(true).empty())
|
if(!gDirUtilp->getLindenUserDir(true).empty())
|
||||||
@@ -2449,7 +2468,6 @@ bool LLAppViewer::initConfiguration()
|
|||||||
|
|
||||||
initMarkerFile();
|
initMarkerFile();
|
||||||
|
|
||||||
checkForCrash();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2469,10 +2487,6 @@ bool LLAppViewer::initConfiguration()
|
|||||||
|
|
||||||
initMarkerFile();
|
initMarkerFile();
|
||||||
|
|
||||||
if(!mSecondInstance)
|
|
||||||
{
|
|
||||||
checkForCrash();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to do this here - need to have initialized global settings first
|
// need to do this here - need to have initialized global settings first
|
||||||
@@ -2488,62 +2502,6 @@ bool LLAppViewer::initConfiguration()
|
|||||||
return true; // Config was successful.
|
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()
|
bool LLAppViewer::initWindow()
|
||||||
{
|
{
|
||||||
LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL;
|
LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL;
|
||||||
@@ -2613,12 +2571,21 @@ bool LLAppViewer::initWindow()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewer::writeDebugInfo()
|
void LLAppViewer::writeDebugInfo(bool isStatic)
|
||||||
{
|
{
|
||||||
std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
|
//Try to do the minimum when writing data during a crash.
|
||||||
llinfos << "Opening debug file " << debug_filename << llendl;
|
std::string* debug_filename;
|
||||||
llofstream out_file(debug_filename);
|
debug_filename = ( isStatic
|
||||||
LLSDSerialize::toPrettyXML(gDebugInfo, out_file);
|
? 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();
|
out_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2669,6 +2636,10 @@ void LLAppViewer::removeCacheFiles(const std::string& file_mask)
|
|||||||
|
|
||||||
void LLAppViewer::writeSystemInfo()
|
void LLAppViewer::writeSystemInfo()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (! gDebugInfo.has("Dynamic") )
|
||||||
|
gDebugInfo["Dynamic"] = LLSD::emptyMap();
|
||||||
|
|
||||||
gDebugInfo["SLLog"] = LLError::logFileName();
|
gDebugInfo["SLLog"] = LLError::logFileName();
|
||||||
|
|
||||||
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
|
gDebugInfo["ClientInfo"]["Name"] = gVersionChannel;
|
||||||
@@ -2707,6 +2678,15 @@ void LLAppViewer::writeSystemInfo()
|
|||||||
// then the value of "CrashNotHandled" will be set to true.
|
// then the value of "CrashNotHandled" will be set to true.
|
||||||
gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)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
|
// Dump some debugging info
|
||||||
LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME")
|
LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME")
|
||||||
<< " version " << gVersionMajor << "." << gVersionMinor << "." << gVersionPatch
|
<< " version " << gVersionMajor << "." << gVersionMinor << "." << gVersionPatch
|
||||||
@@ -2730,13 +2710,6 @@ void LLAppViewer::writeSystemInfo()
|
|||||||
writeDebugInfo(); // Save out debug_info.log early, in case of crash.
|
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()
|
void LLAppViewer::handleViewerCrash()
|
||||||
{
|
{
|
||||||
llinfos << "Handle viewer crash entry." << llendl;
|
llinfos << "Handle viewer crash entry." << llendl;
|
||||||
@@ -2768,8 +2741,12 @@ void LLAppViewer::handleViewerCrash()
|
|||||||
}
|
}
|
||||||
pApp->mReportedCrash = TRUE;
|
pApp->mReportedCrash = TRUE;
|
||||||
|
|
||||||
// Make sure the watchdog gets turned off...
|
// Insert crash host url (url to post crash log to) if configured.
|
||||||
// pApp->destroyMainloopTimeout(); // SJB: Bah. This causes the crash handler to hang, not sure why.
|
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
|
//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
|
//to check against no matter what
|
||||||
@@ -2783,11 +2760,11 @@ void LLAppViewer::handleViewerCrash()
|
|||||||
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
|
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
|
||||||
if ( parcel && parcel->getMusicURL()[0])
|
if ( parcel && parcel->getMusicURL()[0])
|
||||||
{
|
{
|
||||||
gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL();
|
gDebugInfo["Dynamic"]["ParcelMusicURL"] = parcel->getMusicURL();
|
||||||
}
|
}
|
||||||
if ( parcel && parcel->getMediaURL()[0])
|
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["CAFilename"] = gDirUtilp->getCAFile();
|
||||||
gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
|
gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName();
|
||||||
gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
|
gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
|
||||||
gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
|
gDebugInfo["Dynamic"]["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
|
||||||
gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
|
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["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin();
|
||||||
gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
|
gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall");
|
||||||
|
|
||||||
if(gLogoutInProgress)
|
if(gLogoutInProgress)
|
||||||
{
|
{
|
||||||
gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
|
gDebugInfo["Dynamic"]["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2812,23 +2789,23 @@ void LLAppViewer::handleViewerCrash()
|
|||||||
|
|
||||||
if(gAgent.getRegion())
|
if(gAgent.getRegion())
|
||||||
{
|
{
|
||||||
gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
|
gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegionHost().getHostName();
|
||||||
gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName();
|
gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName();
|
||||||
|
|
||||||
const LLVector3& loc = gAgent.getPositionAgent();
|
const LLVector3& loc = gAgent.getPositionAgent();
|
||||||
gDebugInfo["CurrentLocationX"] = loc.mV[0];
|
gDebugInfo["Dynamic"]["CurrentLocationX"] = loc.mV[0];
|
||||||
gDebugInfo["CurrentLocationY"] = loc.mV[1];
|
gDebugInfo["Dynamic"]["CurrentLocationY"] = loc.mV[1];
|
||||||
gDebugInfo["CurrentLocationZ"] = loc.mV[2];
|
gDebugInfo["Dynamic"]["CurrentLocationZ"] = loc.mV[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LLAppViewer::instance()->mMainloopTimeout)
|
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.
|
// The crash is being handled here so set this value to false.
|
||||||
// Otherwise the crash logger will think this crash was a freeze.
|
// 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
|
//Write out the crash status file
|
||||||
//Use marker file style setup, as that's the simplest, especially since
|
//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;
|
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)
|
if (gMessageSystem && gDirUtilp)
|
||||||
{
|
{
|
||||||
std::string filename;
|
std::string filename;
|
||||||
filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log");
|
filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log");
|
||||||
llofstream file(filename, llofstream::binary);
|
llofstream file(filename, llofstream::binary);
|
||||||
if(file.good())
|
if(file.good())
|
||||||
{
|
{
|
||||||
@@ -2870,19 +2854,15 @@ void LLAppViewer::handleViewerCrash()
|
|||||||
gMessageSystem->stopLogging();
|
gMessageSystem->stopLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo);
|
if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo["Dynamic"]);
|
||||||
|
|
||||||
// Close the debug file
|
// Close the debug file
|
||||||
pApp->writeDebugInfo();
|
pApp->writeDebugInfo(false); //false answers the isStatic question with the least overhead.
|
||||||
|
|
||||||
LLError::logToFile("");
|
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
|
// 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);
|
pApp->removeMarkerFile(true);
|
||||||
}
|
}
|
||||||
@@ -2891,11 +2871,6 @@ void LLAppViewer::handleViewerCrash()
|
|||||||
pApp->removeMarkerFile(false);
|
pApp->removeMarkerFile(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call to pure virtual, handled by platform specific llappviewer instance.
|
|
||||||
pApp->handleCrashReporting();
|
|
||||||
|
|
||||||
#endif //!LL_DARWIN
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3020,6 +2995,18 @@ void LLAppViewer::removeMarkerFile(bool leave_logout_marker)
|
|||||||
LLAPRFile::remove( mLogoutMarkerFileName );
|
LLAPRFile::remove( mLogoutMarkerFileName );
|
||||||
mLogoutMarkerFile = NULL;
|
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()
|
void LLAppViewer::forceQuit()
|
||||||
@@ -3592,13 +3579,6 @@ void LLAppViewer::badNetworkHandler()
|
|||||||
|
|
||||||
mPurgeOnExit = TRUE;
|
mPurgeOnExit = TRUE;
|
||||||
|
|
||||||
#if LL_WINDOWS
|
|
||||||
// Generates the minidump.
|
|
||||||
LLWinDebug::generateCrashStacks(NULL);
|
|
||||||
#endif
|
|
||||||
LLAppViewer::handleSyncViewerCrash();
|
|
||||||
LLAppViewer::handleViewerCrash();
|
|
||||||
|
|
||||||
std::string grid_support_msg = "";
|
std::string grid_support_msg = "";
|
||||||
if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty())
|
if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty())
|
||||||
{
|
{
|
||||||
@@ -3617,6 +3597,8 @@ void LLAppViewer::badNetworkHandler()
|
|||||||
"If the problem continues, please report the issue at: \n"
|
"If the problem continues, please report the issue at: \n"
|
||||||
"http://www.singularityviewer.org" << grid_support_msg;
|
"http://www.singularityviewer.org" << grid_support_msg;
|
||||||
forceDisconnect(message.str());
|
forceDisconnect(message.str());
|
||||||
|
|
||||||
|
LLApp::instance()->writeMiniDump();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This routine may get called more than once during the shutdown process.
|
// This routine may get called more than once during the shutdown process.
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
bool quitRequested() { return mQuitRequested; }
|
bool quitRequested() { return mQuitRequested; }
|
||||||
bool logoutRequestSent() { return mLogoutRequestSent; }
|
bool logoutRequestSent() { return mLogoutRequestSent; }
|
||||||
|
|
||||||
void writeDebugInfo();
|
void writeDebugInfo(bool isStatic=true);
|
||||||
|
|
||||||
const LLOSInfo& getOSInfo() const { return mSysOSInfo; }
|
const LLOSInfo& getOSInfo() const { return mSysOSInfo; }
|
||||||
|
|
||||||
@@ -86,11 +86,8 @@ public:
|
|||||||
|
|
||||||
virtual bool restoreErrorTrap() = 0; // Require platform specific override to reset error handling mechanism.
|
virtual bool restoreErrorTrap() = 0; // Require platform specific override to reset error handling mechanism.
|
||||||
// return false if the error trap needed restoration.
|
// return false if the error trap needed restoration.
|
||||||
virtual void handleCrashReporting(bool reportFreeze = false) = 0; // What to do with crash report?
|
virtual void initCrashReporting(bool reportFreeze = false) = 0; // What to do with crash report?
|
||||||
virtual void handleSyncCrashTrace() = 0; // any low-level crash-prep that has to happen in the context of the crashing thread before the crash report is delivered.
|
|
||||||
static void handleViewerCrash(); // Hey! The viewer crashed. Do this, soon.
|
static void 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
|
// Thread accessors
|
||||||
static LLTextureCache* getTextureCache() { return sTextureCache; }
|
static LLTextureCache* getTextureCache() { return sTextureCache; }
|
||||||
@@ -118,6 +115,7 @@ public:
|
|||||||
|
|
||||||
void removeMarkerFile(bool leave_logout_marker = false);
|
void removeMarkerFile(bool leave_logout_marker = false);
|
||||||
|
|
||||||
|
void removeDumpDir();
|
||||||
// LLAppViewer testing helpers.
|
// LLAppViewer testing helpers.
|
||||||
// *NOTE: These will potentially crash the viewer. Only for debugging.
|
// *NOTE: These will potentially crash the viewer. Only for debugging.
|
||||||
virtual void forceErrorLLError();
|
virtual void forceErrorLLError();
|
||||||
|
|||||||
@@ -79,50 +79,6 @@ extern "C" {
|
|||||||
|
|
||||||
const std::string LLAppViewerWin32::sWindowClass = "Second Life";
|
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.
|
// Create app mutex creates a unique global windows object.
|
||||||
// If the object can be created it returns true, otherwise
|
// If the object can be created it returns true, otherwise
|
||||||
@@ -188,8 +144,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
|
|||||||
|
|
||||||
LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine);
|
LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine);
|
||||||
|
|
||||||
LLWinDebug::initExceptionHandler(viewer_windows_exception_handler);
|
|
||||||
|
|
||||||
viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
|
viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash);
|
||||||
|
|
||||||
// Set a debug info flag to indicate if multiple instances are running.
|
// Set a debug info flag to indicate if multiple instances are running.
|
||||||
@@ -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
|
// (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.)
|
// of getting the data back from them.)
|
||||||
//
|
//
|
||||||
llinfos << "Turning off Windows error reporting." << llendl;
|
// llinfos << "Turning off Windows error reporting." << llendl;
|
||||||
disableWinErrorReporting();
|
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()
|
bool LLAppViewerWin32::cleanup()
|
||||||
@@ -424,12 +393,6 @@ bool LLAppViewerWin32::cleanup()
|
|||||||
|
|
||||||
bool LLAppViewerWin32::initLogging()
|
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();
|
return LLAppViewer::initLogging();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,38 +516,65 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
|
|||||||
|
|
||||||
bool LLAppViewerWin32::restoreErrorTrap()
|
bool LLAppViewerWin32::restoreErrorTrap()
|
||||||
{
|
{
|
||||||
return LLWinDebug::checkExceptionHandler();
|
return true;
|
||||||
|
//return LLWinDebug::checkExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLAppViewerWin32::handleSyncCrashTrace()
|
void LLAppViewerWin32::initCrashReporting(bool reportFreeze)
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
|
|
||||||
{
|
{
|
||||||
const char* logger_name = "win_crash_logger.exe";
|
const char* logger_name = "win_crash_logger.exe";
|
||||||
std::string exe_path = gDirUtilp->getExecutableDir();
|
std::string exe_path = gDirUtilp->getExecutableDir();
|
||||||
exe_path += gDirUtilp->getDirDelimiter();
|
exe_path += gDirUtilp->getDirDelimiter();
|
||||||
exe_path += logger_name;
|
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.
|
S32 slen = logdir.length() -1;
|
||||||
if(reportFreeze)
|
S32 end = slen;
|
||||||
|
while (logdir.at(end) == '/' || logdir.at(end) == '\\') end--;
|
||||||
|
|
||||||
|
if (slen !=end)
|
||||||
{
|
{
|
||||||
// Spawn crash logger.
|
logdir = logdir.substr(0,end+1);
|
||||||
// NEEDS to wait until completion, otherwise log files will get smashed.
|
|
||||||
_spawnl(_P_WAIT, exe_path.c_str(), arg_str, NULL);
|
|
||||||
}
|
}
|
||||||
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);
|
//llinfos << "CreateProcess failed " << GetLastError() << llendl;
|
||||||
if(cb != CRASH_BEHAVIOR_NEVER_SEND)
|
return;
|
||||||
{
|
|
||||||
_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//virtual
|
//virtual
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ protected:
|
|||||||
virtual bool initParseCommandLine(LLCommandLineParser& clp);
|
virtual bool initParseCommandLine(LLCommandLineParser& clp);
|
||||||
|
|
||||||
virtual bool restoreErrorTrap();
|
virtual bool restoreErrorTrap();
|
||||||
virtual void handleCrashReporting(bool reportFreeze);
|
virtual void initCrashReporting(bool reportFreeze);
|
||||||
virtual void handleSyncCrashTrace();
|
|
||||||
|
|
||||||
virtual bool sendURLToOtherInstance(const std::string& url);
|
virtual bool sendURLToOtherInstance(const std::string& url);
|
||||||
|
|
||||||
std::string generateSerialNumber();
|
std::string generateSerialNumber();
|
||||||
|
|
||||||
|
|
||||||
static const std::string sWindowClass;
|
static const std::string sWindowClass;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -2,98 +2,35 @@
|
|||||||
* @file llwindebug.cpp
|
* @file llwindebug.cpp
|
||||||
* @brief Windows debugging functions
|
* @brief Windows debugging functions
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "llviewerprecompiledheaders.h"
|
#include "llviewerprecompiledheaders.h"
|
||||||
|
|
||||||
#include <tchar.h>
|
|
||||||
#include <tlhelp32.h>
|
|
||||||
#include "llwindebug.h"
|
#include "llwindebug.h"
|
||||||
#include "llviewercontrol.h"
|
|
||||||
#include "lldir.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: 4200) //nonstandard extension used : zero-sized array in struct/union
|
||||||
#pragma warning(disable: 4100) //unreferenced formal parameter
|
#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
|
// based on dbghelp.h
|
||||||
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
|
||||||
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||||
@@ -103,527 +40,6 @@ typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hF
|
|||||||
|
|
||||||
MINIDUMPWRITEDUMP f_mdwp = NULL;
|
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 {
|
class LLMemoryReserve {
|
||||||
public:
|
public:
|
||||||
@@ -663,66 +79,23 @@ void LLMemoryReserve::release()
|
|||||||
|
|
||||||
static LLMemoryReserve gEmergencyMemoryReserve;
|
static LLMemoryReserve gEmergencyMemoryReserve;
|
||||||
|
|
||||||
#ifndef _M_IX86
|
|
||||||
#error "The following code only works for x86!"
|
LONG NTAPI vectoredHandler(PEXCEPTION_POINTERS exception_infop)
|
||||||
#endif
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
|
|
||||||
{
|
{
|
||||||
if(lpTopLevelExceptionFilter == gFilterFunc)
|
LLWinDebug::instance().generateMinidump(exception_infop);
|
||||||
return gFilterFunc;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
llinfos << "Someone tried to set the exception filter. Listing call stack modules" << llendl;
|
|
||||||
LLSD cs_info;
|
|
||||||
Get_Call_Stack(NULL, NULL, cs_info);
|
|
||||||
|
|
||||||
if(cs_info.has("CallStack") && cs_info["CallStack"].isArray())
|
|
||||||
{
|
|
||||||
LLSD cs = cs_info["CallStack"];
|
|
||||||
for(LLSD::array_iterator i = cs.beginArray();
|
|
||||||
i != cs.endArray();
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
llinfos << "Module: " << (*i)["ModuleName"] << llendl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return gFilterFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL PreventSetUnhandledExceptionFilter()
|
|
||||||
{
|
|
||||||
HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
|
|
||||||
if (hKernel32 == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
|
|
||||||
if(pOrgEntry == NULL)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
unsigned char newJump[ 100 ];
|
|
||||||
DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;
|
|
||||||
dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
|
|
||||||
void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
|
|
||||||
DWORD dwNewEntryAddr = (DWORD) pNewFunc;
|
|
||||||
DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;
|
|
||||||
|
|
||||||
newJump[ 0 ] = 0xE9; // JMP absolute
|
|
||||||
memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
|
|
||||||
SIZE_T bytesWritten;
|
|
||||||
BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
|
|
||||||
pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
|
|
||||||
return bRet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
|
void LLWinDebug::init()
|
||||||
{
|
{
|
||||||
|
|
||||||
static bool s_first_run = true;
|
static bool s_first_run = true;
|
||||||
// Load the dbghelp dll now, instead of waiting for the crash.
|
// Load the dbghelp dll now, instead of waiting for the crash.
|
||||||
// Less potential for stack mangling
|
// Less potential for stack mangling
|
||||||
|
|
||||||
|
// Don't install vectored exception handler if being debugged.
|
||||||
|
if(IsDebuggerPresent()) return;
|
||||||
|
|
||||||
if (s_first_run)
|
if (s_first_run)
|
||||||
{
|
{
|
||||||
// First, try loading from the directory that the app resides in.
|
// First, try loading from the directory that the app resides in.
|
||||||
@@ -753,64 +126,22 @@ void LLWinDebug::initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func)
|
|||||||
gEmergencyMemoryReserve.reserve();
|
gEmergencyMemoryReserve.reserve();
|
||||||
|
|
||||||
s_first_run = false;
|
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)
|
void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename)
|
||||||
|
{
|
||||||
|
// 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 )
|
||||||
{
|
{
|
||||||
if(f_mdwp == NULL || gDirUtilp == NULL)
|
if(f_mdwp == NULL || gDirUtilp == NULL)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
//write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -840,74 +171,23 @@ void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMAT
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// 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,
|
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
|
||||||
"SecondLifeException");
|
"SecondLifeException");
|
||||||
std::string log_path = dump_path + ".log";
|
|
||||||
|
|
||||||
if (exception_infop)
|
if (exception_infop)
|
||||||
{
|
{
|
||||||
// Since there is exception info... Release the hounds.
|
// Since there is exception info... Release the hounds.
|
||||||
gEmergencyMemoryReserve.release();
|
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.ThreadId = ::GetCurrentThreadId();
|
||||||
ExInfo.ExceptionPointers = exception_infop;
|
ExInfo.ExceptionPointers = exception_infop;
|
||||||
ExInfo.ClientPointers = NULL;
|
ExInfo.ClientPointers = NULL;
|
||||||
|
writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLife.dmp");
|
||||||
writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
|
|
||||||
writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
info = Get_Exception_Info(exception_infop);
|
|
||||||
}
|
|
||||||
|
|
||||||
LLSD threads;
|
|
||||||
std::vector<DWORD> thread_ids;
|
|
||||||
GetProcessThreadIDs(GetCurrentProcessId(), thread_ids);
|
|
||||||
|
|
||||||
for(std::vector<DWORD>::iterator th_itr = thread_ids.begin();
|
|
||||||
th_itr != thread_ids.end();
|
|
||||||
++th_itr)
|
|
||||||
{
|
|
||||||
LLSD thread_info;
|
|
||||||
if(*th_itr != GetCurrentThreadId())
|
|
||||||
{
|
|
||||||
GetThreadCallStack(*th_itr, thread_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(thread_info)
|
|
||||||
{
|
|
||||||
threads[llformat("ID %d", *th_itr)] = thread_info;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info["Threads"] = threads;
|
|
||||||
|
|
||||||
llofstream out_file(log_path);
|
|
||||||
LLSDSerialize::toPrettyXML(info, out_file);
|
|
||||||
out_file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLWinDebug::clearCrashStacks()
|
|
||||||
{
|
|
||||||
LLSD info;
|
|
||||||
std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeException.log");
|
|
||||||
LLFile::remove(dump_path);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llwindebug.h
|
* @file llwindebug.h
|
||||||
* @brief LLWinDebug class header file
|
* @brief LLWinDebug class header file
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2004&license=viewergpl$
|
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2004-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -36,40 +30,14 @@
|
|||||||
#include "stdtypes.h"
|
#include "stdtypes.h"
|
||||||
#include <dbghelp.h>
|
#include <dbghelp.h>
|
||||||
|
|
||||||
class LLWinDebug
|
class LLWinDebug:
|
||||||
|
public LLSingleton<LLWinDebug>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static void init();
|
||||||
/**
|
static void generateMinidump(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL);
|
||||||
* @brief initialize the llwindebug exception filter callback
|
|
||||||
*
|
|
||||||
* Hand a windows unhandled exception filter to LLWinDebug
|
|
||||||
* This method should only be called to change the
|
|
||||||
* exception filter used by llwindebug.
|
|
||||||
*
|
|
||||||
* Setting filter_func to NULL will clear any custom filters.
|
|
||||||
**/
|
|
||||||
static void initExceptionHandler(LPTOP_LEVEL_EXCEPTION_FILTER filter_func);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief check the status of the exception filter.
|
|
||||||
*
|
|
||||||
* Resets unhandled exception filter to the filter specified
|
|
||||||
* w/ initExceptionFilter).
|
|
||||||
* Returns false if the exception filter was modified.
|
|
||||||
*
|
|
||||||
* *NOTE:Mani In the past mozlib has been accused of
|
|
||||||
* overriding the exception filter. If the mozlib filter
|
|
||||||
* is required, perhaps we can chain calls from our
|
|
||||||
* filter to mozlib's.
|
|
||||||
**/
|
|
||||||
static bool checkExceptionHandler();
|
|
||||||
|
|
||||||
static void generateCrashStacks(struct _EXCEPTION_POINTERS *pExceptionInfo = NULL);
|
|
||||||
static void clearCrashStacks(); // Delete the crash stack file(s).
|
|
||||||
|
|
||||||
static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);
|
|
||||||
private:
|
private:
|
||||||
|
static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const std::string& filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LL_LLWINDEBUG_H
|
#endif // LL_LLWINDEBUG_H
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
project(win_crash_logger)
|
project(win_crash_logger)
|
||||||
|
|
||||||
include(00-Common)
|
include(00-Common)
|
||||||
include(DirectX)
|
|
||||||
include(LLCommon)
|
include(LLCommon)
|
||||||
include(LLCrashLogger)
|
include(LLCrashLogger)
|
||||||
include(LLMath)
|
include(LLMath)
|
||||||
@@ -12,6 +11,8 @@ include(LLVFS)
|
|||||||
include(LLWindow)
|
include(LLWindow)
|
||||||
include(LLXML)
|
include(LLXML)
|
||||||
include(Linking)
|
include(Linking)
|
||||||
|
include(LLSharedLibs)
|
||||||
|
include(GoogleBreakpad)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${LLCOMMON_INCLUDE_DIRS}
|
${LLCOMMON_INCLUDE_DIRS}
|
||||||
@@ -20,6 +21,11 @@ include_directories(
|
|||||||
${LLWINDOW_INCLUDE_DIRS}
|
${LLWINDOW_INCLUDE_DIRS}
|
||||||
${LLXML_INCLUDE_DIRS}
|
${LLXML_INCLUDE_DIRS}
|
||||||
${LLVFS_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
|
set(win_crash_logger_SOURCE_FILES
|
||||||
@@ -40,7 +46,7 @@ set_source_files_properties(${win_crash_logger_HEADER_FILES}
|
|||||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
|
||||||
set(win_crash_logger_RESOURCE_FILES
|
set(win_crash_logger_RESOURCE_FILES
|
||||||
snowglobe_icon.ico
|
ll_icon.ico
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(${win_crash_logger_RESOURCE_FILES}
|
set_source_files_properties(${win_crash_logger_RESOURCE_FILES}
|
||||||
@@ -59,11 +65,12 @@ list(APPEND
|
|||||||
${win_crash_logger_RESOURCE_FILES}
|
${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})
|
add_executable(windows-crash-logger WIN32 ${win_crash_logger_SOURCE_FILES})
|
||||||
|
|
||||||
target_link_libraries(windows-crash-logger
|
target_link_libraries(windows-crash-logger
|
||||||
|
${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
|
||||||
${LLCRASHLOGGER_LIBRARIES}
|
${LLCRASHLOGGER_LIBRARIES}
|
||||||
${LLWINDOW_LIBRARIES}
|
${LLWINDOW_LIBRARIES}
|
||||||
${LLVFS_LIBRARIES}
|
${LLVFS_LIBRARIES}
|
||||||
@@ -72,11 +79,13 @@ target_link_libraries(windows-crash-logger
|
|||||||
${LLMATH_LIBRARIES}
|
${LLMATH_LIBRARIES}
|
||||||
${LLCOMMON_LIBRARIES}
|
${LLCOMMON_LIBRARIES}
|
||||||
${WINDOWS_LIBRARIES}
|
${WINDOWS_LIBRARIES}
|
||||||
${DXGUID_LIBRARY}
|
# ${DXGUID_LIBRARY}
|
||||||
|
${GOOGLE_PERFTOOLS_LIBRARIES}
|
||||||
user32
|
user32
|
||||||
gdi32
|
gdi32
|
||||||
ole32
|
ole32
|
||||||
oleaut32
|
oleaut32
|
||||||
|
wininet
|
||||||
Wldap32
|
Wldap32
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -87,3 +96,6 @@ if (WINDOWS)
|
|||||||
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\""
|
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\""
|
||||||
)
|
)
|
||||||
endif (WINDOWS)
|
endif (WINDOWS)
|
||||||
|
|
||||||
|
# Singu Note: not used by our build
|
||||||
|
# ll_deploy_sharedlibs_command(windows-crash-logger)
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file StdAfx.cpp
|
* @file StdAfx.cpp
|
||||||
* @brief windows crash logger source file for includes
|
* @brief windows crash logger source file for includes
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file StdAfx.h
|
* @file StdAfx.h
|
||||||
* @brief standard system includes
|
* @brief standard system includes
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -44,7 +38,6 @@
|
|||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
|
||||||
|
|
||||||
// Windows Header Files:
|
// Windows Header Files:
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llcrashloggerwindows.cpp
|
* @file llcrashloggerwindows.cpp
|
||||||
* @brief Windows crash logger implementation
|
* @brief Windows crash logger implementation
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -40,7 +34,6 @@
|
|||||||
|
|
||||||
#include "boost/tokenizer.hpp"
|
#include "boost/tokenizer.hpp"
|
||||||
|
|
||||||
#include "dbghelp.h"
|
|
||||||
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
|
#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
#include "llfile.h"
|
#include "llfile.h"
|
||||||
@@ -49,6 +42,10 @@
|
|||||||
#include "lldxhardware.h"
|
#include "lldxhardware.h"
|
||||||
#include "lldir.h"
|
#include "lldir.h"
|
||||||
#include "llsdserialize.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_LOADSTRING 100
|
||||||
#define MAX_STRING 2048
|
#define MAX_STRING 2048
|
||||||
@@ -71,6 +68,7 @@ BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialo
|
|||||||
std::stringstream gDXInfo;
|
std::stringstream gDXInfo;
|
||||||
bool gSendLogs = false;
|
bool gSendLogs = false;
|
||||||
|
|
||||||
|
LLCrashLoggerWindows* LLCrashLoggerWindows::sInstance = NULL;
|
||||||
|
|
||||||
//Conversion from char* to wchar*
|
//Conversion from char* to wchar*
|
||||||
//Replacement for ATL macros, doesn't allocate memory
|
//Replacement for ATL macros, doesn't allocate memory
|
||||||
@@ -247,14 +245,182 @@ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam
|
|||||||
|
|
||||||
LLCrashLoggerWindows::LLCrashLoggerWindows(void)
|
LLCrashLoggerWindows::LLCrashLoggerWindows(void)
|
||||||
{
|
{
|
||||||
|
if (LLCrashLoggerWindows::sInstance==NULL)
|
||||||
|
{
|
||||||
|
sInstance = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LLCrashLoggerWindows::~LLCrashLoggerWindows(void)
|
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)
|
bool LLCrashLoggerWindows::init(void)
|
||||||
{
|
{
|
||||||
|
initCrashServer();
|
||||||
bool ok = LLCrashLogger::init();
|
bool ok = LLCrashLogger::init();
|
||||||
if(!ok) return false;
|
if(!ok) return false;
|
||||||
|
|
||||||
@@ -298,24 +464,23 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
|
|||||||
SetCursor(gCursorWait);
|
SetCursor(gCursorWait);
|
||||||
// At this point we're responsive enough the user could click the close button
|
// At this point we're responsive enough the user could click the close button
|
||||||
SetCursor(gCursorArrow);
|
SetCursor(gCursorArrow);
|
||||||
mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo();
|
//mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); //Not initialized.
|
||||||
mFileMap["CrashLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLifeException.log");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLCrashLoggerWindows::mainLoop()
|
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
|
// 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.
|
// win_crash_logger.rc has been edited by hand.
|
||||||
// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
|
// Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
|
||||||
gProductName = mProductName;
|
gProductName = mProductName;
|
||||||
|
|
||||||
gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
|
gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
|
||||||
ProcessCaption(gHwndProgress);
|
ProcessCaption(gHwndProgress);
|
||||||
ShowWindow(gHwndProgress, SW_HIDE );
|
ShowWindow(gHwndProgress, SW_HIDE );
|
||||||
|
|
||||||
if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
|
if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
|
||||||
{
|
{
|
||||||
|
llinfos << "Showing crash report submit progress window." << llendl;
|
||||||
ShowWindow(gHwndProgress, SW_SHOW );
|
ShowWindow(gHwndProgress, SW_SHOW );
|
||||||
sendCrashLogs();
|
sendCrashLogs();
|
||||||
}
|
}
|
||||||
@@ -362,7 +527,7 @@ bool LLCrashLoggerWindows::mainLoop()
|
|||||||
|
|
||||||
void LLCrashLoggerWindows::updateApplication(const std::string& message)
|
void LLCrashLoggerWindows::updateApplication(const std::string& message)
|
||||||
{
|
{
|
||||||
LLCrashLogger::updateApplication();
|
LLCrashLogger::updateApplication(message);
|
||||||
if(!message.empty()) show_progress(message);
|
if(!message.empty()) show_progress(message);
|
||||||
update_messages();
|
update_messages();
|
||||||
}
|
}
|
||||||
@@ -376,8 +541,8 @@ bool LLCrashLoggerWindows::cleanup()
|
|||||||
sleep_and_pump_messages(3);
|
sleep_and_pump_messages(3);
|
||||||
}
|
}
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
|
commonCleanup();
|
||||||
|
mKeyMaster.releaseMaster();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file llcrashloggerwindows.h
|
* @file llcrashloggerwindows.h
|
||||||
* @brief Windows crash logger definition
|
* @brief Windows crash logger definition
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -37,21 +31,54 @@
|
|||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
|
|
||||||
|
class LLSD;
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
class CrashGenerationServer;
|
||||||
|
class ClientInfo;
|
||||||
|
}
|
||||||
|
|
||||||
class LLCrashLoggerWindows : public LLCrashLogger
|
class LLCrashLoggerWindows : public LLCrashLogger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLCrashLoggerWindows(void);
|
LLCrashLoggerWindows(void);
|
||||||
~LLCrashLoggerWindows(void);
|
~LLCrashLoggerWindows(void);
|
||||||
|
static LLCrashLoggerWindows* sInstance;
|
||||||
|
|
||||||
virtual bool init();
|
virtual bool init();
|
||||||
virtual bool mainLoop();
|
virtual bool mainLoop();
|
||||||
virtual void updateApplication(const std::string& message = LLStringUtil::null);
|
virtual void updateApplication(const std::string& message = LLStringUtil::null);
|
||||||
virtual bool cleanup();
|
virtual bool cleanup();
|
||||||
virtual void gatherPlatformSpecificFiles();
|
virtual void gatherPlatformSpecificFiles();
|
||||||
//void annotateCallStack();
|
|
||||||
void setHandle(HINSTANCE hInst) { mhInst = hInst; }
|
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:
|
private:
|
||||||
void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
|
void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
|
||||||
void ProcessCaption(HWND hWnd);
|
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;
|
HINSTANCE mhInst;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file resource.h
|
* @file resource.h
|
||||||
* @brief Windows crash logger windows resources
|
* @brief Windows crash logger windows resources
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,79 +2,60 @@
|
|||||||
* @file win_crash_logger.cpp
|
* @file win_crash_logger.cpp
|
||||||
* @brief Windows crash logger implementation
|
* @brief Windows crash logger implementation
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// win_crash_logger.cpp : Defines the entry point for the application.
|
|
||||||
//
|
|
||||||
|
|
||||||
// Must be first include, precompiled headers.
|
|
||||||
#include "linden_common.h"
|
#include "linden_common.h"
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "llcrashloggerwindows.h"
|
#include "llcrashloggerwindows.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Implementation
|
|
||||||
//
|
|
||||||
|
|
||||||
int APIENTRY WinMain(HINSTANCE hInstance,
|
int APIENTRY WinMain(HINSTANCE hInstance,
|
||||||
HINSTANCE hPrevInstance,
|
HINSTANCE hPrevInstance,
|
||||||
LPSTR lpCmdLine,
|
LPSTR lpCmdLine,
|
||||||
int nCmdShow)
|
int nCmdShow)
|
||||||
{
|
{
|
||||||
llinfos << "Starting crash reporter" << llendl;
|
llinfos << "Starting crash reporter." << llendl;
|
||||||
|
|
||||||
LLCrashLoggerWindows app;
|
LLCrashLoggerWindows app;
|
||||||
app.setHandle(hInstance);
|
app.setHandle(hInstance);
|
||||||
bool ok = app.init();
|
app.parseCommandOptions(__argc, __argv);
|
||||||
if(!ok)
|
|
||||||
|
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;
|
llwarns << "Unable to initialize application." << llendl;
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application main loop
|
app.processingLoop();
|
||||||
if(!LLApp::isQuitting()) app.mainLoop();
|
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.cleanup();
|
||||||
}
|
llinfos << "Crash reporter finished normally." << llendl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,25 @@
|
|||||||
* @file win_crash_logger.h
|
* @file win_crash_logger.h
|
||||||
* @brief Windows crash logger project includes
|
* @brief Windows crash logger project includes
|
||||||
*
|
*
|
||||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
||||||
*
|
|
||||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
|
||||||
*
|
|
||||||
* Second Life Viewer Source Code
|
* Second Life Viewer Source Code
|
||||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
* Copyright (C) 2010, Linden Research, Inc.
|
||||||
* to you under the terms of the GNU General Public License, version 2.0
|
|
||||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
||||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
||||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
||||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
||||||
*
|
*
|
||||||
* There are special exceptions to the terms and conditions of the GPL as
|
* This library is free software; you can redistribute it and/or
|
||||||
* it is applied to this Source Code. View the full text of the exception
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
* License as published by the Free Software Foundation;
|
||||||
* online at
|
* version 2.1 of the License only.
|
||||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
||||||
*
|
*
|
||||||
* By copying, modifying or distributing this software, you acknowledge
|
* This library is distributed in the hope that it will be useful,
|
||||||
* that you have read and understood your obligations described above,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* and agree to abide by those obligations.
|
* 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
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
* License along with this library; if not, write to the Free Software
|
||||||
* COMPLETENESS OR PERFORMANCE.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||||
* $/LicenseInfo$
|
* $/LicenseInfo$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user