@@ -19,6 +19,7 @@ extern "C" {
1919#include " sentry_screenshot.h"
2020#include " sentry_sync.h"
2121#include " sentry_transport.h"
22+ #include " sentry_value.h"
2223#ifdef SENTRY_PLATFORM_LINUX
2324# include " sentry_unix_pageallocator.h"
2425#endif
@@ -436,6 +437,167 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context)
436437}
437438#endif
438439
440+ static sentry_value_t
441+ read_msgpack_file (const sentry_path_t *path)
442+ {
443+ size_t size;
444+ char *data = sentry__path_read_to_buffer (path, &size);
445+ if (!data) {
446+ return sentry_value_new_null ();
447+ }
448+ sentry_value_t value = sentry__value_from_msgpack (data, size);
449+ sentry_free (data);
450+ return value;
451+ }
452+
453+ static sentry_path_t *
454+ report_attachments_dir (const crashpad::CrashReportDatabase::Report &report,
455+ const sentry_options_t *options)
456+ {
457+ sentry_path_t *attachments_root
458+ = sentry__path_join_str (options->database_path , " attachments" );
459+ if (!attachments_root) {
460+ return nullptr ;
461+ }
462+
463+ sentry_path_t *attachments_dir = sentry__path_join_str (
464+ attachments_root, report.uuid .ToString ().c_str ());
465+
466+ sentry__path_free (attachments_root);
467+ return attachments_dir;
468+ }
469+
470+ // Converts a completed crashpad report into a sentry envelope by reading the
471+ // event, breadcrumbs, and attachments from the report's attachments directory.
472+ static sentry_envelope_t *
473+ report_to_envelope (const crashpad::CrashReportDatabase::Report &report,
474+ const sentry_options_t *options)
475+ {
476+ #ifdef SENTRY_PLATFORM_WINDOWS
477+ sentry_path_t *minidump_path
478+ = sentry__path_from_wstr (report.file_path .value ().c_str ());
479+ #else
480+ sentry_path_t *minidump_path
481+ = sentry__path_from_str (report.file_path .value ().c_str ());
482+ #endif
483+ sentry_path_t *attachments_dir = report_attachments_dir (report, options);
484+
485+ if (!minidump_path || !attachments_dir) {
486+ sentry__path_free (minidump_path);
487+ sentry__path_free (attachments_dir);
488+ return nullptr ;
489+ }
490+
491+ sentry_value_t event = sentry_value_new_null ();
492+ sentry_value_t breadcrumbs1 = sentry_value_new_null ();
493+ sentry_value_t breadcrumbs2 = sentry_value_new_null ();
494+ sentry_attachment_t *attachments = nullptr ;
495+
496+ sentry_pathiter_t *iter = sentry__path_iter_directory (attachments_dir);
497+ if (iter) {
498+ const sentry_path_t *path;
499+ while ((path = sentry__pathiter_next (iter)) != nullptr ) {
500+ const char *filename = sentry__path_filename (path);
501+ if (strcmp (filename, " __sentry-event" ) == 0 ) {
502+ event = read_msgpack_file (path);
503+ } else if (strcmp (filename, " __sentry-breadcrumb1" ) == 0 ) {
504+ breadcrumbs1 = read_msgpack_file (path);
505+ } else if (strcmp (filename, " __sentry-breadcrumb2" ) == 0 ) {
506+ breadcrumbs2 = read_msgpack_file (path);
507+ } else {
508+ sentry__attachments_add_path (&attachments,
509+ sentry__path_clone (path), ATTACHMENT, nullptr );
510+ }
511+ }
512+ sentry__pathiter_free (iter);
513+ }
514+ sentry__path_free (attachments_dir);
515+
516+ sentry_envelope_t *envelope = nullptr ;
517+ if (!sentry_value_is_null (event)) {
518+ envelope = sentry__envelope_new ();
519+ if (envelope && options->dsn && options->dsn ->is_valid ) {
520+ sentry__envelope_set_header (envelope, " dsn" ,
521+ sentry_value_new_string (sentry_options_get_dsn (options)));
522+ }
523+ }
524+ if (envelope) {
525+ sentry_value_set_by_key (event, " breadcrumbs" ,
526+ sentry__value_merge_breadcrumbs (
527+ breadcrumbs1, breadcrumbs2, options->max_breadcrumbs ));
528+ sentry__attachments_add_path (
529+ &attachments, minidump_path, MINIDUMP, nullptr );
530+
531+ if (sentry__envelope_add_event (envelope, event)) {
532+ sentry__envelope_add_attachments (envelope, attachments);
533+ } else {
534+ sentry_value_decref (event);
535+ sentry_envelope_free (envelope);
536+ envelope = nullptr ;
537+ }
538+ } else {
539+ sentry__path_free (minidump_path);
540+ sentry_value_decref (event);
541+ }
542+
543+ sentry_value_decref (breadcrumbs1);
544+ sentry_value_decref (breadcrumbs2);
545+ sentry__attachments_free (attachments);
546+
547+ return envelope;
548+ }
549+
550+ // Caches completed crashpad reports as sentry envelopes and removes them from
551+ // the crashpad database. Called during startup before the handler is started.
552+ static void
553+ process_completed_reports (
554+ crashpad_state_t *state, const sentry_options_t *options)
555+ {
556+ if (!state || !state->db || !options || !options->cache_keep ) {
557+ return ;
558+ }
559+
560+ std::vector<crashpad::CrashReportDatabase::Report> reports;
561+ if (state->db ->GetCompletedReports (&reports)
562+ != crashpad::CrashReportDatabase::kNoError
563+ || reports.empty ()) {
564+ return ;
565+ }
566+
567+ SENTRY_DEBUGF (" caching %zu completed reports" , reports.size ());
568+
569+ sentry_path_t *cache_dir
570+ = sentry__path_join_str (options->database_path , " cache" );
571+ if (!cache_dir || sentry__path_create_dir_all (cache_dir) != 0 ) {
572+ SENTRY_WARN (" failed to create cache dir" );
573+ sentry__path_free (cache_dir);
574+ return ;
575+ }
576+
577+ for (const auto &report : reports) {
578+ std::string filename = report.uuid .ToString () + " .envelope" ;
579+ sentry_envelope_t *envelope = report_to_envelope (report, options);
580+ if (!envelope) {
581+ SENTRY_WARNF (" failed to convert \" %s\" " , filename.c_str ());
582+ continue ;
583+ }
584+ sentry_path_t *out_path
585+ = sentry__path_join_str (cache_dir, filename.c_str ());
586+ if (!out_path
587+ || (!sentry__path_is_file (out_path)
588+ && sentry_envelope_write_to_path (envelope, out_path) != 0 )) {
589+ SENTRY_WARNF (" failed to cache \" %s\" " , filename.c_str ());
590+ } else if (state->db ->DeleteReport (report.uuid )
591+ != crashpad::CrashReportDatabase::kNoError ) {
592+ SENTRY_WARNF (" failed to delete \" %s\" " , filename.c_str ());
593+ }
594+ sentry__path_free (out_path);
595+ sentry_envelope_free (envelope);
596+ }
597+
598+ sentry__path_free (cache_dir);
599+ }
600+
439601static int
440602crashpad_backend_startup (
441603 sentry_backend_t *backend, const sentry_options_t *options)
@@ -549,6 +711,7 @@ crashpad_backend_startup(
549711 // Initialize database first, flushing the consent later on as part of
550712 // `sentry_init` will persist the upload flag.
551713 data->db = crashpad::CrashReportDatabase::Initialize (database).release ();
714+ process_completed_reports (data, options);
552715 data->client = new crashpad::CrashpadClient;
553716 char *minidump_url
554717 = sentry__dsn_get_minidump_url (options->dsn , options->user_agent );
0 commit comments