From 056e9d4e72c996b5b5237c2812bbc70aa2d33596 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:53:38 +0100 Subject: [PATCH 01/11] add initial caching option --- include/sentry.h | 23 +++++++++++++++++++++++ src/sentry_options.c | 14 ++++++++++++++ src/sentry_options.h | 1 + 3 files changed, 38 insertions(+) diff --git a/include/sentry.h b/include/sentry.h index f9870d5aa..18d1d208a 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -953,6 +953,15 @@ typedef enum { SENTRY_HANDLER_STRATEGY_CHAIN_AT_START = 1, } sentry_handler_strategy_t; +/** + * The caching mode for crash reports. + */ +typedef enum { + SENTRY_CACHE_NONE = -1, // (default) No caching enabled + SENTRY_CACHE_NO_PURGE = 0, // TODO Never purges cached crashes + SENTRY_CACHE_OFFLINE = 1, // TODO Periodically checks if online +} sentry_caching_mode_t; + /** * Creates a new options struct. * Can be freed with `sentry_options_free`. @@ -1374,6 +1383,20 @@ SENTRY_API void sentry_options_set_symbolize_stacktraces( SENTRY_API int sentry_options_get_symbolize_stacktraces( const sentry_options_t *opts); +/** + * Sets the caching mode for crash reports. + * + * This controls how the SDK handles cached crash reports and offline scenarios. + */ +SENTRY_API void sentry_options_set_caching_mode( + sentry_options_t *opts, sentry_caching_mode_t mode); + +/** + * Gets the caching mode for crash reports. + */ +SENTRY_API sentry_caching_mode_t sentry_options_get_caching_mode( + const sentry_options_t *opts); + /** * Adds a new attachment to be sent along. * diff --git a/src/sentry_options.c b/src/sentry_options.c index b9b6ea4c6..5913c16b8 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -53,6 +53,7 @@ sentry_options_new(void) opts->enable_logging_when_crashed = true; opts->propagate_traceparent = false; opts->crashpad_limit_stack_capture_to_sp = false; + opts->caching_mode = SENTRY_CACHE_NONE; opts->symbolize_stacktraces = // AIX doesn't have reliable debug IDs for server-side symbolication, // and the diversity of Android makes it infeasible to have access to debug @@ -475,6 +476,19 @@ sentry_options_get_symbolize_stacktraces(const sentry_options_t *opts) return opts->symbolize_stacktraces; } +void +sentry_options_set_caching_mode( + sentry_options_t *opts, sentry_caching_mode_t mode) +{ + opts->caching_mode = mode; +} + +sentry_caching_mode_t +sentry_options_get_caching_mode(const sentry_options_t *opts) +{ + return opts->caching_mode; +} + void sentry_options_set_system_crash_reporter_enabled( sentry_options_t *opts, int enabled) diff --git a/src/sentry_options.h b/src/sentry_options.h index 280703d40..45b769c5b 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -45,6 +45,7 @@ struct sentry_options_s { bool enable_logging_when_crashed; bool propagate_traceparent; bool crashpad_limit_stack_capture_to_sp; + sentry_caching_mode_t caching_mode; sentry_attachment_t *attachments; sentry_run_t *run; From 6e9466bf0e88520d7e4d045ccc0e84406bf5580e Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:37:04 +0100 Subject: [PATCH 02/11] update caching mode names --- examples/example.c | 3 +++ include/sentry.h | 4 ++-- src/sentry_options.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/example.c b/examples/example.c index 32fbc5587..772e6b02f 100644 --- a/examples/example.c +++ b/examples/example.c @@ -503,6 +503,9 @@ main(int argc, char **argv) if (has_arg(argc, argv, "log-attributes")) { sentry_options_set_logs_with_attributes(options, true); } + if (has_arg(argc, argv, "cache-keep")) { + sentry_options_set_caching_mode(options, SENTRY_CACHE_KEEP); + } if (0 != sentry_init(options)) { return EXIT_FAILURE; diff --git a/include/sentry.h b/include/sentry.h index 18d1d208a..158263356 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -957,8 +957,8 @@ typedef enum { * The caching mode for crash reports. */ typedef enum { - SENTRY_CACHE_NONE = -1, // (default) No caching enabled - SENTRY_CACHE_NO_PURGE = 0, // TODO Never purges cached crashes + SENTRY_CACHE_DEFAULT = -1, // (default) No special caching enabled + SENTRY_CACHE_KEEP = 0, // TODO Keep events/dmps around in secondary folder SENTRY_CACHE_OFFLINE = 1, // TODO Periodically checks if online } sentry_caching_mode_t; diff --git a/src/sentry_options.c b/src/sentry_options.c index 5913c16b8..9f4e88f66 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -53,7 +53,7 @@ sentry_options_new(void) opts->enable_logging_when_crashed = true; opts->propagate_traceparent = false; opts->crashpad_limit_stack_capture_to_sp = false; - opts->caching_mode = SENTRY_CACHE_NONE; + opts->caching_mode = SENTRY_CACHE_DEFAULT; opts->symbolize_stacktraces = // AIX doesn't have reliable debug IDs for server-side symbolication, // and the diversity of Android makes it infeasible to have access to debug From 0c04d1a89cf40e1968013e7f359c0f001475d97f Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:01:04 +0100 Subject: [PATCH 03/11] keep-based caching for inproc/breakpad --- src/backends/sentry_backend_breakpad.cpp | 9 +++++++++ src/backends/sentry_backend_inproc.c | 9 +++++++++ src/sentry_database.c | 25 ++++++++++++++++++++++++ src/sentry_database.h | 9 +++++++++ 4 files changed, 52 insertions(+) diff --git a/src/backends/sentry_backend_breakpad.cpp b/src/backends/sentry_backend_breakpad.cpp index 1c737ce55..63652534a 100644 --- a/src/backends/sentry_backend_breakpad.cpp +++ b/src/backends/sentry_backend_breakpad.cpp @@ -192,6 +192,15 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); + + // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the + // cache directory + if (options->caching_mode == SENTRY_CACHE_KEEP) { + SENTRY_DEBUGF( + "writing crash envelope to cache path: \"%s\"", + options->run->cache_path->path); + sentry__run_write_cache(options->run, envelope); + } } // now that the envelope was written, we can remove the temporary diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 8a8755a75..a80188657 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -691,6 +691,15 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); + + // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the + // cache directory + if (options->caching_mode == SENTRY_CACHE_KEEP) { + SENTRY_DEBUGF( + "writing crash envelope to cache path: \"%s\"", + options->run->cache_path->path); + sentry__run_write_cache(options->run, envelope); + } } } else { SENTRY_DEBUG("event was discarded by the `on_crash` hook"); diff --git a/src/sentry_database.c b/src/sentry_database.c index 337fafeab..a00b85db9 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -49,12 +49,23 @@ sentry__run_new(const sentry_path_t *database_path) return NULL; } + // `/cache` + sentry_path_t *cache_path = sentry__path_join_str(database_path, "cache"); + if (!cache_path) { + sentry__path_free(run_path); + sentry__path_free(lock_path); + sentry__path_free(session_path); + sentry__path_free(external_path); + return NULL; + } + sentry_run_t *run = SENTRY_MAKE(sentry_run_t); if (!run) { sentry__path_free(run_path); sentry__path_free(session_path); sentry__path_free(lock_path); sentry__path_free(external_path); + sentry__path_free(cache_path); return NULL; } @@ -62,6 +73,7 @@ sentry__run_new(const sentry_path_t *database_path) run->run_path = run_path; run->session_path = session_path; run->external_path = external_path; + run->cache_path = cache_path; run->lock = sentry__filelock_new(lock_path); if (!run->lock) { goto error; @@ -95,6 +107,7 @@ sentry__run_free(sentry_run_t *run) sentry__path_free(run->run_path); sentry__path_free(run->session_path); sentry__path_free(run->external_path); + sentry__path_free(run->cache_path); sentry__filelock_free(run->lock); sentry_free(run); } @@ -143,6 +156,18 @@ sentry__run_write_external( return write_envelope(run->external_path, envelope); } +bool +sentry__run_write_cache( + const sentry_run_t *run, const sentry_envelope_t *envelope) +{ + if (sentry__path_create_dir_all(run->cache_path) != 0) { + SENTRY_ERRORF("mkdir failed: \"%s\"", run->cache_path->path); + return false; + } + + return write_envelope(run->cache_path, envelope); +} + bool sentry__run_write_session( const sentry_run_t *run, const sentry_session_t *session) diff --git a/src/sentry_database.h b/src/sentry_database.h index 0cc688907..d8b4476c1 100644 --- a/src/sentry_database.h +++ b/src/sentry_database.h @@ -11,6 +11,7 @@ typedef struct sentry_run_s { sentry_path_t *run_path; sentry_path_t *session_path; sentry_path_t *external_path; + sentry_path_t *cache_path; sentry_filelock_t *lock; } sentry_run_t; @@ -50,6 +51,14 @@ bool sentry__run_write_envelope( bool sentry__run_write_external( const sentry_run_t *run, const sentry_envelope_t *envelope); +/** + * This will serialize and write the given envelope to disk into a file named + * like so: + * `/cache/.envelope` + */ +bool sentry__run_write_cache( + const sentry_run_t *run, const sentry_envelope_t *envelope); + /** * This will serialize and write the given session to disk into a file named: * `/.run/session.json` From 29678221aafe568654699343597fc2a14eccd781 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:26:42 +0100 Subject: [PATCH 04/11] add event caching for Crashpad (no .dmp yet) --- src/backends/sentry_backend_crashpad.cpp | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 181e3d6c4..004f8c394 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -217,6 +217,29 @@ flush_scope_to_event(const sentry_path_t *event_path, } int rv = sentry__path_write_buffer(event_path, mpack, mpack_size); + + // If SENTRY_CACHE_KEEP is enabled, also write to cache directory + if (rv == 0 && options->caching_mode == SENTRY_CACHE_KEEP) { + auto state = static_cast(options->backend->data); + char uuid_str[37]; + sentry_uuid_as_string(&state->crash_event_id, uuid_str); + + sentry_path_t *crash_cache_dir + = sentry__path_join_str(options->run->cache_path, uuid_str); + if (crash_cache_dir) { + if (sentry__path_create_dir_all(crash_cache_dir) == 0) { + sentry_path_t *cache_event_path = sentry__path_join_str( + crash_cache_dir, sentry__path_filename(event_path)); + if (cache_event_path) { + sentry__path_write_buffer( + cache_event_path, mpack, mpack_size); + sentry__path_free(cache_event_path); + } + } + sentry__path_free(crash_cache_dir); + } + } + sentry_free(mpack); if (rv != 0) { @@ -378,6 +401,15 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); + + // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the + // cache directory + if (options->caching_mode == SENTRY_CACHE_KEEP) { + SENTRY_DEBUGF( + "writing session envelope to cache path: \"%s\"", + options->run->cache_path->path); + sentry__run_write_cache(options->run, envelope); + } } } else { SENTRY_DEBUG("event was discarded"); @@ -669,6 +701,33 @@ crashpad_backend_add_breadcrumb(sentry_backend_t *backend, int rv = first_breadcrumb ? sentry__path_write_buffer(breadcrumb_file, mpack, mpack_size) : sentry__path_append_buffer(breadcrumb_file, mpack, mpack_size); + + // If SENTRY_CACHE_KEEP is enabled, also write to cache directory + if (rv == 0 && options->caching_mode == SENTRY_CACHE_KEEP) { + char uuid_str[37]; + sentry_uuid_as_string(&data->crash_event_id, uuid_str); + + sentry_path_t *crash_cache_dir + = sentry__path_join_str(options->run->cache_path, uuid_str); + if (crash_cache_dir) { + if (sentry__path_create_dir_all(crash_cache_dir) == 0) { + sentry_path_t *cache_breadcrumb_path = sentry__path_join_str( + crash_cache_dir, sentry__path_filename(breadcrumb_file)); + if (cache_breadcrumb_path) { + if (first_breadcrumb) { + sentry__path_write_buffer( + cache_breadcrumb_path, mpack, mpack_size); + } else { + sentry__path_append_buffer( + cache_breadcrumb_path, mpack, mpack_size); + } + sentry__path_free(cache_breadcrumb_path); + } + } + sentry__path_free(crash_cache_dir); + } + } + sentry_free(mpack); if (rv != 0) { From 2135f0f196531788f0fdfecb53a1e9c16a3b746f Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:37:14 +0100 Subject: [PATCH 05/11] revert original implementation + add caching on old run processing --- examples/example.c | 2 +- include/sentry.h | 21 +++------ src/backends/sentry_backend_breakpad.cpp | 9 ---- src/backends/sentry_backend_inproc.c | 9 ---- src/path/sentry_path_unix.c | 8 ++++ src/path/sentry_path_windows.c | 12 ++++++ src/sentry_database.c | 54 ++++++++++++------------ src/sentry_database.h | 10 ----- src/sentry_options.c | 15 ++++--- src/sentry_options.h | 5 ++- src/sentry_path.h | 7 +++ 11 files changed, 72 insertions(+), 80 deletions(-) diff --git a/examples/example.c b/examples/example.c index 772e6b02f..fd7f5cf11 100644 --- a/examples/example.c +++ b/examples/example.c @@ -504,7 +504,7 @@ main(int argc, char **argv) sentry_options_set_logs_with_attributes(options, true); } if (has_arg(argc, argv, "cache-keep")) { - sentry_options_set_caching_mode(options, SENTRY_CACHE_KEEP); + sentry_options_set_cache_keep(options, true); } if (0 != sentry_init(options)) { diff --git a/include/sentry.h b/include/sentry.h index 158263356..6c936a7b7 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -953,15 +953,6 @@ typedef enum { SENTRY_HANDLER_STRATEGY_CHAIN_AT_START = 1, } sentry_handler_strategy_t; -/** - * The caching mode for crash reports. - */ -typedef enum { - SENTRY_CACHE_DEFAULT = -1, // (default) No special caching enabled - SENTRY_CACHE_KEEP = 0, // TODO Keep events/dmps around in secondary folder - SENTRY_CACHE_OFFLINE = 1, // TODO Periodically checks if online -} sentry_caching_mode_t; - /** * Creates a new options struct. * Can be freed with `sentry_options_free`. @@ -1384,18 +1375,16 @@ SENTRY_API int sentry_options_get_symbolize_stacktraces( const sentry_options_t *opts); /** - * Sets the caching mode for crash reports. - * - * This controls how the SDK handles cached crash reports and offline scenarios. + * Sets whether we should keep files cached even when sent successfully. + * The database will be cleared based on cache_max_size and cache_max_age */ -SENTRY_API void sentry_options_set_caching_mode( - sentry_options_t *opts, sentry_caching_mode_t mode); +SENTRY_API void sentry_options_set_cache_keep( + sentry_options_t *opts, int enabled); /** * Gets the caching mode for crash reports. */ -SENTRY_API sentry_caching_mode_t sentry_options_get_caching_mode( - const sentry_options_t *opts); +SENTRY_API int sentry_options_get_cache_keep(const sentry_options_t *opts); /** * Adds a new attachment to be sent along. diff --git a/src/backends/sentry_backend_breakpad.cpp b/src/backends/sentry_backend_breakpad.cpp index 63652534a..1c737ce55 100644 --- a/src/backends/sentry_backend_breakpad.cpp +++ b/src/backends/sentry_backend_breakpad.cpp @@ -192,15 +192,6 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); - - // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the - // cache directory - if (options->caching_mode == SENTRY_CACHE_KEEP) { - SENTRY_DEBUGF( - "writing crash envelope to cache path: \"%s\"", - options->run->cache_path->path); - sentry__run_write_cache(options->run, envelope); - } } // now that the envelope was written, we can remove the temporary diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index a80188657..8a8755a75 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -691,15 +691,6 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); - - // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the - // cache directory - if (options->caching_mode == SENTRY_CACHE_KEEP) { - SENTRY_DEBUGF( - "writing crash envelope to cache path: \"%s\"", - options->run->cache_path->path); - sentry__run_write_cache(options->run, envelope); - } } } else { SENTRY_DEBUG("event was discarded by the `on_crash` hook"); diff --git a/src/path/sentry_path_unix.c b/src/path/sentry_path_unix.c index 7b7f63fa3..e831a2b22 100644 --- a/src/path/sentry_path_unix.c +++ b/src/path/sentry_path_unix.c @@ -324,6 +324,14 @@ sentry__path_remove(const sentry_path_t *path) return 1; } +int +sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst) +{ + int status; + EINTR_RETRY(rename(src->path, dst->path), &status); + return status == 0 ? 0 : 1; +} + int sentry__path_create_dir_all(const sentry_path_t *path) { diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index 5b76497f4..bd8269a6e 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -511,6 +511,18 @@ sentry__path_remove(const sentry_path_t *path) return removal_success ? 0 : !is_last_error_path_not_found(); } +int +sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst) +{ + wchar_t *src_w = src->path_w; + wchar_t *dst_w = dst->path_w; + if (!src_w || !dst_w) { + return 1; + } + // MOVEFILE_REPLACE_EXISTING allows overwriting the destination if it exists + return MoveFileExW(src_w, dst_w, MOVEFILE_REPLACE_EXISTING) ? 0 : 1; +} + int sentry__path_create_dir_all(const sentry_path_t *path) { diff --git a/src/sentry_database.c b/src/sentry_database.c index a00b85db9..f03605e09 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -49,23 +49,12 @@ sentry__run_new(const sentry_path_t *database_path) return NULL; } - // `/cache` - sentry_path_t *cache_path = sentry__path_join_str(database_path, "cache"); - if (!cache_path) { - sentry__path_free(run_path); - sentry__path_free(lock_path); - sentry__path_free(session_path); - sentry__path_free(external_path); - return NULL; - } - sentry_run_t *run = SENTRY_MAKE(sentry_run_t); if (!run) { sentry__path_free(run_path); sentry__path_free(session_path); sentry__path_free(lock_path); sentry__path_free(external_path); - sentry__path_free(cache_path); return NULL; } @@ -73,7 +62,6 @@ sentry__run_new(const sentry_path_t *database_path) run->run_path = run_path; run->session_path = session_path; run->external_path = external_path; - run->cache_path = cache_path; run->lock = sentry__filelock_new(lock_path); if (!run->lock) { goto error; @@ -107,7 +95,6 @@ sentry__run_free(sentry_run_t *run) sentry__path_free(run->run_path); sentry__path_free(run->session_path); sentry__path_free(run->external_path); - sentry__path_free(run->cache_path); sentry__filelock_free(run->lock); sentry_free(run); } @@ -155,19 +142,6 @@ sentry__run_write_external( return write_envelope(run->external_path, envelope); } - -bool -sentry__run_write_cache( - const sentry_run_t *run, const sentry_envelope_t *envelope) -{ - if (sentry__path_create_dir_all(run->cache_path) != 0) { - SENTRY_ERRORF("mkdir failed: \"%s\"", run->cache_path->path); - return false; - } - - return write_envelope(run->cache_path, envelope); -} - bool sentry__run_write_session( const sentry_run_t *run, const sentry_session_t *session) @@ -255,9 +229,26 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) if (strcmp(options->run->run_path->path, run_dir->path) == 0) { continue; } + sentry_path_t *cached_run_dir = NULL; + if (options->cache_keep) { + sentry_path_t *cache_dir + = sentry__path_join_str(options->database_path, "cache"); + sentry__path_create_dir_all(cache_dir); + cached_run_dir = sentry__path_join_str( + cache_dir, sentry__path_filename(run_dir)); + sentry__path_create_dir_all(cached_run_dir); + sentry__path_free(cache_dir); + } + sentry_pathiter_t *run_iter = sentry__path_iter_directory(run_dir); const sentry_path_t *file; while (run_iter && (file = sentry__pathiter_next(run_iter)) != NULL) { + sentry_path_t *cached_file = NULL; + if (options->cache_keep) { + cached_file = sentry__path_join_str( + cached_run_dir, sentry__path_filename(file)); + } + if (sentry__path_filename_matches(file, "session.json")) { if (!session_envelope) { session_envelope = sentry__envelope_new(); @@ -301,10 +292,19 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) sentry__capture_envelope(options->transport, envelope); } - sentry__path_remove(file); + if (options->cache_keep) { + sentry__path_rename(file, cached_file); + sentry__path_free(cached_file); + } else { + sentry__path_remove(file); + } } sentry__pathiter_free(run_iter); + if (options->cache_keep) { + sentry__path_free(cached_run_dir); + } + sentry__path_remove_all(run_dir); sentry__filelock_free(lock); } diff --git a/src/sentry_database.h b/src/sentry_database.h index d8b4476c1..00631454d 100644 --- a/src/sentry_database.h +++ b/src/sentry_database.h @@ -11,7 +11,6 @@ typedef struct sentry_run_s { sentry_path_t *run_path; sentry_path_t *session_path; sentry_path_t *external_path; - sentry_path_t *cache_path; sentry_filelock_t *lock; } sentry_run_t; @@ -50,15 +49,6 @@ bool sentry__run_write_envelope( */ bool sentry__run_write_external( const sentry_run_t *run, const sentry_envelope_t *envelope); - -/** - * This will serialize and write the given envelope to disk into a file named - * like so: - * `/cache/.envelope` - */ -bool sentry__run_write_cache( - const sentry_run_t *run, const sentry_envelope_t *envelope); - /** * This will serialize and write the given session to disk into a file named: * `/.run/session.json` diff --git a/src/sentry_options.c b/src/sentry_options.c index 9f4e88f66..7a2fd72bf 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -53,7 +53,9 @@ sentry_options_new(void) opts->enable_logging_when_crashed = true; opts->propagate_traceparent = false; opts->crashpad_limit_stack_capture_to_sp = false; - opts->caching_mode = SENTRY_CACHE_DEFAULT; + opts->cache_keep = false; + opts->cache_max_age = 2; + opts->cache_max_size = 1024 * 8; opts->symbolize_stacktraces = // AIX doesn't have reliable debug IDs for server-side symbolication, // and the diversity of Android makes it infeasible to have access to debug @@ -477,16 +479,15 @@ sentry_options_get_symbolize_stacktraces(const sentry_options_t *opts) } void -sentry_options_set_caching_mode( - sentry_options_t *opts, sentry_caching_mode_t mode) +sentry_options_set_cache_keep(sentry_options_t *opts, int enabled) { - opts->caching_mode = mode; + opts->cache_keep = !!enabled; } -sentry_caching_mode_t -sentry_options_get_caching_mode(const sentry_options_t *opts) +int +sentry_options_get_cache_keep(const sentry_options_t *opts) { - return opts->caching_mode; + return opts->cache_keep; } void diff --git a/src/sentry_options.h b/src/sentry_options.h index 45b769c5b..f3aed3846 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -45,7 +45,10 @@ struct sentry_options_s { bool enable_logging_when_crashed; bool propagate_traceparent; bool crashpad_limit_stack_capture_to_sp; - sentry_caching_mode_t caching_mode; + bool cache_keep; + + int cache_max_age; // TODO in days? + int cache_max_size; // TODO in kb? sentry_attachment_t *attachments; sentry_run_t *run; diff --git a/src/sentry_path.h b/src/sentry_path.h index cd5a3a917..484a6f531 100644 --- a/src/sentry_path.h +++ b/src/sentry_path.h @@ -152,6 +152,13 @@ int sentry__path_remove(const sentry_path_t *path); */ int sentry__path_remove_all(const sentry_path_t *path); +/** + * Rename/move the file or directory from `src` to `dst`. + * This will overwrite `dst` if it already exists. + * Returns 0 on success. + */ +int sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst); + /** * This will create the directory referred to by `path`, and any non-existing * parent directory. From 3597a36b913738907925423b759be2560344e9a2 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 18 Dec 2025 14:21:10 +0100 Subject: [PATCH 06/11] add cache cleanup --- examples/example.c | 1 + include/sentry.h | 9 ++ src/backends/sentry_backend_crashpad.cpp | 72 ++---------- src/sentry_core.c | 4 + src/sentry_database.c | 140 +++++++++++++++++++++++ src/sentry_database.h | 5 + src/sentry_options.c | 12 ++ 7 files changed, 179 insertions(+), 64 deletions(-) diff --git a/examples/example.c b/examples/example.c index fd7f5cf11..cf32fda5a 100644 --- a/examples/example.c +++ b/examples/example.c @@ -505,6 +505,7 @@ main(int argc, char **argv) } if (has_arg(argc, argv, "cache-keep")) { sentry_options_set_cache_keep(options, true); + sentry_options_set_cache_max_size(options, 160); } if (0 != sentry_init(options)) { diff --git a/include/sentry.h b/include/sentry.h index 6c936a7b7..3f432da95 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1380,6 +1380,15 @@ SENTRY_API int sentry_options_get_symbolize_stacktraces( */ SENTRY_API void sentry_options_set_cache_keep( sentry_options_t *opts, int enabled); +/** + * Sets the maximum size (kb)/age (days) for the cache folder. + * On startup, we check new->old entries, and remove those that go over either + * boundary. + */ +SENTRY_API void sentry_options_set_cache_max_size( + sentry_options_t *opts, int size); +SENTRY_API void sentry_options_set_cache_max_age( + sentry_options_t *opts, int age); /** * Gets the caching mode for crash reports. diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 004f8c394..603ebb753 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -217,29 +217,6 @@ flush_scope_to_event(const sentry_path_t *event_path, } int rv = sentry__path_write_buffer(event_path, mpack, mpack_size); - - // If SENTRY_CACHE_KEEP is enabled, also write to cache directory - if (rv == 0 && options->caching_mode == SENTRY_CACHE_KEEP) { - auto state = static_cast(options->backend->data); - char uuid_str[37]; - sentry_uuid_as_string(&state->crash_event_id, uuid_str); - - sentry_path_t *crash_cache_dir - = sentry__path_join_str(options->run->cache_path, uuid_str); - if (crash_cache_dir) { - if (sentry__path_create_dir_all(crash_cache_dir) == 0) { - sentry_path_t *cache_event_path = sentry__path_join_str( - crash_cache_dir, sentry__path_filename(event_path)); - if (cache_event_path) { - sentry__path_write_buffer( - cache_event_path, mpack, mpack_size); - sentry__path_free(cache_event_path); - } - } - sentry__path_free(crash_cache_dir); - } - } - sentry_free(mpack); if (rv != 0) { @@ -401,15 +378,6 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) sentry__capture_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); - - // If SENTRY_CACHE_KEEP mode is enabled, write a copy to the - // cache directory - if (options->caching_mode == SENTRY_CACHE_KEEP) { - SENTRY_DEBUGF( - "writing session envelope to cache path: \"%s\"", - options->run->cache_path->path); - sentry__run_write_cache(options->run, envelope); - } } } else { SENTRY_DEBUG("event was discarded"); @@ -701,33 +669,6 @@ crashpad_backend_add_breadcrumb(sentry_backend_t *backend, int rv = first_breadcrumb ? sentry__path_write_buffer(breadcrumb_file, mpack, mpack_size) : sentry__path_append_buffer(breadcrumb_file, mpack, mpack_size); - - // If SENTRY_CACHE_KEEP is enabled, also write to cache directory - if (rv == 0 && options->caching_mode == SENTRY_CACHE_KEEP) { - char uuid_str[37]; - sentry_uuid_as_string(&data->crash_event_id, uuid_str); - - sentry_path_t *crash_cache_dir - = sentry__path_join_str(options->run->cache_path, uuid_str); - if (crash_cache_dir) { - if (sentry__path_create_dir_all(crash_cache_dir) == 0) { - sentry_path_t *cache_breadcrumb_path = sentry__path_join_str( - crash_cache_dir, sentry__path_filename(breadcrumb_file)); - if (cache_breadcrumb_path) { - if (first_breadcrumb) { - sentry__path_write_buffer( - cache_breadcrumb_path, mpack, mpack_size); - } else { - sentry__path_append_buffer( - cache_breadcrumb_path, mpack, mpack_size); - } - sentry__path_free(cache_breadcrumb_path); - } - } - sentry__path_free(crash_cache_dir); - } - } - sentry_free(mpack); if (rv != 0) { @@ -802,11 +743,14 @@ crashpad_backend_prune_database(sentry_backend_t *backend) // complete database to a maximum of 8M. That might still be a lot for // an embedded use-case, but minidumps on desktop can sometimes be quite // large. - data->db->CleanDatabase(60 * 60 * 24 * 2); - crashpad::BinaryPruneCondition condition(crashpad::BinaryPruneCondition::OR, - new crashpad::DatabaseSizePruneCondition(1024 * 8), - new crashpad::AgePruneCondition(2)); - crashpad::PruneCrashReportDatabase(data->db, &condition); + SENTRY_WITH_OPTIONS (options) { + data->db->CleanDatabase(60 * 60 * 24 * options->cache_max_age); + crashpad::BinaryPruneCondition condition( + crashpad::BinaryPruneCondition::OR, + new crashpad::DatabaseSizePruneCondition(options->cache_max_size), + new crashpad::AgePruneCondition(options->cache_max_age)); + crashpad::PruneCrashReportDatabase(data->db, &condition); + } } #if defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_LINUX) diff --git a/src/sentry_core.c b/src/sentry_core.c index 16b3146f0..486a9b7e4 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -288,6 +288,10 @@ sentry_init(sentry_options_t *options) backend->prune_database_func(backend); } + if (options->cache_keep) { + sentry__cleanup_cache(options); + } + if (options->auto_session_tracking) { sentry_start_session(); } diff --git a/src/sentry_database.c b/src/sentry_database.c index f03605e09..8ed75545d 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -6,6 +6,7 @@ #include "sentry_session.h" #include "sentry_uuid.h" #include +#include #include sentry_run_t * @@ -313,6 +314,145 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) sentry__capture_envelope(options->transport, session_envelope); } +// Cache Pruning below is based on prune_crash_reports.cc from Crashpad + +/** + * A cache entry with its metadata for sorting and pruning decisions. + */ +typedef struct { + sentry_path_t *path; + time_t mtime; + size_t size_in_kb; +} cache_entry_t; + +/** + * Calculate the total size of a directory (sum of all files). + */ +static size_t +get_directory_size_in_kb(const sentry_path_t *dir) +{ + size_t total_bytes = 0; + sentry_pathiter_t *iter = sentry__path_iter_directory(dir); + if (!iter) { + return 0; + } + const sentry_path_t *entry; + while ((entry = sentry__pathiter_next(iter)) != NULL) { + if (sentry__path_is_file(entry)) { + total_bytes += sentry__path_get_size(entry); + } + } + sentry__pathiter_free(iter); + // Round up to next KB boundary + return (total_bytes + 1023) / 1024; +} + +/** + * Comparison function to sort cache entries by mtime, newest first. + */ +static int +compare_cache_entries_newest_first(const void *a, const void *b) +{ + const cache_entry_t *entry_a = (const cache_entry_t *)a; + const cache_entry_t *entry_b = (const cache_entry_t *)b; + // Newest first: if b is newer, return positive (b comes before a) + if (entry_b->mtime > entry_a->mtime) { + return 1; + } + if (entry_b->mtime < entry_a->mtime) { + return -1; + } + return 0; +} + +void +sentry__cleanup_cache(const sentry_options_t *options) +{ + if (!options->database_path) { + return; + } + + sentry_path_t *cache_dir + = sentry__path_join_str(options->database_path, "cache"); + if (!sentry__path_is_dir(cache_dir)) { + sentry__path_free(cache_dir); + return; + } + + // First pass: collect all cache entries with their metadata + size_t entries_capacity = 16; + size_t entries_count = 0; + cache_entry_t *entries + = sentry_malloc(sizeof(cache_entry_t) * entries_capacity); + if (!entries) { + sentry__path_free(cache_dir); + return; + } + + sentry_pathiter_t *iter = sentry__path_iter_directory(cache_dir); + const sentry_path_t *entry; + while (iter && (entry = sentry__pathiter_next(iter)) != NULL) { + if (!sentry__path_is_dir(entry)) { + continue; + } + + // Grow array if needed + if (entries_count >= entries_capacity) { + entries_capacity *= 2; + cache_entry_t *new_entries + = sentry_malloc(sizeof(cache_entry_t) * entries_capacity); + if (!new_entries) { + break; + } + memcpy(new_entries, entries, sizeof(cache_entry_t) * entries_count); + sentry_free(entries); + entries = new_entries; + } + + entries[entries_count].path = sentry__path_clone(entry); + entries[entries_count].mtime = sentry__path_get_mtime(entry); + entries[entries_count].size_in_kb = get_directory_size_in_kb(entry); + entries_count++; + } + sentry__pathiter_free(iter); + + // Sort by mtime, newest first (like crashpad) + // This ensures we keep the newest entries when pruning by size + qsort(entries, entries_count, sizeof(cache_entry_t), + compare_cache_entries_newest_first); + + // Calculate the age threshold + time_t now = time(NULL); + time_t oldest_allowed = now - (options->cache_max_age * 24 * 60 * 60); + + // Prune entries: iterate newest-to-oldest, accumulating size + // Remove if: too old OR accumulated size exceeds limit + size_t accumulated_size_kb = 0; + for (size_t i = 0; i < entries_count; i++) { + bool should_prune = false; + + // Age-based pruning + if (options->cache_max_age > 0 && entries[i].mtime < oldest_allowed) { + should_prune = true; + } + + // Size-based pruning (accumulate size as we go, like crashpad) + accumulated_size_kb += entries[i].size_in_kb; + if (options->cache_max_size > 0 + && accumulated_size_kb > (size_t)options->cache_max_size) { + should_prune = true; + } + + if (should_prune) { + sentry__path_remove_all(entries[i].path); + } + sentry__path_free(entries[i].path); + } + + sentry_free(entries); + sentry__path_free(cache_dir); +} + static const char *g_last_crash_filename = "last_crash"; bool diff --git a/src/sentry_database.h b/src/sentry_database.h index 00631454d..766036941 100644 --- a/src/sentry_database.h +++ b/src/sentry_database.h @@ -76,6 +76,11 @@ bool sentry__run_clear_session(const sentry_run_t *run); */ void sentry__process_old_runs( const sentry_options_t *options, uint64_t last_crash); +/** + * Cleans up the cache based on options.max_cache_size and + * options.max_cache_age. + */ +void sentry__cleanup_cache(const sentry_options_t *options); /** * This will write the current ISO8601 formatted timestamp into the diff --git a/src/sentry_options.c b/src/sentry_options.c index 7a2fd72bf..aeb7fb3d0 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -484,6 +484,18 @@ sentry_options_set_cache_keep(sentry_options_t *opts, int enabled) opts->cache_keep = !!enabled; } +void +sentry_options_set_cache_max_size(sentry_options_t *opts, int size) +{ + opts->cache_max_size = size; +} + +void +sentry_options_set_cache_max_age(sentry_options_t *opts, int age) +{ + opts->cache_max_age = age; +} + int sentry_options_get_cache_keep(const sentry_options_t *opts) { From 3295d4128cd54facb8cb6d38799aa541f6796df6 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:08:47 +0100 Subject: [PATCH 07/11] keep .dmp file for breakpad on cache_keep --- examples/example.c | 2 +- src/backends/sentry_backend_breakpad.cpp | 4 +++- src/sentry_options.c | 2 +- src/sentry_options.h | 2 +- tests/unit/tests.inc | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/example.c b/examples/example.c index cf32fda5a..0890fa5b3 100644 --- a/examples/example.c +++ b/examples/example.c @@ -505,7 +505,7 @@ main(int argc, char **argv) } if (has_arg(argc, argv, "cache-keep")) { sentry_options_set_cache_keep(options, true); - sentry_options_set_cache_max_size(options, 160); + sentry_options_set_cache_max_size(options, 1000 * 8); } if (0 != sentry_init(options)) { diff --git a/src/backends/sentry_backend_breakpad.cpp b/src/backends/sentry_backend_breakpad.cpp index 1c737ce55..1a6b42a22 100644 --- a/src/backends/sentry_backend_breakpad.cpp +++ b/src/backends/sentry_backend_breakpad.cpp @@ -196,7 +196,9 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, // now that the envelope was written, we can remove the temporary // minidump file - sentry__path_remove(dump_path); + if (!options->cache_keep) { + sentry__path_remove(dump_path); + } sentry__path_free(dump_path); } else { SENTRY_DEBUG("event was discarded by the `on_crash` hook"); diff --git a/src/sentry_options.c b/src/sentry_options.c index aeb7fb3d0..431778894 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -485,7 +485,7 @@ sentry_options_set_cache_keep(sentry_options_t *opts, int enabled) } void -sentry_options_set_cache_max_size(sentry_options_t *opts, int size) +sentry_options_set_cache_max_size(sentry_options_t *opts, size_t size) { opts->cache_max_size = size; } diff --git a/src/sentry_options.h b/src/sentry_options.h index f3aed3846..306566a11 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -48,7 +48,7 @@ struct sentry_options_s { bool cache_keep; int cache_max_age; // TODO in days? - int cache_max_size; // TODO in kb? + size_t cache_max_size; // TODO in kb? sentry_attachment_t *attachments; sentry_run_t *run; diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index f8fa3c920..d09250663 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -8,7 +8,6 @@ XX(attachments_bytes) XX(attachments_extend) XX(background_worker) XX(basic_consent_tracking) -XX(query_consent_requirement) XX(basic_function_transport) XX(basic_function_transport_transaction) XX(basic_function_transport_transaction_ts) @@ -122,6 +121,7 @@ XX(process_invalid) XX(process_spawn) XX(procmaps_parser) XX(propagation_context_init) +XX(query_consent_requirement) XX(rate_limit_parsing) XX(read_envelope_from_file) XX(read_write_envelope_to_file_null) From 8e7f904470156f2567adf061ff30a12cc6f05211 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:34:18 +0100 Subject: [PATCH 08/11] fix function signature --- include/sentry.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sentry.h b/include/sentry.h index 3f432da95..9b96dc402 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1386,7 +1386,7 @@ SENTRY_API void sentry_options_set_cache_keep( * boundary. */ SENTRY_API void sentry_options_set_cache_max_size( - sentry_options_t *opts, int size); + sentry_options_t *opts, size_t size); SENTRY_API void sentry_options_set_cache_max_age( sentry_options_t *opts, int age); From a4bf345042ed774f1af3e64f8118a16cd1851822 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:18:21 +0100 Subject: [PATCH 09/11] revert separate caching folder implementation - for now, we just have an option to keep dmp files on crash (for Breakpad) --- examples/example.c | 3 +- include/sentry.h | 19 +-- src/backends/sentry_backend_breakpad.cpp | 2 +- src/sentry_core.c | 4 - src/sentry_database.c | 168 +---------------------- src/sentry_database.h | 5 - src/sentry_options.c | 26 +--- src/sentry_options.h | 5 +- 8 files changed, 9 insertions(+), 223 deletions(-) diff --git a/examples/example.c b/examples/example.c index 0890fa5b3..9a720cab1 100644 --- a/examples/example.c +++ b/examples/example.c @@ -504,8 +504,7 @@ main(int argc, char **argv) sentry_options_set_logs_with_attributes(options, true); } if (has_arg(argc, argv, "cache-keep")) { - sentry_options_set_cache_keep(options, true); - sentry_options_set_cache_max_size(options, 1000 * 8); + sentry_options_set_keep_dmp_on_crash(options, true); } if (0 != sentry_init(options)) { diff --git a/include/sentry.h b/include/sentry.h index 9b96dc402..ce25b919b 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1375,25 +1375,10 @@ SENTRY_API int sentry_options_get_symbolize_stacktraces( const sentry_options_t *opts); /** - * Sets whether we should keep files cached even when sent successfully. - * The database will be cleared based on cache_max_size and cache_max_age + * Sets whether we should keep .dmp files for breakpad crashes */ -SENTRY_API void sentry_options_set_cache_keep( +SENTRY_API void sentry_options_set_keep_dmp_on_crash( sentry_options_t *opts, int enabled); -/** - * Sets the maximum size (kb)/age (days) for the cache folder. - * On startup, we check new->old entries, and remove those that go over either - * boundary. - */ -SENTRY_API void sentry_options_set_cache_max_size( - sentry_options_t *opts, size_t size); -SENTRY_API void sentry_options_set_cache_max_age( - sentry_options_t *opts, int age); - -/** - * Gets the caching mode for crash reports. - */ -SENTRY_API int sentry_options_get_cache_keep(const sentry_options_t *opts); /** * Adds a new attachment to be sent along. diff --git a/src/backends/sentry_backend_breakpad.cpp b/src/backends/sentry_backend_breakpad.cpp index 1a6b42a22..5c1de136b 100644 --- a/src/backends/sentry_backend_breakpad.cpp +++ b/src/backends/sentry_backend_breakpad.cpp @@ -196,7 +196,7 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, // now that the envelope was written, we can remove the temporary // minidump file - if (!options->cache_keep) { + if (!options->keep_dmp_on_crash) { sentry__path_remove(dump_path); } sentry__path_free(dump_path); diff --git a/src/sentry_core.c b/src/sentry_core.c index 486a9b7e4..16b3146f0 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -288,10 +288,6 @@ sentry_init(sentry_options_t *options) backend->prune_database_func(backend); } - if (options->cache_keep) { - sentry__cleanup_cache(options); - } - if (options->auto_session_tracking) { sentry_start_session(); } diff --git a/src/sentry_database.c b/src/sentry_database.c index 8ed75545d..619318cfc 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -6,7 +6,6 @@ #include "sentry_session.h" #include "sentry_uuid.h" #include -#include #include sentry_run_t * @@ -230,26 +229,9 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) if (strcmp(options->run->run_path->path, run_dir->path) == 0) { continue; } - sentry_path_t *cached_run_dir = NULL; - if (options->cache_keep) { - sentry_path_t *cache_dir - = sentry__path_join_str(options->database_path, "cache"); - sentry__path_create_dir_all(cache_dir); - cached_run_dir = sentry__path_join_str( - cache_dir, sentry__path_filename(run_dir)); - sentry__path_create_dir_all(cached_run_dir); - sentry__path_free(cache_dir); - } - sentry_pathiter_t *run_iter = sentry__path_iter_directory(run_dir); const sentry_path_t *file; while (run_iter && (file = sentry__pathiter_next(run_iter)) != NULL) { - sentry_path_t *cached_file = NULL; - if (options->cache_keep) { - cached_file = sentry__path_join_str( - cached_run_dir, sentry__path_filename(file)); - } - if (sentry__path_filename_matches(file, "session.json")) { if (!session_envelope) { session_envelope = sentry__envelope_new(); @@ -293,19 +275,10 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) sentry__capture_envelope(options->transport, envelope); } - if (options->cache_keep) { - sentry__path_rename(file, cached_file); - sentry__path_free(cached_file); - } else { - sentry__path_remove(file); - } + sentry__path_remove(file); } sentry__pathiter_free(run_iter); - if (options->cache_keep) { - sentry__path_free(cached_run_dir); - } - sentry__path_remove_all(run_dir); sentry__filelock_free(lock); } @@ -314,145 +287,6 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) sentry__capture_envelope(options->transport, session_envelope); } -// Cache Pruning below is based on prune_crash_reports.cc from Crashpad - -/** - * A cache entry with its metadata for sorting and pruning decisions. - */ -typedef struct { - sentry_path_t *path; - time_t mtime; - size_t size_in_kb; -} cache_entry_t; - -/** - * Calculate the total size of a directory (sum of all files). - */ -static size_t -get_directory_size_in_kb(const sentry_path_t *dir) -{ - size_t total_bytes = 0; - sentry_pathiter_t *iter = sentry__path_iter_directory(dir); - if (!iter) { - return 0; - } - const sentry_path_t *entry; - while ((entry = sentry__pathiter_next(iter)) != NULL) { - if (sentry__path_is_file(entry)) { - total_bytes += sentry__path_get_size(entry); - } - } - sentry__pathiter_free(iter); - // Round up to next KB boundary - return (total_bytes + 1023) / 1024; -} - -/** - * Comparison function to sort cache entries by mtime, newest first. - */ -static int -compare_cache_entries_newest_first(const void *a, const void *b) -{ - const cache_entry_t *entry_a = (const cache_entry_t *)a; - const cache_entry_t *entry_b = (const cache_entry_t *)b; - // Newest first: if b is newer, return positive (b comes before a) - if (entry_b->mtime > entry_a->mtime) { - return 1; - } - if (entry_b->mtime < entry_a->mtime) { - return -1; - } - return 0; -} - -void -sentry__cleanup_cache(const sentry_options_t *options) -{ - if (!options->database_path) { - return; - } - - sentry_path_t *cache_dir - = sentry__path_join_str(options->database_path, "cache"); - if (!sentry__path_is_dir(cache_dir)) { - sentry__path_free(cache_dir); - return; - } - - // First pass: collect all cache entries with their metadata - size_t entries_capacity = 16; - size_t entries_count = 0; - cache_entry_t *entries - = sentry_malloc(sizeof(cache_entry_t) * entries_capacity); - if (!entries) { - sentry__path_free(cache_dir); - return; - } - - sentry_pathiter_t *iter = sentry__path_iter_directory(cache_dir); - const sentry_path_t *entry; - while (iter && (entry = sentry__pathiter_next(iter)) != NULL) { - if (!sentry__path_is_dir(entry)) { - continue; - } - - // Grow array if needed - if (entries_count >= entries_capacity) { - entries_capacity *= 2; - cache_entry_t *new_entries - = sentry_malloc(sizeof(cache_entry_t) * entries_capacity); - if (!new_entries) { - break; - } - memcpy(new_entries, entries, sizeof(cache_entry_t) * entries_count); - sentry_free(entries); - entries = new_entries; - } - - entries[entries_count].path = sentry__path_clone(entry); - entries[entries_count].mtime = sentry__path_get_mtime(entry); - entries[entries_count].size_in_kb = get_directory_size_in_kb(entry); - entries_count++; - } - sentry__pathiter_free(iter); - - // Sort by mtime, newest first (like crashpad) - // This ensures we keep the newest entries when pruning by size - qsort(entries, entries_count, sizeof(cache_entry_t), - compare_cache_entries_newest_first); - - // Calculate the age threshold - time_t now = time(NULL); - time_t oldest_allowed = now - (options->cache_max_age * 24 * 60 * 60); - - // Prune entries: iterate newest-to-oldest, accumulating size - // Remove if: too old OR accumulated size exceeds limit - size_t accumulated_size_kb = 0; - for (size_t i = 0; i < entries_count; i++) { - bool should_prune = false; - - // Age-based pruning - if (options->cache_max_age > 0 && entries[i].mtime < oldest_allowed) { - should_prune = true; - } - - // Size-based pruning (accumulate size as we go, like crashpad) - accumulated_size_kb += entries[i].size_in_kb; - if (options->cache_max_size > 0 - && accumulated_size_kb > (size_t)options->cache_max_size) { - should_prune = true; - } - - if (should_prune) { - sentry__path_remove_all(entries[i].path); - } - sentry__path_free(entries[i].path); - } - - sentry_free(entries); - sentry__path_free(cache_dir); -} - static const char *g_last_crash_filename = "last_crash"; bool diff --git a/src/sentry_database.h b/src/sentry_database.h index 766036941..00631454d 100644 --- a/src/sentry_database.h +++ b/src/sentry_database.h @@ -76,11 +76,6 @@ bool sentry__run_clear_session(const sentry_run_t *run); */ void sentry__process_old_runs( const sentry_options_t *options, uint64_t last_crash); -/** - * Cleans up the cache based on options.max_cache_size and - * options.max_cache_age. - */ -void sentry__cleanup_cache(const sentry_options_t *options); /** * This will write the current ISO8601 formatted timestamp into the diff --git a/src/sentry_options.c b/src/sentry_options.c index 431778894..4db2c851c 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -53,9 +53,7 @@ sentry_options_new(void) opts->enable_logging_when_crashed = true; opts->propagate_traceparent = false; opts->crashpad_limit_stack_capture_to_sp = false; - opts->cache_keep = false; - opts->cache_max_age = 2; - opts->cache_max_size = 1024 * 8; + opts->keep_dmp_on_crash = false; opts->symbolize_stacktraces = // AIX doesn't have reliable debug IDs for server-side symbolication, // and the diversity of Android makes it infeasible to have access to debug @@ -479,27 +477,9 @@ sentry_options_get_symbolize_stacktraces(const sentry_options_t *opts) } void -sentry_options_set_cache_keep(sentry_options_t *opts, int enabled) +sentry_options_set_keep_dmp_on_crash(sentry_options_t *opts, int enabled) { - opts->cache_keep = !!enabled; -} - -void -sentry_options_set_cache_max_size(sentry_options_t *opts, size_t size) -{ - opts->cache_max_size = size; -} - -void -sentry_options_set_cache_max_age(sentry_options_t *opts, int age) -{ - opts->cache_max_age = age; -} - -int -sentry_options_get_cache_keep(const sentry_options_t *opts) -{ - return opts->cache_keep; + opts->keep_dmp_on_crash = !!enabled; } void diff --git a/src/sentry_options.h b/src/sentry_options.h index 306566a11..bac09de0b 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -45,10 +45,7 @@ struct sentry_options_s { bool enable_logging_when_crashed; bool propagate_traceparent; bool crashpad_limit_stack_capture_to_sp; - bool cache_keep; - - int cache_max_age; // TODO in days? - size_t cache_max_size; // TODO in kb? + bool keep_dmp_on_crash; sentry_attachment_t *attachments; sentry_run_t *run; From e70cb3b87736fcf8b450de986801f0d49e0c7741 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:20:56 +0100 Subject: [PATCH 10/11] cleanup --- src/backends/sentry_backend_crashpad.cpp | 13 +++++-------- src/path/sentry_path_unix.c | 8 -------- src/path/sentry_path_windows.c | 13 ------------- src/sentry_database.c | 1 + src/sentry_database.h | 1 + src/sentry_path.h | 8 -------- 6 files changed, 7 insertions(+), 37 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 603ebb753..181e3d6c4 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -743,14 +743,11 @@ crashpad_backend_prune_database(sentry_backend_t *backend) // complete database to a maximum of 8M. That might still be a lot for // an embedded use-case, but minidumps on desktop can sometimes be quite // large. - SENTRY_WITH_OPTIONS (options) { - data->db->CleanDatabase(60 * 60 * 24 * options->cache_max_age); - crashpad::BinaryPruneCondition condition( - crashpad::BinaryPruneCondition::OR, - new crashpad::DatabaseSizePruneCondition(options->cache_max_size), - new crashpad::AgePruneCondition(options->cache_max_age)); - crashpad::PruneCrashReportDatabase(data->db, &condition); - } + data->db->CleanDatabase(60 * 60 * 24 * 2); + crashpad::BinaryPruneCondition condition(crashpad::BinaryPruneCondition::OR, + new crashpad::DatabaseSizePruneCondition(1024 * 8), + new crashpad::AgePruneCondition(2)); + crashpad::PruneCrashReportDatabase(data->db, &condition); } #if defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_LINUX) diff --git a/src/path/sentry_path_unix.c b/src/path/sentry_path_unix.c index e831a2b22..7b7f63fa3 100644 --- a/src/path/sentry_path_unix.c +++ b/src/path/sentry_path_unix.c @@ -324,14 +324,6 @@ sentry__path_remove(const sentry_path_t *path) return 1; } -int -sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst) -{ - int status; - EINTR_RETRY(rename(src->path, dst->path), &status); - return status == 0 ? 0 : 1; -} - int sentry__path_create_dir_all(const sentry_path_t *path) { diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index bd8269a6e..5dc70a058 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -510,19 +510,6 @@ sentry__path_remove(const sentry_path_t *path) : DeleteFileW(path_w); return removal_success ? 0 : !is_last_error_path_not_found(); } - -int -sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst) -{ - wchar_t *src_w = src->path_w; - wchar_t *dst_w = dst->path_w; - if (!src_w || !dst_w) { - return 1; - } - // MOVEFILE_REPLACE_EXISTING allows overwriting the destination if it exists - return MoveFileExW(src_w, dst_w, MOVEFILE_REPLACE_EXISTING) ? 0 : 1; -} - int sentry__path_create_dir_all(const sentry_path_t *path) { diff --git a/src/sentry_database.c b/src/sentry_database.c index 619318cfc..337fafeab 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -142,6 +142,7 @@ sentry__run_write_external( return write_envelope(run->external_path, envelope); } + bool sentry__run_write_session( const sentry_run_t *run, const sentry_session_t *session) diff --git a/src/sentry_database.h b/src/sentry_database.h index 00631454d..0cc688907 100644 --- a/src/sentry_database.h +++ b/src/sentry_database.h @@ -49,6 +49,7 @@ bool sentry__run_write_envelope( */ bool sentry__run_write_external( const sentry_run_t *run, const sentry_envelope_t *envelope); + /** * This will serialize and write the given session to disk into a file named: * `/.run/session.json` diff --git a/src/sentry_path.h b/src/sentry_path.h index 484a6f531..f43251d8c 100644 --- a/src/sentry_path.h +++ b/src/sentry_path.h @@ -151,14 +151,6 @@ int sentry__path_remove(const sentry_path_t *path); * Returns 0 on success. */ int sentry__path_remove_all(const sentry_path_t *path); - -/** - * Rename/move the file or directory from `src` to `dst`. - * This will overwrite `dst` if it already exists. - * Returns 0 on success. - */ -int sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst); - /** * This will create the directory referred to by `path`, and any non-existing * parent directory. From 85ccfea9052f20bffa58e07845dd035699e0f269 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:21:25 +0100 Subject: [PATCH 11/11] cleanup --- src/sentry_path.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry_path.h b/src/sentry_path.h index f43251d8c..cd5a3a917 100644 --- a/src/sentry_path.h +++ b/src/sentry_path.h @@ -151,6 +151,7 @@ int sentry__path_remove(const sentry_path_t *path); * Returns 0 on success. */ int sentry__path_remove_all(const sentry_path_t *path); + /** * This will create the directory referred to by `path`, and any non-existing * parent directory.