diff --git a/autobuild.xml b/autobuild.xml index 3bb082e87..5edc42b07 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -389,6 +389,54 @@ version 2.3 + crashpad + + canonical_repo + https://git.alchemyviewer.org/alchemy/thirdparty/3p-crashpad + copyright + Copyright 2014 The Crashpad Authors. All rights reserved. + description + Crashpad is a crash-reporting system. + license + Apache 2.0 + license_file + LICENSES/crashpad.txt + name + crashpad + platforms + + windows + + archive + + hash + 849f515d4d77162c15614ebff8599c13 + hash_algorithm + md5 + url + https://pkg.alchemyviewer.org/repository/autobuild-external/crashpad/windows/crashpad-365c520b.6-windows-6.tar.bz2 + + name + windows + + windows64 + + archive + + hash + 7ea820b6a2cfa172f47d673c07dfa62c + hash_algorithm + md5 + url + https://pkg.alchemyviewer.org/repository/autobuild-external/crashpad/windows64/crashpad-365c520b.6-windows64-6.tar.bz2 + + name + windows64 + + + version + 365c520b.6 + curl copyright @@ -1237,104 +1285,6 @@ version 1.0pre3.190390340 - google_breakpad - - copyright - Copyright (c) 2006, Google Inc. - description - Breakpad is a crossplatform library for capturing crash callstacks and runtime data. - license - bsd - license_file - LICENSES/google_breakpad.txt - name - google_breakpad - platforms - - darwin - - archive - - hash - 9f963eb1728e6d5077d4feba805d4896 - hash_algorithm - md5 - url - https://bitbucket.org/alchemyviewer/publiclibs-darwin/downloads/google_breakpad-9e60a27-darwin-201511222009.tar.bz2 - - name - darwin - - darwin64 - - archive - - hash - 2d43c6a149cd9c89ba19e884579b1e25 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/1836/4096/google_breakpad-1413.501824-darwin64-501824.tar.bz2 - - name - darwin64 - - linux - - archive - - hash - 52257e5eb166a0b69c9c0c38f6e1920e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-google-breakpad/rev/273079/arch/Linux/installer/google_breakpad-0.0.0-rev1099-linux-20130329.tar.bz2 - - name - linux - - linux64 - - archive - - hash - 3709a51d4d5dff5ec0c4656623eaa05d - hash_algorithm - md5 - url - http://depot.alchemyviewer.org/pub/linux64/lib-trusty/google_breakpad-9e60a27-linux64-201603240004.tar.bz2 - - name - linux64 - - windows - - archive - - hash - fa7f683ba4ddd7db777c78c8213d2e46 - hash_algorithm - md5 - url - https://depot.alchemyviewer.org/pub/windows/lib-vc141/google_breakpad-7398ce15b79da-windows-201703090621.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 71ffc5cae4da7e2e7aac856da44cb8c4 - hash_algorithm - md5 - url - https://depot.alchemyviewer.org/pub/windows64/lib-vc141/google_breakpad-7398ce15b79da-windows64-201703081616.tar.bz2 - - name - windows64 - - - version - 9e60a27 - gperftools copyright diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 32be5e4b4..dede5fa20 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -17,19 +17,6 @@ set(CMAKE_C_FLAGS_RELEASE set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DLL_RELEASE=1 -D_SECURE_SCL=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1") -# Configure crash reporting -set(RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in release builds") -set(NON_RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in developer builds") - -if(RELEASE_CRASH_REPORTING) - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DLL_SEND_CRASH_REPORTS=1") -endif() - -if(NON_RELEASE_CRASH_REPORTING) - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLL_SEND_CRASH_REPORTS=1") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLL_SEND_CRASH_REPORTS=1") -endif() - # Don't bother with a MinSizeRel build. set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING "Supported build types." FORCE) @@ -55,6 +42,14 @@ if (WINDOWS) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") endif (WORD_SIZE EQUAL 32) + if (FULL_DEBUG_SYMS OR USE_CRASHPAD) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG:FULL") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FULL") + else () + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG:FASTLINK") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG:FASTLINK") + endif () + if (USE_LTO) if(INCREMENTAL_LINK) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG:INCREMENTAL") @@ -306,6 +301,13 @@ endif (LINUX OR DARWIN) if (STANDALONE) add_definitions(-DLL_STANDALONE=1) else (STANDALONE) + #Enforce compile-time correctness for fmt strings + add_definitions(-DFMT_STRING_ALIAS=1) + + if(USE_CRASHPAD) + add_definitions(-DUSE_CRASHPAD=1 -DCRASHPAD_URL="${CRASHPAD_URL}") + endif() + set(${ARCH}_linux_INCLUDES atk-1.0 cairo diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 0431ff6bb..8202868a1 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -22,6 +22,7 @@ set(cmake_SOURCE_FILES ConfigurePkgConfig.cmake CURL.cmake Copy3rdPartyLibs.cmake + CrashPad.cmake Cwdebug.cmake DBusGlib.cmake DeploySharedLibs.cmake @@ -32,7 +33,6 @@ set(cmake_SOURCE_FILES FindCARes.cmake FindColladadom.cmake FindGLOD.cmake - FindGoogleBreakpad.cmake FindGooglePerfTools.cmake FindHunSpell.cmake FindNDOF.cmake @@ -47,7 +47,6 @@ set(cmake_SOURCE_FILES GStreamer010Plugin.cmake Glui.cmake Glut.cmake - GoogleBreakpad.cmake GooglePerfTools.cmake Hunspell.cmake JPEG.cmake diff --git a/indra/cmake/CrashPad.cmake b/indra/cmake/CrashPad.cmake new file mode 100644 index 000000000..d3f4ed6b7 --- /dev/null +++ b/indra/cmake/CrashPad.cmake @@ -0,0 +1,22 @@ +# -*- cmake -*- +include(Prebuilt) +include(Variables) + +if(USE_CRASHPAD) + +if (USESYSTEMLIBS) +else (USESYSTEMLIBS) + use_prebuilt_binary(crashpad) + if (WINDOWS) + set(CRASHPAD_LIBRARIES + debug client.lib util.lib base.lib + optimized client.lib util.lib base.lib) + elseif (LINUX) + + else (DARWIN) + + endif () + set(CRASHPAD_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include/crashpad) +endif (USESYSTEMLIBS) + +endif() diff --git a/indra/cmake/FindGoogleBreakpad.cmake b/indra/cmake/FindGoogleBreakpad.cmake deleted file mode 100644 index 18e0b56ed..000000000 --- a/indra/cmake/FindGoogleBreakpad.cmake +++ /dev/null @@ -1,40 +0,0 @@ -# -*- cmake -*- - -# - Find Google BreakPad -# Find the Google BreakPad includes and library -# This module defines -# BREAKPAD_INCLUDE_DIRECTORIES, where to find the Goole BreakPad includes. -# BREAKPAD_EXCEPTION_HANDLER_LIBRARIES, the libraries needed to use Google BreakPad. -# BREAKPAD_EXCEPTION_HANDLER_FOUND, If false, do not try to use Google BreakPad. -# also defined, but not for general use are -# BREAKPAD_EXCEPTION_HANDLER_LIBRARY, where to find the Google BreakPad library. - -FIND_PATH(BREAKPAD_INCLUDE_DIRECTORIES common/using_std_string.h PATH_SUFFIXES google_breakpad) - -SET(BREAKPAD_EXCEPTION_HANDLER_NAMES ${BREAKPAD_EXCEPTION_HANDLER_NAMES} breakpad_client) -FIND_LIBRARY(BREAKPAD_EXCEPTION_HANDLER_LIBRARY - NAMES ${BREAKPAD_EXCEPTION_HANDLER_NAMES} - ) - -IF (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_INCLUDE_DIRECTORIES) - SET(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES ${BREAKPAD_EXCEPTION_HANDLER_LIBRARY}) - SET(BREAKPAD_EXCEPTION_HANDLER_FOUND "YES") -ELSE (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_INCLUDE_DIRECTORIES) - SET(BREAKPAD_EXCEPTION_HANDLER_FOUND "NO") -ENDIF (BREAKPAD_EXCEPTION_HANDLER_LIBRARY AND BREAKPAD_INCLUDE_DIRECTORIES) - - -IF (BREAKPAD_EXCEPTION_HANDLER_FOUND) - IF (NOT BREAKPAD_EXCEPTION_HANDLER_FIND_QUIETLY) - MESSAGE(STATUS "Found Google BreakPad: ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}") - ENDIF (NOT BREAKPAD_EXCEPTION_HANDLER_FIND_QUIETLY) -ELSE (BREAKPAD_EXCEPTION_HANDLER_FOUND) - IF (BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find Google BreakPad library") - ENDIF (BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED) -ENDIF (BREAKPAD_EXCEPTION_HANDLER_FOUND) - -MARK_AS_ADVANCED( - BREAKPAD_EXCEPTION_HANDLER_LIBRARY - BREAKPAD_INCLUDE_DIRECTORIES - ) diff --git a/indra/cmake/GoogleBreakpad.cmake b/indra/cmake/GoogleBreakpad.cmake deleted file mode 100644 index de899a462..000000000 --- a/indra/cmake/GoogleBreakpad.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# -*- cmake -*- -include(Prebuilt) - -if (STANDALONE) - set(BREAKPAD_EXCEPTION_HANDLER_FIND_REQUIRED ON) - include(FindGoogleBreakpad) -else (STANDALONE) - use_prebuilt_binary(google_breakpad) - if (DARWIN) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler) - endif (DARWIN) - if (LINUX) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES breakpad_client) - endif (LINUX) - if (WINDOWS) - set(BREAKPAD_EXCEPTION_HANDLER_LIBRARIES exception_handler crash_generation_client crash_generation_server common) - endif (WINDOWS) - # yes, this does look dumb, no, it's not incorrect - # I think it's incorrect: the second one should go --Aleric - set(BREAKPAD_INCLUDE_DIRECTORIES - ${LIBS_PREBUILT_DIR}/include/google_breakpad - ${LIBS_PREBUILT_LEGACY_DIR}/include/google_breakpad - ${LIBS_PREBUILT_DIR}/include/google_breakpad/google_breakpad - ${LIBS_PREBUILT_LEGACY_DIR}/include/google_breakpad/google_breakpad - ) -endif (STANDALONE) diff --git a/indra/cmake/Variables.cmake b/indra/cmake/Variables.cmake index 189efe536..51e10a09a 100644 --- a/indra/cmake/Variables.cmake +++ b/indra/cmake/Variables.cmake @@ -36,6 +36,11 @@ option(USE_PRECOMPILED_HEADERS "Enable use of precompiled header directives wher option(USE_LTO "Enable Whole Program Optimization and related folding and binary reduction routines" OFF) option(UNATTENDED "Disable use of uneeded tooling for automated builds" OFF) +# Configure crash reporting +option(USE_CRASHPAD "Build support for crashpad reporting engine" OFF) +set(CRASHPAD_URL "" CACHE STRING "Crashpad endpoint url") +set(VIEWER_SYMBOL_FILE "" CACHE STRING "Name of tarball into which to place symbol files") + # Media Plugins option(ENABLE_MEDIA_PLUGINS "Turn off building media plugins if they are imported by third-party library mechanism" ON) option(LIBVLCPLUGIN "Turn off building support for libvlc plugin" ON) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 6479c7bca..cae62271d 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -13,7 +13,6 @@ include(Boost) include(OpenSSL) include(LLSharedLibs) include(Json) -include(GoogleBreakpad) include(Copy3rdPartyLibs) include(ZLIB) include(URIPARSER) @@ -291,7 +290,6 @@ target_link_libraries( llcommon PUBLIC absl::hash - ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES} ${APRUTIL_LIBRARIES} ${APR_LIBRARIES} ${EXPAT_LIBRARIES} diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index 142f47e2a..8fa21bd7e 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -368,13 +368,6 @@ const U32 MAP_ITEM_CLASSIFIED = 0x08; const U32 MAP_ITEM_ADULT_EVENT = 0x09; const U32 MAP_ITEM_LAND_FOR_SALE_ADULT = 0x0a; -// Crash reporter behavior -const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; -const char* const CRASH_BEHAVIOR_SETTING = "CrashSubmitBehavior"; -const S32 CRASH_BEHAVIOR_ASK = 0; -const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1; -const S32 CRASH_BEHAVIOR_NEVER_SEND = 2; - // Export/Import return values const S32 EXPORT_SUCCESS = 0; const S32 EXPORT_ERROR_PERMISSIONS = -1; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index b6498ffbf..36c6b0f61 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -25,9 +25,8 @@ */ #include "linden_common.h" -#include "llapp.h" -#include +#include "llapp.h" #ifdef LL_DARWIN #include @@ -45,7 +44,6 @@ #include "llstl.h" // for DeletePointer() #include "llstring.h" #include "lleventtimer.h" -#include "exception_handler.h" // // Signal handling @@ -57,12 +55,6 @@ LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop); BOOL ConsoleCtrlHandler(DWORD fdwCtrlType); -bool windows_post_minidump_callback(const wchar_t* dump_path, - const wchar_t* minidump_id, - void* context, - EXCEPTION_POINTERS* exinfo, - MDRawAssertionInfo* assertion, - bool succeeded); #else # include # include // for fork() @@ -70,18 +62,6 @@ bool windows_post_minidump_callback(const wchar_t* dump_path, void setup_signals(); void default_unix_signal_handler(int signum, siginfo_t *info, void *); -#if LL_LINUX -#include "client/linux/handler/minidump_descriptor.h" -static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, - void* context, - bool succeeded); -#else -// Called by breakpad exception handler after the minidump has been generated. -bool unix_post_minidump_callback(const char *dump_dir, - const char *minidump_id, - void *context, bool succeeded); -#endif - # if LL_DARWIN /* OSX doesn't support SIGRT* */ S32 LL_SMACKDOWN_SIGNAL = SIGUSR1; @@ -98,7 +78,7 @@ S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2; #endif // LL_WINDOWS // the static application instance -LLApp* LLApp::sApplication = NULL; +LLApp* LLApp::sApplication = nullptr; // Allows the generation of core files for post mortem under gdb // and disables crashlogger @@ -110,7 +90,7 @@ BOOL LLApp::sLogInSignal = FALSE; // static LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status -LLAppErrorHandler LLApp::sErrorHandler = NULL; +LLAppErrorHandler LLApp::sErrorHandler = nullptr; BOOL LLApp::sErrorThreadRunning = FALSE; #if !LL_WINDOWS LLApp::child_map LLApp::sChildMap; @@ -119,13 +99,12 @@ LLAppChildCallback LLApp::sDefaultChildCallback = NULL; #endif -LLApp::LLApp() : mThreadErrorp(NULL) +LLApp::LLApp() + : mThreadErrorp(nullptr) { commonCtor(); } -static void* sCrashLoggerReserve = NULL; - void LLApp::commonCtor() { // Set our status to running @@ -150,34 +129,11 @@ void LLApp::commonCtor() // Set the application to this instance. sApplication = this; - mExceptionHandler = 0; - -#if LL_WINDOWS - sCrashLoggerReserve = VirtualAlloc(NULL, 512*1024, MEM_COMMIT|MEM_RESERVE, PAGE_NOACCESS); -#else - sCrashLoggerReserve = malloc(512*1024); -#endif - // 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"; } -#if LL_WINDOWS -static bool clear_CrashLoggerReserve_callback(void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) -{ - VirtualFree(sCrashLoggerReserve, 0, MEM_RELEASE); - return true; -} -#else -static bool clear_CrashLoggerReserve_callback(void* context) -{ - free(sCrashLoggerReserve); - return true; -} -#endif - LLApp::LLApp(LLErrorThread *error_thread) : mThreadErrorp(error_thread) { @@ -198,11 +154,9 @@ LLApp::~LLApp() if (mThreadErrorp) { delete mThreadErrorp; - mThreadErrorp = NULL; + mThreadErrorp = nullptr; } - if(mExceptionHandler != 0) delete mExceptionHandler; - LLCommon::cleanupClass(); } @@ -216,8 +170,8 @@ LLApp* LLApp::instance() LLSD LLApp::getOption(const std::string& name) const { LLSD rv; - LLSD::array_const_iterator iter = mOptions.beginArray(); - LLSD::array_const_iterator end = mOptions.endArray(); + auto iter = mOptions.beginArray(); + auto end = mOptions.endArray(); for(; iter != end; ++iter) { rv = (*iter)[name]; @@ -264,9 +218,9 @@ bool LLApp::parseCommandOptions(int argc, char** argv) #if LL_WINDOWS //Windows changed command line parsing. Deal with it. - S32 slen = value.length() - 1; - S32 start = 0; - S32 end = slen; + size_t slen = value.length() - 1; + size_t start = 0; + size_t end = slen; if (argv[ii][start]=='"')start++; if (argv[ii][end]=='"')end--; if (start!=0 || end!=slen) @@ -343,92 +297,17 @@ void LLApp::setupErrorHandling() // Error handling is done by starting up an error handling thread, which just sleeps and // occasionally checks to see if the app is in an error state, and sees if it needs to be run. -#if LL_WINDOWS - -#if LL_SEND_CRASH_REPORTS - EnableCrashingOnCrashes(); - - // This sets a callback to handle w32 signals to the console window. - // The viewer shouldn't be affected, sicne its a windowed app. - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE); - - // Install the Google Breakpad crash handler for Windows - if(mExceptionHandler == 0) - { - mExceptionHandler = new google_breakpad::ExceptionHandler( - std::wstring(mDumpPath.begin(),mDumpPath.end()), //Dump path - clear_CrashLoggerReserve_callback, - windows_post_minidump_callback, - 0, - google_breakpad::ExceptionHandler::HANDLER_ALL); - if (mExceptionHandler) - { - mExceptionHandler->set_handle_debug_exceptions(true); - } - } -#endif -#else +#if !LL_WINDOWS // // Start up signal handling. // // There are two different classes of signals. Synchronous signals are delivered to a specific // thread, asynchronous signals can be delivered to any thread (in theory) // - setup_signals(); - - // Add google breakpad exception handler configured for Darwin/Linux. - 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; - } - - if(installHandler && (mExceptionHandler == 0)) - { - mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, clear_CrashLoggerReserve_callback, &unix_post_minidump_callback, 0, true, 0); - } - #endif -#elif LL_LINUX - if(installHandler && (mExceptionHandler == 0)) - { - if (mDumpPath.empty()) - { - mDumpPath = "/tmp"; - } - google_breakpad::MinidumpDescriptor desc(mDumpPath); - mExceptionHandler = new google_breakpad::ExceptionHandler(desc, clear_CrashLoggerReserve_callback, unix_minidump_callback, NULL, true, -1); - } -#endif - +#endif // ! LL_WINDOWS +#if !defined(USE_CRASHPAD) + startErrorThread(); #endif } @@ -466,7 +345,6 @@ void LLApp::setErrorHandler(LLAppErrorHandler handler) LLApp::sErrorHandler = handler; } - // static void LLApp::runErrorHandler() { @@ -479,7 +357,6 @@ void LLApp::runErrorHandler() LLApp::setStopped(); } - // static void LLApp::setStatus(EAppStatus status) { @@ -494,43 +371,12 @@ void LLApp::setError() setStatus(APP_STATUS_ERROR); } -void LLApp::setMiniDumpDir(const std::string &path) -{ - if (path.empty()) - { - mDumpPath = "/tmp"; - } - else - { - mDumpPath = path; - } - - if(mExceptionHandler == 0) return; -#ifdef LL_WINDOWS - wchar_t buffer[MAX_MINDUMP_PATH_LENGTH]; - mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH); - mExceptionHandler->set_dump_path(std::wstring(buffer)); -#elif LL_LINUX - //google_breakpad::MinidumpDescriptor desc("/tmp"); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched. - google_breakpad::MinidumpDescriptor desc(mDumpPath); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched. - mExceptionHandler->set_minidump_descriptor(desc); -#else - mExceptionHandler->set_dump_path(mDumpPath); -#endif -} - void LLApp::setDebugFileNames(const std::string &path) { mStaticDebugFileName = path + "static_debug_info.log"; mDynamicDebugFileName = path + "dynamic_debug_info.log"; } -void LLApp::writeMiniDump() -{ - if(mExceptionHandler == 0) return; - mExceptionHandler->WriteMinidump(); -} - // static void LLApp::setQuitting() { @@ -585,13 +431,6 @@ bool LLApp::isExiting() void LLApp::disableCrashlogger() { - // Disable Breakpad exception handler. - if (mExceptionHandler != 0) - { - delete mExceptionHandler; - mExceptionHandler = 0; - } - sDisableCrashlogger = TRUE; } @@ -660,12 +499,6 @@ LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *except ms_sleep(10); } - // - // Generate a minidump if we can. - // - // TODO: This needs to be ported over form the viewer-specific - // LLWinDebug class - // // At this point, we always want to exit the app. There's no graceful // recovery for an unhandled exception. @@ -965,150 +798,4 @@ 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: /.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); - - LL_INFOS() << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL; - 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: /.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); - } - - LL_INFOS() << "generated minidump: " << path << LL_ENDL; - LLApp::runErrorHandler(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - clear_signals(); - return false; -#else - return true; -#endif -} #endif // !WINDOWS - -#ifdef LL_WINDOWS -bool windows_post_minidump_callback(const wchar_t* dump_path, - const wchar_t* minidump_id, - void* context, - EXCEPTION_POINTERS* exinfo, - MDRawAssertionInfo* assertion, - bool succeeded) -{ - char * path = LLApp::instance()->getMiniDumpFilename(); - S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; - size_t bytesUsed; - - bytesUsed = wcstombs(path, dump_path, static_cast(remaining)); - remaining -= bytesUsed; - path += bytesUsed; - if(remaining > 0 && bytesUsed > 0 && path[-1] != '\\') - { - *path++ = '\\'; - --remaining; - } - if(remaining > 0) - { - bytesUsed = wcstombs(path, minidump_id, static_cast(remaining)); - remaining -= bytesUsed; - path += bytesUsed; - } - if(remaining > 0) - { - strncpy(path, ".dmp", remaining); - } - - LL_INFOS() << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL; - // *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 - LL_INFOS() << "Entering Windows Exception Handler..." << LL_ENDL; - - if (LLApp::isError()) - { - LL_WARNS() << "Got another fatal signal while in the error handler, die now!" << LL_ENDL; - } - - // 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 diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index aab871799..2a8fddfa2 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -60,10 +60,6 @@ public: }; #endif -namespace google_breakpad { - class ExceptionHandler; // See exception_handler.h -} - class LL_COMMON_API LLApp : public LLOptionInterface { friend class LLErrorThread; @@ -233,22 +229,11 @@ public: static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred. //@} - // the maximum length of the minidump filename returned by getMiniDumpFilename() - static const U32 MAX_MINDUMP_PATH_LENGTH = 256; - - // change the directory where Breakpad minidump files are written to - void setMiniDumpDir(const std::string &path); void setDebugFileNames(const std::string &path); - // Return the Google Breakpad minidump filename after a crash. - char *getMiniDumpFilename() { return mMinidumpPath; } std::string* getStaticDebugFile() { return &mStaticDebugFileName; } std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; } - // Write out a Google Breakpad minidump file. - void writeMiniDump(); - - #if !LL_WINDOWS // // Child process handling (Unix only for now) @@ -281,8 +266,6 @@ protected: static BOOL sDisableCrashlogger; // Let the OS handle crashes for us. std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting. - std::string mDumpPath; //output path for google breakpad. Dependency workaround. - #if !LL_WINDOWS static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received. typedef std::map child_map; // Map key is a PID @@ -290,16 +273,13 @@ protected: static LLAppChildCallback sDefaultChildCallback; #endif - void startErrorThread(); - /** * @brief This method is called at the end, just prior to deinitializing curl. */ void stopErrorThread(); private: - // Contains the filename of the minidump file after a crash. - char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH]; + void startErrorThread(); std::string mStaticDebugFileName; std::string mDynamicDebugFileName; @@ -323,11 +303,8 @@ private: std::vector mLiveFiles; //@} -private: // the static application instance if it was created. static LLApp* sApplication; - google_breakpad::ExceptionHandler * mExceptionHandler; - #if !LL_WINDOWS friend void default_unix_signal_handler(int signum, siginfo_t *info, void *); diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 705b4770d..70e71c1d7 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -960,5 +960,4 @@ P(webProfileResponders); P(wholeModelFeeResponder); P(wholeModelUploadResponder); P2(XMLRPCResponder, connect_40s); -P2(crashLoggerResponder, transfer_300s); P(getUpdateInfoResponder); \ No newline at end of file diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d8b2673b0..181d5cf46 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -12,6 +12,7 @@ include(BuildPackagesInfo) include(BuildVersion) include(BuildBranding) include(CMakeCopyIfDifferent) +include(CrashPad) include(DBusGlib) include(FMODSTUDIO) include(GeneratePrecompiledHeader) @@ -49,6 +50,11 @@ include(WinManifest) include(ZLIB) include(URIPARSER) + +if(USE_CRASHPAD) + include_directories(${CRASHPAD_INCLUDE_DIRS}) +endif(USE_CRASHPAD) + include_directories( ${STATEMACHINE_INCLUDE_DIRS} ${DBUSGLIB_INCLUDE_DIRS} @@ -143,7 +149,6 @@ set(viewer_SOURCE_FILES llconfirmationmanager.cpp llconsole.cpp llcontrolavatar.cpp - llcrashlogger.cpp llcurrencyuimanager.cpp llcylinder.cpp lldaycyclemanager.cpp @@ -681,7 +686,6 @@ set(viewer_HEADER_FILES llconfirmationmanager.h llconsole.h llcontrolavatar.h - llcrashlogger.h llcurrencyuimanager.h llcylinder.h lldaycyclemanager.h @@ -1375,7 +1379,6 @@ set(viewer_APPSETTINGS_FILES app_settings/settings.xml app_settings/settings_ascent.xml app_settings/settings_ascent_coa.xml - app_settings/settings_crash_behavior.xml app_settings/settings_files.xml app_settings/settings_per_account.xml app_settings/settings_sh.xml @@ -1624,6 +1627,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${PNG_PRELOAD_ARCHIVES} ${ZLIB_PRELOAD_ARCHIVES} ${GOOGLE_PERFTOOLS_LIBRARIES} + ${CRASHPAD_LIBRARIES} ${LLAUDIO_LIBRARIES} ${LLAUDIO_VORBIS_LIBRARIES} ${LLCHARACTER_LIBRARIES} @@ -1823,8 +1827,10 @@ if (INSTALL) include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake) endif (INSTALL) -if (PACKAGE) +if (PACKAGE AND USE_CRASHPAD) + # Breakpad symbol-file generation set(SYMBOL_NAME ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${VIEWER_CHANNEL_NOSPACE}-${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}-symbols) + set(SYMBOL_SEARCH_DIRS "") if (WINDOWS) list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}") @@ -1854,7 +1860,6 @@ if (PACKAGE) set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest) endif (LINUX) - if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) if(GEN_IS_MULTI_CONFIG) set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) else() @@ -1875,14 +1880,14 @@ if (PACKAGE) DEPENDS generate_breakpad_symbols.py VERBATIM) - add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}") - add_dependencies(generate_breakpad_symbols ${VIEWER_BINARY_NAME}) -if(VIEWER_COPY_MANIFEST) - add_dependencies(generate_breakpad_symbols ${VIEWER_COPY_MANIFEST}) -endif(VIEWER_COPY_MANIFEST) - add_dependencies(llpackage generate_breakpad_symbols) - endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING) -endif (PACKAGE) + add_custom_target(generate_symbols DEPENDS "${VIEWER_SYMBOL_FILE}" ${VIEWER_BINARY_NAME} "${VIEWER_COPY_MANIFEST}") + add_dependencies(generate_symbols ${VIEWER_BINARY_NAME}) + if (WINDOWS OR LINUX) + add_dependencies(generate_symbols "${VIEWER_COPY_MANIFEST}") + endif (WINDOWS OR LINUX) + + add_dependencies(llpackage generate_symbols) +endif () # Add tests if (LL_TESTS) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b074c964b..e1ba77b93 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2538,17 +2538,6 @@ This should be as low as possible, but too low may break functionality 1 - CrashHostUrl - - Comment - A URL pointing to a crash report handler; overrides cluster negotiation to locate crash handler. - Persist - 1 - Type - String - Value - http://crash.singularityviewer.org/report.php> - AFKTimeout Comment @@ -18971,24 +18960,13 @@ This should be as low as possible, but too low may break functionality CrashSubmitBehavior Comment - Controls behavior when viewer crashes (0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report) + Controls behavior when viewer crashes (0 = never send crash report, 1 = always send crash report) Persist 1 Type S32 Value - 0 - - CrashReportID - - Comment - ID of the last crash report sent. Zero indicates that no crash report has been sent. Non-zero value can be useful infermation for developers to track a specific issue - Persist - 1 - Type - S32 - Value - 0 + -1 EveryoneCopy diff --git a/indra/newview/app_settings/settings_crash_behavior.xml b/indra/newview/app_settings/settings_crash_behavior.xml deleted file mode 100644 index 74704f8ad..000000000 --- a/indra/newview/app_settings/settings_crash_behavior.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - CrashSubmitBehavior - - Comment - Controls behavior when viewer crashes (0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report) - Persist - 2 - Type - S32 - Value - 1 - - - diff --git a/indra/newview/app_settings/settings_files.xml b/indra/newview/app_settings/settings_files.xml index ac304f759..cfb4504f2 100644 --- a/indra/newview/app_settings/settings_files.xml +++ b/indra/newview/app_settings/settings_files.xml @@ -43,13 +43,6 @@ Requirement 1 - CrashSettings - - Name - settings_crash_behavior.xml - Requirement - 1 - User @@ -65,11 +58,6 @@ NameFromSetting ClientSettingsFile - CrashSettings - - Name - settings_crash_behavior.xml - Account diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index dcde01d9a..67d87861c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -87,7 +87,6 @@ #include "llappearancemgr.h" // [/SL:KB] #include "llfloaterteleporthistory.h" -#include "llcrashlogger.h" #include "llweb.h" #include "llsecondlifeurls.h" #include "llavatarrenderinfoaccountant.h" @@ -211,6 +210,20 @@ #include +#ifdef USE_CRASHPAD +#pragma warning(disable:4265) + +#include +#include +#include +#include + +#include + +#include "llnotificationsutil.h" +#include "llversioninfo.h" +#endif + ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor // @@ -237,6 +250,8 @@ F32 gSimFrames; BOOL gShowObjectUpdates = FALSE; +S32 gLastExecDuration = -1; // (<0 indicates unknown) + BOOL gAcceptTOS = FALSE; BOOL gAcceptCriticalMessage = FALSE; @@ -297,8 +312,9 @@ BOOL gLogoutInProgress = FALSE; //////////////////////////////////////////////////////////// // Internal globals... that should be removed. static std::string gArgs; - +const int MAX_MARKER_LENGTH = 1024; const std::string MARKER_FILE_NAME("Singularity.exec_marker"); +const std::string START_MARKER_FILE_NAME("Singularity.start_marker");; const std::string ERROR_MARKER_FILE_NAME("Singularity.error_marker"); const std::string LLERROR_MARKER_FILE_NAME("Singularity.llerror_marker"); const std::string LOGOUT_MARKER_FILE_NAME("Singularity.logout_marker"); @@ -367,7 +383,6 @@ void init_default_trans_args() const char *VFS_DATA_FILE_BASE = "data.db2.x."; const char *VFS_INDEX_FILE_BASE = "index.db2.x."; -static std::string gSecondLife; std::string gWindowTitle; std::string gLoginPage; @@ -434,24 +449,6 @@ void request_initial_instant_messages() } } -// A settings system callback for CrashSubmitBehavior -bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue) -{ - S32 cb = newvalue.asInteger(); - const S32 NEVER_SUBMIT_REPORT = 2; - if(cb == NEVER_SUBMIT_REPORT) - { -// LLWatchdog::getInstance()->cleanup(); // SJB: cleaning up a running watchdog thread is unsafe - LLAppViewer::instance()->destroyMainloopTimeout(); - } - else if(gSavedSettings.getBOOL("WatchdogEnabled") == TRUE) - { - // Don't re-enable the watchdog when we change the setting; this may get called before it's started -// LLWatchdog::getInstance()->init(); - } - return true; -} - // Use these strictly for things that are constructed at startup, // or for things that are performance critical. JC static void settings_to_globals() @@ -536,7 +533,6 @@ LLAppViewer* LLAppViewer::sInstance = NULL; const std::string LLAppViewer::sGlobalSettingsName = "Global"; const std::string LLAppViewer::sPerAccountSettingsName = "PerAccount"; -const std::string LLAppViewer::sCrashSettingsName = "CrashSettings"; LLTextureCache* LLAppViewer::sTextureCache = NULL; LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; @@ -544,7 +540,7 @@ LLTextureFetch* LLAppViewer::sTextureFetch = NULL; LLAppViewer::LLAppViewer() : mMarkerFile(), - mLogoutMarkerFile(NULL), + mLogoutMarkerFile(), mReportedCrash(false), mNumSessions(0), mPurgeCache(false), @@ -556,13 +552,11 @@ LLAppViewer::LLAppViewer() : mMainloopTimeout(NULL), mAgentRegionLastAlive(false) { - if(NULL != sInstance) + if(nullptr != sInstance) { LL_ERRS() << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << LL_ENDL; } - mDumpPath.clear(); - // Need to do this initialization before we do anything else, since anything // that touches files should really go through the lldir API { @@ -580,7 +574,26 @@ LLAppViewer::LLAppViewer() : gDirUtilp->initAppDirs("SecondLife", newview_path); } + // + // IMPORTANT! Do NOT put anything that will write + // into the log files during normal startup until AFTER + // we run the "program crashed last time" error handler below. + // sInstance = this; + + + initLoggingAndGetLastDuration(); + + processMarkerFiles(); + // + // OK to write stuff to logs now, we've now crash reported if necessary + // +#if !defined(USE_CRASHPAD) + // write dump files to a per-run dump directory to avoid multiple viewer issues. + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); + + setDebugFileNames(logdir); +#endif } @@ -589,13 +602,13 @@ LLAppViewer::~LLAppViewer() destroyMainloopTimeout(); // If we got to this destructor somehow, the app didn't hang. - removeMarkerFile(); + removeMarkerFiles(); } class LLUITranslationBridge : public LLTranslationBridge { public: - virtual std::string getString(const std::string &xml_desc) + std::string getString(const std::string &xml_desc) override { return LLTrans::getString(xml_desc); } @@ -634,17 +647,151 @@ void fast_exit(int rc) } */ +#ifdef USE_CRASHPAD +base::FilePath databasePath() +{ + // Cache directory that will store crashpad information and minidumps + std::string crashpad_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashpad"); + return base::FilePath(ll_convert_string_to_wide(crashpad_path)); +} + +static void handleCrashSubmitBehaviorChanged(LLControlVariable*, const LLSD& val) +{ + if (auto db = crashpad::CrashReportDatabase::Initialize(databasePath())) + { + if (auto settings = db->GetSettings()) + { + settings->SetUploadsEnabled(val.asBoolean()); + } + } +} + +static void configureCrashUploads() +{ + auto database = databasePath(); + auto db = crashpad::CrashReportDatabase::InitializeWithoutCreating(database); + if (!db) return; + auto settings = db->GetSettings(); + if (!settings) return; + auto control = gSavedSettings.getControl("CrashSubmitBehavior"); + control->getSignal()->connect(handleCrashSubmitBehaviorChanged); + if (control->get().asInteger() == -1) + { + LLNotificationsUtil::add("SubmitCrashReports", LLSD(), LLSD(), [control](const LLSD& p, const LLSD& f) { + control->set(!LLNotificationsUtil::getSelectedOption(p, f)); + }); + } + + if (!settings->SetUploadsEnabled(control->get().asInteger() == 1)) + { + LL_WARNS() << "Failed to set enable upload of crash database." << LL_ENDL; + } +} +#endif + +void LLAppViewer::initCrashReporting() +{ +#ifdef USE_CRASHPAD + // Path to the out-of-process handler executable + std::string handler_path = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "crashpad_handler.exe"); + if (!gDirUtilp->fileExists(handler_path)) + { + LL_ERRS() << "Failed to initialize crashpad due to missing handler executable." << LL_ENDL; + return; + } + base::FilePath handler(ll_convert_string_to_wide(handler_path)); + + auto database = databasePath(); + + // URL used to submit minidumps to + std::string url(CRASHPAD_URL); + + // Optional annotations passed via --annotations to the handler + std::map annotations; + +#if 0 + unsigned char node_id[6]; + if (LLUUID::getNodeID(node_id) > 0) + { + char md5str[MD5HEX_STR_SIZE] = { 0 }; + LLMD5 hashed_unique_id; + hashed_unique_id.update(node_id, 6); + hashed_unique_id.finalize(); + hashed_unique_id.hex_digest((char*)md5str); + annotations.emplace("sentry[contexts][app][device_app_hash]", std::string(md5str)); + } +#endif + + annotations.emplace("sentry[contexts][app][app_name]", LLVersionInfo::getChannel()); + annotations.emplace("sentry[contexts][app][app_version]", LLVersionInfo::getVersion()); + annotations.emplace("sentry[contexts][app][app_build]", LLVersionInfo::getChannelAndVersion()); + + annotations.emplace("sentry[tags][second_instance]", fmt::to_string(isSecondInstance())); + //annotations.emplace("sentry[tags][bitness]", fmt::to_string(ADDRESS_SIZE)); + annotations.emplace("sentry[tags][bitness]", +#if defined(_WIN64) || defined(__x86_64__) + "64" +#else + "32" +#endif + ); + + // Optional arguments to pass to the handler + std::vector arguments; + arguments.push_back("--no-upload-gzip"); + arguments.push_back("--no-rate-limit"); + arguments.push_back("--monitor-self"); + + if (isSecondInstance()) + { + arguments.push_back("--no-periodic-tasks"); + } + else + { + auto db = crashpad::CrashReportDatabase::Initialize(database); + if (db == nullptr) + { + LL_WARNS() << "Failed to initialize crashpad database at path: " << wstring_to_utf8str(database.value()) << LL_ENDL; + return; + } + + auto prune_condition = crashpad::PruneCondition::GetDefault(); + if (prune_condition != nullptr) + { + auto ret = crashpad::PruneCrashReportDatabase(db.get(), prune_condition.get()); + LL_INFOS() << "Pruned " << ret << " reports from the crashpad database." << LL_ENDL; + } + } + + crashpad::CrashpadClient client; + bool success = client.StartHandler( + handler, + database, + database, + url, + annotations, + arguments, + /* restartable */ true, + /* asynchronous_start */ false + ); + if (success) + LL_INFOS() << "Crashpad init success" << LL_ENDL; + else + LL_WARNS() << "FAILED TO INITIALIZE CRASHPAD" << LL_ENDL; +#endif +} + bool LLAppViewer::init() -{ +{ +#ifdef USE_CRASHPAD + initCrashReporting(); +#endif + setupErrorHandling(); // // Start of the application // - // IMPORTANT! Do NOT put anything that will write - // into the log files during normal startup until AFTER - // we run the "program crashed last time" error handler below. - // LLFastTimer::reset(); // initialize LLWearableType translation bridge. @@ -667,15 +814,12 @@ bool LLAppViewer::init() // this allows simple skinned file lookups to work gDirUtilp->setSkinFolder("default", "en-us"); - initLogging(); +// initLoggingAndGetLastDuration(); // // Curl must be initialized before any thread is running. AICurlInterface::initCurl(); - // Logging is initialized. Now it's safe to start the error thread. - startErrorThread(); - // // OK to write stuff to logs now, we've now crash reported if necessary // @@ -686,7 +830,6 @@ bool LLAppViewer::init() LL_INFOS("InitInfo") << "Configuration initialized." << LL_ENDL ; - // initialize skinning util LLSkinningUtil::initClass(); @@ -711,25 +854,9 @@ bool LLAppViewer::init() AIHTTPTimeoutPolicy::setDefaultCurlTimeout(policy_tmp); } - // Check if we have a crash report to send - LLCrashLogger crashLogger; - crashLogger.checkCrashDump(); - - // 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")); - { - // Viewer metrics initialization - //static LLCachedControl metrics_submode(gSavedSettings, - // "QAModeMetrics", - // false, - // "Enables QA features (logging, faster cycling) for metrics collector"); + { if (gSavedSettings.getBOOL("QAModeMetrics")) { app_metrics_qa_mode = true; @@ -782,6 +909,11 @@ bool LLAppViewer::init() LLNotifications::instance().createDefaultChannels(); LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; +#ifdef USE_CRASHPAD + // Now that we have Settings and Notifications, we can configure crash uploads + configureCrashUploads(); +#endif + writeSystemInfo(); ////////////////////////////////////////////////////////////////////////////// @@ -797,10 +929,6 @@ bool LLAppViewer::init() LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; - // Get the single value from the crash settings file, if it exists - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - gCrashSettings.loadFromFile(crash_settings_filename); - ///////////////////////////////////////////////// // OS-specific login dialogs ///////////////////////////////////////////////// @@ -898,6 +1026,7 @@ bool LLAppViewer::init() if (!initCache()) { + LL_WARNS("InitInfo") << "Failed to init cache" << LL_ENDL; std::ostringstream msg; msg << LLTrans::getString("MBUnableToAccessFile"); OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); @@ -1051,9 +1180,8 @@ void LLAppViewer::initMaxHeapSize() //currently SL is built under 32-bit setting, we set its max heap size no more than 1.6 GB. //F32 max_heap_size_gb = llmin(1.6f, (F32)gSavedSettings.getF32("MaxHeapSize")) ; - F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize") ; - + //This is all a bunch of CRAP. We run LAA on windows. 64bit windows supports LAA out of the box. 32bit does not, unless PAE is on. #if LL_WINDOWS //http://msdn.microsoft.com/en-us/library/windows/desktop/ms684139%28v=vs.85%29.aspx @@ -1081,7 +1209,7 @@ void LLAppViewer::initMaxHeapSize() } #endif - BOOL enable_mem_failure_prevention = (BOOL)gSavedSettings.getBOOL("MemoryFailurePreventionEnabled") ; + BOOL enable_mem_failure_prevention = gSavedSettings.getBOOL("MemoryFailurePreventionEnabled") ; LLMemory::initMaxHeapSizeGB(max_heap_size_gb, enable_mem_failure_prevention) ; } @@ -1263,7 +1391,6 @@ bool LLAppViewer::mainLoop() gKeyboard->scanKeyboard(); if (gAgent.isCrouching()) gAgent.moveUp(-1); - } // Update state based on messages, user input, object idle. @@ -1311,11 +1438,11 @@ bool LLAppViewer::mainLoop() pingMainloopTimeout("Main:Display"); gGLActive = TRUE; display(); + pingMainloopTimeout("Main:Snapshot"); LLFloaterSnapshot::update(); // take snapshots gGLActive = FALSE; } - } pingMainloopTimeout("Main:Sleep"); @@ -1459,7 +1586,7 @@ bool LLAppViewer::mainLoop() void LLAppViewer::flushVFSIO() { - while (1) + while (true) { S32 pending = LLVFSThread::updateClass(0); pending += LLLFSThread::updateClass(0); @@ -1477,7 +1604,8 @@ extern void cleanup_pose_stand(void); bool LLAppViewer::cleanup() { //ditch LLVOAvatarSelf instance - gAgentAvatarp = NULL; + gAgentAvatarp = nullptr; + // remove any old breakpad minidump files from the log directory if (! isError()) @@ -1502,7 +1630,7 @@ bool LLAppViewer::cleanup() release_start_screen(); // just in case - LLError::logToFixedBuffer(NULL); + LLError::logToFixedBuffer(nullptr); // stop the fixed buffer recorder LL_INFOS() << "Cleaning Up" << LL_ENDL; @@ -1533,7 +1661,7 @@ bool LLAppViewer::cleanup() } delete gAssetStorage; - gAssetStorage = NULL; + gAssetStorage = nullptr; LLPolyMesh::freeAllMeshes(); @@ -1541,7 +1669,10 @@ bool LLAppViewer::cleanup() // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. - LLWorldMap::getInstance()->reset(); // release any images + if (LLWorldMap::instanceExists()) + { + LLWorldMap::getInstance()->reset(); // release any images + } LLCalc::cleanUp(); @@ -1610,7 +1741,7 @@ bool LLAppViewer::cleanup() // This may generate window reshape and activation events. // Therefore must do this before destroying the message system. delete gViewerWindow; - gViewerWindow = NULL; + gViewerWindow = nullptr; LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; } @@ -1618,7 +1749,7 @@ bool LLAppViewer::cleanup() // viewer UI relies on keyboard so keep it aound until viewer UI isa gone delete gKeyboard; - gKeyboard = NULL; + gKeyboard = nullptr; LL_INFOS() << "Cleaning up Objects" << LL_ENDL; @@ -1695,10 +1826,6 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Saved settings" << LL_ENDL; } - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - // save all settings, even if equals defaults - gCrashSettings.saveToFile(crash_settings_filename, FALSE); - // Save URL history file LLURLHistory::saveFile("url_history.xml"); @@ -1712,15 +1839,15 @@ bool LLAppViewer::cleanup() // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); + if (mPurgeOnExit) { LL_INFOS() << "Purging all cache files on exit" << LL_ENDL; gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),"*.*"); } - removeMarkerFile(); // Any crashes from here on we'll just have to ignore // moved this stuff from above to make it conditional here... - if(!anotherInstanceRunning()) + if(!mSecondInstance) { removeCacheFiles("*.wav"); removeCacheFiles("*.tmp"); @@ -1736,8 +1863,7 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Not removing cache files. Other viewer instance detected." << LL_ENDL; } // - - removeDumpDir(); + writeDebugInfo(); // Stop the plugin read thread if it's running. @@ -1751,7 +1877,7 @@ bool LLAppViewer::cleanup() LLTimer idleTimer; idleTimer.reset(); const F64 max_idle_time = 5.f; // 5 seconds - while(1) + while(true) { S32 pending = 0; pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread @@ -1761,7 +1887,7 @@ bool LLAppViewer::cleanup() pending += LLLFSThread::updateClass(0); if (!pending) { - break; + break ; //done } else if (idleTimer.getElapsedTimeF64() >= max_idle_time) { @@ -1775,14 +1901,16 @@ bool LLAppViewer::cleanup() sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); + sTextureFetch->shutDownTextureCacheThread(); sTextureFetch->shutDownImageDecodeThread(); delete sTextureCache; - sTextureCache = NULL; + sTextureCache = nullptr; delete sTextureFetch; - sTextureFetch = NULL; + sTextureFetch = nullptr; delete sImageDecodeThread; - sImageDecodeThread = NULL; + sImageDecodeThread = nullptr; + LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL; @@ -1815,9 +1943,9 @@ bool LLAppViewer::cleanup() // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve delete gStaticVFS; - gStaticVFS = NULL; + gStaticVFS = nullptr; delete gVFS; - gVFS = NULL; + gVFS = nullptr; LLWatchdog::getInstance()->cleanup(); @@ -1832,7 +1960,6 @@ bool LLAppViewer::cleanup() // Cleanup settings last in case other classes reference them. gSavedSettings.cleanup(); gColors.cleanup(); - gCrashSettings.cleanup(); LLViewerAssetStatsFF::cleanup(); // If we're exiting to launch an URL, do that here so the screen @@ -1842,7 +1969,7 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Launch file on quit." << LL_ENDL; #if LL_WINDOWS // Indicate an application is starting. - SetCursor(LoadCursor(NULL, IDC_WAIT)); + SetCursor(LoadCursor(nullptr, IDC_WAIT)); #endif // HACK: Attempt to wait until the screen res. switch is complete. @@ -1863,6 +1990,8 @@ bool LLAppViewer::cleanup() LLError::LLCallStacks::cleanup(); + removeMarkerFiles(); + MEM_TRACK_RELEASE LL_INFOS() << "Goodbye!" << LL_ENDL; @@ -1871,13 +2000,13 @@ bool LLAppViewer::cleanup() return true; } -// A callback for llerrs to call during the watchdog error. +// A callback for LL_ERRS() to call during the watchdog error. void watchdog_llerrs_callback(const std::string &error_string) { gLLErrorActivated = true; #ifdef LL_WINDOWS - RaiseException(0,0,0,0); + RaiseException(0,0,0,nullptr); #else raise(SIGQUIT); #endif @@ -1898,9 +2027,8 @@ bool LLAppViewer::initThreads() static const bool enable_threads = true; #endif - const S32 NEVER_SUBMIT_REPORT = 2; bool use_watchdog = gSavedSettings.getBOOL("WatchdogEnabled"); - bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT; + bool send_reports = gSavedSettings.getS32("CrashSubmitBehavior") == 1; if(use_watchdog && send_reports) { LLWatchdog::getInstance()->init(watchdog_killer_callback); @@ -1937,8 +2065,8 @@ void errorCallback(const std::string &error_string) static std::string last_message; if(last_message != error_string) { - U32 response = OSMessageBox(error_string, "Crash Loop?", OSMB_YESNO); - if(response) + U32 response = OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_YESNO); + if (response == OSBTN_NO) { last_message = error_string; return; @@ -1947,38 +2075,91 @@ void errorCallback(const std::string &error_string) //Set the ErrorActivated global so we know to create a marker file gLLErrorActivated = true; + gDebugInfo["FatalMessage"] = error_string; + // We're not already crashing -- we simply *intend* to crash. Since we + // haven't actually trashed anything yet, we can afford to write the whole + // static info file. + LLAppViewer::instance()->writeDebugInfo(); + LLError::crashAndLoop(error_string); } } -bool init_logging(); -bool LLAppViewer::initLogging() +void LLAppViewer::initLoggingAndGetLastDuration() { // // Set up logging defaults for the viewer // LLError::initForApplication( - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "") + ); LLError::setFatalFunction(errorCallback); + //LLError::setTimeFunction(getRuntime); - return init_logging(); + initLoggingInternal(); } -bool init_logging() +void LLAppViewer::initLoggingInternal() { // Remove the last ".old" log file. - std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, OLD_LOG_FILE); + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + OLD_LOG_FILE); LLFile::remove(old_log_file); + // Get name of the log file + std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + LOG_FILE); + /* + * Before touching any log files, compute the duration of the last run + * by comparing the ctime of the previous start marker file with the ctime + * of the last log file. + */ + std::string start_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, START_MARKER_FILE_NAME); + llstat start_marker_stat; + llstat log_file_stat; + std::ostringstream duration_log_stream; // can't log yet, so save any message for when we can below + int start_stat_result = LLFile::stat(start_marker_file_name, &start_marker_stat); + int log_stat_result = LLFile::stat(log_file, &log_file_stat); + if ( 0 == start_stat_result && 0 == log_stat_result ) + { + int elapsed_seconds = log_file_stat.st_ctime - start_marker_stat.st_ctime; + // only report a last run time if the last viewer was the same version + // because this stat will be counted against this version + if ( markerIsSameVersion(start_marker_file_name) ) + { + gLastExecDuration = elapsed_seconds; + } + else + { + duration_log_stream << "start marker from some other version; duration is not reported"; + gLastExecDuration = -1; + } + } + else + { + // at least one of the LLFile::stat calls failed, so we can't compute the run time + duration_log_stream << "duration stat failure; start: "<< start_stat_result << " log: " << log_stat_result; + gLastExecDuration = -1; // unknown + } + std::string duration_log_msg(duration_log_stream.str()); + + // Create a new start marker file for comparison with log file time for the next run + LLAPRFile start_marker_file ; + start_marker_file.open(start_marker_file_name, LL_APR_WB); + if (start_marker_file.getFileHandle()) + { + recordMarkerVersion(start_marker_file); + start_marker_file.close(); + } + // Rename current log file to ".old" - std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOG_FILE); LLFile::rename(log_file, old_log_file); - // Set the log file to Singularity.log - + // Set the log file to SecondLife.log LLError::logToFile(log_file); - - // *FIX:Mani no error handling here! - return true; + if (!duration_log_msg.empty()) + { + LL_WARNS("MarkerFile") << duration_log_msg << LL_ENDL; + } } bool LLAppViewer::loadSettingsFromDirectory(AIReadAccess const& settings_r, @@ -2098,7 +2279,6 @@ bool LLAppViewer::initConfiguration() //Set up internal pointers settings[sGlobalSettingsName] = &gSavedSettings; settings[sPerAccountSettingsName] = &gSavedPerAccountSettings; - settings[sCrashSettingsName] = &gCrashSettings; //Load settings files list std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml"); @@ -2125,9 +2305,9 @@ bool LLAppViewer::initConfiguration() bool set_defaults = true; if(!loadSettingsFromDirectory(settings_w, "Default", set_defaults)) { - std::ostringstream msg; - msg << "Unable to load default settings file. The installation may be corrupted."; - OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); + OSMessageBox( + "Unable to load default settings file. The installation may be corrupted.", + LLStringUtil::null,OSMB_OK); return false; } @@ -2139,6 +2319,7 @@ bool LLAppViewer::initConfiguration() gSavedSettings.connectCOAVars(gSavedPerAccountSettings); // - set procedural settings + // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet gSavedSettings.setString("ClientSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); @@ -2171,8 +2352,6 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setBOOL("WatchdogEnabled", FALSE); #endif - gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2)); - // These are warnings that appear on the first experience of that condition. // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse // for disable/reset ability @@ -2231,7 +2410,7 @@ bool LLAppViewer::initConfiguration() const std::string log = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOG_FILE); LL_INFOS() << "Attempting to use portable settings and cache!" << LL_ENDL; gDirUtilp->makePortable(); - init_logging(); // Switch to portable log file + initLoggingInternal(); // Switch to portable log file LL_INFOS() << "Portable viewer configuration initialized!" << LL_ENDL; LLFile::remove(log); LL_INFOS() << "Cleaned up local log file to keep this computer untouched." << LL_ENDL; @@ -2246,7 +2425,7 @@ bool LLAppViewer::initConfiguration() gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, clp.getOption("settings")[0]); gSavedSettings.setString("ClientSettingsFile", user_settings_filename); - LL_INFOS() << "Using command line specified settings filename: " + LL_INFOS("Settings") << "Using command line specified settings filename: " << user_settings_filename << LL_ENDL; } else @@ -2289,7 +2468,7 @@ bool LLAppViewer::initConfiguration() if(clp.hasOption("disablecrashlogger")) { LL_WARNS() << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" <disableCrashlogger(); + disableCrashlogger(); } // Handle initialization from settings. @@ -2306,7 +2485,7 @@ bool LLAppViewer::initConfiguration() LL_INFOS() << msg.str() << LL_ENDL; OSMessageBox( - msg.str().c_str(), + msg.str(), LLStringUtil::null, OSMB_OK); @@ -2406,22 +2585,6 @@ bool LLAppViewer::initConfiguration() gSavedSettings.getString("Language")); } - // XUI:translate - gSecondLife = LLTrans::getString("APP_NAME"); - - // Read skin/branding settings if specified. - //if (! gDirUtilp->getSkinDir().empty() ) - //{ - // std::string skin_def_file = gDirUtilp->findSkinnedFilename("skin.xml"); - // LLXmlTree skin_def_tree; - - // if (!skin_def_tree.parseFile(skin_def_file)) - // { - // LL_ERRS() << "Failed to parse skin definition." << LL_ENDL; - // } - - //} - #if LL_DARWIN // Initialize apple menubar and various callbacks init_apple_menu(LLTrans::getString("APP_NAME").c_str()); @@ -2469,46 +2632,29 @@ bool LLAppViewer::initConfiguration() } } - if (!gSavedSettings.getBOOL("AllowMultipleViewers")) + + // + // Check for another instance of the app running + // + if (mSecondInstance && !gSavedSettings.getBOOL("AllowMultipleViewers")) { - // - // Check for another instance of the app running - // - - mSecondInstance = anotherInstanceRunning(); - - if (mSecondInstance) - { - std::ostringstream msg; - msg << LLTrans::getString("MBAlreadyRunning"); - OSMessageBox( - msg.str(), - LLStringUtil::null, - OSMB_OK); - return false; - } - - initMarkerFile(); - + OSMessageBox( + LLTrans::getString("MBAlreadyRunning"), + LLStringUtil::null, + OSMB_OK); + return false; } - else - { - mSecondInstance = anotherInstanceRunning(); - - if (mSecondInstance) - { - // This is the second instance of SL. Turn off voice support, - // but make sure the setting is *not* persisted. - LLControlVariable* disable_voice = gSavedSettings.getControl("CmdLineDisableVoice"); - if(disable_voice && !gSavedSettings.getBOOL("VoiceMultiInstance")) - { - const BOOL DO_NOT_PERSIST = FALSE; - disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST); - } - } - initMarkerFile(); - + if (mSecondInstance) + { + // This is the second instance of SL. Turn off voice support, + // but make sure the setting is *not* persisted. + LLControlVariable* disable_voice = gSavedSettings.getControl("CmdLineDisableVoice"); + if(disable_voice && !gSavedSettings.getBOOL("VoiceMultiInstance")) + { + const BOOL DO_NOT_PERSIST = FALSE; + disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST); + } } // need to do this here - need to have initialized global settings first @@ -2592,25 +2738,25 @@ bool LLAppViewer::initWindow() //gViewerWindow->getWindow()->show(); LL_INFOS("AppInit") << "Window initialization done." << LL_ENDL; + return true; } void LLAppViewer::writeDebugInfo(bool isStatic) { +#if !defined(USE_CRASHPAD) //Try to do the minimum when writing data during a crash. std::string* debug_filename; debug_filename = ( isStatic ? getStaticDebugFile() : getDynamicDebugFile() ); - LL_INFOS() << "Opening debug file " << *debug_filename << LL_ENDL; - llofstream out_file(*debug_filename); + LL_INFOS() << "Writing debug file " << *debug_filename << LL_ENDL; + llofstream out_file(debug_filename->c_str()); isStatic ? LLSDSerialize::toPrettyXML(gDebugInfo, out_file) : LLSDSerialize::toPrettyXML(gDebugInfo["Dynamic"], out_file); - - - out_file.close(); +#endif } void LLAppViewer::cleanupSavedSettings() @@ -2673,12 +2819,13 @@ void LLAppViewer::writeSystemInfo() gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - + gDebugInfo["ClientInfo"]["AddressSize"] = #if defined(_WIN64) || defined(__x86_64__) - gDebugInfo["ClientInfo"]["Architecture"] = "x86_64"; + "64"; #else - gDebugInfo["ClientInfo"]["Architecture"] = "i386"; + "32"; #endif + gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString(); @@ -2742,9 +2889,9 @@ void LLAppViewer::writeSystemInfo() void LLAppViewer::handleViewerCrash() { - LL_INFOS() << "Handle viewer crash entry." << LL_ENDL; + LL_INFOS("CRASHREPORT") << "Handle viewer crash entry." << LL_ENDL; - LL_INFOS() << "Last render pool type: " << LLPipeline::sCurRenderPoolType << LL_ENDL ; + LL_INFOS("CRASHREPORT") << "Last render pool type: " << LLPipeline::sCurRenderPoolType << LL_ENDL ; LLMemory::logMemoryInfo(true) ; @@ -2787,8 +2934,7 @@ void LLAppViewer::handleViewerCrash() { gDebugInfo["Dynamic"]["ParcelMediaURL"] = parcel->getMediaURL(); } - - + gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); @@ -2805,7 +2951,7 @@ void LLAppViewer::handleViewerCrash() } else { - gDebugInfo["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH; + gDebugInfo["Dynamic"]["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH; } if(gAgent.getRegion()) @@ -2833,26 +2979,27 @@ void LLAppViewer::handleViewerCrash() //we're already in a crash situation if (gDirUtilp) { - std::string crash_file_name; - if(gLLErrorActivated) crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LLERROR_MARKER_FILE_NAME); - else crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,ERROR_MARKER_FILE_NAME); - LL_INFOS() << "Creating crash marker file " << crash_file_name << LL_ENDL; - - LLAPRFile crash_file(crash_file_name, LL_APR_W); - if (crash_file.getFileHandle()) + std::string crash_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + gLLErrorActivated + ? LLERROR_MARKER_FILE_NAME + : ERROR_MARKER_FILE_NAME); + LLAPRFile crash_marker_file ; + crash_marker_file.open(crash_marker_file_name, LL_APR_WB); + if (crash_marker_file.getFileHandle()) { - LL_INFOS("MarkerFile") << "Created crash marker file " << crash_file_name << LL_ENDL; + LL_INFOS("MarkerFile") << "Created crash marker file " << crash_marker_file_name << LL_ENDL; + recordMarkerVersion(crash_marker_file); } else { - LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_file_name << LL_ENDL; + LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_marker_file_name << LL_ENDL; } } - char *minidump_file = pApp->getMiniDumpFilename(); - if(minidump_file && minidump_file[0] != 0) + else { - gDebugInfo["Dynamic"]["MinidumpPath"] = minidump_file; + LL_WARNS("MarkerFile") << "No gDirUtilp with which to create error marker file name" << LL_ENDL; } + LL_WARNS("CRASHREPORT") << "no minidump file? ah yeah, boi!" << LL_ENDL; gDebugInfo["Dynamic"]["CrashType"]="crash"; @@ -2860,13 +3007,17 @@ void LLAppViewer::handleViewerCrash() { std::string filename; filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log"); - llofstream file(filename, llofstream::binary); + LL_DEBUGS("CRASHREPORT") << "recording stats " << filename << LL_ENDL; + llofstream file(filename.c_str(), std::ios_base::binary); if(file.good()) { - LL_INFOS() << "Handle viewer crash generating stats log." << LL_ENDL; gMessageSystem->summarizeLogs(file); file.close(); } + else + { + LL_WARNS("CRASHREPORT") << "problem recording stats" << LL_ENDL; + } } if (gMessageSystem) @@ -2879,158 +3030,241 @@ void LLAppViewer::handleViewerCrash() // Close the debug file pApp->writeDebugInfo(false); //false answers the isStatic question with the least overhead. - - LLError::logToFile(""); - - // Remove the marker file, since otherwise we'll spawn a process that'll keep it locked - if(gDebugInfo["Dynamic"]["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH) - { - pApp->removeMarkerFile(true); - } - else - { - pApp->removeMarkerFile(false); - } - - return; } -bool LLAppViewer::anotherInstanceRunning() +// static +void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file) { - // We create a marker file when the program starts and remove the file when it finishes. - // If the file is currently locked, that means another process is already running. - - std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, MARKER_FILE_NAME); - LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; - - //Freeze case checks - if (LLAPRFile::isExist(marker_file, LL_APR_RB)) + std::string marker_version(LLVersionInfo::getChannelAndVersion()); + if ( marker_version.length() > MAX_MARKER_LENGTH ) { - // File exists, try opening with write permissions - LLAPRFile outfile(marker_file, LL_APR_WB); - apr_file_t* fMarker = outfile.getFileHandle() ; - if (!fMarker) - { - // Another instance is running. Skip the rest of these operations. - LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; - return true; - } - if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) //flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1) - { - LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; - return true; - } - // No other instances; we'll lock this file now & delete on quit. + LL_WARNS_ONCE("MarkerFile") << "Version length ("<< marker_version.length()<< ")" + << " greater than maximum (" << MAX_MARKER_LENGTH << ")" + << ": marker matching may be incorrect" + << LL_ENDL; } - LL_DEBUGS("MarkerFile") << "Marker file isn't locked." << LL_ENDL; - return false; + + // record the viewer version in the marker file + marker_file.write(marker_version.data(), marker_version.length()); } -void LLAppViewer::initMarkerFile() +bool LLAppViewer::markerIsSameVersion(const std::string& marker_name) const { - //First, check for the existence of other files. - //There are marker files for two different types of crashes - - mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); - LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; + bool sameVersion = false; + std::string my_version(LLVersionInfo::getChannelAndVersion()); + char marker_version[MAX_MARKER_LENGTH]; + + LLAPRFile marker_file; + marker_file.open(marker_name, LL_APR_RB); + if (marker_file.getFileHandle()) + { + S32 marker_version_length = marker_file.read(marker_version, sizeof(marker_version)); + std::string marker_string(marker_version, marker_version_length); + if ( 0 == my_version.compare( 0, my_version.length(), marker_version, 0, marker_version_length ) ) + { + sameVersion = true; + } + LL_DEBUGS("MarkerFile") << "Compare markers for '" << marker_name << "': " + << "\n mine '" << my_version << "'" + << "\n marker '" << marker_string << "'" + << "\n " << ( sameVersion ? "same" : "different" ) << " version" + << LL_ENDL; + marker_file.close(); + } + return sameVersion; +} + +void LLAppViewer::processMarkerFiles() +{ //We've got 4 things to test for here - // - Other Process Running (Singularity.exec_marker present, locked) - // - Freeze (Singularity.exec_marker present, not locked) - // - LLError Crash (Singularity.llerror_marker present) - // - Other Crash (Singularity.error_marker present) + // - Other Process Running (SecondLife.exec_marker present, locked) + // - Freeze (SecondLife.exec_marker present, not locked) + // - LLError Crash (SecondLife.llerror_marker present) + // - Other Crash (SecondLife.error_marker present) // These checks should also remove these files for the last 2 cases if they currently exist - //LLError/Error checks. Only one of these should ever happen at a time. - std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); - std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME); - std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); + bool marker_is_same_version = true; + // first, look for the marker created at startup and deleted on a clean exit + mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); + if (LLAPRFile::isExist(mMarkerFileName, LL_APR_RB)) + { + // File exists... + // first, read it to see if it was created by the same version (we need this later) + marker_is_same_version = markerIsSameVersion(mMarkerFileName); - if (LLAPRFile::isExist(mMarkerFileName, LL_APR_RB) && !anotherInstanceRunning()) - { - gLastExecEvent = LAST_EXEC_FROZE; - LL_INFOS("MarkerFile") << "Exec marker found: program froze on previous execution" << LL_ENDL; - } - if(LLAPRFile::isExist(logout_marker_file, LL_APR_RB)) - { - gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; - LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(logout_marker_file); - } - if(LLAPRFile::isExist(llerror_marker_file, LL_APR_RB)) - { - LL_INFOS() << "Last exec LLError crashed, setting LastExecEvent to " << LAST_EXEC_LLERROR_CRASH << LL_ENDL; - if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; - else gLastExecEvent = LAST_EXEC_LLERROR_CRASH; - LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(llerror_marker_file); - } - if(LLAPRFile::isExist(error_marker_file, LL_APR_RB)) - { - LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << LAST_EXEC_OTHER_CRASH << LL_ENDL; - if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; - else gLastExecEvent = LAST_EXEC_OTHER_CRASH; - LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(error_marker_file); - } - - // No new markers if another instance is running. - if(anotherInstanceRunning()) - { - return; - } - - // Create the marker file for this execution & lock it - apr_status_t s; - s = mMarkerFile.open(mMarkerFileName, LL_APR_W, LLAPRFile::long_lived); - - if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) - { - LL_DEBUGS("MarkerFile") << "Marker file created." << LL_ENDL; - } - else - { - LL_INFOS("MarkerFile") << "Failed to create marker file." << LL_ENDL; - return; - } - if (apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) - { - mMarkerFile.close() ; - LL_INFOS("MarkerFile") << "Marker file cannot be locked." << LL_ENDL; - return; - } - - LL_DEBUGS("MarkerFile") << "Marker file locked." << LL_ENDL; -} - -void LLAppViewer::removeMarkerFile(bool leave_logout_marker) -{ - LL_DEBUGS("MarkerFile") << "removeMarkerFile()" << LL_ENDL; - if (mMarkerFile.getFileHandle()) - { - mMarkerFile.close() ; - LLAPRFile::remove( mMarkerFileName ); - } - if (mLogoutMarkerFile != NULL) - { - if(!leave_logout_marker) + // now test to see if this file is locked by a running process (try to open for write) + LL_DEBUGS("MarkerFile") << "Checking exec marker file for lock..." << LL_ENDL; + mMarkerFile.open(mMarkerFileName, LL_APR_WB); + apr_file_t* fMarker = mMarkerFile.getFileHandle() ; + if (!fMarker) { - LLAPRFile::remove( mLogoutMarkerFileName ); - mLogoutMarkerFile = NULL; + LL_INFOS("MarkerFile") << "Exec marker file open failed - assume it is locked." << LL_ENDL; + mSecondInstance = true; // lock means that instance is running. } else { - LL_WARNS("MarkerFile") << "leaving markers because this is a second instance" << LL_ENDL; + // We were able to open it, now try to lock it ourselves... + if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) + { + LL_WARNS_ONCE("MarkerFile") << "Locking exec marker failed." << LL_ENDL; + mSecondInstance = true; // lost a race? be conservative + } + else + { + // No other instances; we've locked this file now, so record our version; delete on quit. + recordMarkerVersion(mMarkerFile); + LL_DEBUGS("MarkerFile") << "Exec marker file existed but was not locked; rewritten." << LL_ENDL; + } } + + if (mSecondInstance) + { + LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' owned by another instance" << LL_ENDL; + } + else if (marker_is_same_version) + { + // the file existed, is ours, and matched our version, so we can report on what it says + LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec FROZE" << LL_ENDL; + gLastExecEvent = LAST_EXEC_FROZE; + + } + else + { + LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found, but versions did not match" << LL_ENDL; + } + } + else // marker did not exist... last exec (if any) did not freeze + { + // Create the marker file for this execution & lock it; it will be deleted on a clean exit + apr_status_t s; + s = mMarkerFile.open(mMarkerFileName, LL_APR_WB, TRUE); + + if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) + { + LL_DEBUGS("MarkerFile") << "Exec marker file '"<< mMarkerFileName << "' created." << LL_ENDL; + if (APR_SUCCESS == apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE)) + { + recordMarkerVersion(mMarkerFile); + LL_DEBUGS("MarkerFile") << "Exec marker file locked." << LL_ENDL; + } + else + { + LL_WARNS("MarkerFile") << "Exec marker file cannot be locked." << LL_ENDL; + } + } + else + { + LL_WARNS("MarkerFile") << "Failed to create exec marker file '"<< mMarkerFileName << "'." << LL_ENDL; + } + } + + // now check for cases in which the exec marker may have been cleaned up by crash handlers + + // check for any last exec event report based on whether or not it happened during logout + // (the logout marker is created when logout begins) + std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); + if(LLAPRFile::isExist(logout_marker_file, LL_APR_RB)) + { + if (markerIsSameVersion(logout_marker_file)) + { + gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; + LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL; + } + else + { + LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL; + } + LLAPRFile::remove(logout_marker_file); + } + // further refine based on whether or not a marker created during an llerr crash is found + std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME); + if(LLAPRFile::isExist(llerror_marker_file, LL_APR_RB)) + { + if (markerIsSameVersion(llerror_marker_file)) + { + if ( gLastExecEvent == LAST_EXEC_LOGOUT_FROZE ) + { + gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; + LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL; + } + else + { + gLastExecEvent = LAST_EXEC_LLERROR_CRASH; + LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' crashed, setting LastExecEvent to LLERROR_CRASH" << LL_ENDL; + } + } + else + { + LL_INFOS("MarkerFile") << "LLError marker '"<< llerror_marker_file << "' found, but versions did not match" << LL_ENDL; + } + LLAPRFile::remove(llerror_marker_file); + } + // and last refine based on whether or not a marker created during a non-llerr crash is found + std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); + if(LLAPRFile::isExist(error_marker_file, LL_APR_RB)) + { + if (markerIsSameVersion(error_marker_file)) + { + if (gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) + { + gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; + LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to LOGOUT_CRASH" << LL_ENDL; + } + else + { + gLastExecEvent = LAST_EXEC_OTHER_CRASH; + LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; + } + } + else + { + LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL; + } + LLAPRFile::remove(error_marker_file); + } +} + +void LLAppViewer::removeMarkerFiles() +{ + if (!mSecondInstance) + { + if (mMarkerFile.getFileHandle()) + { + mMarkerFile.close() ; + LLAPRFile::remove( mMarkerFileName ); + LL_DEBUGS("MarkerFile") << "removed exec marker '"<getExpandedFilename(LL_PATH_DUMP, ""); gDirUtilp->deleteDirAndContents(dump_dir); +#endif } void LLAppViewer::forceQuit() @@ -3050,7 +3284,7 @@ void LLAppViewer::fastQuit(S32 error_code) // figure out the error code S32 final_error_code = error_code ? error_code : (S32)isError(); // this isn't a crash - removeMarkerFile(); + removeMarkerFiles(); // get outta here _exit(final_error_code); } @@ -3171,8 +3405,7 @@ void LLAppViewer::migrateCacheDirectory() { gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); - std::string delimiter = gDirUtilp->getDirDelimiter(); - std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; + std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache"); std::string new_cache_dir = gDirUtilp->getCacheDir(true); if (gDirUtilp->fileExists(old_cache_dir)) @@ -3188,8 +3421,8 @@ void LLAppViewer::migrateCacheDirectory() while (iter.next(file_name)) { if (file_name == "." || file_name == "..") continue; - std::string source_path = old_cache_dir + delimiter + file_name; - std::string dest_path = new_cache_dir + delimiter + file_name; + std::string source_path = gDirUtilp->add(old_cache_dir, file_name); + std::string dest_path = gDirUtilp->add(new_cache_dir, file_name); if (!LLFile::rename(source_path, dest_path)) { file_count++; @@ -3239,6 +3472,7 @@ void dumpVFSCaches() gStaticVFS->dumpFiles(); SetCurrentDirectory(w_str); #endif + LL_INFOS() << "========= Dynamic VFS ====" << LL_ENDL; gVFS->listFiles(); #if LL_WINDOWS @@ -3299,6 +3533,7 @@ bool LLAppViewer::initCache() if (gSavedSettings.getBOOL("PurgeCacheOnStartup") || gSavedSettings.getBOOL("PurgeCacheOnNextStartup")) { + LL_INFOS("AppCache") << "Startup cache purge requested: " << (gSavedSettings.getBOOL("PurgeCacheOnStartup") ? "ALWAYS" : "ONCE") << LL_ENDL; gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false); mPurgeCache = true; // STORM-1141 force purgeAllTextures to get called to prevent a crash here. -brad @@ -3314,6 +3549,7 @@ bool LLAppViewer::initCache() std::string new_cache_location = gSavedSettings.getString("NewCacheLocation"); if (new_cache_location != cache_location) { + LL_INFOS("AppCache") << "Cache location changed, cache needs purging" << LL_ENDL; gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")); purgeCache(); // purge old cache gSavedSettings.setString("CacheLocation", new_cache_location); @@ -3341,6 +3577,7 @@ bool LLAppViewer::initCache() std::random_device rnddev; std::mt19937 rng(rnddev()); std::uniform_int_distribution<> dist(0, 4); + LLSplashScreen::update(LLTrans::getString( llformat("StartupInitializingTextureCache%d", dist(rng)))); } @@ -3385,7 +3622,7 @@ bool LLAppViewer::initCache() // This has to happen BEFORE starting the vfs // time_t ltime; - srand(time(NULL)); // Flawfinder: ignore + srand(time(nullptr)); // Flawfinder: ignore U32 old_salt = gSavedSettings.getU32("VFSSalt"); U32 new_salt; std::string old_vfs_data_file; @@ -3427,10 +3664,10 @@ bool LLAppViewer::initCache() LLDirIterator iter(dir, mask); if (iter.next(found_file)) { - old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; + old_vfs_data_file = gDirUtilp->add(dir, found_file); - S32 start_pos = found_file.find_last_of('.'); - if (start_pos > 0) + size_t start_pos = found_file.find_last_of('.'); + if (start_pos != std::string::npos && start_pos != 0) { sscanf(found_file.substr(start_pos+1).c_str(), "%d", &old_salt); } @@ -3539,7 +3776,7 @@ void LLAppViewer::purgeCache() const std::string& LLAppViewer::getSecondLifeTitle() const { - return gSecondLife; + return LLTrans::getString("APP_NAME"); } const std::string& LLAppViewer::getWindowTitle() const @@ -3576,9 +3813,9 @@ void LLAppViewer::forceDisconnect(const std::string& mesg) return; } - // Translate the message if possible + // *TODO: Translate the message if possible std::string big_reason = LLAgent::sTeleportErrorMessages[mesg]; - if ( big_reason.size() == 0 ) + if (big_reason.empty()) { big_reason = mesg; } @@ -3609,26 +3846,25 @@ void LLAppViewer::badNetworkHandler() mPurgeOnExit = TRUE; - std::string grid_support_msg = ""; - if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty()) - { - grid_support_msg = "\n\nOr visit the grid support page at: \n " - + gHippoGridManager->getCurrentGrid()->getSupportUrl(); - } std::ostringstream message; message << "The viewer has detected mangled network data indicative\n" "of a bad upstream network connection or an incomplete\n" - "local installation of " << gSecondLife << ". \n" + "local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n" " \n" "Try uninstalling and reinstalling to see if this resolves \n" "the issue. \n" " \n" "If the problem continues, please report the issue at: \n" - "http://www.singularityviewer.org" << grid_support_msg; + "http://www.singularityviewer.org"; + + if (!gHippoGridManager->getCurrentGrid()->getSupportUrl().empty()) + { + message << "\n\nOr visit the grid support page at: \n" + << gHippoGridManager->getCurrentGrid()->getSupportUrl(); + } + forceDisconnect(message.str()); - - LLApp::instance()->writeMiniDump(); } // This routine may get called more than once during the shutdown process. @@ -3659,11 +3895,11 @@ void LLAppViewer::saveFinalSnapshot() void LLAppViewer::loadNameCache() { - // Phoenix: Wolfspirit: Loads the Display Name Cache. And set if we are using Display Names. + // display names cache std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); LL_INFOS("AvNameCache") << filename << LL_ENDL; - llifstream name_cache_stream(filename); + llifstream name_cache_stream(filename.c_str()); if(name_cache_stream.is_open()) { LLAvatarNameCache::importFile(name_cache_stream); @@ -3673,7 +3909,7 @@ void LLAppViewer::loadNameCache() std::string name_cache; name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); - llifstream cache_file(name_cache); + llifstream cache_file(name_cache.c_str()); if(cache_file.is_open()) { if(gCacheName->importFile(cache_file)) return; @@ -3682,26 +3918,29 @@ void LLAppViewer::loadNameCache() void LLAppViewer::saveNameCache() { - // Phoenix: Wolfspirit: Saves the Display Name Cache. + // display names cache std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); - llofstream name_cache_stream(filename); + llofstream name_cache_stream(filename.c_str()); if(name_cache_stream.is_open()) { LLAvatarNameCache::exportFile(name_cache_stream); } - if (!gCacheName) return; - - std::string name_cache; - name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); - llofstream cache_file(name_cache); - if(cache_file.is_open()) - { - gCacheName->exportFile(cache_file); + // real names cache + if (gCacheName) + { + std::string name_cache; + name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + llofstream cache_file(name_cache.c_str()); + if(cache_file.is_open()) + { + gCacheName->exportFile(cache_file); + } } } + /*! @brief This class is an LLFrameTimer that can be created with an elapsed time that starts counting up from the given value rather than 0.0. @@ -3781,11 +4020,12 @@ void LLAppViewer::idle() // Smoothly weight toward current frame gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f; - F32 qas = gSavedSettings.getF32("QuitAfterSeconds"); + static LLCachedControl qas(gSavedSettings, "QuitAfterSeconds"); if (qas > 0.f) { if (gRenderStartTime.getElapsedTimeF32() > qas) { + LL_INFOS() << "Quitting after " << qas << " seconds. See setting \"QuitAfterSeconds\"." << LL_ENDL; LLAppViewer::instance()->forceQuit(); } } @@ -3842,7 +4082,8 @@ void LLAppViewer::idle() // Update simulator agent state // - if (gSavedSettings.getBOOL("RotateRight")) + static LLCachedControl rotateRight(gSavedSettings, "RotateRight"); + if (rotateRight) { gAgent.moveYaw(-1.f); } @@ -3859,7 +4100,8 @@ void LLAppViewer::idle() // When appropriate, update agent location to the simulator. F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); - BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags()); + BOOL flags_changed = gAgent.controlFlagsDirty() + || (last_control_flags != gAgent.getControlFlags()); if (flags_changed || (agent_update_time > (1.0f / (F32)AGENT_UPDATES_PER_SECOND))) { @@ -3885,7 +4127,7 @@ void LLAppViewer::idle() reset_statistics(); // Update session stats every large chunk of time - // *FIX: (???) SAMANTHA + // *FIX: (?) SAMANTHA if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected) { LL_INFOS() << "Transmitting sessions stats" << LL_ENDL; @@ -4304,13 +4546,11 @@ void LLAppViewer::sendLogoutRequest() //Set internal status variables and marker files gLogoutInProgress = TRUE; mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME); - - LLAPRFile outfile(mLogoutMarkerFileName, LL_APR_W); - mLogoutMarkerFile = outfile.getFileHandle() ; - if (mLogoutMarkerFile) + + if (mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_W) == APR_SUCCESS) { LL_INFOS() << "Created logout marker file " << mLogoutMarkerFileName << LL_ENDL; - apr_file_close(mLogoutMarkerFile); + mLogoutMarkerFile.close(); } else { @@ -4632,26 +4872,31 @@ void LLAppViewer::disconnectViewer() void LLAppViewer::forceErrorLLError() { - LL_ERRS() << "This is an llerror" << LL_ENDL; + LL_ERRS() << "This is a deliberate llerror" << LL_ENDL; } void LLAppViewer::forceErrorBreakpoint() { + LL_WARNS() << "Forcing a deliberate breakpoint" << LL_ENDL; #ifdef LL_WINDOWS DebugBreak(); +#else + asm ("int $3"); #endif return; } void LLAppViewer::forceErrorBadMemoryAccess() { - S32* crash = NULL; + LL_WARNS() << "Forcing a deliberate bad memory access" << LL_ENDL; + S32* crash = nullptr; *crash = 0xDEADBEEF; return; } void LLAppViewer::forceErrorInfiniteLoop() { + LL_WARNS() << "Forcing a deliberate infinite loop" << LL_ENDL; while(true) { ; @@ -4661,13 +4906,15 @@ void LLAppViewer::forceErrorInfiniteLoop() void LLAppViewer::forceErrorSoftwareException() { + LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL; // *FIX: Any way to insure it won't be handled? throw; } void LLAppViewer::forceErrorDriverCrash() { - glDeleteTextures(1, NULL); + LL_WARNS() << "Forcing a deliberate driver crash" << LL_ENDL; + glDeleteTextures(1, nullptr); } void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) @@ -4684,7 +4931,7 @@ void LLAppViewer::destroyMainloopTimeout() if(mMainloopTimeout) { delete mMainloopTimeout; - mMainloopTimeout = NULL; + mMainloopTimeout = nullptr; } } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index e646aa2b5..06da8ee82 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -76,6 +76,7 @@ public: bool quitRequested() { return mQuitRequested; } bool logoutRequestSent() { return mLogoutRequestSent; } + bool isSecondInstance() { return mSecondInstance; } void writeDebugInfo(bool isStatic=true); @@ -86,7 +87,7 @@ public: virtual bool restoreErrorTrap() = 0; // Require platform specific override to reset error handling mechanism. // return false if the error trap needed restoration. - virtual void initCrashReporting(bool reportFreeze = false) = 0; // What to do with crash report? + void initCrashReporting(); // What to do with crash report? static void handleViewerCrash(); // Hey! The viewer crashed. Do this, soon. // Thread accessors @@ -113,7 +114,7 @@ public: void loadNameCache(); void saveNameCache(); - void removeMarkerFile(bool leave_logout_marker = false); + void removeMarkerFiles(); void removeDumpDir(); // LLAppViewer testing helpers. @@ -125,14 +126,13 @@ public: virtual void forceErrorSoftwareException(); virtual void forceErrorDriverCrash(); - // *NOTE: There are currently 3 settings files: - // "Global", "PerAccount" and "CrashSettings" + // *NOTE: There are currently 2 settings files: + // "Global", and "PerAccount" // The list is found in app_settings/settings_files.xml // but since they are used explicitly in code, // the follow consts should also do the trick. static const std::string sGlobalSettingsName; static const std::string sPerAccountSettingsName; - static const std::string sCrashSettingsName; // Load settings from the location specified by loction_key. // Key availale and rules for loading, are specified in @@ -158,6 +158,7 @@ public: void handleLoginComplete(); LLAllocator & getAllocator() { return mAlloc; } + // On LoginCompleted callback typedef boost::signals2::signal login_completed_signal_t; login_completed_signal_t mOnLoginCompleted; @@ -172,7 +173,8 @@ public: static void metricsSend(bool enable_reporting); protected: virtual bool initWindow(); // Initialize the viewer's window. - virtual bool initLogging(); // Initialize log files, logging system, return false on failure. + virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system + void initLoggingInternal(); virtual void initConsole() {}; // Initialize OS level debugging console. virtual bool initHardwareTest() { return true; } // A false result indicates the app should quit. virtual bool initSLURLHandler(); @@ -189,7 +191,6 @@ private: void initMaxHeapSize(); bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. - bool initCache(); // Initialize local client cache. void checkMemory() ; @@ -202,8 +203,9 @@ private: void writeSystemInfo(); // Write system info to "debug_info.log" - bool anotherInstanceRunning(); - void initMarkerFile(); + void processMarkerFiles(); + static void recordMarkerVersion(LLAPRFile& marker_file); + bool markerIsSameVersion(const std::string& marker_name) const; void idle(); void idleShutdown(); @@ -226,8 +228,7 @@ private: LLAPRFile mMarkerFile; // A file created to indicate the app is running. std::string mLogoutMarkerFileName; - apr_file_t* mLogoutMarkerFile; // A file created to indicate the app is running. - + LLAPRFile mLogoutMarkerFile; // A file created to indicate the app is running. LLOSInfo mSysOSInfo; bool mReportedCrash; diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 764d36e4d..0f2957bc9 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -119,7 +119,6 @@ int main( int argc, char **argv ) } delete viewer_app_ptr; viewer_app_ptr = NULL; - return 0; } @@ -143,20 +142,12 @@ bool LLAppViewerLinux::init() bool success = LLAppViewer::init(); -#if LL_SEND_CRASH_REPORTS - if (success) - { - LLAppViewer* pApp = LLAppViewer::instance(); - pApp->initCrashReporting(); - } -#endif - return success; } bool LLAppViewerLinux::restoreErrorTrap() { - // *NOTE:Mani there is a case for implementing this or the mac. + // *NOTE:Mani there is a case for implementing this on the mac. // Linux doesn't need it to my knowledge. return true; } @@ -347,12 +338,6 @@ bool LLAppViewerLinux::sendURLToOtherInstance(const std::string& url) } #endif // LL_DBUS_ENABLED -void LLAppViewerLinux::initCrashReporting(bool reportFreeze) -{ - // Singu Note: this is where original code forks crash logger process. - // Singularity doesn't need it -} - bool LLAppViewerLinux::beingDebugged() { static enum {unknown, no, yes} debugged = unknown; @@ -397,7 +382,7 @@ bool LLAppViewerLinux::beingDebugged() #endif } -bool LLAppViewerLinux::initLogging() +void LLAppViewerLinux::initLoggingAndGetLastDuration() { // Remove the last stack trace, if any // This file is no longer created, since the move to Google Breakpad @@ -406,7 +391,7 @@ bool LLAppViewerLinux::initLogging() gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log"); LLFile::remove(old_stack_file); - return LLAppViewer::initLogging(); + LLAppViewer::initLoggingAndGetLastDuration(); } bool LLAppViewerLinux::initParseCommandLine(LLCommandLineParser& clp) diff --git a/indra/newview/llappviewerlinux.h b/indra/newview/llappviewerlinux.h index 6de13b81c..a751ad10b 100644 --- a/indra/newview/llappviewerlinux.h +++ b/indra/newview/llappviewerlinux.h @@ -66,7 +66,7 @@ protected: virtual bool restoreErrorTrap(); virtual void initCrashReporting(bool reportFreeze); - virtual bool initLogging(); + virtual void initLoggingAndGetLastDuration(); virtual bool initParseCommandLine(LLCommandLineParser& clp); virtual bool initSLURLHandler(); diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 300480634..d4eef2b2b 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -260,12 +260,6 @@ bool LLAppViewerMacOSX::restoreErrorTrap() return reset_count == 0; } -void LLAppViewerMacOSX::initCrashReporting(bool reportFreeze) -{ - // Singu Note: this is where original code forks crash logger process. - // Singularity doesn't need it -} - std::string LLAppViewerMacOSX::generateSerialNumber() { char serial_md5[MD5HEX_STR_SIZE]; // Flawfinder: ignore diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 9ca2feeb6..3dc530a57 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -32,12 +32,6 @@ #include "llviewerprecompiledheaders.h" -#if defined(_DEBUG) -# if _MSC_VER >= 1400 // Visual C++ 2005 or later -# define WINDOWS_CRT_MEM_CHECKS 1 -# endif -#endif - #include "llwindowwin32.h" // *FIX: for setting gIconResource. #include "llappviewerwin32.h" @@ -73,19 +67,6 @@ #include "llcommandlineparser.h" #include "lltrans.h" -// *FIX:Mani - This hack is to fix a linker issue with libndofdev.lib -// The lib was compiled under VS2005 - in VS2003 we need to remap assert -#ifdef LL_DEBUG -#ifdef LL_MSVC7 -extern "C" { - void _wassert(const wchar_t * _Message, const wchar_t *_File, unsigned _Line) - { - LL_ERRS() << _Message << LL_ENDL; - } -} -#endif -#endif - const std::string LLAppViewerWin32::sWindowClass = "Second Life"; // Create app mutex creates a unique global windows object. @@ -101,7 +82,7 @@ bool create_app_mutex() bool result = true; LPCWSTR unique_mutex_name = L"SecondLifeAppMutex"; HANDLE hMutex; - hMutex = CreateMutex(NULL, TRUE, unique_mutex_name); + hMutex = CreateMutex(nullptr, TRUE, unique_mutex_name); if(GetLastError() == ERROR_ALREADY_EXISTS) { result = false; @@ -240,7 +221,9 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(lpCmdLine); +#if !defined(USE_CRASHPAD) viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); +#endif // Set a debug info flag to indicate if multiple instances are running. bool found_other_instance = !create_app_mutex(); @@ -332,7 +315,7 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, } delete viewer_app_ptr; - viewer_app_ptr = NULL; + viewer_app_ptr = nullptr; //start updater if(LLAppViewer::sUpdaterInfo) @@ -392,13 +375,13 @@ void LLAppViewerWin32::disableWinErrorReporting() const S32 MAX_CONSOLE_LINES = 500; -void create_console() +static bool create_console() { CONSOLE_SCREEN_BUFFER_INFO coninfo; // allocate a console for this app - AllocConsole(); + const bool isConsoleAllocated = AllocConsole(); // set the screen buffer to be big enough to let us scroll text GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); @@ -410,16 +393,16 @@ void create_console() freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); - setvbuf( stdin, NULL, _IONBF, 0 ); - setvbuf( stdout, NULL, _IONBF, 0 ); - setvbuf( stderr, NULL, _IONBF, 0 ); - + setvbuf( stdin, nullptr, _IONBF, 0 ); + setvbuf( stdout, nullptr, _IONBF, 0 ); + setvbuf( stderr, nullptr, _IONBF, 0 ); + return isConsoleAllocated; } - LLAppViewerWin32::LLAppViewerWin32(const char* cmd_line) : - mCmdLine(cmd_line) + mCmdLine(cmd_line), + mIsConsoleAllocated(false) { } @@ -442,15 +425,6 @@ bool LLAppViewerWin32::init() 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; @@ -462,18 +436,24 @@ bool LLAppViewerWin32::cleanup() gDXHardware.cleanup(); + if (mIsConsoleAllocated) + { + FreeConsole(); + mIsConsoleAllocated = false; + } + return result; } -bool LLAppViewerWin32::initLogging() +void LLAppViewerWin32::initLoggingAndGetLastDuration() { - return LLAppViewer::initLogging(); + LLAppViewer::initLoggingAndGetLastDuration(); } void LLAppViewerWin32::initConsole() { // pop up debug console - create_console(); + mIsConsoleAllocated = create_console(); return LLAppViewer::initConsole(); } @@ -571,7 +551,7 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp) } // Find the system language. - FL_Locale *locale = NULL; + FL_Locale *locale = nullptr; FL_Success success = FL_FindLocale(&locale, FL_MESSAGES); if (success != 0) { @@ -595,12 +575,6 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp) bool LLAppViewerWin32::restoreErrorTrap() { return true; - //return LLWinDebug::checkExceptionHandler(); -} - -void LLAppViewerWin32::initCrashReporting(bool reportFreeze) -{ - // Singu Note: we don't fork the crash logger on start } //virtual @@ -610,9 +584,9 @@ bool LLAppViewerWin32::sendURLToOtherInstance(const std::string& url) mbstowcs(window_class, sWindowClass.c_str(), 255); window_class[255] = 0; // Use the class instead of the window name. - HWND other_window = FindWindow(window_class, NULL); + HWND other_window = FindWindow(window_class, nullptr); - if (other_window != NULL) + if (other_window != nullptr) { LL_DEBUGS() << "Found other window with the name '" << getWindowTitle() << "'" << LL_ENDL; COPYDATASTRUCT cds; @@ -638,13 +612,13 @@ std::string LLAppViewerWin32::generateSerialNumber() DWORD serial = 0; DWORD flags = 0; BOOL success = GetVolumeInformation( - L"C:\\", - NULL, // volume name buffer + TEXT("C:\\"), + nullptr, // volume name buffer 0, // volume name buffer size &serial, // volume serial - NULL, // max component length + nullptr, // max component length &flags, // file system flags - NULL, // file system name buffer + nullptr, // file system name buffer 0); // file system name buffer size if (success) { diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 757f6ea0c..96f834798 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -46,22 +46,20 @@ public: // // Main application logic // - virtual bool init(); // Override to do application initialization - virtual bool cleanup(); + bool init() override; // Override to do application initialization + bool cleanup() override; protected: - virtual bool initLogging(); // Override to clean stack_trace info. - virtual void initConsole(); // Initialize OS level debugging console. - virtual bool initHardwareTest(); // Win32 uses DX9 to test hardware. - virtual bool initParseCommandLine(LLCommandLineParser& clp); + void initLoggingAndGetLastDuration() override; // Override to clean stack_trace info. + void initConsole() override; // Initialize OS level debugging console. + bool initHardwareTest() override; // Win32 uses DX9 to test hardware. + bool initParseCommandLine(LLCommandLineParser& clp) override; - virtual bool restoreErrorTrap(); - virtual void initCrashReporting(bool reportFreeze); + bool restoreErrorTrap() override; - virtual bool sendURLToOtherInstance(const std::string& url); - - std::string generateSerialNumber(); + bool sendURLToOtherInstance(const std::string& url) override; + std::string generateSerialNumber() override; static const std::string sWindowClass; @@ -69,6 +67,7 @@ private: void disableWinErrorReporting(); std::string mCmdLine; + bool mIsConsoleAllocated; }; #endif // LL_LLAPPVIEWERWIN32_H diff --git a/indra/newview/llcrashlogger.cpp b/indra/newview/llcrashlogger.cpp deleted file mode 100644 index 6a6dfa861..000000000 --- a/indra/newview/llcrashlogger.cpp +++ /dev/null @@ -1,418 +0,0 @@ - /** -* @file llcrashlogger.cpp -* @brief Crash logger implementation -* -* $LicenseInfo:firstyear=2003&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 "llviewerprecompiledheaders.h" - -#include "llcrashlogger.h" -#include "linden_common.h" -#include "llstring.h" -#include "indra_constants.h" // CRASH_BEHAVIOR_... -#include "llerror.h" -#include "llerrorcontrol.h" -#include "lltimer.h" -#include "lldir.h" -#include "llfile.h" -#include "llsdserialize.h" -#include "lliopipe.h" -#include "llpumpio.h" -#include "llhttpclient.h" -#include "llsdserialize.h" -#include "llproxy.h" -#include "llwindow.h" -#include "lltrans.h" -#include "aistatemachine.h" -#include "boost/filesystem.hpp" - -class AIHTTPTimeoutPolicy; -extern AIHTTPTimeoutPolicy crashLoggerResponder_timeout; -extern const std::string OLD_LOG_FILE; - -class LLCrashLoggerResponder : public LLHTTPClient::ResponderWithResult -{ -public: - LLCrashLoggerResponder() - { - } - - virtual void httpFailure(void) - { - LL_WARNS() << "Crash report sending failed: " << mReason << LL_ENDL; - } - - virtual void httpSuccess(void) - { - std::string msg = "Crash report successfully sent"; - if (mContent.has("message")) - { - msg += ": " + mContent["message"].asString(); - } - LL_INFOS() << msg << LL_ENDL; - - if (mContent.has("report_id")) - { - gSavedSettings.setS32("CrashReportID", mContent["report_id"].asInteger()); - } - - } - - virtual AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const - { - return crashLoggerResponder_timeout; - } - - virtual char const* getName(void) const - { - return "LLCrashLoggerResponder"; - } -}; - -LLCrashLogger::LLCrashLogger() : - mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND), - mCrashInPreviousExec(false), - mCrashHost("") -{ -} - -LLCrashLogger::~LLCrashLogger() -{ - -} - -// TRIM_SIZE must remain larger than LINE_SEARCH_SIZE. -const int TRIM_SIZE = 128000; -const int LINE_SEARCH_DIST = 500; -const std::string SKIP_TEXT = "\n ...Skipping... \n"; -void trimSLLog(std::string& sllog) -{ - if(sllog.length() > TRIM_SIZE * 2) - { - std::string::iterator head = sllog.begin() + TRIM_SIZE; - std::string::iterator tail = sllog.begin() + sllog.length() - TRIM_SIZE; - std::string::iterator new_head = std::find(head, head - LINE_SEARCH_DIST, '\n'); - if(new_head != head - LINE_SEARCH_DIST) - { - head = new_head; - } - - std::string::iterator new_tail = std::find(tail, tail + LINE_SEARCH_DIST, '\n'); - if(new_tail != tail + LINE_SEARCH_DIST) - { - tail = new_tail; - } - - sllog.erase(head, tail); - sllog.insert(head, SKIP_TEXT.begin(), SKIP_TEXT.end()); - } -} - -std::string getStartupStateFromLog(std::string& sllog) -{ - std::string startup_state = "STATE_FIRST"; - std::string startup_token = "Startup state changing from "; - - int index = sllog.rfind(startup_token); - if (index < 0 || index + startup_token.length() > sllog.length()) { - return startup_state; - } - - // find new line - char cur_char = sllog[index + startup_token.length()]; - std::string::size_type newline_loc = index + startup_token.length(); - while(cur_char != '\n' && newline_loc < sllog.length()) - { - newline_loc++; - cur_char = sllog[newline_loc]; - } - - // get substring and find location of " to " - std::string state_line = sllog.substr(index, newline_loc - index); - std::string::size_type state_index = state_line.find(" to "); - startup_state = state_line.substr(state_index + 4, state_line.length() - state_index - 4); - - return startup_state; -} - -bool miniDumpExists(const std::string& dumpDir) -{ - bool found = false; - - try - { - if (!boost::filesystem::exists(dumpDir)) - { - return false; - } - - boost::filesystem::directory_iterator end_itr; - for (boost::filesystem::directory_iterator i(dumpDir); i != end_itr; ++i) - { - if (!boost::filesystem::is_regular_file(i->status())) continue; - if (".dmp" == i->path().extension()) - { - found = true; - break; - } - } - } - catch (const boost::filesystem::filesystem_error& e) - { - LL_WARNS() << "Failed to determine existance of the minidump file: '" + e.code().message() +"'" << LL_ENDL; - } - - return found; -} - -bool LLCrashLogger::readDebugFromXML(LLSD& dest, const std::string& filename ) -{ - std::string db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,filename); - std::ifstream debug_log_file(db_file_name.c_str()); - - // Look for it in the debug_info.log file - if (debug_log_file.is_open()) - { - LLSDSerialize::fromXML(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(&(data[0])),length); - minidump_stream.close(); - - mCrashInfo["Minidump"] = data; - } - return (length>0?true:false); -} - -void LLCrashLogger::gatherFiles() -{ - LL_INFOS() << "Gathering logs..." << LL_ENDL; - - LLSD static_sd; - LLSD dynamic_sd; - - bool has_logs = readDebugFromXML( static_sd, "static_debug_info.log" ); - has_logs |= readDebugFromXML( dynamic_sd, "dynamic_debug_info.log" ); - - if ( has_logs ) - { - mDebugLog = static_sd; - mergeLogs(dynamic_sd); - mCrashInPreviousExec = mDebugLog["CrashNotHandled"].asBoolean(); - - mFileMap["SecondLifeLog"] = mDebugLog["SLLog"].asString(); - mFileMap["SettingsXml"] = mDebugLog["SettingsFilename"].asString(); - if(mDebugLog.has("CAFilename")) - { - LLCurl::setCAFile(mDebugLog["CAFilename"].asString()); - } - else - { - LLCurl::setCAFile(gDirUtilp->getCAFile()); - } - - LL_INFOS() << "Using log file from debug log " << mFileMap["SecondLifeLog"] << LL_ENDL; - LL_INFOS() << "Using settings file from debug log " << mFileMap["SettingsXml"] << LL_ENDL; - } - else - { - // Figure out the filename of the second life log - LLCurl::setCAFile(gDirUtilp->getCAFile()); - mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log"); - mFileMap["SettingsXml"] = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml"); - } - - if(mCrashInPreviousExec) - { - // Restarting after freeze. - // Replace the log file ext with .old, since the - // instance that launched this process has overwritten - // SecondLife.log - std::string log_filename = mFileMap["SecondLifeLog"]; - log_filename.replace(log_filename.size() - 4, 4, ".old"); - mFileMap["SecondLifeLog"] = log_filename; - } - - gatherPlatformSpecificFiles(); - - mCrashInfo["DebugLog"] = mDebugLog; - mFileMap["StatsLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"stats.log"); - // Singu Note: we have just started again, log has been renamed - mFileMap["SecondLifeLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, OLD_LOG_FILE); - - LL_INFOS() << "Encoding files..." << LL_ENDL; - - for(std::map::iterator itr = mFileMap.begin(); itr != mFileMap.end(); ++itr) - { - std::ifstream f((*itr).second.c_str()); - if(!f.is_open()) - { - LL_INFOS() << "Can't find file " << (*itr).second << LL_ENDL; - continue; - } - std::stringstream s; - s << f.rdbuf(); - - std::string crash_info = s.str(); - if(itr->first == "SecondLifeLog") - { - if(!mCrashInfo["DebugLog"].has("StartupState")) - { - mCrashInfo["DebugLog"]["StartupState"] = getStartupStateFromLog(crash_info); - } - trimSLLog(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 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() -{ - return mCrashInfo; -} - - -bool LLCrashLogger::sendCrashLog(std::string dump_dir) -{ - gDirUtilp->setDumpDir( dump_dir ); - - std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SingularityCrashReport"); - std::string report_file = dump_path + ".log"; - - gatherFiles(); - - LLSD post_data; - post_data = constructPostData(); - - LL_INFOS() << "Sending reports..." << LL_ENDL; - - std::ofstream out_file(report_file.c_str()); - LLSDSerialize::toPrettyXML(post_data, out_file); - out_file.close(); - - LLHTTPClient::post(mCrashHost, post_data, new LLCrashLoggerResponder()); - - return true; -} - - -void LLCrashLogger::checkCrashDump() -{ -#if LL_SEND_CRASH_REPORTS - // 0 - ask, 1 - always send, 2 - never send - S32 pref = gSavedSettings.getS32("CrashSubmitBehavior"); - if (pref == 2) return; //never send - - mCrashHost = gSavedSettings.getString("CrashHostUrl"); - std::string dumpDir = gDirUtilp->getDumpDir(); - - // Do we have something to send, and somewhere to send it - if (!mCrashHost.empty() && miniDumpExists(dumpDir)) - { - if (pref == 1) // always send - { - sendCrashLog(dumpDir); - } - else // ask - { - U32 response = OSMessageBox(LLTrans::getString("MBFrozenCrashed"), LLTrans::getString("MBAlert"), OSMB_YESNO); - if (response == OSBTN_YES) - { - sendCrashLog(dumpDir); - } - } - } -#endif -} diff --git a/indra/newview/llcrashlogger.h b/indra/newview/llcrashlogger.h deleted file mode 100644 index c510acb98..000000000 --- a/indra/newview/llcrashlogger.h +++ /dev/null @@ -1,67 +0,0 @@ -/** -* @file llcrashlogger.h -* @brief Crash Logger Definition -* -* $LicenseInfo:firstyear=2003&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 LLCRASHLOGGER_H -#define LLCRASHLOGGER_H - -#include - -#include "linden_common.h" - -#include "llapp.h" -#include "llsd.h" -#include "llcontrol.h" - -class LLCrashLogger -{ -public: - LLCrashLogger(); - virtual ~LLCrashLogger(); - S32 loadCrashBehaviorSetting(); - bool readDebugFromXML(LLSD& dest, const std::string& filename ); - void gatherFiles(); - void mergeLogs( LLSD src_sd ); - - virtual void gatherPlatformSpecificFiles() {} - bool saveCrashBehaviorSetting(S32 crash_behavior); - bool sendCrashLog(std::string dump_dir); - LLSD constructPostData(); - void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; } - S32 getCrashBehavior() { return mCrashBehavior; } - bool readMinidump(std::string minidump_path); - void checkCrashDump(); - -protected: - S32 mCrashBehavior; - BOOL mCrashInPreviousExec; - std::map mFileMap; - std::string mGridName; - std::string mProductName; - LLSD mCrashInfo; - std::string mCrashHost; - LLSD mDebugLog; -}; - -#endif //LLCRASHLOGGER_H diff --git a/indra/newview/llpanelgeneral.cpp b/indra/newview/llpanelgeneral.cpp index d794e43da..30a0e6430 100644 --- a/indra/newview/llpanelgeneral.cpp +++ b/indra/newview/llpanelgeneral.cpp @@ -87,7 +87,7 @@ BOOL LLPanelGeneral::postBuild() childSetValue("ui_auto_scale", gSavedSettings.getBOOL("UIAutoScale")); LLComboBox* crash_behavior_combobox = getChild("crash_behavior_combobox"); - crash_behavior_combobox->setCurrentByIndex(gSavedSettings.getS32(CRASH_BEHAVIOR_SETTING)); + crash_behavior_combobox->setValue(gSavedSettings.getS32("CrashSubmitBehavior")); childSetValue("language_combobox", gSavedSettings.getString("Language")); @@ -163,7 +163,7 @@ void LLPanelGeneral::apply() gSavedSettings.setString("Language", childGetValue("language_combobox")); LLComboBox* crash_behavior_combobox = getChild("crash_behavior_combobox"); - gSavedSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior_combobox->getCurrentIndex()); + gSavedSettings.setS32("CrashSubmitBehavior", crash_behavior_combobox->getValue()); } void LLPanelGeneral::cancel() diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index f7f8d22cb..1e4e210f2 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -94,7 +94,6 @@ AIThreadSafeDC gSettings; LLControlGroup gSavedSettings("Global"); // saved at end of session LLControlGroup gSavedPerAccountSettings("PerAccount"); // saved at end of session LLControlGroup gColors("Colors"); // saved at end of session -LLControlGroup gCrashSettings("CrashSettings"); // saved at end of session std::string gLastRunVersion; diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h index 0fd6b9cd4..203c5cfb3 100644 --- a/indra/newview/llviewercontrol.h +++ b/indra/newview/llviewercontrol.h @@ -62,9 +62,6 @@ void create_graphics_group(LLControlGroup& group); // Read-only extern LLControlGroup gColors; -// Saved at end of session -extern LLControlGroup gCrashSettings; - // Set after settings loaded extern std::string gLastRunVersion; diff --git a/indra/newview/skins/default/xui/en-us/notifications.xml b/indra/newview/skins/default/xui/en-us/notifications.xml index 534d3eaa9..d9128f46c 100644 --- a/indra/newview/skins/default/xui/en-us/notifications.xml +++ b/indra/newview/skins/default/xui/en-us/notifications.xml @@ -11450,6 +11450,19 @@ Do you wish to export anyway? Object successfully exported to: [FILENAME] + +Would you like to help improve [SHORT_APP_NAME] by sending crash reports? + +(You can change your answer in Preferences -> General -> Crash reports) + + + <Type region name> - Ask before sending - Always send - Never send + Never send + Always send