diff options
-rw-r--r-- | autobuild.xml | 42 | ||||
-rw-r--r-- | indra/cmake/Copy3rdPartyLibs.cmake | 12 | ||||
-rw-r--r-- | indra/cmake/bugsplat.cmake | 20 | ||||
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 13 | ||||
-rw-r--r-- | indra/llcommon/llapp.cpp | 60 | ||||
-rw-r--r-- | indra/llcommon/llapp.h | 10 | ||||
-rwxr-xr-x | indra/newview/viewer_manifest.py | 283 |
7 files changed, 298 insertions, 142 deletions
diff --git a/autobuild.xml b/autobuild.xml index 4ccc4c5784..ef1c11a7ec 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -225,6 +225,48 @@ <key>version</key> <string>1.57</string> </map> + <key>bugsplat</key> + <map> + <key>copyright</key> + <string>Copyright 2003-2017, BugSplat</string> + <key>description</key> + <string>Bugsplat crash reporting package</string> + <key>license</key> + <string>Proprietary</string> + <key>license_file</key> + <string>LICENSES/BUGSPLAT_LICENSE.txt</string> + <key>name</key> + <string>bugsplat</string> + <key>platforms</key> + <map> + <key>windows</key> + <map> + <key>archive</key> + <map> + <key>hash</key> + <string>2710e051eb1b12dba574f470834baa9e</string> + <key>url</key> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/9992/49191/bugsplat-3.5.0.5-windows-509980.tar.bz2</string> + </map> + <key>name</key> + <string>windows</string> + </map> + <key>windows64</key> + <map> + <key>archive</key> + <map> + <key>hash</key> + <string>f2bb37c69f35522e49aae547d3408fd8</string> + <key>url</key> + <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/9991/49185/bugsplat-3.5.0.5-windows64-509980.tar.bz2</string> + </map> + <key>name</key> + <string>windows64</string> + </map> + </map> + <key>version</key> + <string>3.5.0.5</string> + </map> <key>chardet</key> <map> <key>copyright</key> diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index ce9f005e9f..c98d806b98 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -42,6 +42,18 @@ if(WINDOWS) libhunspell.dll ) + # Filenames are different for 32/64 bit BugSplat file and we don't + # have any control over them so need to branch. + if(ADDRESS_SIZE EQUAL 32) + set(release_files ${release_files} BugSplat.dll) + set(release_files ${release_files} BugSplatRc.dll) + set(release_files ${release_files} BsSndRpt.exe) + else(ADDRESS_SIZE EQUAL 32) + set(release_files ${release_files} BugSplat64.dll) + set(release_files ${release_files} BugSplatRc64.dll) + set(release_files ${release_files} BsSndRpt64.exe) + endif(ADDRESS_SIZE EQUAL 32) + if (FMODEX) if(ADDRESS_SIZE EQUAL 32) diff --git a/indra/cmake/bugsplat.cmake b/indra/cmake/bugsplat.cmake new file mode 100644 index 0000000000..e993979902 --- /dev/null +++ b/indra/cmake/bugsplat.cmake @@ -0,0 +1,20 @@ +include(Prebuilt) + +set(BUGSPLAT_FIND_QUIETLY ON) +set(BUGSPLAT_FIND_REQUIRED ON) + +if (USESYSTEMLIBS) + include(FindBUGSPLAT) +else (USESYSTEMLIBS) + use_prebuilt_binary(bugsplat) + if (WINDOWS) + set(BUGSPLAT_LIBRARIES + ${ARCH_PREBUILT_DIRS_RELEASE}/bugsplat.lib + ) + elseif (DARWIN) + + else (WINDOWS) + + endif (WINDOWS) + set(BUGSPLAT_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/bugsplat) +endif (USESYSTEMLIBS) diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d9eb13d65a..9c5481a977 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -13,6 +13,8 @@ include(GoogleBreakpad) include(Copy3rdPartyLibs) include(ZLIB) include(URIPARSER) +include(BUGSPLAT) +include(BuildVersion) include_directories( ${EXPAT_INCLUDE_DIRS} @@ -21,6 +23,7 @@ include_directories( ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ${URIPARSER_INCLUDE_DIRS} + ${BUGSPLAT_INCLUDE_DIR} ) # add_executable(lltreeiterators lltreeiterators.cpp) @@ -253,7 +256,14 @@ set(llcommon_HEADER_FILES ) set_source_files_properties(${llcommon_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) + PROPERTIES HEADER_FILE_ONLY TRUE + ) + +# bring in version information for BugSplat crash reporting +set_source_files_properties(${llcommon_SOURCE_FILES} + PROPERTIES + COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" # see BuildVersion.cmake + ) list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) @@ -291,6 +301,7 @@ target_link_libraries( ${BOOST_SYSTEM_LIBRARY} ${GOOGLE_PERFTOOLS_LIBRARIES} ${URIPARSER_LIBRARIES} + ${BUGSPLAT_LIBRARIES} ) if (DARWIN) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 6cc9e804d4..3e652dbdb5 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -49,6 +49,20 @@ #include "google_breakpad/exception_handler.h" #include "stringize.h" #include "llcleanup.h" +#include "BugSplat.h" + +// TESTING ONLY - REMOVE FOR PRODUCTION +// (Want to only invoke BugSplat crash reporting in the same way we did for Breakpad - for Release viewers +// but need to test here in a ReleaseWithDebugInfo environment) +#if BUGSPLAT_ENABLED +#define LL_SEND_CRASH_REPORTS 1 +#endif + +// BugSplat crash reporting tool - http://bugsplat.com +#if BUGSPLAT_ENABLED +bool BugSplatExceptionCallback(unsigned int nCode, void* lpVal1, void* lpVal2); +MiniDmpSender *gBugSplatSender; +#endif // // Signal handling @@ -385,6 +399,26 @@ void EnableCrashingOnCrashes() } #endif +#if BUGSPLAT_ENABLED +bool BugSplatExceptionCallback(unsigned int nCode, void* lpVal1, void* lpVal2) +{ + switch (nCode) + { + case MDSCB_EXCEPTIONCODE: + { + // send the main viewer log file (Clearly a temporary hack since we don't have access to the gDir*** set of functions in newview + const std::string appdata = std::string(getenv("APPDATA")); + const std::string logfile = appdata + "\\SecondLife\\logs\\Secondlife.log"; + const std::wstring wide_logfile(logfile.begin(), logfile.end()); + gBugSplatSender->sendAdditionalFile((const __wchar_t *)wide_logfile.c_str()); + } + break; + } + + return false; +} +#endif + void LLApp::setupErrorHandling(bool second_instance) { // Error handling is done by starting up an error handling thread, which just sleeps and @@ -393,6 +427,25 @@ void LLApp::setupErrorHandling(bool second_instance) #if LL_WINDOWS #if LL_SEND_CRASH_REPORTS + +#if BUGSPLAT_ENABLED + // TODOCP: populate these fields correctly + static const wchar_t *bugdb_name = L"second_life_callum_test"; + + // build (painfully) the app/channel name + #define stringize_inner(x) L#x + #define stringize_outer(x) stringize_inner(x) + std::wstring app_name(stringize_outer(LL_VIEWER_CHANNEL)); + + // build in real app version now we leveraged CMake to build in BuildVersion.cmake into LLCommon + wchar_t version_string[MAX_STRING]; + wsprintf(version_string, L"%d.%d.%d.%d", LL_VIEWER_VERSION_MAJOR, LL_VIEWER_VERSION_MINOR, LL_VIEWER_VERSION_PATCH, LL_VIEWER_VERSION_BUILD); + + gBugSplatSender = new MiniDmpSender((const __wchar_t *)bugdb_name, (const __wchar_t *)app_name.c_str(), (const __wchar_t *)version_string, NULL); + + gBugSplatSender->setCallback(BugSplatExceptionCallback); +#else + EnableCrashingOnCrashes(); // This sets a callback to handle w32 signals to the console window. @@ -454,8 +507,9 @@ void LLApp::setupErrorHandling(bool second_instance) mExceptionHandler->set_handle_debug_exceptions(true); } } -#endif -#else +#endif // BUGSPLAT_ENABLED +#endif // LL_SEND_CRASH_REPORTS +#else // not LL_WINDOWS // // Start up signal handling. // @@ -516,7 +570,7 @@ void LLApp::setupErrorHandling(bool second_instance) } #endif -#endif +#endif // LL_WINDOWS startErrorThread(); } diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index acd829d864..5a4b7f13df 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -30,6 +30,7 @@ #include <map> #include "llrun.h" #include "llsd.h" + // Forward declarations template <typename Type> class LLAtomic32; typedef LLAtomic32<U32> LLAtomicU32; @@ -39,6 +40,14 @@ class LLLiveFile; #include <signal.h> #endif +// first version of Bugsplat (http://bugsplat.com) crash reporting tool +// is only supported on Windows - macOS to follow. +#define BUGSPLAT_ENABLED LL_WINDOWS + +#if BUGSPLAT_ENABLED +class __declspec(dllexport) MiniDmpSender; +#endif + typedef void (*LLAppErrorHandler)(); #if !LL_WINDOWS @@ -316,7 +325,6 @@ private: google_breakpad::ExceptionHandler * mExceptionHandler; - #if !LL_WINDOWS friend void default_unix_signal_handler(int signum, siginfo_t *info, void *); #endif diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f930e8b2ce..3be314b03b 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -491,9 +491,9 @@ class WindowsManifest(ViewerManifest): self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) with self.prefix(src=os.path.join(pkgdir, "VMP"), dst=""): - # include the compiled launcher scripts so that it gets included in the file_list + # include the compiled launcher scripts so that it gets included in the file_list self.path('SL_Launcher.exe') - #IUM is not normally executed directly, just imported. No exe needed. + #IUM is not normally executed directly, just imported. No exe needed. self.path("InstallerUserMessage.py") with self.prefix(src=self.icon_path(), dst="vmp_icons"): @@ -578,6 +578,16 @@ class WindowsManifest(ViewerManifest): # Hunspell self.path("libhunspell.dll") + # BugSplat + if(self.address_size == 64): + self.path("BsSndRpt64.exe") + self.path("BugSplat64.dll") + self.path("BugSplatRc64.dll") + else: + self.path("BsSndRpt.exe") + self.path("BugSplat.dll") + self.path("BugSplatRc.dll") + # For google-perftools tcmalloc allocator. try: if self.args['configuration'].lower() == 'debug': @@ -587,7 +597,6 @@ class WindowsManifest(ViewerManifest): except: print "Skipping libtcmalloc_minimal.dll" - self.path(src="licenses-win32.txt", dst="licenses.txt") self.path("featuretable.txt") self.path("ca-bundle.crt") @@ -609,16 +618,16 @@ class WindowsManifest(ViewerManifest): # CEF runtime files - not debug (release, relwithdebinfo etc.) config = 'debug' if self.args['configuration'].lower() == 'debug' else 'release' with self.prefix(src=os.path.join(pkgdir, 'bin', config), dst="llplugin"): - self.path("chrome_elf.dll") - self.path("d3dcompiler_43.dll") - self.path("d3dcompiler_47.dll") - self.path("libcef.dll") - self.path("libEGL.dll") - self.path("libGLESv2.dll") - self.path("dullahan_host.exe") - self.path("natives_blob.bin") - self.path("snapshot_blob.bin") - self.path("widevinecdmadapter.dll") + self.path("chrome_elf.dll") + self.path("d3dcompiler_43.dll") + self.path("d3dcompiler_47.dll") + self.path("libcef.dll") + self.path("libEGL.dll") + self.path("libGLESv2.dll") + self.path("dullahan_host.exe") + self.path("natives_blob.bin") + self.path("snapshot_blob.bin") + self.path("widevinecdmadapter.dll") # MSVC DLLs needed for CEF and have to be in same directory as plugin with self.prefix(src=os.path.join(os.pardir, 'sharedlibs', 'Release'), dst="llplugin"): @@ -944,7 +953,7 @@ open "%s" --args "$@" "Info.plist") # copy VMP libs to MacOS - with self.prefix(dst="MacOS"): + with self.prefix(dst="MacOS"): #this copies over the python wrapper script, #associated utilities and required libraries, see #SL-321, SL-322, SL-323 @@ -955,13 +964,13 @@ open "%s" --args "$@" # our custom version to get our ca-bundle.crt self.path("certifi") with self.prefix(src=os.path.join(pkgdir, "lib", "python"), dst=""): - # llbase provides our llrest service layer and llsd decoding + # llbase provides our llrest service layer and llsd decoding with self.prefix("llbase"): # (Why is llbase treated specially here? What # DON'T we want to copy out of lib/python/llbase?) self.path("*.py") self.path("_cllsd.so") - #requests module needed by llbase/llrest.py + #requests module needed by llbase/llrest.py #this is only needed on POSIX, because in Windows #we compile it into the EXE for pypkg in "chardet", "idna", "requests", "urllib3": @@ -1032,7 +1041,7 @@ open "%s" --args "$@" with self.prefix(dst="Resources"): # defer cross-platform file copies until we're in the right # nested Resources directory - super(DarwinManifest, self).construct() + super(DarwinManifest, self).construct() with self.prefix(src=self.icon_path(), dst="") : self.path(viewer_icon) @@ -1041,130 +1050,130 @@ open "%s" --args "$@" self.path("libndofdev.dylib") self.path("libhunspell-1.3.0.dylib") - with self.prefix("cursors_mac"): - self.path("*.tif") + with self.prefix("cursors_mac"): + self.path("*.tif") - self.path("licenses-mac.txt", dst="licenses.txt") - self.path("featuretable_mac.txt") - self.path("SecondLife.nib") - self.path("ca-bundle.crt") + self.path("licenses-mac.txt", dst="licenses.txt") + self.path("featuretable_mac.txt") + self.path("SecondLife.nib") + self.path("ca-bundle.crt") self.path("SecondLife.nib") - # Translations - self.path("English.lproj/language.txt") - self.replace_in(src="English.lproj/InfoPlist.strings", - dst="English.lproj/InfoPlist.strings", - searchdict={'%%VERSION%%':'.'.join(self.args['version'])} - ) - self.path("German.lproj") - self.path("Japanese.lproj") - self.path("Korean.lproj") - self.path("da.lproj") - self.path("es.lproj") - self.path("fr.lproj") - self.path("hu.lproj") - self.path("it.lproj") - self.path("nl.lproj") - self.path("pl.lproj") - self.path("pt.lproj") - self.path("ru.lproj") - self.path("tr.lproj") - self.path("uk.lproj") - self.path("zh-Hans.lproj") - - def path_optional(src, dst): - """ - For a number of our self.path() calls, not only do we want - to deal with the absence of src, we also want to remember - which were present. Return either an empty list (absent) - or a list containing dst (present). Concatenate these - return values to get a list of all libs that are present. - """ - # This was simple before we started needing to pass - # wildcards. Fortunately, self.path() ends up appending a - # (source, dest) pair to self.file_list for every expanded - # file processed. Remember its size before the call. - oldlen = len(self.file_list) - self.path(src, dst) - # The dest appended to self.file_list has been prepended - # with self.get_dst_prefix(). Strip it off again. - added = [os.path.relpath(d, self.get_dst_prefix()) - for s, d in self.file_list[oldlen:]] - if not added: - print "Skipping %s" % dst - return added - - # dylibs is a list of all the .dylib files we expect to need - # in our bundled sub-apps. For each of these we'll create a - # symlink from sub-app/Contents/Resources to the real .dylib. - # Need to get the llcommon dll from any of the build directories as well. + # Translations + self.path("English.lproj/language.txt") + self.replace_in(src="English.lproj/InfoPlist.strings", + dst="English.lproj/InfoPlist.strings", + searchdict={'%%VERSION%%':'.'.join(self.args['version'])} + ) + self.path("German.lproj") + self.path("Japanese.lproj") + self.path("Korean.lproj") + self.path("da.lproj") + self.path("es.lproj") + self.path("fr.lproj") + self.path("hu.lproj") + self.path("it.lproj") + self.path("nl.lproj") + self.path("pl.lproj") + self.path("pt.lproj") + self.path("ru.lproj") + self.path("tr.lproj") + self.path("uk.lproj") + self.path("zh-Hans.lproj") + + def path_optional(src, dst): + """ + For a number of our self.path() calls, not only do we want + to deal with the absence of src, we also want to remember + which were present. Return either an empty list (absent) + or a list containing dst (present). Concatenate these + return values to get a list of all libs that are present. + """ + # This was simple before we started needing to pass + # wildcards. Fortunately, self.path() ends up appending a + # (source, dest) pair to self.file_list for every expanded + # file processed. Remember its size before the call. + oldlen = len(self.file_list) + self.path(src, dst) + # The dest appended to self.file_list has been prepended + # with self.get_dst_prefix(). Strip it off again. + added = [os.path.relpath(d, self.get_dst_prefix()) + for s, d in self.file_list[oldlen:]] + if not added: + print "Skipping %s" % dst + return added + + # dylibs is a list of all the .dylib files we expect to need + # in our bundled sub-apps. For each of these we'll create a + # symlink from sub-app/Contents/Resources to the real .dylib. + # Need to get the llcommon dll from any of the build directories as well. libfile_parent = self.get_dst_prefix() - libfile = "libllcommon.dylib" - dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir, - "llcommon", - self.args['configuration'], - libfile), - os.path.join(relpkgdir, libfile)), - dst=libfile) - - for libfile in ( - "libapr-1.0.dylib", - "libaprutil-1.0.dylib", - "libcollada14dom.dylib", - "libexpat.1.dylib", - "libexception_handler.dylib", - "libGLOD.dylib", - # libnghttp2.dylib is a symlink to + libfile = "libllcommon.dylib" + dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir, + "llcommon", + self.args['configuration'], + libfile), + os.path.join(relpkgdir, libfile)), + dst=libfile) + + for libfile in ( + "libapr-1.0.dylib", + "libaprutil-1.0.dylib", + "libcollada14dom.dylib", + "libexpat.1.dylib", + "libexception_handler.dylib", + "libGLOD.dylib", + # libnghttp2.dylib is a symlink to # libnghttp2.major.dylib, which is a symlink to # libnghttp2.version.dylib. Get all of them. - "libnghttp2.*dylib", - ): - dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) - - # SLVoice and vivox lols, no symlinks needed - for libfile in ( - 'libortp.dylib', - 'libsndfile.dylib', - 'libvivoxoal.dylib', - 'libvivoxsdk.dylib', - 'libvivoxplatform.dylib', - 'SLVoice', - ): - self.path2basename(relpkgdir, libfile) - - # dylibs that vary based on configuration - if self.args['configuration'].lower() == 'debug': - for libfile in ( - "libfmodexL.dylib", - ): - dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile) - else: - for libfile in ( - "libfmodex.dylib", - ): - dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) - - # our apps + "libnghttp2.*dylib", + ): + dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + + # SLVoice and vivox lols, no symlinks needed + for libfile in ( + 'libortp.dylib', + 'libsndfile.dylib', + 'libvivoxoal.dylib', + 'libvivoxsdk.dylib', + 'libvivoxplatform.dylib', + 'SLVoice', + ): + self.path2basename(relpkgdir, libfile) + + # dylibs that vary based on configuration + if self.args['configuration'].lower() == 'debug': + for libfile in ( + "libfmodexL.dylib", + ): + dylibs += path_optional(os.path.join(debpkgdir, libfile), libfile) + else: + for libfile in ( + "libfmodex.dylib", + ): + dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + + # our apps executable_path = {} - for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"), - # plugin launcher - (os.path.join("llplugin", "slplugin"), "SLPlugin.app"), - ): - self.path2basename(os.path.join(os.pardir, - app_bld_dir, self.args['configuration']), - app) + for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"), + # plugin launcher + (os.path.join("llplugin", "slplugin"), "SLPlugin.app"), + ): + self.path2basename(os.path.join(os.pardir, + app_bld_dir, self.args['configuration']), + app) executable_path[app] = \ self.dst_path_of(os.path.join(app, "Contents", "MacOS")) - # our apps dependencies on shared libs - # for each app, for each dylib we collected in dylibs, - # create a symlink to the real copy of the dylib. + # our apps dependencies on shared libs + # for each app, for each dylib we collected in dylibs, + # create a symlink to the real copy of the dylib. with self.prefix(dst=os.path.join(app, "Contents", "Resources")): - for libfile in dylibs: + for libfile in dylibs: self.relsymlinkf(os.path.join(libfile_parent, libfile)) - # Dullahan helper apps go inside SLPlugin.app + # Dullahan helper apps go inside SLPlugin.app with self.prefix(dst=os.path.join( "SLPlugin.app", "Contents", "Frameworks")): @@ -1231,10 +1240,10 @@ open "%s" --args "$@" change_command + [newpath, self.dst_path_of('DullahanHelper')]) - # SLPlugin plugins + # SLPlugin plugins with self.prefix(dst="llplugin"): dylibexecutable = 'media_plugin_cef.dylib' - self.path2basename("../media_plugins/cef/" + self.args['configuration'], + self.path2basename("../media_plugins/cef/" + self.args['configuration'], dylibexecutable) # Do this install_name_tool *after* media plugin is copied over. @@ -1251,17 +1260,17 @@ open "%s" --args "$@" change_command + [newpath, self.dst_path_of(dylibexecutable)]) - # copy LibVLC plugin itself - self.path2basename("../media_plugins/libvlc/" + self.args['configuration'], - "media_plugin_libvlc.dylib") + # copy LibVLC plugin itself + self.path2basename("../media_plugins/libvlc/" + self.args['configuration'], + "media_plugin_libvlc.dylib") - # copy LibVLC dynamic libraries + # copy LibVLC dynamic libraries with self.prefix(src=relpkgdir, dst="lib"): - self.path( "libvlc*.dylib*" ) - # copy LibVLC plugins folder + self.path( "libvlc*.dylib*" ) + # copy LibVLC plugins folder with self.prefix(src='plugins', dst=""): - self.path( "*.dylib" ) - self.path( "plugins.dat" ) + self.path( "*.dylib" ) + self.path( "plugins.dat" ) def package_finish(self): global CHANNEL_VENDOR_BASE |