diff options
Diffstat (limited to 'indra/newview')
-rw-r--r-- | indra/newview/Info-SecondLife.plist | 2 | ||||
-rw-r--r-- | indra/newview/llappdelegate-objc.mm | 162 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 36 | ||||
-rw-r--r-- | indra/newview/llappviewermacosx-for-objc.h | 53 | ||||
-rw-r--r-- | indra/newview/llappviewermacosx.cpp | 88 | ||||
-rw-r--r-- | indra/newview/llappviewerwin32.cpp | 155 | ||||
-rw-r--r-- | indra/newview/llversioninfo.cpp | 7 | ||||
-rw-r--r-- | indra/newview/llviewerregion.cpp | 19 | ||||
-rw-r--r-- | indra/newview/tests/llversioninfo_test.cpp | 6 |
9 files changed, 495 insertions, 33 deletions
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 31b4201f47..9482f07524 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -21,7 +21,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm index aebae4c434..1d55537427 100644 --- a/indra/newview/llappdelegate-objc.mm +++ b/indra/newview/llappdelegate-objc.mm @@ -25,7 +25,16 @@ */ #import "llappdelegate-objc.h" +#if defined(LL_BUGSPLAT) +#include <boost/filesystem.hpp> +#include <vector> +@import BugsplatMac; +// derived from BugsplatMac's BugsplatTester/AppDelegate.m +@interface LLAppDelegate () <BugsplatStartupManagerDelegate> +@end +#endif #include "llwindowmacosx-objc.h" +#include "llappviewermacosx-for-objc.h" #include <Carbon/Carbon.h> // Used for Text Input Services ("Safe" API - it's supported) @implementation LLAppDelegate @@ -47,6 +56,25 @@ - (void) applicationDidFinishLaunching:(NSNotification *)notification { + // Call constructViewer() first so our logging subsystem is in place. This + // risks missing crashes in the LLAppViewerMacOSX constructor, but for + // present purposes it's more important to get the startup sequence + // properly logged. + // Someday I would like to modify the logging system so that calls before + // it's initialized are cached in a std::ostringstream and then, once it's + // initialized, "played back" into whatever handlers have been set up. + constructViewer(); + +#if defined(LL_BUGSPLAT) + // Engage BugsplatStartupManager *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]; +#endif + frameTimer = nil; [self languageUpdated]; @@ -179,4 +207,138 @@ return true; } +#if defined(LL_BUGSPLAT) + +- (NSString *)applicationLogForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +{ + CrashMetadata& meta(CrashMetadata_instance()); + // As of BugsplatMac 1.0.6, userName and userEmail properties are now + // exposed by the BugsplatStartupManager. Set them here, since the + // defaultUserNameForBugsplatStartupManager and + // defaultUserEmailForBugsplatStartupManager methods are called later, for + // the *current* run, rather than for the previous crashed run whose crash + // report we are about to send. + infos("applicationLogForBugsplatStartupManager setting userName = '" + + meta.agentFullname + '"'); + bugsplatStartupManager.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 = + [NSString stringWithCString:meta.OSInfo.c_str() + encoding:NSUTF8StringEncoding]; + // This strangely-named override method's return value contributes the + // User Description metadata field. + infos("applicationLogForBugsplatStartupManager -> '" + meta.fatalMessage + "'"); + return [NSString stringWithCString:meta.fatalMessage.c_str() + encoding:NSUTF8StringEncoding]; +} + +- (NSString *)applicationKeyForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager signal:(NSString *)signal exceptionName:(NSString *)exceptionName exceptionReason:(NSString *)exceptionReason { + // TODO: exceptionName, exceptionReason + + // Windows sends location within region as well, but that's because + // BugSplat for Windows intercepts crashes during the same run, and that + // information can be queried once. On the Mac, any metadata we have is + // written (and rewritten) to the static_debug_info.log file that we read + // at the start of the next viewer run. It seems ridiculously expensive to + // rewrite that file on every frame in which the avatar moves. + std::string regionName(CrashMetadata_instance().regionName); + infos("applicationKeyForBugsplatStartupManager -> '" + regionName + "'"); + return [NSString stringWithCString:regionName.c_str() + 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): + pathname(path), + basename(boost::filesystem::path(path).filename().string()), + mimetype(type) + {} + + std::string pathname, basename, mimetype; +}; + +- (NSArray<BugsplatAttachment *> *)attachmentsForBugsplatStartupManager:(BugsplatStartupManager *)bugsplatStartupManager +{ + const CrashMetadata& metadata(CrashMetadata_instance()); + + // Since we must do very similar processing for each of several file + // pathnames, start by collecting them into a vector so we can iterate + // instead of spelling out the logic for each. + std::vector<AttachmentInfo> info{ + AttachmentInfo(metadata.logFilePathname, "text/plain"), + AttachmentInfo(metadata.userSettingsPathname, "text/xml"), + AttachmentInfo(metadata.staticDebugPathname, "text/xml") + }; + + // We "happen to know" that info[0].basename is "SecondLife.old" -- due to + // the fact that BugsplatMac only notices a crash during the viewer run + // following the crash. Replace .old with .log to reduce confusion. + info[0].basename = + boost::filesystem::path(info[0].pathname).stem().string() + ".log"; + + NSMutableArray *attachments = [[NSMutableArray alloc] init]; + + // Iterate over each AttachmentInfo in info vector + for (const AttachmentInfo& attach : info) + { + NSString *nspathname = [NSString stringWithCString:attach.pathname.c_str() + encoding:NSUTF8StringEncoding]; + NSString *nsbasename = [NSString stringWithCString:attach.basename.c_str() + encoding:NSUTF8StringEncoding]; + NSString *nsmimetype = [NSString stringWithCString:attach.mimetype.c_str() + encoding:NSUTF8StringEncoding]; + NSData *nsdata = [NSData dataWithContentsOfFile:nspathname]; + + BugsplatAttachment *attachment = + [[BugsplatAttachment alloc] initWithFilename:nsbasename + attachmentData:nsdata + contentType:nsmimetype]; + + [attachments addObject:attachment]; + infos("attachmentsForBugsplatStartupManager attaching " + attach.pathname); + } + + return attachments; +} + +- (void)bugsplatStartupManagerDidFinishSendingCrashReport:(BugsplatStartupManager *)bugsplatStartupManager +{ + infos("Sent crash report to BugSplat"); +} + +- (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 bc4ce19f77..56e79bed74 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -707,6 +707,22 @@ LLAppViewer::LLAppViewer() // LLLoginInstance::instance().setPlatformInfo(gPlatform, LLOSInfo::instance().getOSVersionString(), LLOSInfo::instance().getOSStringSimple()); + + // Under some circumstances we want to read the static_debug_info.log file + // from the previous viewer run between this constructor call and the + // init() call, which will overwrite the static_debug_info.log file for + // THIS run. So setDebugFileNames() early. +#if LL_BUGSPLAT + // MAINT-8917: don't create a dump directory just for the + // static_debug_info.log file + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); +#else // ! LL_BUGSPLAT + // write Google Breakpad minidump files to a per-run dump directory to avoid multiple viewer issues. + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, ""); +#endif // ! LL_BUGSPLAT + mDumpPath = logdir; + setMiniDumpDir(logdir); + setDebugFileNames(logdir); } LLAppViewer::~LLAppViewer() @@ -781,13 +797,6 @@ bool LLAppViewer::init() initMaxHeapSize() ; LLCoros::instance().setStackSize(gSavedSettings.getS32("CoroutineStackSize")); - // 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); - // Although initLoggingAndGetLastDuration() is the right place to mess with // setFatalFunction(), we can't query gSavedSettings until after @@ -2182,6 +2191,12 @@ 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); } @@ -3049,14 +3064,11 @@ void LLAppViewer::writeDebugInfo(bool isStatic) ? getStaticDebugFile() : getDynamicDebugFile() ); - LL_INFOS() << "Opening debug file " << *debug_filename << LL_ENDL; - llofstream out_file(debug_filename->c_str()); + 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(); } LLSD LLAppViewer::getViewerInfo() const diff --git a/indra/newview/llappviewermacosx-for-objc.h b/indra/newview/llappviewermacosx-for-objc.h new file mode 100644 index 0000000000..37e8a3917a --- /dev/null +++ b/indra/newview/llappviewermacosx-for-objc.h @@ -0,0 +1,53 @@ +/** + * @file llappviewermacosx-for-objc.h + * @author Nat Goodspeed + * @date 2018-06-15 + * @brief llappviewermacosx.h publishes the C++ API for + * llappviewermacosx.cpp, just as + * llappviewermacosx-objc.h publishes the Objective-C++ API for + * llappviewermacosx-objc.mm. + * + * This header is intended to publish for Objective-C++ consumers a + * subset of the C++ API presented by llappviewermacosx.cpp. It's a + * subset because, if an Objective-C++ consumer were to #include + * the full llappviewermacosx.h, we would almost surely run into + * trouble due to the discrepancy between Objective-C++'s BOOL versus + * classic Microsoft/Linden BOOL. + * + * $LicenseInfo:firstyear=2018&license=viewerlgpl$ + * Copyright (c) 2018, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) +#define LL_LLAPPVIEWERMACOSX_FOR_OBJC_H + +#include <string> + +void constructViewer(); +bool initViewer(); +void handleUrl(const char* url_utf8); +bool pumpMainLoop(); +void handleQuit(); +void cleanupViewer(); +void infos(const std::string& message); + +// This struct is malleable; it only serves as a way to convey a number of +// fields from llappviewermacosx.cpp's CrashMetadata_instance() function to the +// consuming functions in llappdelegate-objc.mm. As long as both those sources +// are compiled with this same header, the content and order of CrashMetadata +// can change as needed. +struct CrashMetadata +{ + std::string logFilePathname; + std::string userSettingsPathname; + std::string staticDebugPathname; + std::string OSInfo; + std::string agentFullname; + std::string regionName; + std::string fatalMessage; +}; + +CrashMetadata& CrashMetadata_instance(); + +#endif /* ! defined(LL_LLAPPVIEWERMACOSX_FOR_OBJC_H) */ diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index d472f8926b..81f04744f8 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -36,20 +36,25 @@ #include "llappviewermacosx-objc.h" #include "llappviewermacosx.h" +#include "llappviewermacosx-for-objc.h" #include "llwindowmacosx-objc.h" #include "llcommandlineparser.h" +#include "llsdserialize.h" #include "llviewernetwork.h" #include "llviewercontrol.h" #include "llmd5.h" #include "llfloaterworldmap.h" #include "llurldispatcher.h" +#include "llerrorcontrol.h" +#include "llvoavatarself.h" // for gAgentAvatarp->getFullname() #include <ApplicationServices/ApplicationServices.h> #ifdef LL_CARBON_CRASH_HANDLER #include <Carbon/Carbon.h> #endif #include <vector> #include <exception> +#include <fstream> #include "lldir.h" #include <signal.h> @@ -81,7 +86,7 @@ static void exceptionTerminateHandler() gOldTerminateHandler(); // call old terminate() handler } -bool initViewer() +void constructViewer() { // Set the working dir to <bundle>/Contents/Resources if (chdir(gDirUtilp->getAppRODataDir().c_str()) == -1) @@ -97,18 +102,20 @@ bool initViewer() gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler); gViewerAppPtr->setErrorHandler(LLAppViewer::handleViewerCrash); +} - +bool initViewer() +{ bool ok = gViewerAppPtr->init(); if(!ok) { LL_WARNS() << "Application init failed." << LL_ENDL; } - else if (!gHandleSLURL.empty()) - { - dispatchUrl(gHandleSLURL); - gHandleSLURL = ""; - } + else if (!gHandleSLURL.empty()) + { + dispatchUrl(gHandleSLURL); + gHandleSLURL = ""; + } return ok; } @@ -147,6 +154,73 @@ void cleanupViewer() gViewerAppPtr = NULL; } +// The BugsplatMac API is structured as a number of different method +// overrides, each returning a different piece of metadata. But since we +// obtain such metadata by opening and parsing a file, it seems ridiculous to +// reopen and reparse it for every individual string desired. What we want is +// to open and parse the file once, retaining the data for subsequent +// requests. That's why this is an LLSingleton. +// Another approach would be to provide a function that simply returns +// CrashMetadata, storing the struct in LLAppDelegate, but nat doesn't know +// enough Objective-C++ to code that. We'd still have to detect which of the +// method overrides is called first so that the results are order-insensitive. +class CrashMetadataSingleton: public CrashMetadata, public LLSingleton<CrashMetadataSingleton> +{ + LLSINGLETON(CrashMetadataSingleton); + + // convenience method to log each metadata field retrieved by constructor + std::string get_metadata(const LLSD& info, const LLSD::String& key) const + { + std::string data(info[key].asString()); + LL_INFOS() << " " << key << "='" << data << "'" << LL_ENDL; + return data; + } +}; + +// Populate the fields of our public base-class struct. +CrashMetadataSingleton::CrashMetadataSingleton() +{ + // Note: we depend on being able to read the static_debug_info.log file + // from the *previous* run before we overwrite it with the new one for + // *this* run. LLAppViewer initialization must happen in the Right Order. + staticDebugPathname = *gViewerAppPtr->getStaticDebugFile(); + std::ifstream static_file(staticDebugPathname); + LLSD info; + if (! static_file.is_open()) + { + LL_INFOS() << "Can't open '" << staticDebugPathname + << "'; no metadata about previous run" << LL_ENDL; + } + else if (! LLSDSerialize::deserialize(info, static_file, LLSDSerialize::SIZE_UNLIMITED)) + { + LL_INFOS() << "Can't parse '" << staticDebugPathname + << "'; no metadata about previous run" << LL_ENDL; + } + else + { + LL_INFOS() << "Metadata from '" << staticDebugPathname << "':" << LL_ENDL; + logFilePathname = get_metadata(info, "SLLog"); + userSettingsPathname = get_metadata(info, "SettingsFilename"); + OSInfo = get_metadata(info, "OSInfo"); + agentFullname = get_metadata(info, "LoginName"); + // Translate underscores back to spaces + LLStringUtil::replaceChar(agentFullname, '_', ' '); + regionName = get_metadata(info, "CurrentRegion"); + fatalMessage = get_metadata(info, "FatalMessage"); + } +} + +// Avoid having to compile all of our LLSingleton machinery in Objective-C++. +CrashMetadata& CrashMetadata_instance() +{ + return CrashMetadataSingleton::instance(); +} + +void infos(const std::string& message) +{ + LL_INFOS() << message << LL_ENDL; +} + int main( int argc, char **argv ) { // Store off the command line args for use later. diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index f0a4a54fbf..0de942d507 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -66,8 +66,101 @@ #endif #include "stringize.h" +#include "lldir.h" +#include "llerrorcontrol.h" +#include <fstream> #include <exception> + +// Bugsplat (http://bugsplat.com) crash reporting tool +#ifdef LL_BUGSPLAT +#include "BugSplat.h" +#include "reader.h" // JsonCpp +#include "llagent.h" // for agent location +#include "llviewerregion.h" +#include "llvoavatarself.h" // for agent name + +namespace +{ + // MiniDmpSender's constructor is defined to accept __wchar_t* instead of + // plain wchar_t*. That said, wunder() returns std::basic_string<__wchar_t>, + // NOT plain __wchar_t*, despite the apparent convenience. Calling + // wunder(something).c_str() as an argument expression is fine: that + // std::basic_string instance will survive until the function returns. + // Calling c_str() on a std::basic_string local to wunder() would be + // Undefined Behavior: we'd be left with a pointer into a destroyed + // std::basic_string instance. But we can do that with a macro... + #define WCSTR(string) wunder(string).c_str() + + // It would be nice if, when wchar_t is the same as __wchar_t, this whole + // function would optimize away. However, we use it only for the arguments + // to the BugSplat API -- a handful of calls. + inline std::basic_string<__wchar_t> wunder(const std::wstring& str) + { + return { str.begin(), str.end() }; + } + + // when what we have in hand is a std::string, convert from UTF-8 using + // specific wstringize() overload + inline std::basic_string<__wchar_t> wunder(const std::string& str) + { + return wunder(wstringize(str)); + } + + // Irritatingly, MiniDmpSender::setCallback() is defined to accept a + // classic-C function pointer instead of an arbitrary C++ callable. If it + // did accept a modern callable, we could pass a lambda that binds our + // MiniDmpSender pointer. As things stand, though, we must define an + // actual function and store the pointer statically. + static MiniDmpSender *sBugSplatSender = nullptr; + + bool bugsplatSendLog(UINT nCode, LPVOID lpVal1, LPVOID lpVal2) + { + if (nCode == MDSCB_EXCEPTIONCODE) + { + // send the main viewer log file + // widen to wstring, convert to __wchar_t, then pass c_str() + sBugSplatSender->sendAdditionalFile( + WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife.log"))); + + sBugSplatSender->sendAdditionalFile( + WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "settings.xml"))); + + sBugSplatSender->sendAdditionalFile( + WCSTR(*LLAppViewer::instance()->getStaticDebugFile())); + + // We don't have an email address for any user. Hijack this + // metadata field for the platform identifier. + sBugSplatSender->setDefaultUserEmail( + WCSTR(STRINGIZE(LLOSInfo::instance().getOSStringSimple() << " (" + << ADDRESS_SIZE << "-bit)"))); + + if (gAgentAvatarp) + { + // user name, when we have it + sBugSplatSender->setDefaultUserName(WCSTR(gAgentAvatarp->getFullname())); + } + + // LL_ERRS message, when there is one + sBugSplatSender->setDefaultUserDescription(WCSTR(LLError::getFatalMessage())); + + if (gAgent.getRegion()) + { + // region location, when we have it + LLVector3 loc = gAgent.getPositionAgent(); + sBugSplatSender->resetAppIdentifier( + WCSTR(STRINGIZE(gAgent.getRegion()->getName() + << '/' << loc.mV[0] + << '/' << loc.mV[1] + << '/' << loc.mV[2]))); + } + } // MDSCB_EXCEPTIONCODE + + return false; + } +} +#endif // LL_BUGSPLAT + namespace { void (*gOldTerminateHandler)() = NULL; @@ -495,15 +588,69 @@ bool LLAppViewerWin32::init() LLWinDebug::instance(); #endif -#if LL_WINDOWS #if LL_SEND_CRASH_REPORTS - +#if ! defined(LL_BUGSPLAT) +#pragma message("Building without BugSplat") LLAppViewer* pApp = LLAppViewer::instance(); pApp->initCrashReporting(); -#endif -#endif +#else // LL_BUGSPLAT +#pragma message("Building with BugSplat") + + std::string build_data_fname( + gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "build_data.json")); + std::ifstream inf(build_data_fname.c_str()); + if (! inf.is_open()) + { + LL_WARNS() << "Can't initialize BugSplat, can't read '" << build_data_fname + << "'" << LL_ENDL; + } + else + { + Json::Reader reader; + Json::Value build_data; + if (! reader.parse(inf, build_data, false)) // don't collect comments + { + // gah, the typo is baked into Json::Reader API + LL_WARNS() << "Can't initialize BugSplat, can't parse '" << build_data_fname + << "': " << reader.getFormatedErrorMessages() << LL_ENDL; + } + else + { + Json::Value BugSplat_DB = build_data["BugSplat DB"]; + if (! BugSplat_DB) + { + LL_WARNS() << "Can't initialize BugSplat, no 'BugSplat DB' entry in '" + << build_data_fname << "'" << LL_ENDL; + } + else + { + // Got BugSplat_DB, onward! + std::wstring version_string(WSTRINGIZE(LL_VIEWER_VERSION_MAJOR << '.' << + LL_VIEWER_VERSION_MINOR << '.' << + LL_VIEWER_VERSION_PATCH << '.' << + LL_VIEWER_VERSION_BUILD)); + + // have to convert normal wide strings to strings of __wchar_t + sBugSplatSender = new MiniDmpSender( + WCSTR(BugSplat_DB.asString()), + WCSTR(LL_TO_WSTRING(LL_VIEWER_CHANNEL)), + WCSTR(version_string), + nullptr, // szAppIdentifier -- set later + MDSF_NONINTERACTIVE | // automatically submit report without prompting + MDSF_PREVENTHIJACKING); // disallow swiping Exception filter + sBugSplatSender->setCallback(bugsplatSendLog); + + // engage stringize() overload that converts from wstring + LL_INFOS() << "Engaged BugSplat(" << LL_TO_STRING(LL_VIEWER_CHANNEL) + << ' ' << stringize(version_string) << ')' << LL_ENDL; + } // got BugSplat_DB + } // parsed build_data.json + } // opened build_data.json + +#endif // LL_BUGSPLAT +#endif // LL_SEND_CRASH_REPORTS bool success = LLAppViewer::init(); diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 375dce485d..4e07223784 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -101,14 +101,11 @@ namespace { // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The // macro expands to the string name of the channel, but without quotes. We - // need to turn it into a quoted string. This macro trick does that. -#define stringize_inner(x) #x -#define stringize_outer(x) stringize_inner(x) - + // need to turn it into a quoted string. LL_TO_STRING() does that. /// Storage of the channel name the viewer is using. // The channel name is set by hardcoded constant, // or by calling LLVersionInfo::resetChannel() - std::string sWorkingChannelName(stringize_outer(LL_VIEWER_CHANNEL)); + std::string sWorkingChannelName(LL_TO_STRING(LL_VIEWER_CHANNEL)); // Storage for the "version and channel" string. // This will get reset too. diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 4f0460da29..8b8ce3ca9e 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -44,6 +44,7 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llappviewer.h" #include "llavatarrenderinfoaccountant.h" #include "llcallingcard.h" #include "llcommandhandler.h" @@ -104,6 +105,18 @@ typedef std::map<std::string, std::string> CapabilityMap; static void log_capabilities(const CapabilityMap &capmap); +namespace +{ + +void newRegionEntry(LLViewerRegion& region) +{ + LL_INFOS("LLViewerRegion") << "Entering region [" << region.getName() << "]" << LL_ENDL; + gDebugInfo["CurrentRegion"] = region.getName(); + LLAppViewer::instance()->writeDebugInfo(); +} + +} // anonymous namespace + // support for secondlife:///app/region/{REGION} SLapps // N.B. this is defined to work exactly like the classic secondlife://{REGION} // However, the later syntax cannot support spaces in the region name because @@ -249,6 +262,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) return; // this error condition is not recoverable. } + // record that we just entered a new region + newRegionEntry(*regionp); + // After a few attempts, continue login. But keep trying to get the caps: if (mSeedCapAttempts >= mSeedCapMaxAttemptsBeforeLogin && STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) @@ -369,6 +385,9 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) break; // this error condition is not recoverable. } + // record that we just entered a new region + newRegionEntry(*regionp); + LLSD capabilityNames = LLSD::emptyArray(); buildCapabilityNames(capabilityNames); diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index 2f7a4e9601..58f0469552 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -33,10 +33,8 @@ // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The // macro expands to the string name of the channel, but without quotes. We -// need to turn it into a quoted string. This macro trick does that. -#define stringize_inner(x) #x -#define stringize_outer(x) stringize_inner(x) -#define ll_viewer_channel stringize_outer(LL_VIEWER_CHANNEL) +// need to turn it into a quoted string. LL_TO_STRING() does that. +#define ll_viewer_channel LL_TO_STRING(LL_VIEWER_CHANNEL) namespace tut { |