Skip to content

Commit cfa744f

Browse files
committed
feat: invoke on_crash when handling a crash
- don't invoke before_send if on_crash is set - if on_crash returns 0 discard crash report
1 parent 6694b19 commit cfa744f

File tree

6 files changed

+154
-69
lines changed

6 files changed

+154
-69
lines changed

include/sentry.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ extern "C" {
8686

8787
#include <inttypes.h>
8888
#include <stdarg.h>
89+
#include <stdbool.h>
8990
#include <stddef.h>
9091

9192
/* context type dependencies */
@@ -776,6 +777,31 @@ typedef sentry_value_t (*sentry_event_function_t)(
776777
SENTRY_API void sentry_options_set_before_send(
777778
sentry_options_t *opts, sentry_event_function_t func, void *data);
778779

780+
/**
781+
* Type of the `on_crash` callback.
782+
*
783+
* The callback passes a pointer to sentry_ucontext_s structure
784+
* when exception handler is invoked (not working with breakpad on Linux).
785+
*
786+
* If the callback returns false outgoing crash report will be discarded.
787+
*
788+
* This function may be invoked inside of a signal handler and must be safe for
789+
* that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html.
790+
* On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see
791+
* the documentation on SEH (structured exception handling) for more information
792+
* https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
793+
*/
794+
typedef bool (*sentry_crash_function_t)(
795+
const sentry_ucontext_t *uctx, void *closure);
796+
797+
/**
798+
* Sets the `on_crash` callback.
799+
*
800+
* See the `sentry_crash_function_t` typedef above for more information.
801+
*/
802+
SENTRY_API void sentry_options_set_on_crash(
803+
sentry_options_t *opts, sentry_crash_function_t func, void *data);
804+
779805
/**
780806
* Sets the DSN.
781807
*/

src/backends/sentry_backend_breakpad.cpp

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ extern "C" {
4040
static bool
4141
sentry__breakpad_backend_callback(const wchar_t *breakpad_dump_path,
4242
const wchar_t *minidump_id, void *UNUSED(context),
43-
EXCEPTION_POINTERS *UNUSED(exinfo), MDRawAssertionInfo *UNUSED(assertion),
43+
EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *UNUSED(assertion),
4444
bool succeeded)
4545
#elif defined(SENTRY_PLATFORM_DARWIN)
4646
static bool
@@ -92,45 +92,64 @@ sentry__breakpad_backend_callback(
9292
dump_path = sentry__path_new(descriptor.path());
9393
#endif
9494

95+
bool capture_crash = true;
96+
9597
SENTRY_WITH_OPTIONS (options) {
9698
sentry__write_crash_marker(options);
9799

98-
sentry_value_t event = sentry_value_new_event();
99-
sentry_envelope_t *envelope
100-
= sentry__prepare_event(options, event, NULL);
101-
// the event we just prepared is empty, so no error is recorded for it
102-
sentry__record_errors_on_current_session(1);
103-
sentry_session_t *session = sentry__end_current_session_with_status(
104-
SENTRY_SESSION_STATUS_CRASHED);
105-
sentry__envelope_add_session(envelope, session);
106-
107-
// the minidump is added as an attachment, with type `event.minidump`
108-
sentry_envelope_item_t *item
109-
= sentry__envelope_add_from_path(envelope, dump_path, "attachment");
110-
if (item) {
111-
sentry__envelope_item_set_header(item, "attachment_type",
112-
sentry_value_new_string("event.minidump"));
113-
114-
sentry__envelope_item_set_header(item, "filename",
115100
#ifdef SENTRY_PLATFORM_WINDOWS
116-
sentry__value_new_string_from_wstr(
101+
if (options->on_crash_func) {
102+
sentry_ucontext_t uctx;
103+
uctx.exception_ptrs = *exinfo;
104+
105+
SENTRY_TRACE("invoking `on_crash` hook");
106+
capture_crash
107+
= options->on_crash_func(&uctx, options->on_crash_data);
108+
}
109+
#endif
110+
111+
if (capture_crash) {
112+
sentry_value_t event = sentry_value_new_event();
113+
sentry_envelope_t *envelope
114+
= sentry__prepare_event(options, event, NULL);
115+
// the event we just prepared is empty,
116+
// so no error is recorded for it
117+
sentry__record_errors_on_current_session(1);
118+
sentry_session_t *session = sentry__end_current_session_with_status(
119+
SENTRY_SESSION_STATUS_CRASHED);
120+
sentry__envelope_add_session(envelope, session);
121+
122+
// the minidump is added as an attachment,
123+
// with type `event.minidump`
124+
sentry_envelope_item_t *item = sentry__envelope_add_from_path(
125+
envelope, dump_path, "attachment");
126+
if (item) {
127+
sentry__envelope_item_set_header(item, "attachment_type",
128+
sentry_value_new_string("event.minidump"));
129+
130+
sentry__envelope_item_set_header(item, "filename",
131+
#ifdef SENTRY_PLATFORM_WINDOWS
132+
sentry__value_new_string_from_wstr(
117133
#else
118-
sentry_value_new_string(
134+
sentry_value_new_string(
119135
#endif
120-
sentry__path_filename(dump_path)));
121-
}
136+
sentry__path_filename(dump_path)));
137+
}
122138

123-
// capture the envelope with the disk transport
124-
sentry_transport_t *disk_transport
125-
= sentry_new_disk_transport(options->run);
126-
sentry__capture_envelope(disk_transport, envelope);
127-
sentry__transport_dump_queue(disk_transport, options->run);
128-
sentry_transport_free(disk_transport);
129-
130-
// now that the envelope was written, we can remove the temporary
131-
// minidump file
132-
sentry__path_remove(dump_path);
133-
sentry__path_free(dump_path);
139+
// capture the envelope with the disk transport
140+
sentry_transport_t *disk_transport
141+
= sentry_new_disk_transport(options->run);
142+
sentry__capture_envelope(disk_transport, envelope);
143+
sentry__transport_dump_queue(disk_transport, options->run);
144+
sentry_transport_free(disk_transport);
145+
146+
// now that the envelope was written, we can remove the temporary
147+
// minidump file
148+
sentry__path_remove(dump_path);
149+
sentry__path_free(dump_path);
150+
} else {
151+
SENTRY_TRACE("event was discarded by the `on_crash` hook");
152+
}
134153

135154
// after capturing the crash event, try to dump all the in-flight
136155
// data of the previous transports

src/backends/sentry_backend_crashpad.cpp

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,11 @@ sentry__crashpad_backend_flush_scope(
122122
#if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_WINDOWS)
123123
# ifdef SENTRY_PLATFORM_WINDOWS
124124
static bool
125-
sentry__crashpad_handler(EXCEPTION_POINTERS *UNUSED(ExceptionInfo))
125+
sentry__crashpad_handler(EXCEPTION_POINTERS *ExceptionInfo)
126126
{
127127
# else
128128
static bool
129-
sentry__crashpad_handler(int UNUSED(signum), siginfo_t *UNUSED(info),
130-
ucontext_t *UNUSED(user_context))
129+
sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context)
131130
{
132131
sentry__page_allocator_enable();
133132
sentry__enter_signal_handler();
@@ -137,27 +136,46 @@ sentry__crashpad_handler(int UNUSED(signum), siginfo_t *UNUSED(info),
137136
SENTRY_WITH_OPTIONS (options) {
138137
sentry__write_crash_marker(options);
139138

140-
sentry_value_t event = sentry_value_new_event();
141-
if (options->before_send_func) {
139+
bool capture_crash = true;
140+
141+
if (options->on_crash_func) {
142+
sentry_ucontext_t uctx;
143+
# ifdef SENTRY_PLATFORM_WINDOWS
144+
uctx.exception_ptrs = *ExceptionInfo;
145+
# else
146+
uctx.signum = signum;
147+
uctx.siginfo = info;
148+
uctx.user_context = user_context;
149+
# endif
150+
151+
SENTRY_TRACE("invoking `on_crash` hook");
152+
capture_crash
153+
= options->on_crash_func(&uctx, options->on_crash_data);
154+
} else if (options->before_send_func) {
155+
sentry_value_t event = sentry_value_new_event();
142156
SENTRY_TRACE("invoking `before_send` hook");
143157
event = options->before_send_func(
144-
event, NULL, options->before_send_data);
158+
event, nullptr, options->before_send_data);
159+
sentry_value_decref(event);
145160
}
146-
sentry_value_decref(event);
147-
148-
sentry__record_errors_on_current_session(1);
149-
sentry_session_t *session = sentry__end_current_session_with_status(
150-
SENTRY_SESSION_STATUS_CRASHED);
151-
if (session) {
152-
sentry_envelope_t *envelope = sentry__envelope_new();
153-
sentry__envelope_add_session(envelope, session);
154-
155-
// capture the envelope with the disk transport
156-
sentry_transport_t *disk_transport
157-
= sentry_new_disk_transport(options->run);
158-
sentry__capture_envelope(disk_transport, envelope);
159-
sentry__transport_dump_queue(disk_transport, options->run);
160-
sentry_transport_free(disk_transport);
161+
162+
if (capture_crash) {
163+
sentry__record_errors_on_current_session(1);
164+
sentry_session_t *session = sentry__end_current_session_with_status(
165+
SENTRY_SESSION_STATUS_CRASHED);
166+
if (session) {
167+
sentry_envelope_t *envelope = sentry__envelope_new();
168+
sentry__envelope_add_session(envelope, session);
169+
170+
// capture the envelope with the disk transport
171+
sentry_transport_t *disk_transport
172+
= sentry_new_disk_transport(options->run);
173+
sentry__capture_envelope(disk_transport, envelope);
174+
sentry__transport_dump_queue(disk_transport, options->run);
175+
sentry_transport_free(disk_transport);
176+
}
177+
} else {
178+
SENTRY_TRACE("event was discarded by the `on_crash` hook");
161179
}
162180

163181
sentry__transport_dump_queue(options->transport, options->run);

src/backends/sentry_backend_inproc.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,33 @@ handle_ucontext(const sentry_ucontext_t *uctx)
258258
SENTRY_WITH_OPTIONS (options) {
259259
sentry__write_crash_marker(options);
260260

261-
sentry_envelope_t *envelope
262-
= sentry__prepare_event(options, event, NULL);
263-
// TODO(tracing): Revisit when investigating transaction flushing during
264-
// hard crashes.
265-
266-
sentry_session_t *session = sentry__end_current_session_with_status(
267-
SENTRY_SESSION_STATUS_CRASHED);
268-
sentry__envelope_add_session(envelope, session);
269-
270-
// capture the envelope with the disk transport
271-
sentry_transport_t *disk_transport
272-
= sentry_new_disk_transport(options->run);
273-
sentry__capture_envelope(disk_transport, envelope);
274-
sentry__transport_dump_queue(disk_transport, options->run);
275-
sentry_transport_free(disk_transport);
261+
bool capture_crash = true;
262+
263+
if (options->on_crash_func) {
264+
SENTRY_TRACE("invoking `on_crash` hook");
265+
capture_crash
266+
= options->on_crash_func(uctx, options->on_crash_data);
267+
}
268+
269+
if (capture_crash) {
270+
sentry_envelope_t *envelope
271+
= sentry__prepare_event(options, event, NULL);
272+
// TODO(tracing): Revisit when investigating transaction flushing
273+
// during hard crashes.
274+
275+
sentry_session_t *session = sentry__end_current_session_with_status(
276+
SENTRY_SESSION_STATUS_CRASHED);
277+
sentry__envelope_add_session(envelope, session);
278+
279+
// capture the envelope with the disk transport
280+
sentry_transport_t *disk_transport
281+
= sentry_new_disk_transport(options->run);
282+
sentry__capture_envelope(disk_transport, envelope);
283+
sentry__transport_dump_queue(disk_transport, options->run);
284+
sentry_transport_free(disk_transport);
285+
} else {
286+
SENTRY_TRACE("event was discarded by the `on_crash` hook");
287+
}
276288

277289
// after capturing the crash event, dump all the envelopes to disk
278290
sentry__transport_dump_queue(options->transport, options->run);

src/sentry_options.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ sentry_options_set_before_send(
119119
opts->before_send_data = data;
120120
}
121121

122+
void
123+
sentry_options_set_on_crash(
124+
sentry_options_t *opts, sentry_crash_function_t func, void *data)
125+
{
126+
opts->on_crash_func = func;
127+
opts->on_crash_data = data;
128+
}
129+
122130
void
123131
sentry_options_set_dsn(sentry_options_t *opts, const char *raw_dsn)
124132
{

src/sentry_options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ typedef struct sentry_options_s {
5454
sentry_transport_t *transport;
5555
sentry_event_function_t before_send_func;
5656
void *before_send_data;
57+
sentry_crash_function_t on_crash_func;
58+
void *on_crash_data;
5759

5860
/* Experimentally exposed */
5961
double traces_sample_rate;

0 commit comments

Comments
 (0)