Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

**Features**:

- Add `sentry_get_last_crash` to allow fetching `event_id` from last crash. ([#1711](https://github.com/getsentry/sentry-native/pull/1711))

**Fixes**:

- Reject overly deep msgpack payloads during deserialization. ([#1727](https://github.com/getsentry/sentry-native/pull/1727))
Expand Down
33 changes: 33 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -3562,6 +3562,39 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_iter_headers(
*/
SENTRY_EXPERIMENTAL_API int sentry_get_crashed_last_run(void);

/**
* Information about the crash from the last run.
*/
typedef struct sentry_last_crash_s {
/**
* The crash timestamp in microseconds since the Unix epoch, or 0 if it is
* unavailable.
*/
uint64_t timestamp;
/**
* The crash event ID, or a nil UUID if it is unavailable.
*/
sentry_uuid_t event_id;
} sentry_last_crash_t;

/**
* Returns whether the application crashed on the last run, and populates
* `crash` with available information.
*
* Pass `sizeof(sentry_last_crash_t)` as `crash_size` so future SDK versions can
* add fields without breaking callers compiled against older headers. Call
* `sentry_clear_crashed_last_run()` to reset for the next app run.
*
* Note: This does not work with crashpad on macOS, similar to `on_crash`.
*
* Possible return values:
* 1 = the last run was a crash
* 0 = no crash recognized
* -1 = sentry_init() hasn't been called yet
*/
SENTRY_API int sentry_get_last_crash(
sentry_last_crash_t *crash, size_t crash_size);

/**
* Clear the status of the "crashed-last-run". You should explicitly call
* this after sentry_init() if you're using sentry_get_crashed_last_run().
Expand Down
5 changes: 3 additions & 2 deletions src/backends/sentry_backend_breakpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,13 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor,
#else
dump_path = sentry__path_new(descriptor.path());
#endif
sentry_value_t event = sentry_value_new_event();
sentry_uuid_t event_id = sentry__new_event_id();
sentry_value_t event = sentry__value_new_event_with_id(&event_id);
sentry_value_set_by_key(
event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL));

SENTRY_WITH_OPTIONS (options) {
sentry__write_crash_marker(options);
sentry__write_crash_marker(options, &event_id);

bool should_handle = true;

Expand Down
2 changes: 1 addition & 1 deletion src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context)

if (should_dump) {
flush_scope_from_handler(options, crash_event);
sentry__write_crash_marker(options);
sentry__write_crash_marker(options, &state->crash_event_id);

sentry__record_errors_on_current_session(1);
sentry_session_t *session = sentry__end_current_session_with_status(
Expand Down
11 changes: 7 additions & 4 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -999,9 +999,10 @@ get_instruction_pointer(const sentry_ucontext_t *uctx)

static sentry_value_t
make_signal_event(const struct signal_slot *sig_slot,
const sentry_ucontext_t *uctx, sentry_handler_strategy_t strategy)
const sentry_ucontext_t *uctx, sentry_handler_strategy_t strategy,
const sentry_uuid_t *event_id)
{
sentry_value_t event = sentry_value_new_event();
sentry_value_t event = sentry__value_new_event_with_id(event_id);
sentry_value_set_by_key(
event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL));

Expand Down Expand Up @@ -1111,9 +1112,11 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx,
options ? sentry_options_get_handler_strategy(options) :
#endif
SENTRY_HANDLER_STRATEGY_DEFAULT;
sentry_value_t event = make_signal_event(sig_slot, uctx, strategy);
sentry_uuid_t event_id = sentry__new_event_id();
sentry_value_t event
= make_signal_event(sig_slot, uctx, strategy, &event_id);
bool should_handle = true;
sentry__write_crash_marker(options);
sentry__write_crash_marker(options, &event_id);

if (options->on_crash_func && !skip_hooks) {
SENTRY_DEBUG("invoking `on_crash` hook");
Expand Down
5 changes: 3 additions & 2 deletions src/backends/sentry_backend_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -986,10 +986,11 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx)
}

// Write crash marker
sentry__write_crash_marker(options);
sentry_uuid_t event_id = sentry__new_event_id();
sentry__write_crash_marker(options, &event_id);

// Create crash event
sentry_value_t event = sentry_value_new_event();
sentry_value_t event = sentry__value_new_event_with_id(&event_id);
sentry_value_set_by_key(
event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL));

Expand Down
32 changes: 31 additions & 1 deletion src/sentry_core.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "sentry_boot.h"

#include <stdarg.h>
#include <stddef.h>
#include <string.h>

#include "sentry_attachment.h"
Expand Down Expand Up @@ -44,6 +45,13 @@ static sentry_mutex_t g_options_lock = SENTRY__MUTEX_INIT;
#endif
/// see sentry_get_crashed_last_run() for the possible values
static int g_last_crash = -1;
static sentry_last_crash_t g_last_crash_info;

static inline bool
last_crash_has_field(size_t struct_size, size_t offset, size_t size)
{
return offset <= struct_size && size <= struct_size - offset;
}

const sentry_options_t *
sentry__options_getref(void)
Expand Down Expand Up @@ -194,7 +202,7 @@ sentry_init(sentry_options_t *options)
last_crash = backend->get_last_crash_func(backend);
}

g_last_crash = sentry__has_crash_marker(options);
g_last_crash = sentry__read_crash_marker(options, &g_last_crash_info);
g_options = options;

// *after* setting the global options, trigger a scope and consent flush,
Expand Down Expand Up @@ -1745,6 +1753,28 @@ sentry_get_crashed_last_run(void)
return g_last_crash;
}

int
sentry_get_last_crash(sentry_last_crash_t *crash, size_t crash_size)
{
if (crash && crash_size > 0) {
memset(crash, 0, crash_size);

if (g_last_crash == 1
&& last_crash_has_field(crash_size,
offsetof(sentry_last_crash_t, timestamp),
sizeof(crash->timestamp))) {
crash->timestamp = g_last_crash_info.timestamp;
}
if (g_last_crash == 1
&& last_crash_has_field(crash_size,
offsetof(sentry_last_crash_t, event_id),
sizeof(crash->event_id))) {
crash->event_id = g_last_crash_info.event_id;
}
}
return g_last_crash;
}

int
sentry_clear_crashed_last_run(void)
{
Expand Down
135 changes: 111 additions & 24 deletions src/sentry_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,58 @@
#include <stdlib.h>
#include <string.h>

static char *
read_metadata(
const sentry_path_t *path, sentry_slice_t *first, sentry_slice_t *second)
{
size_t size = 0;
char *contents_buf = sentry__path_read_to_buffer(path, &size);
if (!contents_buf) {
return NULL;
}

sentry_slice_t contents
= sentry__slice_trim((sentry_slice_t) { contents_buf, size });
if (contents.len == 0) {
return contents_buf;
}

size_t first_len = 0;
while (first_len < contents.len
&& !isspace((unsigned char)contents.ptr[first_len])) {
first_len++;
}
*first = (sentry_slice_t) { contents.ptr, first_len };
*second = sentry__slice_trim((sentry_slice_t) {
contents.ptr + first_len, contents.len - first_len });

return contents_buf;
}

static int
write_metadata(const sentry_path_t *path, const char *first, size_t first_len,
const char *second, size_t second_len)
{
bool has_second = second != NULL;
size_t buf_len = first_len + (has_second ? 1 + second_len + 1 : 0);
char *buf = sentry_malloc(buf_len);
if (!buf) {
return 1;
}

memcpy(buf, first, first_len);
if (has_second) {
buf[first_len] = '\n';
memcpy(buf + first_len + 1, second, second_len);
buf[first_len + 1 + second_len] = '\n';
}

int rv = sentry__path_write_buffer(path, buf, buf_len);
sentry_free(buf);

return rv;
}

sentry_run_t *
sentry__run_new(const sentry_path_t *database_path)
{
Expand Down Expand Up @@ -149,35 +201,24 @@ sentry__run_load_installation_id(sentry_run_t *run,

const size_t uuid_len = 36;
char uuid_str[37] = { 0 };
size_t size = 0;
char *contents = sentry__path_read_to_buffer(id_path, &size);
if (contents && size >= uuid_len) {
// expect: "<uuid>(\s+<public_key>)?"
sentry_slice_t tail = { contents + uuid_len, size - uuid_len };
sentry_slice_t key = sentry__slice_trim(tail);
if ((tail.len == 0 || isspace((unsigned char)tail.ptr[0]))
&& sentry__slice_eqs(key, public_key)) {
memcpy(uuid_str, contents, uuid_len);
uuid_str[uuid_len] = '\0';
}
sentry_slice_t first = { 0 };
sentry_slice_t second = { 0 };
char *contents = read_metadata(id_path, &first, &second);
// expect: "<uuid>(\s+<public_key>)?"
if (first.len == uuid_len && sentry__slice_eqs(second, public_key)) {
memcpy(uuid_str, first.ptr, uuid_len);
uuid_str[uuid_len] = '\0';
}
sentry_free(contents);

if (uuid_str[0] == '\0') {
sentry_uuid_t uuid = sentry_uuid_new_v4();
sentry_uuid_as_string(&uuid, uuid_str);

const size_t buf_len = uuid_len + 1 + key_len + 1;
char *buf = sentry_malloc(buf_len);
if (buf) {
memcpy(buf, uuid_str, uuid_len);
buf[uuid_len] = '\n';
memcpy(buf + uuid_len + 1, public_key, key_len);
buf[uuid_len + 1 + key_len] = '\n';
if (sentry__path_write_buffer(id_path, buf, buf_len) != 0) {
SENTRY_WARN("failed to persist installation ID");
}
sentry_free(buf);
int rv
= write_metadata(id_path, uuid_str, uuid_len, public_key, key_len);
if (rv) {
SENTRY_WARN("failed to persist installation ID");
}
}

Expand Down Expand Up @@ -974,7 +1015,8 @@ sentry__cleanup_cache(const sentry_options_t *options)
static const char *g_last_crash_filename = "last_crash";

bool
sentry__write_crash_marker(const sentry_options_t *options)
sentry__write_crash_marker(
const sentry_options_t *options, const sentry_uuid_t *event_id)
{
char *iso_time = sentry__usec_time_to_iso8601(sentry__usec_time());
if (!iso_time) {
Expand All @@ -989,7 +1031,17 @@ sentry__write_crash_marker(const sentry_options_t *options)
}

size_t iso_time_len = strlen(iso_time);
int rv = sentry__path_write_buffer(marker_path, iso_time, iso_time_len);
char event_id_str[37];
const char *event_id_value = NULL;
size_t event_id_len = 0;
if (event_id && !sentry_uuid_is_nil(event_id)) {
sentry_uuid_as_string(event_id, event_id_str);
event_id_value = event_id_str;
event_id_len = strlen(event_id_str);
}

int rv = write_metadata(
marker_path, iso_time, iso_time_len, event_id_value, event_id_len);
sentry_free(iso_time);
sentry__path_free(marker_path);

Expand All @@ -999,6 +1051,41 @@ sentry__write_crash_marker(const sentry_options_t *options)
return !rv;
}

int
sentry__read_crash_marker(
const sentry_options_t *options, sentry_last_crash_t *crash)
{
if (crash) {
memset(crash, 0, sizeof(*crash));
}

sentry_path_t *marker_path
= sentry__path_join_str(options->database_path, g_last_crash_filename);
if (!marker_path) {
return 0;
}

sentry_slice_t first = { 0 };
sentry_slice_t second = { 0 };
char *contents = read_metadata(marker_path, &first, &second);
int crashed = contents ? 1 : 0;
if (crash && first.len > 0) {
crash->timestamp = sentry__iso8601_to_usec(first.ptr, first.len);
}

if (crash && (second.len == 32 || second.len == 36)) {
sentry_uuid_t parsed
= sentry_uuid_from_string_n(second.ptr, second.len);
if (!sentry_uuid_is_nil(&parsed)) {
crash->event_id = parsed;
}
}

sentry_free(contents);
sentry__path_free(marker_path);
return crashed;
}

bool
sentry__has_crash_marker(const sentry_options_t *options)
{
Expand Down
9 changes: 8 additions & 1 deletion src/sentry_database.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,14 @@ void sentry__cleanup_cache(const sentry_options_t *options);
* This will write the current ISO8601 formatted timestamp into the
* `<database>/last_crash` file.
*/
bool sentry__write_crash_marker(const sentry_options_t *options);
bool sentry__write_crash_marker(
const sentry_options_t *options, const sentry_uuid_t *event_id);

/**
* This will read the crash information from the `<database>/last_crash` file.
*/
int sentry__read_crash_marker(
const sentry_options_t *options, sentry_last_crash_t *crash);

/**
* This will check whether the `<database>/last_crash` file exists.
Expand Down
2 changes: 1 addition & 1 deletion src/sentry_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ sentry__session_from_json(const char *buf, size_t buflen)
rv->errors = (uint64_t)sentry_value_as_int32(
sentry_value_get_by_key(value, "errors"));
rv->started_us = sentry__iso8601_to_usec(
sentry_value_as_string(sentry_value_get_by_key(value, "started")));
sentry_value_as_string(sentry_value_get_by_key(value, "started")), 0);

double duration
= sentry_value_as_double(sentry_value_get_by_key(value, "duration"));
Expand Down
4 changes: 2 additions & 2 deletions src/sentry_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,9 @@ sentry__usec_time_to_iso8601(uint64_t time)

#ifndef SENTRY_PLATFORM_PS
uint64_t
sentry__iso8601_to_usec(const char *iso)
sentry__iso8601_to_usec(const char *iso, size_t iso_len)
{
size_t len = sentry__guarded_strlen(iso);
size_t len = iso_len ? iso_len : sentry__guarded_strlen(iso);
if (len != 20 && len != 27) {
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/sentry_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ char *sentry__usec_time_to_iso8601(uint64_t time);
* This only accepts the format `YYYY-MM-DD'T'hh:mm:ss(.zzzzzz)'Z'`, which is
* produced by the `sentry__usec_time_to_iso8601` function.
*/
uint64_t sentry__iso8601_to_usec(const char *iso);
uint64_t sentry__iso8601_to_usec(const char *iso, size_t iso_len);

/**
* Locale independent (or rather, using "C" locale) `strtod`.
Expand Down
Loading
Loading