From 4ea810cae1feb7799de421d00d1c8205661b00f9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 12 Nov 2025 18:21:00 +0200 Subject: [PATCH 1/2] #4939 Bugsplat update to version 1.2.6 --- autobuild.xml | 8 +- indra/cmake/bugsplat.cmake | 6 ++ indra/llwindow/llappdelegate-objc.h | 11 +-- indra/newview/llappdelegate-objc.mm | 126 ++++++++++++++++------------ indra/newview/llappviewer.cpp | 4 +- indra/newview/viewer_manifest.py | 25 +++++- 6 files changed, 115 insertions(+), 65 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index dbbe1981b61..d58a785b6b6 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -166,11 +166,11 @@ archive hash - cd1f1d55a2488657ec2253774b3a414621f81b24 + 9bcafdd7e1df6b92096fa850627d4f3437630d4a hash_algorithm sha1 url - https://github.com/secondlife/3p-bugsplat/releases/download/v1.1.5-71fc41e/bugsplat-1.1.1-9599607655-darwin64-9599607655.tar.zst + https://github.com/secondlife/3p-bugsplat/releases/download/v1.2.6-a475cbb/bugsplat-1.2.6-19430122611-darwin64-19430122611.tar.zst name darwin64 @@ -180,11 +180,11 @@ archive hash - 9fb0615d17988bd89a2e5ae6d4d19e150afb54a9 + 4e9f0c1cdbc1cebf6185ecc45228ced7f9af1532 hash_algorithm sha1 url - https://github.com/secondlife/3p-bugsplat/releases/download/v1.1.5-71fc41e/bugsplat-5.0.1.0-9599607655-windows64-9599607655.tar.zst + https://github.com/secondlife/3p-bugsplat/releases/download/v1.2.6-a475cbb/bugsplat-6.1.1.0-19430122611-windows64-19430122611.tar.zst name windows64 diff --git a/indra/cmake/bugsplat.cmake b/indra/cmake/bugsplat.cmake index 509981d72cd..d2a8fcca46a 100644 --- a/indra/cmake/bugsplat.cmake +++ b/indra/cmake/bugsplat.cmake @@ -23,8 +23,14 @@ if (USE_BUGSPLAT) elseif (DARWIN) find_library(BUGSPLAT_LIBRARIES BugsplatMac REQUIRED NO_DEFAULT_PATH PATHS "${ARCH_PREBUILT_DIRS_RELEASE}") + find_library(CRASHREPORTED_LIBRARIES CrashReporter REQUIRED + NO_DEFAULT_PATH PATHS "${ARCH_PREBUILT_DIRS_RELEASE}") + find_library(HOCKEYSDK_LIBRARIES HockeySDK REQUIRED + NO_DEFAULT_PATH PATHS "${ARCH_PREBUILT_DIRS_RELEASE}") target_link_libraries( ll::bugsplat INTERFACE ${BUGSPLAT_LIBRARIES} + ${CRASHREPORTED_LIBRARIES} + ${HOCKEYSDK_LIBRARIES} ) else (WINDOWS) message(FATAL_ERROR "BugSplat is not supported; add -DUSE_BUGSPLAT=OFF") diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h index ef36f7d4a85..5b6e11e4e4b 100644 --- a/indra/llwindow/llappdelegate-objc.h +++ b/indra/llwindow/llappdelegate-objc.h @@ -36,16 +36,17 @@ std::string secondLogPath; } -@property (assign) IBOutlet LLNSWindow *window; -@property (assign) IBOutlet NSWindow *inputWindow; -@property (assign) IBOutlet LLNonInlineTextView *inputView; +@property (assign) IBOutlet LLNSWindow * _Nullable window; +@property (assign) IBOutlet NSWindow * _Nullable inputWindow; +@property (assign) IBOutlet LLNonInlineTextView * _Nullable inputView; -@property (retain) NSString *currentInputLanguage; +@property (retain) NSString * _Nullable currentInputLanguage; - (void) oneFrame; -- (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent; +- (void) showInputWindow:(bool)show withEvent:(nullable NSEvent *)textEvent; - (void) languageUpdated; - (bool) romanScript; +- (void) setBugsplatValue:(nullable NSString *)value forAttribute:(nullable NSString *)attribute; @end @interface LLApplication : NSApplication diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index af18dca1854..a5b8ddd25cf 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -28,9 +28,11 @@ #if defined(LL_BUGSPLAT) #include #include -@import BugsplatMac; +@import CrashReporter; +@import HockeySDK; +@import BugSplatMac; // derived from BugsplatMac's BugsplatTester/AppDelegate.m -@interface LLAppDelegate () +@interface LLAppDelegate () @end #endif #include "llwindowmacosx-objc.h" @@ -68,13 +70,22 @@ - (void) applicationDidFinishLaunching:(NSNotification *)notification #if defined(LL_BUGSPLAT) infos("bugsplat setup"); - // Engage BugsplatStartupManager *before* calling initViewer() to handle + // Engage BugSplat *before* calling initViewer() to handle // any crashes during initialization. // https://www.bugsplat.com/docs/platforms/os-x#initialization - [BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES; - [BugsplatStartupManager sharedManager].askUserDetails = NO; - [BugsplatStartupManager sharedManager].delegate = self; - [[BugsplatStartupManager sharedManager] start]; + + // Initialize BugSplat + [[BugSplat shared] setDelegate:self]; + [[BugSplat shared] setAutoSubmitCrashReport:YES]; + [[BugSplat shared] setPersistUserDetails:NO]; + [[BugSplat shared] setAskUserDetails:NO]; + [BugSplat shared].expirationTimeInterval = 0; + [[BugSplat shared] start]; + + // Optionally, add some attributes to your crash reports. + // Attributes are artibrary key/value pairs that are searchable in the BugSplat dashboard. + // [[BugSplat shared] setValue:@"Value of Plain Attribute" forAttribute:@"PlainAttribute"]; + #endif infos("post-bugsplat setup"); @@ -213,9 +224,52 @@ - (bool) romanScript return true; } +- (void) setBugsplatValue:(nullable NSString *)value forAttribute:(NSString *)attribute +{ + //[[BugSplat shared] setValue:@"Value of not so plain Attribute" forAttribute:@"NotSoPlainAttribute"]; + [[BugSplat shared] setValue:value forAttribute:attribute]; +} + #if defined(LL_BUGSPLAT) -- (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +- (void)bugSplatWillSendCrashReport:(BugSplat *)bugSplat +{ + infos("bugSplatWillSendCrashReport"); +} + +- (void)bugSplatWillSendCrashReportsAlways:(BugSplat *)bugSplat +{ + infos("bugSplatWillSendCrashReportsAlways"); +} + +- (void)bugSplatDidFinishSendingCrashReport:(BugSplat *)bugSplat +{ + infos("bugSplatDidFinishSendingCrashReport"); + + if(!secondLogPath.empty()) + { + boost::filesystem::remove(secondLogPath); + } + clearDumpLogsDir(); +} + +- (void)bugSplatWillCancelSendingCrashReport:(BugSplat *)bugSplat +{ + infos("bugSplatWillCancelSendingCrashReport"); +} + +- (void)bugSplatWillShowSubmitCrashReportAlert:(BugSplat *)bugSplat +{ + infos("bugSplatWillShowSubmitCrashReportAlert"); +} + +- (void)bugSplat:(BugSplat *)bugSplat didFailWithError:(NSError *)error +{ + std::string error_str([[error localizedDescription] UTF8String]); + infos("bugSplat:didFailWithError: " + error_str); +} + +- (NSString *)applicationLogForBugSplat:(BugSplat *)bugSplat; { CrashMetadata& meta(CrashMetadata_instance()); // As of BugsplatMac 1.0.6, userName and userEmail properties are now @@ -226,16 +280,21 @@ - (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *) // report we are about to send. infos("applicationLogForBugsplatStartupManager setting userName = '" + meta.agentFullname + '"'); - bugsplatStartupManager.userName = + bugSplat.userName = [NSString stringWithCString:meta.agentFullname.c_str() encoding:NSUTF8StringEncoding]; // Use the email field for OS version, just as we do on Windows, until // BugSplat provides more metadata fields. infos("applicationLogForBugsplatStartupManager setting userEmail = '" + meta.OSInfo + '"'); - bugsplatStartupManager.userEmail = + bugSplat.userEmail = [NSString stringWithCString:meta.OSInfo.c_str() encoding:NSUTF8StringEncoding]; + + //bugSplat.userID = + // [NSString stringWithCString:meta.regionName.c_str() + // encoding:NSUTF8StringEncoding]; + // This strangely-named override method's return value contributes the // User Description metadata field. infos("applicationLogForBugsplatStartupManager -> '" + meta.fatalMessage + "'"); @@ -243,7 +302,8 @@ - (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *) encoding:NSUTF8StringEncoding]; } -- (NSString *)applicationKeyForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason { +- (NSString *)applicationKeyForBugSplat:(BugSplat *)bugSplat signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason +{ // TODO: exceptionName, exceptionReason // Windows sends location within region as well, but that's because @@ -258,27 +318,6 @@ - (NSString *)applicationKeyForBugsplatStartupManager:(BugsplatStartupManager *) encoding:NSUTF8StringEncoding]; } -- (NSString *)defaultUserNameForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { - std::string agentFullname(CrashMetadata_instance().agentFullname); - infos("defaultUserNameForBugsplatStartupManager -> '" + agentFullname + "'"); - return [NSString stringWithCString:agentFullname.c_str() - encoding:NSUTF8StringEncoding]; -} - -- (NSString *)defaultUserEmailForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager { - // Use the email field for OS version, just as we do on Windows, until - // BugSplat provides more metadata fields. - std::string OSInfo(CrashMetadata_instance().OSInfo); - infos("defaultUserEmailForBugsplatStartupManager -> '" + OSInfo + "'"); - return [NSString stringWithCString:OSInfo.c_str() - encoding:NSUTF8StringEncoding]; -} - -- (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugsplatStartupManager -{ - infos("bugsplatStartupManagerWillSendCrashReport"); -} - struct AttachmentInfo { AttachmentInfo(const std::string& path, const std::string& type): @@ -290,7 +329,7 @@ - (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugs std::string pathname, basename, mimetype; }; -- (NSArray *)attachmentsForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +- (NSArray *)attachmentsForBugSplat:(BugSplat *)bugSplat { const CrashMetadata& metadata(CrashMetadata_instance()); @@ -334,8 +373,8 @@ - (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugs encoding:NSUTF8StringEncoding]; NSData *nsdata = [NSData dataWithContentsOfFile:nspathname]; - BugsplatAttachment *attachment = - [[BugsplatAttachment alloc] initWithFilename:nsbasename + BugSplatAttachment *attachment = + [[BugSplatAttachment alloc] initWithFilename:nsbasename attachmentData:nsdata contentType:nsmimetype]; @@ -346,23 +385,6 @@ - (void)bugsplatStartupManagerWillSendCrashReport:(BugsplatStartupManager *)bugs return attachments; } -- (void)bugsplatStartupManagerDidFinishSendingCrashReport:(BugsplatStartupManager *)bugsplatStartupManager -{ - infos("Sent crash report to BugSplat"); - - if(!secondLogPath.empty()) - { - boost::filesystem::remove(secondLogPath); - } - clearDumpLogsDir(); -} - -- (void)bugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager didFailWithError:(NSError *)error -{ - // TODO: message string from NSError - infos("Could not send crash report to BugSplat"); -} - #endif // LL_BUGSPLAT @end diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7ed65e7027c..9bc5b57695d 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3625,7 +3625,9 @@ void LLAppViewer::writeSystemInfo() #if LL_WINDOWS && !LL_BUGSPLAT gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); #else - //Not ideal but sufficient for good reporting. + // Far from ideal, especially when multiple instances get involved. + // Note that attachmentsForBugSplat expects .old extendion. + // Todo: improve. gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); //LLError::logFileName(); #endif diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 0e2c9d177e3..109f00c9ae6 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -861,13 +861,12 @@ def construct(self): with self.prefix(src="", dst="Contents"): # everything goes in Contents bugsplat_db = self.args.get('bugsplat') if bugsplat_db: - # Inject BugsplatServerURL into Info.plist if provided. + # Inject Bugsplat's db into Info.plist if provided. Info_plist = self.dst_path_of("Info.plist") with open(Info_plist, 'rb') as f: Info = plistlib.load(f) # https://www.bugsplat.com/docs/platforms/os-x#configuration - Info["BugsplatServerURL"] = \ - "https://{}.bugsplat.com/".format(bugsplat_db) + Info["BugSplatDatabase"] = bugsplat_db self.put_in_file( plistlib.dumps(Info), os.path.basename(Info_plist), @@ -881,6 +880,8 @@ def construct(self): if self.args.get('bugsplat'): self.path2basename(relpkgdir, "BugsplatMac.framework") + self.path2basename(relpkgdir, "CrashReporter.framework") + self.path2basename(relpkgdir, "HockeySDK.framework") # OpenAL dylibs if self.args['openal'] == 'ON': @@ -918,6 +919,24 @@ def construct(self): # stamped into the framework. # Let exception, if any, propagate -- if this doesn't # work, we need the build to noisily fail! + oldpath = subprocess.check_output( + ['objdump', '--macho', '--dylib-id', '--non-verbose', + os.path.join(relpkgdir, "HockeySDK.framework", "HockeySDK")], + text=True + ).splitlines()[-1] # take the last line of output + self.run_command( + ['install_name_tool', '-change', oldpath, + '@executable_path/../Frameworks/HockeySDK.framework/HockeySDK', + executable]) + oldpath = subprocess.check_output( + ['objdump', '--macho', '--dylib-id', '--non-verbose', + os.path.join(relpkgdir, "CrashReporter.framework", "CrashReporter")], + text=True + ).splitlines()[-1] # take the last line of output + self.run_command( + ['install_name_tool', '-change', oldpath, + '@executable_path/../Frameworks/CrashReporter.framework/CrashReporter', + executable]) oldpath = subprocess.check_output( ['objdump', '--macho', '--dylib-id', '--non-verbose', os.path.join(relpkgdir, "BugsplatMac.framework", "BugsplatMac")], From e78f99806e3c73c64483dd1dbb1f33cd93b0e63c Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 17 Nov 2025 20:30:37 +0200 Subject: [PATCH 2/2] #4939 Bugsplat often confuses log files on mac, try to make it more reliable --- indra/newview/llappdelegate-objc.mm | 4 ++-- indra/newview/llappviewer.cpp | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index a5b8ddd25cf..11761dbaae8 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -350,12 +350,12 @@ - (NSString *)applicationKeyForBugSplat:(BugSplat *)bugSplat signal:(NSString *) info.push_back(AttachmentInfo(secondLogPath, "text/xml")); } - // We "happen to know" that info[0].basename is "SecondLife.old" -- due to + // We "happen to know" that info[0].basename is "SecondLife.crash" -- due to // the fact that BugsplatMac only notices a crash during the viewer run // following the crash. // The Bugsplat service doesn't respect the MIME type above when returning // the log data to a browser, so take this opportunity to rename the file - // from .old to _log.txt + // from .crash to _log.txt info[0].basename = boost::filesystem::path(info[0].pathname).stem().string() + "_log.txt"; infos("attachmentsForBugsplatStartupManager attaching log " + info[0].basename); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9bc5b57695d..9b16cbd3bf9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3622,7 +3622,10 @@ void LLAppViewer::writeSystemInfo() if (! gDebugInfo.has("Dynamic") ) gDebugInfo["Dynamic"] = LLSD::emptyMap(); -#if LL_WINDOWS && !LL_BUGSPLAT +#if LL_DARWIN + // crash processing in CrashMetadataSingleton reads SLLog + gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.crash"); +#elif LL_WINDOWS && !LL_BUGSPLAT gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); #else // Far from ideal, especially when multiple instances get involved. @@ -4034,6 +4037,22 @@ void LLAppViewer::processMarkerFiles() } LLAPRFile::remove(error_marker_file); } + +#if LL_DARWIN + if (!mSecondInstance && gLastExecEvent != LAST_EXEC_NORMAL) + { + // While windows reports crashes immediately, mac reports next run and + // may take a while to trigger crash report so it has a special file. + // Remove .crash file if exists + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.old"); + std::string crash_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.crash"); + LLFile::remove(crash_log_file); + // Rename ".old" log file to ".crash" + LLFile::rename(old_log_file, crash_log_file); + } +#endif } void LLAppViewer::removeMarkerFiles()