-
-
Notifications
You must be signed in to change notification settings - Fork 201
feat(crashpad): offline caching #1493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
536c4a1
5742eda
6c75ccf
8f3ffd5
3410b6c
799d219
3c40494
a37ac86
20815bd
ef96547
e96d9d4
93aefb2
f4936d5
7eddee4
8d40ec1
b28663f
ab62f90
7bc7856
9e09bd0
747edb1
b1a4671
b101fce
2e1c63b
e0cb46e
317968c
e30846f
1d565d6
76a5f81
f0e5075
25da5e1
fc0f6cc
57cb740
fd3c257
8a17813
5ece6d5
6a80d77
dd0ef77
bbfb4d2
43450d2
683e521
d03d9fb
badab46
ceb4912
4031e45
74713f6
bd2bafe
f5e6a7a
0c291b1
30e3320
8a22d2c
7c72f1c
97b2723
709d7f2
2985e08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ extern "C" { | |
| #include "sentry_screenshot.h" | ||
| #include "sentry_sync.h" | ||
| #include "sentry_transport.h" | ||
| #include "sentry_value.h" | ||
| #ifdef SENTRY_PLATFORM_LINUX | ||
| # include "sentry_unix_pageallocator.h" | ||
| #endif | ||
|
|
@@ -436,6 +437,167 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) | |
| } | ||
| #endif | ||
|
|
||
| static sentry_value_t | ||
| read_msgpack_file(const sentry_path_t *path) | ||
| { | ||
| size_t size; | ||
| char *data = sentry__path_read_to_buffer(path, &size); | ||
| if (!data) { | ||
| return sentry_value_new_null(); | ||
| } | ||
| sentry_value_t value = sentry__value_from_msgpack(data, size); | ||
| sentry_free(data); | ||
| return value; | ||
| } | ||
|
|
||
| static sentry_path_t * | ||
| report_attachments_dir(const crashpad::CrashReportDatabase::Report &report, | ||
| const sentry_options_t *options) | ||
| { | ||
| sentry_path_t *attachments_root | ||
| = sentry__path_join_str(options->database_path, "attachments"); | ||
| if (!attachments_root) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| sentry_path_t *attachments_dir = sentry__path_join_str( | ||
| attachments_root, report.uuid.ToString().c_str()); | ||
|
|
||
| sentry__path_free(attachments_root); | ||
| return attachments_dir; | ||
| } | ||
|
|
||
| // Converts a completed crashpad report into a sentry envelope by reading the | ||
| // event, breadcrumbs, and attachments from the report's attachments directory. | ||
| static sentry_envelope_t * | ||
| report_to_envelope(const crashpad::CrashReportDatabase::Report &report, | ||
| const sentry_options_t *options) | ||
| { | ||
| #ifdef SENTRY_PLATFORM_WINDOWS | ||
| sentry_path_t *minidump_path | ||
| = sentry__path_from_wstr(report.file_path.value().c_str()); | ||
| #else | ||
| sentry_path_t *minidump_path | ||
| = sentry__path_from_str(report.file_path.value().c_str()); | ||
| #endif | ||
| sentry_path_t *attachments_dir = report_attachments_dir(report, options); | ||
|
|
||
| if (!minidump_path || !attachments_dir) { | ||
| sentry__path_free(minidump_path); | ||
| sentry__path_free(attachments_dir); | ||
| return nullptr; | ||
| } | ||
|
|
||
| sentry_value_t event = sentry_value_new_null(); | ||
| sentry_value_t breadcrumbs1 = sentry_value_new_null(); | ||
| sentry_value_t breadcrumbs2 = sentry_value_new_null(); | ||
| sentry_attachment_t *attachments = nullptr; | ||
|
|
||
| sentry_pathiter_t *iter = sentry__path_iter_directory(attachments_dir); | ||
| if (iter) { | ||
| const sentry_path_t *path; | ||
| while ((path = sentry__pathiter_next(iter)) != nullptr) { | ||
| const char *filename = sentry__path_filename(path); | ||
| if (strcmp(filename, "__sentry-event") == 0) { | ||
| event = read_msgpack_file(path); | ||
| } else if (strcmp(filename, "__sentry-breadcrumb1") == 0) { | ||
| breadcrumbs1 = read_msgpack_file(path); | ||
| } else if (strcmp(filename, "__sentry-breadcrumb2") == 0) { | ||
| breadcrumbs2 = read_msgpack_file(path); | ||
| } else { | ||
| sentry__attachments_add_path(&attachments, | ||
| sentry__path_clone(path), ATTACHMENT, nullptr); | ||
| } | ||
| } | ||
| sentry__pathiter_free(iter); | ||
| } | ||
| sentry__path_free(attachments_dir); | ||
|
|
||
| sentry_envelope_t *envelope = nullptr; | ||
| if (!sentry_value_is_null(event)) { | ||
| envelope = sentry__envelope_new(); | ||
| if (envelope && options->dsn && options->dsn->is_valid) { | ||
| sentry__envelope_set_header(envelope, "dsn", | ||
| sentry_value_new_string(sentry_options_get_dsn(options))); | ||
| } | ||
| } | ||
| if (envelope) { | ||
| sentry_value_set_by_key(event, "breadcrumbs", | ||
| sentry__value_merge_breadcrumbs( | ||
| breadcrumbs1, breadcrumbs2, options->max_breadcrumbs)); | ||
| sentry__attachments_add_path( | ||
| &attachments, minidump_path, MINIDUMP, nullptr); | ||
|
|
||
| if (sentry__envelope_add_event(envelope, event)) { | ||
| sentry__envelope_add_attachments(envelope, attachments); | ||
| } else { | ||
| sentry_value_decref(event); | ||
| sentry_envelope_free(envelope); | ||
| envelope = nullptr; | ||
| } | ||
jpnurmi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| sentry__path_free(minidump_path); | ||
| sentry_value_decref(event); | ||
| } | ||
|
|
||
| sentry_value_decref(breadcrumbs1); | ||
| sentry_value_decref(breadcrumbs2); | ||
| sentry__attachments_free(attachments); | ||
|
|
||
| return envelope; | ||
| } | ||
|
|
||
| // Caches completed crashpad reports as sentry envelopes and removes them from | ||
| // the crashpad database. Called during startup before the handler is started. | ||
| static void | ||
| process_completed_reports( | ||
| crashpad_state_t *state, const sentry_options_t *options) | ||
| { | ||
| if (!state || !state->db || !options || !options->cache_keep) { | ||
| return; | ||
| } | ||
|
|
||
| std::vector<crashpad::CrashReportDatabase::Report> reports; | ||
| if (state->db->GetCompletedReports(&reports) | ||
| != crashpad::CrashReportDatabase::kNoError | ||
| || reports.empty()) { | ||
| return; | ||
| } | ||
|
|
||
| SENTRY_DEBUGF("caching %zu completed reports", reports.size()); | ||
|
|
||
| sentry_path_t *cache_dir | ||
| = sentry__path_join_str(options->database_path, "cache"); | ||
| if (!cache_dir || sentry__path_create_dir_all(cache_dir) != 0) { | ||
| SENTRY_WARN("failed to create cache dir"); | ||
| sentry__path_free(cache_dir); | ||
| return; | ||
| } | ||
|
|
||
| for (const auto &report : reports) { | ||
| std::string filename = report.uuid.ToString() + ".envelope"; | ||
| sentry_envelope_t *envelope = report_to_envelope(report, options); | ||
| if (!envelope) { | ||
| SENTRY_WARNF("failed to convert \"%s\"", filename.c_str()); | ||
| continue; | ||
| } | ||
| sentry_path_t *out_path | ||
| = sentry__path_join_str(cache_dir, filename.c_str()); | ||
| if (!out_path | ||
| || (!sentry__path_is_file(out_path) | ||
| && sentry_envelope_write_to_path(envelope, out_path) != 0)) { | ||
| SENTRY_WARNF("failed to cache \"%s\"", filename.c_str()); | ||
| } else if (state->db->DeleteReport(report.uuid) | ||
| != crashpad::CrashReportDatabase::kNoError) { | ||
| SENTRY_WARNF("failed to delete \"%s\"", filename.c_str()); | ||
| } | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Partial cache file causes report deletion and data lossMedium Severity If
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure... To validate the file's integrity, we would either have to store the checksum somewhere or parse the previously written envelope. 🤔 The lack of atomic (temp+rename) file writes could bite in several places in sentry-native. Another scenario: crash during envelope write -> truncated |
||
| sentry__path_free(out_path); | ||
| sentry_envelope_free(envelope); | ||
| } | ||
|
|
||
| sentry__path_free(cache_dir); | ||
| } | ||
|
|
||
| static int | ||
| crashpad_backend_startup( | ||
| sentry_backend_t *backend, const sentry_options_t *options) | ||
|
|
@@ -549,6 +711,7 @@ crashpad_backend_startup( | |
| // Initialize database first, flushing the consent later on as part of | ||
| // `sentry_init` will persist the upload flag. | ||
| data->db = crashpad::CrashReportDatabase::Initialize(database).release(); | ||
| process_completed_reports(data, options); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| data->client = new crashpad::CrashpadClient; | ||
| char *minidump_url | ||
| = sentry__dsn_get_minidump_url(options->dsn, options->user_agent); | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.