Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Crashed last run #685

Merged
merged 5 commits into from
Mar 9, 2022
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
**Features**:

- Removed the `SENTRY_PERFORMANCE_MONITORING` compile flag requirement to access performance monitoring in the Sentry SDK. Performance monitoring is now available to everybody who has opted into the experimental API.
- New API to check whether the application has crashed in the previous run: `sentry_get_crashed_last_run()` and `sentry_clear_crashed_last_run()` ([#685](https://github.com/getsentry/sentry-native/pull/685)).
- Allow overriding the SDK name at build time - set the `SENTRY_SDK_NAME` CMake cache variable.

## 0.4.15
Expand Down
28 changes: 28 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,34 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_iter_headers(
sentry_transaction_t *tx, sentry_iter_headers_function_t callback,
void *userdata);

/**
* Returns whether the application has crashed on the last run.
*
* Notes:
* * The underlying value is set by sentry_init() - it must be called first.
* * Call sentry_clear_crashed_last_run() to reset for the next app run.
*
* Possible return values:
* 1 = the last run was a crash
* 0 = no crash recognized
* -1 = sentry_init() hasn't been called yet
*/
SENTRY_EXPERIMENTAL_API int sentry_get_crashed_last_run();

/**
* 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().
* Otherwise, the same information is reported on any subsequent runs.
*
* Notes:
* * This doesn't change the value of sentry_get_crashed_last_run() yet.
* However, if sentry_init() is called again, the value will change.
* * This may only be called after sentry_init() and before sentry_close().
*
* Returns 0 on success, 1 on error.
*/
SENTRY_EXPERIMENTAL_API int sentry_clear_crashed_last_run();

#ifdef __cplusplus
}
#endif
Expand Down
22 changes: 22 additions & 0 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
static sentry_options_t *g_options = NULL;
static sentry_mutex_t g_options_lock = SENTRY__MUTEX_INIT;

/// see sentry_get_crashed_last_run() for the possible values
static int g_last_crash = -1;

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

g_last_crash = sentry__has_crash_marker(options);
g_options = options;

// *after* setting the global options, trigger a scope and consent flush,
Expand Down Expand Up @@ -999,3 +1003,21 @@ sentry_span_finish(sentry_span_t *opaque_span)
sentry__span_free(opaque_span);
return;
}

int
sentry_get_crashed_last_run()
{
return g_last_crash;
}

int
sentry_clear_crashed_last_run()
{
bool success = false;
sentry_options_t *options = sentry__options_lock();
if (options) {
success = sentry__clear_crash_marker(options);
}
sentry__options_unlock();
return success ? 0 : 1;
}
35 changes: 34 additions & 1 deletion src/sentry_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash)
sentry__capture_envelope(options->transport, session_envelope);
}

static const char *g_last_crash_filename = "last_crash";

bool
sentry__write_crash_marker(const sentry_options_t *options)
{
Expand All @@ -244,7 +246,7 @@ sentry__write_crash_marker(const sentry_options_t *options)
}

sentry_path_t *marker_path
= sentry__path_join_str(options->database_path, "last_crash");
= sentry__path_join_str(options->database_path, g_last_crash_filename);
if (!marker_path) {
sentry_free(iso_time);
return false;
Expand All @@ -260,3 +262,34 @@ sentry__write_crash_marker(const sentry_options_t *options)
}
return !rv;
}

bool
sentry__has_crash_marker(const sentry_options_t *options)
{
sentry_path_t *marker_path
= sentry__path_join_str(options->database_path, g_last_crash_filename);
if (!marker_path) {
return false;
}

bool result = sentry__path_is_file(marker_path);
sentry__path_free(marker_path);
return result;
}

bool
sentry__clear_crash_marker(const sentry_options_t *options)
{
sentry_path_t *marker_path
= sentry__path_join_str(options->database_path, g_last_crash_filename);
if (!marker_path) {
return false;
}

int rv = sentry__path_remove(marker_path);
sentry__path_free(marker_path);
if (rv) {
SENTRY_DEBUG("removing the crash timestamp file has failed");
}
return !rv;
}
10 changes: 10 additions & 0 deletions src/sentry_database.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,14 @@ void sentry__process_old_runs(
*/
bool sentry__write_crash_marker(const sentry_options_t *options);

/**
* This will check whether the `<database>/last_crash` file exists.
*/
bool sentry__has_crash_marker(const sentry_options_t *options);

/**
* This will remove the `<database>/last_crash` file.
*/
bool sentry__clear_crash_marker(const sentry_options_t *options);

#endif
69 changes: 69 additions & 0 deletions tests/unit/test_basic.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "sentry_core.h"
#include "sentry_database.h"
#include "sentry_testsupport.h"
#include "sentry_utils.h"

static void
send_envelope_test_basic(const sentry_envelope_t *envelope, void *data)
Expand Down Expand Up @@ -96,3 +98,70 @@ SENTRY_TEST(sampling_before_send)
// well, its random after all
TEST_CHECK(called_beforesend > 50 && called_beforesend < 100);
}

SENTRY_TEST(crash_marker)
{
sentry_options_t *options = sentry_options_new();

// clear returns true, regardless if the file exists
TEST_CHECK(sentry__clear_crash_marker(options));

// write should normally be true, even when called multiple times
TEST_CHECK(!sentry__has_crash_marker(options));
TEST_CHECK(sentry__write_crash_marker(options));
TEST_CHECK(sentry__has_crash_marker(options));
TEST_CHECK(sentry__write_crash_marker(options));
TEST_CHECK(sentry__has_crash_marker(options));

TEST_CHECK(sentry__clear_crash_marker(options));
TEST_CHECK(!sentry__has_crash_marker(options));
TEST_CHECK(sentry__clear_crash_marker(options));

sentry_options_free(options);
}

SENTRY_TEST(crashed_last_run)
{
// fails before init() is called
TEST_CHECK_INT_EQUAL(sentry_clear_crashed_last_run(), 1);

// clear any leftover from previous test runs
sentry_options_t *options = sentry_options_new();
TEST_CHECK(sentry__clear_crash_marker(options));
sentry_options_free(options);

// -1 before sentry_init()
TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), -1);

options = sentry_options_new();
sentry_options_set_dsn(options, "https://foo@sentry.invalid/42");
TEST_CHECK_INT_EQUAL(sentry_init(options), 0);
sentry_close();

TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), 0);

options = sentry_options_new();
sentry_options_set_dsn(options, "https://foo@sentry.invalid/42");

// simulate a crash
TEST_CHECK(sentry__write_crash_marker(options));

TEST_CHECK_INT_EQUAL(sentry_init(options), 0);

TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), 1);

// clear the status and re-init
TEST_CHECK_INT_EQUAL(sentry_clear_crashed_last_run(), 0);

sentry_close();

// no change yet before sentry_init() is called
TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), 1);

options = sentry_options_new();
sentry_options_set_dsn(options, "https://foo@sentry.invalid/42");
TEST_CHECK_INT_EQUAL(sentry_init(options), 0);
sentry_close();

TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), 0);
}
2 changes: 2 additions & 0 deletions tests/unit/tests.inc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ XX(child_spans)
XX(concurrent_init)
XX(concurrent_uninit)
XX(count_sampled_events)
XX(crash_marker)
XX(crashed_last_run)
XX(custom_logger)
XX(distributed_headers)
XX(drop_unfinished_spans)
Expand Down