Skip to content

Commit 231d9a6

Browse files
jpnurmiclaude
andauthored
fix(native): external crash reporter (#1589)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3354b27 commit 231d9a6

8 files changed

Lines changed: 139 additions & 99 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
**Fixes**:
6+
7+
- Fix external crash reporter to work with the new experimental `native` backend. ([#1589](https://github.com/getsentry/sentry-native/pull/1589))
8+
39
## 0.13.3
410

511
**Features**:

src/backends/native/sentry_crash_daemon.c

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,10 +2032,13 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options,
20322032

20332033
// Serialize event to JSON
20342034
char *event_json = sentry_value_to_json(event);
2035+
char *event_id = sentry__string_clone(
2036+
sentry_value_as_string(sentry_value_get_by_key(event, "event_id")));
20352037
sentry_value_decref(event);
20362038

20372039
if (!event_json) {
20382040
SENTRY_WARN("Failed to serialize native crash event to JSON");
2041+
sentry_free(event_id);
20392042
return false;
20402043
}
20412044

@@ -2054,6 +2057,7 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options,
20542057
if (fd < 0) {
20552058
SENTRY_WARN("Failed to open envelope file for writing");
20562059
sentry_free(event_json);
2060+
sentry_free(event_id);
20572061
return false;
20582062
}
20592063

@@ -2062,12 +2066,19 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options,
20622066
= options && options->dsn ? sentry_options_get_dsn(options) : NULL;
20632067
char header_buf[SENTRY_CRASH_ENVELOPE_HEADER_SIZE];
20642068
int header_len;
2065-
if (dsn) {
2069+
if (dsn && event_id && event_id[0] != '\0') {
2070+
header_len = snprintf(header_buf, sizeof(header_buf),
2071+
"{\"dsn\":\"%s\",\"event_id\":\"%s\"}\n", dsn, event_id);
2072+
} else if (dsn) {
20662073
header_len = snprintf(
20672074
header_buf, sizeof(header_buf), "{\"dsn\":\"%s\"}\n", dsn);
2075+
} else if (event_id && event_id[0] != '\0') {
2076+
header_len = snprintf(header_buf, sizeof(header_buf),
2077+
"{\"event_id\":\"%s\"}\n", event_id);
20682078
} else {
20692079
header_len = snprintf(header_buf, sizeof(header_buf), "{}\n");
20702080
}
2081+
sentry_free(event_id);
20712082
if (header_len > 0 && header_len < (int)sizeof(header_buf)) {
20722083
#if defined(SENTRY_PLATFORM_UNIX)
20732084
if (write(fd, header_buf, header_len) != header_len) {
@@ -2238,6 +2249,23 @@ write_envelope_with_minidump(const sentry_options_t *options,
22382249
const char *envelope_path, const char *event_msgpack_path,
22392250
const char *minidump_path, sentry_path_t *run_folder)
22402251
{
2252+
// Read event JSON data
2253+
size_t event_size = 0;
2254+
char *event_json = NULL;
2255+
char *event_id = NULL;
2256+
sentry_path_t *ev_path = sentry__path_from_str(event_msgpack_path);
2257+
if (ev_path) {
2258+
event_json = sentry__path_read_to_buffer(ev_path, &event_size);
2259+
sentry__path_free(ev_path);
2260+
if (event_json && event_size > 0) {
2261+
sentry_value_t event
2262+
= sentry__value_from_json(event_json, event_size);
2263+
event_id = sentry__string_clone(sentry_value_as_string(
2264+
sentry_value_get_by_key(event, "event_id")));
2265+
sentry_value_decref(event);
2266+
}
2267+
}
2268+
22412269
// Open envelope file for writing
22422270
#if defined(SENTRY_PLATFORM_UNIX)
22432271
int fd = open(envelope_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
@@ -2251,20 +2279,29 @@ write_envelope_with_minidump(const sentry_options_t *options,
22512279
#endif
22522280
if (fd < 0) {
22532281
SENTRY_WARN("Failed to open envelope file for writing");
2282+
sentry_free(event_json);
2283+
sentry_free(event_id);
22542284
return false;
22552285
}
22562286

2257-
// Write envelope headers (just DSN if available)
2287+
// Write envelope headers (just DSN and event ID if available)
22582288
const char *dsn
22592289
= options && options->dsn ? sentry_options_get_dsn(options) : NULL;
22602290
char header_buf[SENTRY_CRASH_ENVELOPE_HEADER_SIZE];
22612291
int header_len;
2262-
if (dsn) {
2292+
if (dsn && event_id && event_id[0] != '\0') {
2293+
header_len = snprintf(header_buf, sizeof(header_buf),
2294+
"{\"dsn\":\"%s\",\"event_id\":\"%s\"}\n", dsn, event_id);
2295+
} else if (dsn) {
22632296
header_len = snprintf(
22642297
header_buf, sizeof(header_buf), "{\"dsn\":\"%s\"}\n", dsn);
2298+
} else if (event_id && event_id[0] != '\0') {
2299+
header_len = snprintf(header_buf, sizeof(header_buf),
2300+
"{\"event_id\":\"%s\"}\n", event_id);
22652301
} else {
22662302
header_len = snprintf(header_buf, sizeof(header_buf), "{}\n");
22672303
}
2304+
sentry_free(event_id);
22682305
if (header_len > 0 && header_len < (int)sizeof(header_buf)) {
22692306
#if defined(SENTRY_PLATFORM_UNIX)
22702307
if (write(fd, header_buf, header_len) != header_len) {
@@ -2275,39 +2312,30 @@ write_envelope_with_minidump(const sentry_options_t *options,
22752312
#endif
22762313
}
22772314

2278-
// Read event JSON data
2279-
sentry_path_t *ev_path = sentry__path_from_str(event_msgpack_path);
2280-
if (ev_path) {
2281-
size_t event_size = 0;
2282-
char *event_json = sentry__path_read_to_buffer(ev_path, &event_size);
2283-
sentry__path_free(ev_path);
2284-
2285-
if (event_json && event_size > 0) {
2286-
// Write event item header
2287-
char event_header[SENTRY_CRASH_ITEM_HEADER_SIZE];
2288-
int ev_header_len = snprintf(event_header, sizeof(event_header),
2289-
"{\"type\":\"event\",\"length\":%zu}\n", event_size);
2290-
if (ev_header_len > 0
2291-
&& ev_header_len < (int)sizeof(event_header)) {
2315+
// Write event item
2316+
if (event_json && event_size > 0) {
2317+
char event_header[SENTRY_CRASH_ITEM_HEADER_SIZE];
2318+
int ev_header_len = snprintf(event_header, sizeof(event_header),
2319+
"{\"type\":\"event\",\"length\":%zu}\n", event_size);
2320+
if (ev_header_len > 0 && ev_header_len < (int)sizeof(event_header)) {
22922321
#if defined(SENTRY_PLATFORM_UNIX)
2293-
if (write(fd, event_header, ev_header_len) != ev_header_len) {
2294-
SENTRY_WARN("Failed to write event header to envelope");
2295-
}
2296-
if (write(fd, event_json, event_size) != (ssize_t)event_size) {
2297-
SENTRY_WARN("Failed to write event data to envelope");
2298-
}
2299-
if (write(fd, "\n", 1) != 1) {
2300-
SENTRY_WARN("Failed to write event newline to envelope");
2301-
}
2322+
if (write(fd, event_header, ev_header_len) != ev_header_len) {
2323+
SENTRY_WARN("Failed to write event header to envelope");
2324+
}
2325+
if (write(fd, event_json, event_size) != (ssize_t)event_size) {
2326+
SENTRY_WARN("Failed to write event data to envelope");
2327+
}
2328+
if (write(fd, "\n", 1) != 1) {
2329+
SENTRY_WARN("Failed to write event newline to envelope");
2330+
}
23022331
#elif defined(SENTRY_PLATFORM_WINDOWS)
2303-
_write(fd, event_header, (unsigned int)ev_header_len);
2304-
_write(fd, event_json, (unsigned int)event_size);
2305-
_write(fd, "\n", 1);
2332+
_write(fd, event_header, (unsigned int)ev_header_len);
2333+
_write(fd, event_json, (unsigned int)event_size);
2334+
_write(fd, "\n", 1);
23062335
#endif
2307-
}
2308-
sentry_free(event_json);
23092336
}
23102337
}
2338+
sentry_free(event_json);
23112339

23122340
// Add minidump as attachment
23132341
#if defined(SENTRY_PLATFORM_UNIX)
@@ -2712,14 +2740,17 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc)
27122740

27132741
SENTRY_DEBUG("Envelope loaded, sending via transport");
27142742

2715-
// Send directly via transport
2716-
if (options && options->transport) {
2717-
SENTRY_DEBUG("Calling transport send_envelope");
2718-
sentry__transport_send_envelope(options->transport, envelope);
2719-
SENTRY_DEBUG("Crash envelope sent to transport (queued)");
2720-
} else {
2721-
SENTRY_WARN("No transport available for sending envelope");
2722-
sentry_envelope_free(envelope);
2743+
// Send directly via transport, or to external crash reporter
2744+
if (!sentry__launch_external_crash_reporter(options, envelope)) {
2745+
// Send directly via transport
2746+
if (options && options->transport) {
2747+
SENTRY_DEBUG("Calling transport send_envelope");
2748+
sentry__transport_send_envelope(options->transport, envelope);
2749+
SENTRY_DEBUG("Crash envelope sent to transport (queued)");
2750+
} else {
2751+
SENTRY_WARN("No transport available for sending envelope");
2752+
sentry_envelope_free(envelope);
2753+
}
27232754
}
27242755

27252756
// Clean up temporary envelope file (keep minidump for

src/backends/sentry_backend_breakpad.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor,
190190
sentry__attachment_free(screenshot);
191191
}
192192

193-
if (!sentry__launch_external_crash_reporter(envelope)) {
193+
if (!sentry__launch_external_crash_reporter(options, envelope)) {
194194
// capture the envelope with the disk transport
195195
sentry_transport_t *disk_transport
196196
= sentry_new_disk_transport(options->run);

src/backends/sentry_backend_inproc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx,
11901190
sentry__attachment_free(screenshot);
11911191
}
11921192

1193-
if (!sentry__launch_external_crash_reporter(envelope)) {
1193+
if (!sentry__launch_external_crash_reporter(options, envelope)) {
11941194
// capture the envelope with the disk transport
11951195
sentry_transport_t *disk_transport
11961196
= sentry_new_disk_transport(options->run);

src/backends/sentry_backend_native.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ native_backend_free(sentry_backend_t *backend)
541541

542542
static void
543543
native_backend_flush_scope(
544-
sentry_backend_t *backend, const sentry_options_t *options)
544+
sentry_backend_t *backend, const sentry_options_t *UNUSED(options))
545545
{
546546
native_backend_state_t *state = (native_backend_state_t *)backend->data;
547547
if (!state || !state->event_path) {
@@ -658,16 +658,6 @@ native_backend_flush_scope(
658658
}
659659
}
660660
}
661-
662-
// Flush external crash report envelope if configured
663-
if (options->external_crash_reporter && state->envelope_path) {
664-
sentry_envelope_t *envelope = sentry__envelope_new();
665-
if (envelope && options->session) {
666-
sentry__envelope_add_session(envelope, options->session);
667-
sentry__run_write_external(options->run, envelope);
668-
}
669-
sentry_envelope_free(envelope);
670-
}
671661
}
672662

673663
static void

src/sentry_core.c

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,44 +1660,44 @@ sentry_capture_feedback_with_hint(
16601660
}
16611661

16621662
bool
1663-
sentry__launch_external_crash_reporter(sentry_envelope_t *envelope)
1663+
sentry__launch_external_crash_reporter(
1664+
const sentry_options_t *options, sentry_envelope_t *envelope)
16641665
{
1665-
SENTRY_WITH_OPTIONS (options) {
1666-
if (!options->external_crash_reporter) {
1667-
return false;
1668-
}
1669-
1670-
sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope);
1671-
char *envelope_filename
1672-
= sentry__uuid_as_filename(&event_id, ".envelope");
1673-
if (!envelope_filename) {
1674-
return false;
1675-
}
1666+
if (!options || !options->run || !options->external_crash_reporter
1667+
|| !options->external_crash_reporter->path
1668+
|| options->external_crash_reporter->path[0] == '\0') {
1669+
return false;
1670+
}
16761671

1677-
sentry_path_t *report_path = sentry__path_join_str(
1678-
options->run->external_path, envelope_filename);
1679-
if (!report_path) {
1680-
sentry_free(envelope_filename);
1681-
return false;
1682-
}
1672+
sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope);
1673+
char *envelope_filename = sentry__uuid_as_filename(&event_id, ".envelope");
1674+
if (!envelope_filename) {
1675+
return false;
1676+
}
16831677

1684-
// capture the envelope with the disk transport
1685-
sentry_transport_t *disk_transport
1686-
= sentry_new_external_disk_transport(options->run);
1687-
if (!disk_transport) {
1688-
sentry__path_free(report_path);
1689-
sentry_free(envelope_filename);
1690-
return false;
1691-
}
1692-
sentry__capture_envelope(disk_transport, envelope);
1693-
sentry__transport_dump_queue(disk_transport, options->run);
1694-
sentry_transport_free(disk_transport);
1678+
sentry_path_t *report_path
1679+
= sentry__path_join_str(options->run->external_path, envelope_filename);
1680+
if (!report_path) {
1681+
sentry_free(envelope_filename);
1682+
return false;
1683+
}
16951684

1696-
sentry__process_spawn(
1697-
options->external_crash_reporter, report_path->path, NULL);
1685+
// capture the envelope with the disk transport
1686+
sentry_transport_t *disk_transport
1687+
= sentry_new_external_disk_transport(options->run);
1688+
if (!disk_transport) {
16981689
sentry__path_free(report_path);
16991690
sentry_free(envelope_filename);
1691+
return false;
17001692
}
1693+
sentry__transport_send_envelope(disk_transport, envelope);
1694+
sentry__transport_dump_queue(disk_transport, options->run);
1695+
sentry_transport_free(disk_transport);
1696+
1697+
sentry__process_spawn(
1698+
options->external_crash_reporter, report_path->path, NULL);
1699+
sentry__path_free(report_path);
1700+
sentry_free(envelope_filename);
17011701
return true;
17021702
}
17031703

src/sentry_core.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ void sentry__set_propagation_context(const char *key, sentry_value_t value);
128128
void sentry__apply_attributes(
129129
sentry_value_t telemetry, sentry_value_t attributes);
130130

131-
bool sentry__launch_external_crash_reporter(sentry_envelope_t *envelope);
131+
bool sentry__launch_external_crash_reporter(
132+
const sentry_options_t *options, sentry_envelope_t *envelope);
132133

133134
#define SENTRY_WITH_OPTIONS(Options) \
134135
for (const sentry_options_t *Options = sentry__options_getref(); Options; \

tests/test_integration_native.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
import pytest
1313

1414
from . import (
15+
is_feedback_envelope,
1516
make_dsn,
1617
run,
1718
Envelope,
19+
split_log_request_cond,
1820
)
1921
from .assertions import (
22+
assert_breadcrumb,
2023
assert_meta,
2124
assert_session,
25+
assert_user_feedback,
2226
)
2327
from .conditions import has_native, is_kcov, is_asan
2428

@@ -472,26 +476,34 @@ def test_native_external_crash_reporter(cmake, httpserver):
472476
)
473477

474478
env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver))
475-
httpserver.expect_request("/api/123456/envelope/").respond_with_data("OK")
479+
httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK")
480+
httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK")
476481

477482
# Crash and use external reporter
478-
run_crash(
479-
tmp_path,
480-
"sentry_example",
481-
["log", "crash-reporter", "crash"],
482-
env=env,
483-
)
483+
with httpserver.wait(timeout=10) as waiting:
484+
run_crash(
485+
tmp_path,
486+
"sentry_example",
487+
["log", "crash-reporter", "crash"],
488+
env=env,
489+
)
490+
assert waiting.result
484491

485-
# Give time for external reporter to run
486-
time.sleep(2)
492+
# Should have sent crash report and feedback via external reporter
493+
assert len(httpserver.log) == 2
494+
feedback_request, crash_request = split_log_request_cond(
495+
httpserver.log, is_feedback_envelope
496+
)
497+
feedback = feedback_request.get_data()
498+
crash = crash_request.get_data()
487499

488-
# Should have sent crash report via external reporter
489-
assert len(httpserver.log) >= 1
500+
# Verify it's a minidump crash report and user feedback
501+
envelope = Envelope.deserialize(crash)
502+
assert_meta(envelope)
503+
assert_breadcrumb(envelope)
490504

491-
# Verify it's a minidump crash report
492-
envelope = Envelope.deserialize(httpserver.log[0][0].get_data())
493-
event = envelope.get_event()
494-
assert event is not None
505+
envelope = Envelope.deserialize(feedback)
506+
assert_user_feedback(envelope)
495507

496508

497509
def test_crash_mode_minidump_only(cmake, httpserver):

0 commit comments

Comments
 (0)