From 45e4f355c9348499bcbe18683b7e4dfea4ba7450 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 4 Aug 2021 16:37:04 -0400
Subject: [PATCH] Replace windows bugsplat code with Sentry

---
 indra/newview/llappviewerwin32.cpp | 221 +++++------------------------
 1 file changed, 38 insertions(+), 183 deletions(-)

diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index df3b03d1238..cc0e57dd328 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -76,96 +76,11 @@
 #include <fstream>
 #include <exception>
 
-// Bugsplat (http://bugsplat.com) crash reporting tool
-#ifdef LL_BUGSPLAT
-#include "BugSplat.h"
-#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, "Alchemy.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()));
-
-                sBugSplatSender->sendAdditionalFile(
-                    WCSTR(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "settings_per_account.xml")));
-            }
-
-            // 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
+// Sentry (https://sentry.io) crash reporting tool
+#if defined(USE_SENTRY)
+#include <sentry.h>
+#include "llviewerbuildconfig.h"
+#endif
 
 namespace
 {
@@ -464,6 +379,10 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
 	}
 #endif
 
+#if defined(USE_SENTRY)
+	sentry_close();
+#endif
+
 	return 0;
 }
 
@@ -599,96 +518,9 @@ bool LLAppViewerWin32::init()
 	LLWinDebug::instance();
 #endif
 
-#if LL_SEND_CRASH_REPORTS
-#if ! defined(LL_BUGSPLAT)
-#pragma message("Building without BugSplat")
-
 	LLAppViewer* pApp = LLAppViewer::instance();
 	pApp->initCrashReporting();
 
-#else // LL_BUGSPLAT
-#pragma message("Building with BugSplat")
-
-    if (!isSecondInstance())
-    {
-        // Cleanup previous session
-        std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "bugsplat.log");
-        LLFile::remove(log_file, ENOENT);
-    }
-
-	std::string build_data_fname(
-		gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "build_data.json"));
-	// Use llifstream instead of std::ifstream because LL_PATH_EXECUTABLE
-	// could contain non-ASCII characters, which std::ifstream doesn't handle.
-	llifstream inf(build_data_fname.c_str());
-	if (! inf.is_open())
-	{
-		LL_WARNS("BUGSPLAT") << "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("BUGSPLAT") << "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("BUGSPLAT") << "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));
-
-                DWORD dwFlags = MDSF_NONINTERACTIVE | // automatically submit report without prompting
-                                MDSF_PREVENTHIJACKING; // disallow swiping Exception filter
-
-                bool needs_log_file = !isSecondInstance() && debugLoggingEnabled("BUGSPLAT");
-                if (needs_log_file)
-                {
-                    // Startup only!
-                    LL_INFOS("BUGSPLAT") << "Engaged BugSplat logging to bugsplat.log" << LL_ENDL;
-                    dwFlags |= MDSF_LOGFILE | MDSF_LOG_VERBOSE;
-                }
-
-				// 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
-					dwFlags);
-				sBugSplatSender->setCallback(bugsplatSendLog);
-
-                if (needs_log_file)
-                {
-                    // Log file will be created in %TEMP%, but it will be moved into logs folder in case of crash
-                    std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "bugsplat.log");
-                    sBugSplatSender->setLogFilePath(WCSTR(log_file));
-                }
-
-				// engage stringize() overload that converts from wstring
-				LL_INFOS("BUGSPLAT") << "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();
 
     return success;
@@ -711,12 +543,14 @@ bool LLAppViewerWin32::cleanup()
 
 void LLAppViewerWin32::reportCrashToBugsplat(void* pExcepInfo)
 {
-#if defined(LL_BUGSPLAT)
-    if (sBugSplatSender)
-    {
-        sBugSplatSender->createReport((EXCEPTION_POINTERS*)pExcepInfo);
-    }
-#endif // LL_BUGSPLAT
+#if defined(USE_SENTRY)
+	if (mSentryInitialized)
+	{
+		sentry_ucontext_s exc_context = {};
+		exc_context.exception_ptrs = *(EXCEPTION_POINTERS*)pExcepInfo;
+		sentry_handle_exception(&exc_context);
+	}
+#endif
 }
 
 void LLAppViewerWin32::setCrashUserMetadata(const LLUUID& user_id, const std::string& avatar_name)
@@ -836,6 +670,27 @@ bool LLAppViewerWin32::restoreErrorTrap()
 
 void LLAppViewerWin32::initCrashReporting(bool reportFreeze)
 {
+#if defined(USE_SENTRY)
+	sentry_options_t* options = sentry_options_new();
+	sentry_options_set_dsn(options, SENTRY_DSN);
+	sentry_options_set_release(options, LL_VIEWER_CHANNEL_AND_VERSION);
+
+	std::string crashpad_path = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "crashpad_handler.exe");
+	sentry_options_set_handler_pathw(options, ll_convert_string_to_wide(crashpad_path).c_str());
+
+	std::string database_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "sentry");
+	sentry_options_set_database_pathw(options, ll_convert_string_to_wide(database_path).c_str());
+
+	mSentryInitialized = (sentry_init(options) == 0);
+	if (mSentryInitialized)
+	{
+		LL_INFOS() << "Successfully initialized Sentry" << LL_ENDL;
+	}
+	else
+	{
+		LL_WARNS() << "Failed to initialize Sentry" << LL_ENDL;
+	}
+#endif
 }
 
 //virtual
-- 
GitLab