diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index ffb6747fd7..731687b637 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -371,6 +371,8 @@ void AssetServer::completeSetup() { auto& domainHandler = nodeList->getDomainHandler(); const QJsonObject& settingsObject = domainHandler.getSettingsObject(); + commonParseSettingsObject(settingsObject); + static const QString ASSET_SERVER_SETTINGS_KEY = "asset_server"; if (!settingsObject.contains(ASSET_SERVER_SETTINGS_KEY)) { diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 4ba5802026..788dfeab93 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -38,6 +38,7 @@ #include "AudioMixerClientData.h" #include "AvatarAudioStream.h" #include "InjectedAudioStream.h" +#include "crash-handler/CrashHandler.h" using namespace std; @@ -49,6 +50,7 @@ static const QString AUDIO_ENV_GROUP_KEY = "audio_env"; static const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer"; static const QString AUDIO_THREADING_GROUP_KEY = "audio_threading"; + int AudioMixer::_numStaticJitterFrames{ DISABLE_STATIC_JITTER_FRAMES }; float AudioMixer::_noiseMutingThreshold{ DEFAULT_NOISE_MUTING_THRESHOLD }; float AudioMixer::_attenuationPerDoublingInDistance{ DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE }; @@ -560,6 +562,8 @@ void AudioMixer::clearDomainSettings() { void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { qCDebug(audio) << "AVX2 Support:" << (cpuSupportsAVX2() ? "enabled" : "disabled"); + commonParseSettingsObject(settingsObject); + if (settingsObject.contains(AUDIO_THREADING_GROUP_KEY)) { QJsonObject audioThreadingGroupObject = settingsObject[AUDIO_THREADING_GROUP_KEY].toObject(); const QString AUTO_THREADS = "auto_threads"; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 330bb0fd1b..7279627daf 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -988,6 +988,8 @@ void AvatarMixer::handlePacketVersionMismatch(PacketType type, const SockAddr& s } void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + commonParseSettingsObject(domainSettings); + const QString AVATAR_MIXER_SETTINGS_KEY = "avatar_mixer"; QJsonObject avatarMixerGroupObject = domainSettings[AVATAR_MIXER_SETTINGS_KEY].toObject(); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 94847e0340..d27a69ff7c 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -272,7 +272,7 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN #ifdef EXTRA_ERASE_DEBUGGING if (packetsSent > 0) { - qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of " + qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of " << totalBytes << " total bytes to node:" << node->getUUID(); } #endif @@ -326,14 +326,14 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio } else { tree->setEntityScriptSourceWhitelist(""); } - + auto entityEditFilters = DependencyManager::get(); - + QString filterURL; if (readOptionString("entityEditFilter", settingsSectionObject, filterURL) && !filterURL.isEmpty()) { // connect the filterAdded signal, and block edits until you hear back connect(entityEditFilters.data(), &EntityEditFilters::filterAdded, this, &EntityServer::entityFilterAdded); - + entityEditFilters->addFilter(EntityItemID(), filterURL); } } @@ -367,7 +367,7 @@ void EntityServer::nodeKilled(SharedNodePointer node) { // FIXME - this stats tracking is somewhat temporary to debug the Whiteboard issues. It's not a bad // set of stats to have, but we'd probably want a different data structure if we keep it very long. -// Since this version uses a single shared QMap for all senders, there could be some lock contention +// Since this version uses a single shared QMap for all senders, there could be some lock contention // on this QWriteLocker void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& sessionID) { QWriteLocker locker(&_viewerSendingStatsLock); diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 93040734ef..7b4f12756d 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -19,13 +19,8 @@ int main(int argc, char* argv[]) { setupHifiApplication(BuildInfo::ASSIGNMENT_CLIENT_NAME); - auto &ch = CrashHandler::getInstance(); - ch.start(argv[0]); - ch.setAnnotation("program", "assignment-client"); - - - AssignmentClientApp app(argc, argv); + auto &ch = CrashHandler::getInstance(); ch.startMonitor(&app); diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 017d3a80b7..74efadf6e1 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -121,6 +121,8 @@ void MessagesMixer::domainSettingsRequestComplete() { } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + commonParseSettingsObject(domainSettings); + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; QJsonObject messagesMixerGroupObject = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject(); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index b70ae63a99..d9a2faa2de 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1023,6 +1023,8 @@ void OctreeServer::readConfiguration() { const QJsonObject& settingsObject = DependencyManager::get()->getDomainHandler().getSettingsObject(); + commonParseSettingsObject(settingsObject); + QString settingsKey = getMyDomainSettingsKey(); QJsonObject settingsSectionObject = settingsObject[settingsKey].toObject(); _settings = settingsSectionObject; // keep this for later diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index d8fb0bed06..b16e4561d6 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -149,11 +149,14 @@ void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer(); auto& domainHandler = nodeList->getDomainHandler(); const QJsonObject& settingsObject = domainHandler.getSettingsObject(); + commonParseSettingsObject(settingsObject); + static const QString ENTITY_SCRIPT_SERVER_SETTINGS_KEY = "entity_script_server"; if (!settingsObject.contains(ENTITY_SCRIPT_SERVER_SETTINGS_KEY)) { @@ -292,7 +295,7 @@ void EntityScriptServer::run() { entityScriptingInterface->init(); _entityViewer.init(); - + // setup the JSON filter that asks for entities with a non-default serverScripts property QJsonObject queryJSONParameters; queryJSONParameters[EntityJSONQueryProperties::SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault; @@ -303,7 +306,7 @@ void EntityScriptServer::run() { queryFlags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY] = true; queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags; - + // setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter _entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters); @@ -380,7 +383,7 @@ void EntityScriptServer::nodeKilled(SharedNodePointer killedNode) { if (!hasAnotherEntityServer) { clear(); } - + break; } case NodeType::Agent: { @@ -584,7 +587,7 @@ void EntityScriptServer::sendStatsPacket() { } scriptEngineStats["number_running_scripts"] = numberRunningScripts; statsObject["script_engine_stats"] = scriptEngineStats; - + auto nodeList = DependencyManager::get(); QJsonObject nodesObject; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 1087b6ae31..ebfb519eac 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -76,7 +76,8 @@ { "label": "Networking / Crash Reporting", "name": "crash_reporting", - "restart": false, + "restart": true, + "assignment-types": [ 0, 1, 3, 4, 5, 6 ], "settings": [ { "name": "enable_crash_reporter", @@ -85,6 +86,22 @@ "default": false, "type": "checkbox", "advanced": true + }, + { + "name": "custom_crash_url", + "label": "Custom crash URL", + "help": "If this is set, it overrides the internal crash reporting URL. This can be used for instance to direct crash reports to Sentry.", + "default": "", + "type": "string", + "advanced": true + }, + { + "name": "custom_crash_token", + "label": "Custom crash token", + "help": "This is a token that identifies the thing sending a crash report, such as a product name and version number. If not set, the compile time default will be used.", + "default": "", + "type": "string", + "advanced": true } ] }, diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 13ab81fa93..c59fc43d34 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -63,7 +63,9 @@ const QString SETTINGS_VIEWPOINT_KEY = "viewpoint"; DomainServerSettingsManager::DomainServerSettingsManager() { // load the description object from the settings description - QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); + qDebug() << "Application dir: " << QCoreApplication::applicationDirPath(); + QString descriptionFilePath = QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH; + QFile descriptionFile(descriptionFilePath); descriptionFile.open(QIODevice::ReadOnly); QJsonParseError parseError; @@ -89,7 +91,7 @@ DomainServerSettingsManager::DomainServerSettingsManager() { static const QString MISSING_SETTINGS_DESC_MSG = QString("Did not find settings description in JSON at %1 - Unable to continue. domain-server will quit.\n%2 at %3") - .arg(SETTINGS_DESCRIPTION_RELATIVE_PATH).arg(parseError.errorString()).arg(parseError.offset); + .arg(descriptionFilePath).arg(parseError.errorString()).arg(parseError.offset); static const int MISSING_SETTINGS_DESC_ERROR_CODE = 6; QMetaObject::invokeMethod(QCoreApplication::instance(), "queuedQuit", Qt::QueuedConnection, diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index 4c74f2301b..6f23737059 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -50,12 +50,42 @@ enum SettingsType { ContentSettings }; +/** + * @brief Manages the domain-wide settings + * + * The domain server doesn't use the interface's QSettings based system, but this one. + * The domain holds all the settings and distributes it to the connected assignment clients. + * + * Assignment clients request their settings by making a DomainSettingsRequest. The request + * is specific to the client's type. + * + * The response is generated in settingsResponseObjectForType and filtered according to what + * the client is allowed to see. + * + * To add a new setting, add it in resources/describe-settings.json + * + * To make a setting be sent to assignment clients, set the assignment-types value to an array + * of the desired assignment clients. The type is defined numerically. + * + * The canonical list of assignment types is in the Assignment::Type enum. + * + * + * + */ class DomainServerSettingsManager : public QObject { Q_OBJECT public: DomainServerSettingsManager(); bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url); + + /** + * @brief Loads the configuration from the specified file + * + * Performs version upgrades when we're loading an older version of the config. + * + * @param userConfigFilename + */ void setupConfigMap(const QString& userConfigFilename); // each of the three methods in this group takes a read lock of _settingsLock @@ -126,7 +156,21 @@ public: enum DefaultSettingsInclusion { NoDefaultSettings, IncludeDefaultSettings }; enum SettingsBackupFlag { NotForBackup, ForBackup }; - /// thread safe method to retrieve a JSON representation of settings + /** + * @brief Generates a JSON representation of settings + * + * This is what answers an assignment client's request for domain settings. + * + * @note thread safe + * + * @param typeValue Type of assignment client + * @param authentication + * @param domainSettingsInclusion + * @param contentSettingsInclusion + * @param defaultSettingsInclusion + * @param settingsBackupFlag + * @return QJsonObject + */ QJsonObject settingsResponseObjectForType(const QString& typeValue, SettingsRequestAuthentication authentication = NotAuthenticated, DomainSettingsInclusion domainSettingsInclusion = IncludeDomainSettings, @@ -211,7 +255,7 @@ private: // keep track of answers to api queries about which users are in which groups QHash> _groupMembership; // QHash> - /// guard read/write access from multiple threads to settings + /// guard read/write access from multiple threads to settings QReadWriteLock _settingsLock { QReadWriteLock::Recursive }; friend class DomainServer; diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 640643d9b3..ef8b68d1a1 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -35,7 +35,7 @@ int main(int argc, char* argv[]) { // use a do-while to handle domain-server restart auto &ch = CrashHandler::getInstance(); - ch.start(argv[0]); + ch.setPath(argv[0]); if ( DomainServer::forceCrashReporting() ) { ch.setEnabled(true); diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 3931fb97cf..cb5111f456 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -323,6 +323,13 @@ int main(int argc, const char* argv[]) { auto& ual = UserActivityLogger::getInstance(); auto& ch = CrashHandler::getInstance(); + QObject::connect(&ch, &CrashHandler::enabledChanged, [](bool enabled) { + Settings s; + s.beginGroup("Crash"); + s.setValue("ReportingEnabled", enabled); + s.endGroup(); + }); + // once the settings have been loaded, check if we need to flip the default for UserActivityLogger if (!ual.isDisabledSettingSet()) { // the user activity logger is opt-out for Interface @@ -337,13 +344,6 @@ int main(int argc, const char* argv[]) { ch.setEnabled(true); } - auto crashHandlerStarted = ch.start(argv[0]); - if (crashHandlerStarted) { - qDebug() << "Crash handler started"; - } else { - qWarning() << "Crash handler failed to start"; - } - ch.setAnnotation("program", "interface"); const QString& applicationName = getInterfaceSharedMemoryName(); diff --git a/libraries/monitoring/CMakeLists.txt b/libraries/monitoring/CMakeLists.txt index 2800a2379b..ee4077ae0e 100644 --- a/libraries/monitoring/CMakeLists.txt +++ b/libraries/monitoring/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME monitoring) setup_hifi_library() -link_hifi_libraries(shared networking) +link_hifi_libraries(shared) add_crashpad() target_breakpad() diff --git a/libraries/monitoring/src/crash-handler/CrashHandler.cpp b/libraries/monitoring/src/crash-handler/CrashHandler.cpp index 3345bc61a7..71ab38ec1a 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandler.cpp +++ b/libraries/monitoring/src/crash-handler/CrashHandler.cpp @@ -11,6 +11,8 @@ #include "CrashHandler.h" #include "CrashHandlerBackend.h" +#include +#include CrashHandler& CrashHandler::getInstance() { @@ -18,13 +20,32 @@ CrashHandler& CrashHandler::getInstance() { return sharedInstance; } -bool CrashHandler::start(const QString &path) { +CrashHandler::CrashHandler(QObject *parent) : QObject(parent) { + +} + + +void CrashHandler::setPath(const QString &path) { + QFileInfo fi(path); + if (isStarted()) { - qCWarning(crash_handler) << "Crash handler already started"; + qCWarning(crash_handler) << "Crash handler already started, too late to set the path."; + } + + if (fi.isFile()) { + _path = fi.absolutePath(); + } else { + _path = path; + } +} + +bool CrashHandler::start() { + if (isStarted()) { + //qCWarning(crash_handler) << "Crash handler already started"; return false; } - auto started = startCrashHandler(path.toStdString()); + auto started = startCrashHandler(_path.toStdString(), _crashUrl.toStdString(), _crashToken.toStdString()); setStarted(started); if ( started ) { @@ -41,21 +62,50 @@ void CrashHandler::startMonitor(QCoreApplication *app) { } void CrashHandler::setEnabled(bool enabled) { - if (enabled != _crashReportingEnabled.get()) { - _crashReportingEnabled.set(enabled); + start(); + if (enabled != _crashReportingEnabled) { + _crashReportingEnabled = enabled; setCrashReportingEnabled(enabled); + + emit enabledChanged(enabled); + } +} + +void CrashHandler::setUrl(const QString &url) { + // This can be called both from the settings system in an assignment client + // and from the commandline parser. We only emit a warning if the commandline + // argument causes the domain setting to be ignored. + + if (isStarted() && url != _crashUrl) { + qCWarning(crash_handler) << "Setting crash reporting URL to " << url << "after the crash handler is already running has no effect"; + } else { + _crashUrl = url; + } +} + +void CrashHandler::setToken(const QString &token) { + if (isStarted() && token != _crashToken) { + qCWarning(crash_handler) << "Setting crash reporting token to " << token << "after the crash handler is already running has no effect"; + } else { + _crashToken = token; } } void CrashHandler::setAnnotation(const std::string &key, const char *value) { + setAnnotation(key, std::string(value)); setCrashAnnotation(key, std::string(value)); } void CrashHandler::setAnnotation(const std::string &key, const QString &value) { - setCrashAnnotation(key, value.toStdString()); + setAnnotation(key, value.toStdString()); } void CrashHandler::setAnnotation(const std::string &key, const std::string &value) { + if (!isStarted()) { + qCWarning(crash_handler) << "Can't set annotation" << QString::fromStdString(key) << "to" << QString::fromStdString(value) << "crash handler not yet started"; + return; + } + setCrashAnnotation(key, value); } \ No newline at end of file diff --git a/libraries/monitoring/src/crash-handler/CrashHandler.h b/libraries/monitoring/src/crash-handler/CrashHandler.h index 9433dc80c3..3f863326ad 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandler.h +++ b/libraries/monitoring/src/crash-handler/CrashHandler.h @@ -21,7 +21,31 @@ /** * @brief The global object in charge of setting up and controlling crash reporting. * - * This object initializes and talks to crash reporting backends. + * This object initializes and talks to crash reporting backends. For those, see + * CrashHandlerBackend.h and the .cpp files that implement that interface. + * + * The crash URL and token can only be passed to the underlying system on start, so + * things should be set up in such a way that startup is only done after those are set. + * + * start() will be automatically called when setEnabled() is called with true. + * setAnnotation() can only be called after start. + * + * + * To use, follow this general pattern in an application: + * + * @code {.cpp} + * auto &ch = CrashHandler::getInstance(); + * ch.setPath(...); + * ch.setUrl("https://server.com/crash-reports"); + * ch.setToken("1.2beta"); + * ch.setEnabled(true); + * ch.setAnnotation("version", "1.3"); // Needs a started handler to work + * @endcode + * + * For an assignment client, there are two potential ways to start, through the command-line + * and through the settings system. Since the path, URL and token only apply on startup, the + * code must be written such that if command arguments are not given, setEnabled() or start() + * are not called until receiving the settings from the domain. * */ class CrashHandler : public QObject { @@ -30,18 +54,42 @@ class CrashHandler : public QObject { public: static CrashHandler& getInstance(); + public slots: + + /** + * @brief Set the directory for the crash reports + * + * This sets the path for writing crash reports. This should be done on application startup. + * + * @param path Directory where to store crash reports. It's allowed to set this to argv[0], + * if the path is a filename, then the base directory will be automatically used. + */ + void setPath(const QString &path); + /** * @brief Start the crash handler * - * @param path Database path + * This is called automatically if it wasn't started yet when setEnabled() is called. + * + * @param path Path where to store the crash database * @return true Started successfully * @return false Failed to start */ - bool start(const QString &path); + bool start(); + /** + * @brief Starts the unhandled exception monitor. + * + * On Windows, it's possible for the unhandled exception handler to be reset. This starts a timer + * to periodically set it back. + * + * On non-Windows systems this has no effect. + * + * @param app Main application + */ void startMonitor(QCoreApplication *app); @@ -70,7 +118,7 @@ public slots: * @return true Crashes will be reported to CMAKE_BACKTRACE_URL * @return false Crashes will not be reported */ - bool isEnabled() const { return _crashReportingEnabled.get(); } + bool isEnabled() const { return _crashReportingEnabled; } /** * @brief Set whether we want to submit crash reports to the report server @@ -78,17 +126,84 @@ public slots: * The report server is configured with CMAKE_BACKTRACE_URL. * Emits crashReportingEnabledChanged signal. * + * @note This automatically calls start(), so it should be called after setPath(), setUrl() and setToken() * @param enabled Whether it's enabled. */ void setEnabled(bool enabled); + /** + * @brief Set the URL where to send crash reports to + * + * If not set, a predefined URL specified at compile time via CMAKE_BACKTRACE_URL + * will be used. + * + * @param url URL + */ + void setUrl(const QString &url); + /** + * @brief Set the token for the crash reporter + * + * This is an identifier in the crash collection service, such as Sentry, and may contain + * a branch name or a version number. + * + * If not set, a predefined token specified at compile time via CMAKE_BACKTRACE_TOKEN + * will be used. + * + * @param token Token + */ + void setToken(const QString &token); + + + + /** + * @brief Set an annotation to be added to a crash + * + * Annotations add extra information, such as the application's version number, + * the current user, or any other information of interest. + * + * @param key Key + * @param value Value + */ void setAnnotation(const std::string &key, const char *value); + + /** + * @brief Set an annotation to be added to a crash + * + * Annotations add extra information, such as the application's version number, + * the current user, or any other information of interest. + * + * @param key Key + * @param value Value + */ void setAnnotation(const std::string &key, const QString &value); + + /** + * @brief Set an annotation to be added to a crash + * + * Annotations add extra information, such as the application's version number, + * the current user, or any other information of interest. + * + * @param key Key + * @param value Value + */ void setAnnotation(const std::string &key, const std::string &value); +signals: + + /** + * @brief Emitted when the enabled/disabled state of the crash handler changes + * + * This can be used to store it as a setting. + * + * @param enabled Whether the crash handler is now enabled + */ + void enabledChanged(bool enabled); private: + CrashHandler(QObject *parent = nullptr); + + /** * @brief Marks the crash monitor as started * @@ -99,9 +214,12 @@ private: void setStarted(bool started) { _crashMonitorStarted = started; } - - Setting::Handle _crashReportingEnabled { "CrashReportingEnabled", false }; bool _crashMonitorStarted {false}; + bool _crashReportingEnabled {false}; + + QString _path; + QString _crashUrl; + QString _crashToken; }; diff --git a/libraries/monitoring/src/crash-handler/CrashHandlerBackend.h b/libraries/monitoring/src/crash-handler/CrashHandlerBackend.h index f0d7e31b43..55c1ef2760 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandlerBackend.h +++ b/libraries/monitoring/src/crash-handler/CrashHandlerBackend.h @@ -18,7 +18,7 @@ Q_DECLARE_LOGGING_CATEGORY(crash_handler) -bool startCrashHandler(std::string appPath); +bool startCrashHandler(std::string appPath, std::string url="", std::string token=""); void setCrashAnnotation(std::string name, std::string value); void startCrashHookMonitor(QCoreApplication* app); void setCrashReportingEnabled(bool value); diff --git a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Breakpad.cpp b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Breakpad.cpp index fa24b43107..984cabe507 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Breakpad.cpp +++ b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Breakpad.cpp @@ -57,7 +57,7 @@ void flushAnnotations() { settings.sync(); } -bool startCrashHandler(std::string appPath) { +bool startCrashHandler(std::string appPath, std::string crashURL, std::string crashToken) { annotations["version"] = BuildInfo::VERSION; annotations["build_number"] = BuildInfo::BUILD_NUMBER; annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING; diff --git a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Crashpad.cpp b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Crashpad.cpp index be479a2be3..298465b5d2 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Crashpad.cpp +++ b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_Crashpad.cpp @@ -49,6 +49,10 @@ Q_LOGGING_CATEGORY(crash_handler, "overte.crash_handler") static const std::string BACKTRACE_URL{ CMAKE_BACKTRACE_URL }; static const std::string BACKTRACE_TOKEN{ CMAKE_BACKTRACE_TOKEN }; +std::string custom_backtrace_url; +std::string custom_backtrace_token; + + // ------------------------------------------------------------------------------------------------ // SpinLock - a lock that can timeout attempting to lock a block of code, and is in a busy-wait cycle while trying to acquire // note that this code will malfunction if you attempt to grab a lock while already holding it @@ -351,8 +355,16 @@ static QString findBinaryDir() { return QString(); } -bool startCrashHandler(std::string appPath) { - if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) { +bool startCrashHandler(std::string appPath, std::string crashURL, std::string crashToken) { + if (crashURL.empty()) { + crashURL = BACKTRACE_URL; + } + + if (crashToken.empty()) { + crashToken = BACKTRACE_TOKEN; + } + + if (crashURL.empty() || crashToken.empty()) { qCCritical(crash_handler) << "Backtrace URL or token not set, crash handler disabled."; return false; } @@ -362,7 +374,7 @@ bool startCrashHandler(std::string appPath) { std::vector arguments; std::map annotations; - annotations["sentry[release]"] = BACKTRACE_TOKEN; + annotations["sentry[release]"] = crashToken; annotations["sentry[contexts][app][app_version]"] = BuildInfo::VERSION.toStdString(); annotations["sentry[contexts][app][app_build]"] = BuildInfo::BUILD_NUMBER.toStdString(); annotations["build_type"] = BuildInfo::BUILD_TYPE_STRING.toStdString(); @@ -389,10 +401,10 @@ bool startCrashHandler(std::string appPath) { qCDebug(crash_handler) << "Locating own directory by platform-specific method"; interfaceDir.setPath(binaryDir); } else { + // Getting the base dir from argv[0] is already handled by CrashHandler, so we use + // the path as-is here. qCDebug(crash_handler) << "Locating own directory by argv[0]"; interfaceDir.setPath(QString::fromStdString(appPath)); - // argv[0] gets us the path including the binary file - interfaceDir.cdUp(); } if (!interfaceDir.exists(CRASHPAD_HANDLER_NAME)) { @@ -429,7 +441,7 @@ bool startCrashHandler(std::string appPath) { crashpadDatabase->GetSettings()->SetUploadsEnabled(CrashHandler::getInstance().isEnabled()); - if (!client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true)) { + if (!client->StartHandler(handler, db, db, crashURL, annotations, arguments, true, true)) { qCCritical(crash_handler) << "Failed to start crashpad handler"; return false; } diff --git a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_None.cpp b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_None.cpp index 53ff5b8286..5944125575 100644 --- a/libraries/monitoring/src/crash-handler/CrashHandlerBackend_None.cpp +++ b/libraries/monitoring/src/crash-handler/CrashHandlerBackend_None.cpp @@ -20,7 +20,7 @@ Q_LOGGING_CATEGORY(crash_handler, "overte.crash_handler") -bool startCrashHandler(std::string appPath) { +bool startCrashHandler(std::string appPath, std::string crashURL, std::string crashToken) { qCWarning(crash_handler) << "No crash handler available."; return false; } diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index c635059d1b..009eb6a34a 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME networking) setup_hifi_library(Network WebSockets) -link_hifi_libraries(shared platform) +link_hifi_libraries(shared platform monitoring) target_openssl() target_tbb() diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index cddfdbe6fe..03a4a91295 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -16,11 +16,17 @@ #include #include #include +#include +#include + #include "udt/PacketHeaders.h" #include "SharedUtil.h" #include "UUID.h" +static const QString CRASH_REPORTING_GROUP_KEY = "crash_reporting"; + + Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { switch (nodeType) { case NodeType::AudioMixer: @@ -51,7 +57,7 @@ Assignment::Assignment() : _payload(), _isStatic(false) { - + } Assignment::Assignment(Assignment::Command command, Assignment::Type type, const QString& pool, Assignment::Location location, QString dataDirectory) : @@ -84,9 +90,9 @@ Assignment::Assignment(ReceivedMessage& message) : } else if (message.getType() == PacketType::CreateAssignment) { _command = Assignment::CreateCommand; } - + QDataStream packetStream(message.getMessage()); - + packetStream >> *this; } @@ -113,7 +119,7 @@ Assignment& Assignment::operator=(const Assignment& rhsAssignment) { void Assignment::swap(Assignment& otherAssignment) { using std::swap; - + swap(_uuid, otherAssignment._uuid); swap(_command, otherAssignment._command); swap(_type, otherAssignment._type); @@ -148,10 +154,36 @@ const char* Assignment::typeToString(Assignment::Type type) { } } + +void Assignment::commonParseSettingsObject(const QJsonObject &settingsObject) { + + if (settingsObject.contains(CRASH_REPORTING_GROUP_KEY)) { + + auto &ch = CrashHandler::getInstance(); + QJsonObject crashGroupObject = settingsObject[CRASH_REPORTING_GROUP_KEY].toObject(); + + const QString CRASH_REPORTING_ENABLED = "enable_crash_reporter"; + const QString CRASH_REPORTING_CUSTOM_URL = "custom_crash_url"; + const QString CRASH_REPORTING_CUSTOM_TOKEN = "custom_crash_token"; + + bool enabled = crashGroupObject[CRASH_REPORTING_ENABLED].toBool(); + QString url = crashGroupObject[CRASH_REPORTING_CUSTOM_URL].toString(); + QString token = crashGroupObject[CRASH_REPORTING_CUSTOM_TOKEN].toString(); + + ch.setUrl(url); + ch.setToken(token); + ch.setEnabled(enabled); + + ch.setAnnotation("program", "assignment-client"); + ch.setAnnotation("assignment-client", "audio-mixer"); + } + +} + QDebug operator<<(QDebug debug, const Assignment &assignment) { debug.nospace() << "UUID: " << qPrintable(assignment.getUUID().toString()) << ", Type: " << assignment.getTypeName() << " (" << assignment.getType() << ")"; - + if (!assignment.getPool().isEmpty()) { debug << ", Pool: " << assignment.getPool(); } @@ -161,11 +193,11 @@ QDebug operator<<(QDebug debug, const Assignment &assignment) { QDataStream& operator<<(QDataStream &out, const Assignment& assignment) { out << (quint8) assignment._type << assignment._uuid << assignment._pool << assignment._payload; - + if (assignment._command == Assignment::RequestCommand) { out << assignment._nodeVersion; } - + return out; } @@ -173,11 +205,11 @@ QDataStream& operator>>(QDataStream &in, Assignment& assignment) { quint8 packedType; in >> packedType >> assignment._uuid >> assignment._pool >> assignment._payload; assignment._type = (Assignment::Type) packedType; - + if (assignment._command == Assignment::RequestCommand) { in >> assignment._nodeVersion; } - + return in; } @@ -187,3 +219,4 @@ uint qHash(const Assignment::Type& key, uint seed) { // strongly typed enum for PacketType return qHash((uint8_t) key, seed); } + diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index 513450d910..4bbe158466 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -14,10 +14,12 @@ #include + #include "ReceivedMessage.h" #include "NodeList.h" + const int MAX_PAYLOAD_BYTES = 1024; const QString emptyPool = QString(); @@ -80,12 +82,12 @@ public: void setPool(const QString& pool) { _pool = pool; }; const QString& getPool() const { return _pool; } - + void setIsStatic(bool isStatic) { _isStatic = isStatic; } bool isStatic() const { return _isStatic; } - + const QString& getNodeVersion() const { return _nodeVersion; } - + const char* getTypeName() const; static const char* typeToString(Assignment::Type type); @@ -94,6 +96,16 @@ public: friend QDataStream& operator>>(QDataStream &in, Assignment& assignment); protected: + /** + * @brief Parse the part of the settings object common to all assignment clients + * + * Currently this is the crash reporting settings. + * + * @param settingsObject + */ + void commonParseSettingsObject(const QJsonObject &settingsObject); + + QUuid _uuid; /// the 16 byte UUID for this assignment Assignment::Command _command; /// the command for this assignment (Create, Deploy, Request) Assignment::Type _type; /// the type of the assignment, defines what the assignee will do