diff --git a/README.md b/README.md index 531fb167..bfdc75db 100644 --- a/README.md +++ b/README.md @@ -566,24 +566,36 @@ For target Yocto builds `CMAKE_INSTALL_PREFIX` defaults to `/usr` Sentry-native support is available for Crash Handling. This pushes a mini-dump to the cloud for triage and tracking. -To create user account and get DNS See https://sentry.io/welcome/ +> To create user account and get a DSN +> see https://sentry.io/welcome/ -CMake Variables +### Configuration + +1. Specify your DSN via environment variable `SENTRY_DSN` + +2. Add the following sections to your `config.toml` file with the following structure: + +```toml +[sentry] +release = "homescreen-1.0.0" +env = "production" +attachments = [ + "/path/to/crash.log", + "/path/to/config.toml" +] + +[sentry.tags] +platform = "linux" +device = "ivi" +``` + +### CMake Variables + +To enable crash handler support: -DBUILD_CRASH_HANDLER=ON - -DCRASH_HANDLER_DSN="dsn from your account. If not defined, can be set at runtime via environment variable" -DSENTRY_NATIVE_LIBDIR="directory where sentry native is installed, will look in CMAKE_INSTALL_PREFIX directory if not defined" -DCRASHPAD_BINARY_DIR="directory where crashpad_handler executable is installed, will look in CMAKE_INSTALL_PREFIX directory if not defined" - -DCRASH_HANDLER_ATTACHMENTS="paths to files you'd like to attach to coredump reports, separated by commas (,)" - -DCRASH_HANDLER_TAGS="tags to accompany Sentry coredump report in the style of "TAG_NAME=tagvalue", multiple tags can be defined separated by commas (,)" - -Optional Environment Variables - - SENTRY_DSN -- Overrides CMake CRASH_HANDLER_DSN value - SENTRY_HOMESCREEN_ENV -- Homescreen Sentry environment (default: CMAKE_BUILD_TYPE) - SENTRY_HOMESCREEN_RELEASE -- Homescreen Sentry release identifier - SENTRY_HOMESCREEN_TAGS -- Homescreen tags in "key:value,key2:value2" format - SENTRY_ATTACHMENTS -- Additional file attachments to add to Sentry crash reports (Appended to those defined in CRASH_HANDLER_ATTACHMENTS), multiple files can be defined, separated by commas (,) To resolve crash dump stack trace, debug binaries and symbols need to be uploaded to Sentry via sentry-cli tool: https://docs.sentry.io/cli/installation/ @@ -593,18 +605,22 @@ Required source repo: https://github.com/getsentry/sentry-native sentry build - git clone https://github.com/getsentry/sentry-native - mkdir build && cd build - cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_STAGING_PREFIX=`pwd`/out/usr - make install +```bash +git clone https://github.com/getsentry/sentry-native +mkdir build && cd build +cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_STAGING_PREFIX=`pwd`/out/usr +make install +``` ivi-homescreen build - git clone https://github.com/toyota-connected/ivi-homescreen - mkdir build && cd build - cmake .. -DBUILD_CRASH_HANDLER=ON -DCRASH_HANDLER_DSN="dsn from your account" - make -j - LD_LIBRARY_PATH=/lib homescreen --b= --f +```bash +git clone https://github.com/toyota-connected/ivi-homescreen +mkdir build && cd build +cmake .. -DBUILD_CRASH_HANDLER=ON -DSENTRY_NATIVE_LIBDIR=`pwd`/../sentry-native/build/out/usr/lib -DCRASHPAD_BINARY_DIR=`pwd`/../sentry-native/build/out/usr/bin +make -j +SENTRY_DSN= homescreen --b= --f +``` ## Yocto recipes diff --git a/cmake/config_common.h.in b/cmake/config_common.h.in index 5db50c77..fe7ad388 100644 --- a/cmake/config_common.h.in +++ b/cmake/config_common.h.in @@ -87,12 +87,8 @@ #cmakedefine01 BUILD_CRASH_HANDLER #if BUILD_CRASH_HANDLER -static constexpr char kCrashHandlerDsn[] = "@CRASH_HANDLER_DSN@"; -static constexpr char kCrashHandlerRelease[] = "@PROJECT_NAME@@@BUILD_VER@_@BUILD_NUMBER@_dev"; -static constexpr char kDefaultCrashHandlerRelease[] = "homescreen-dev"; +static constexpr char kSentryEnvDefault[] = "@CMAKE_BUILD_TYPE@"; static constexpr char kCrashpadBinaryPath[] = "@CRASHPAD_BINARY_DIR@/crashpad_handler"; -static constexpr char kCrashpadAttachments[] = "@CRASH_HANDLER_ATTACHMENTS@"; -static constexpr char kCrashpadTags[] = "@CRASH_HANDLER_TAGS@"; #endif #cmakedefine01 DEBUG_PLATFORM_MESSAGES diff --git a/cmake/options.cmake b/cmake/options.cmake index 5c151fab..5e02536c 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -224,13 +224,9 @@ if (BUILD_CRASH_HANDLER) set(CRASHPAD_BINARY_DIR ${CMAKE_INSTALL_PREFIX}/bin) else () message(FATAL_ERROR "System crashpad_handler not found at ${CMAKE_INSTALL_PREFIX}, please set CRASHPAD_BINARY_DIR") - endif () - endif () - - if (NOT CRASH_HANDLER_DSN) - message(STATUS "Sentry DSN not set, use environment variable SENTRY_DSN to direct coredumps") - endif () - + endif() + endif() + find_package(sentry REQUIRED) find_package(PkgConfig) pkg_check_modules(UNWIND REQUIRED IMPORTED_TARGET libunwind) diff --git a/shell/crash_handler.cc b/shell/crash_handler.cc index 39c7c89d..af9d3d7b 100644 --- a/shell/crash_handler.cc +++ b/shell/crash_handler.cc @@ -3,9 +3,12 @@ #include "utils.h" +#include +#include #include -#include -#include + +#define TOML_EXCEPTIONS 0 +#include #include "sentry.h" @@ -15,105 +18,99 @@ void CrashHandler::trigger_crash() { memset(invalid_mem, 1, 100); } -const char* CrashHandler::get_dsn() { - const auto dsn_env = getenv("SENTRY_DSN"); - const char* dsn; +CrashHandler::SentryConfig CrashHandler::LoadConfig( + const std::string& bundle_path) { + SentryConfig config; - if (dsn_env && *dsn_env) { - dsn = dsn_env; - } else { - dsn = kCrashHandlerDsn; + // Defaults + config.release = ""; + config.env = kSentryEnvDefault; + + // DSN from environment variable + const char* dsn_env = std::getenv("SENTRY_DSN"); + config.dsn = dsn_env ? dsn_env : ""; + + std::filesystem::path toml_path; + if (bundle_path.empty()) { + spdlog::warn("Bundle path is empty, using defaults"); + return config; } - return dsn; -} + toml_path = bundle_path; + toml_path /= kViewConfigToml; -void CrashHandler::set_sentry_attachments(sentry_options_t* options) { - const auto attachment_env = getenv("SENTRY_ATTACHMENTS"); - std::string attachments(kCrashpadAttachments); - if (attachment_env && *attachment_env) { - attachments.append(","); - attachments.append(attachment_env); + if (!std::filesystem::exists(toml_path)) { + spdlog::warn("Global config file not found at {}, using defaults", + toml_path.string()); + return config; } - std::stringstream ss(attachments); - std::string attachment; - while (getline(ss, attachment, ',')) { - std::filesystem::path attachment_path(attachment); - if (exists(attachment_path)) { - sentry_options_add_attachment(options, attachment_path.c_str()); + // Parse TOML file + auto result = toml::parse_file(toml_path.string()); + if (!result) { + spdlog::error("TOML parsing failed: {}", toml_path.string()); + return config; + } + + auto& tbl = result.table(); + + // Extract [sentry] section + if (auto* sentry_section = tbl.get("sentry")) { + if (auto* release = sentry_section->as_table()->get("release")) + config.release = release->value_or(""); + if (auto* env = sentry_section->as_table()->get("env")) + config.env = env->value_or(kSentryEnvDefault); + + // Extract [sentry.tags] subsection + if (auto* tags_node = sentry_section->as_table()->get("tags")) { + if (auto* tags_tbl = tags_node->as_table()) { + for (auto& [key, val] : *tags_tbl) { + if (auto str_val = val.value()) + config.tags[std::string(key)] = *str_val; + } + } + } + + // Extract [sentry] attachments array + if (auto* arr_node = sentry_section->as_table()->get("attachments")) { + if (auto* arr = arr_node->as_array()) { + for (auto& val : *arr) { + if (auto str_val = val.value()) + config.attachments.push_back(*str_val); + } + } } } + + return config; } -void CrashHandler::set_sentry_tags() { - const auto tags_env = getenv("SENTRY_TAGS"); - std::string tags(kCrashpadTags); +CrashHandler::CrashHandler(const std::string& bundle_path) { + config_ = LoadConfig(bundle_path); - if (tags_env && *tags_env) { - tags.append(","); - tags.append(tags_env); - } - std::stringstream ss(tags); - std::string tag; - while (getline(ss, tag, ',')) { - size_t del = tag.find("=", 0); - std::string tag_name = tag.substr(0, del); - size_t tag_val_size = tag.size() - tag_name.size() - 1; - std::string tag_val = tag.substr(del + 1, tag_val_size); - sentry_set_tag(tag_name.c_str(), tag_val.c_str()); - } -} - -const char* CrashHandler::get_sentry_env() { - const auto env = getenv("SENTRY_HOMESCREEN_ENV"); - if (env && *env) - return env; -#if defined(CMAKE_BUILD_TYPE) - return CMAKE_BUILD_TYPE; -#else - return "development"; -#endif -} - -const char* CrashHandler::get_sentry_release() { - const auto release = getenv("SENTRY_HOMESCREEN_RELEASE"); - if (release && *release) - return release; - return kCrashHandlerRelease; -} - -CrashHandler::CrashHandler() { sentry_options_t* options = sentry_options_new(); - sentry_options_set_dsn(options, get_dsn()); + sentry_options_set_dsn(options, config_.dsn.c_str()); auto home_path = Utils::GetConfigHomePath(); std::filesystem::path db_path = home_path; db_path /= ".sentry"; sentry_options_set_handler_path(options, kCrashpadBinaryPath); sentry_options_set_database_path(options, db_path.c_str()); - // Use SENTRY_HOMESCREEN_RELEASE only - sentry_options_set_release(options, get_sentry_release()); + sentry_options_set_release(options, config_.release.c_str()); + sentry_options_set_environment(options, config_.env.c_str()); - // Use SENTRY_HOMESCREEN_ENV only - sentry_options_set_environment(options, get_sentry_env()); - - // Use SENTRY_HOMESCREEN_TAGS only - const auto homescreen_tags_env = getenv("SENTRY_HOMESCREEN_TAGS"); - if (homescreen_tags_env && *homescreen_tags_env) { - std::stringstream ss(homescreen_tags_env); - std::string tag; - while (getline(ss, tag, ',')) { - size_t del = tag.find("=", 0); - if (del != std::string::npos) { - std::string tag_name = tag.substr(0, del); - std::string tag_val = tag.substr(del + 1); - sentry_set_tag(tag_name.c_str(), tag_val.c_str()); - } - } + // Apply tags + for (auto& [tag_name, tag_val] : config_.tags) { + sentry_set_tag(tag_name.c_str(), tag_val.c_str()); } - set_sentry_attachments(options); + // Apply attachments + for (auto& attachment : config_.attachments) { + std::filesystem::path attachment_path(attachment); + if (exists(attachment_path)) { + sentry_options_add_attachment(options, attachment_path.c_str()); + } + } sentry_options_set_symbolize_stacktraces(options, true); sentry_options_set_debug(options, 0); diff --git a/shell/crash_handler.h b/shell/crash_handler.h index 3478b1e1..44ad2c3f 100644 --- a/shell/crash_handler.h +++ b/shell/crash_handler.h @@ -1,23 +1,31 @@ #pragma once +#include #include +#include #include "sentry.h" class CrashHandler { public: - CrashHandler(); + explicit CrashHandler(const std::string& bundle_path); ~CrashHandler(); static void trigger_crash(); - static const char* get_dsn(); - static void set_sentry_attachments(sentry_options_t* options); - static void set_sentry_tags(); - - // Homescreen-specific helpers - static const char* get_sentry_env(); - static const char* get_sentry_release(); CrashHandler(const CrashHandler&) = delete; CrashHandler& operator=(const CrashHandler&) = delete; + + private: + struct SentryConfig { + std::string dsn; + std::string release; + std::string env; + std::map tags; + std::vector attachments; + }; + + static SentryConfig LoadConfig(const std::string& bundle_path); + + SentryConfig config_; }; diff --git a/shell/main.cc b/shell/main.cc index 61a29111..4f047167 100644 --- a/shell/main.cc +++ b/shell/main.cc @@ -86,12 +86,19 @@ void InstallShutdownHandlers() { * wayland, flutter */ int main(const int argc, char** argv) { + gLogger = std::make_unique(); + const auto configs = Configuration::ParseArgcArgv(argc, argv); + assert(!configs.empty()); + #if BUILD_CRASH_HANDLER - auto crash_handler = std::make_unique(); + std::string first_bundle_path; + if (!configs.empty()) { + first_bundle_path = configs.front().view.bundle_path; + } + auto crash_handler = std::make_unique(first_bundle_path); #endif -#if BUILD_BACKEND_DRM_KMS_EGL || BUILD_BACKEND_DRM_KMS_VULKAN || \ - BUILD_BACKEND_SOFTWARE +#if BUILD_BACKEND_DRM_KMS_EGL || BUILD_BACKEND_SOFTWARE // Handle --drm-list-modes[=] before the main config parse so the // user doesn't need to supply a bundle path just to inspect modes. On // software builds this lists the DRM dumb-sink's modes (the values valid @@ -146,11 +153,6 @@ int main(const int argc, char** argv) { } #endif - gLogger = std::make_unique(); - - const auto configs = Configuration::ParseArgcArgv(argc, argv); - assert(!configs.empty()); - const App app(configs); // Construct the waker (publishing its eventfd) before installing the