Skip to content
Closed
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
15 changes: 13 additions & 2 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -834,12 +834,23 @@ SENTRY_API void sentry_options_set_symbolize_stacktraces(
SENTRY_API int sentry_options_get_symbolize_stacktraces(
const sentry_options_t *opts);

/**
* Adds a new pattern for *in-app* classification.
*
* When on-device symbolication is enabled (see
* `sentry_options_set_symbolize_stacktraces`), this will flag stack frames as
* *in-app*, which match this pattern.
* Matching is done on both symbols names and object file names they belong to.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Pattern" here is underspecified. If you compare with existing SDKs, such as JavaScript documented here, this is only a prefix match on the module.

@untitaker @mitsuhiko - did we never support functions for this? Being able to do a prefix match on functions sounds pretty essential, tbh.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix match on the module was entirely sufficient for python. function names of a library don't share a common pattern one could match against

istm native just needs a different API, but that's fine IMO

*/
SENTRY_API void sentry_options_add_in_app_include(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add in_app_exclude. After merging, let's add native to supported platforms here:

https://github.com/getsentry/sentry-docs/blob/322a46a6bd98f7f564db74e8d7752312d94c3cee/src/platforms/common/configuration/options.mdx#L157-L171

sentry_options_t *opts, const char *pattern);

/**
* Adds a new attachment to be sent along.
*
* `path` is assumed to be in platform-specific filesystem path encoding.
* API Users on windows are encouraged to use `sentry_options_add_attachmentw`
* instead.
* API Users on windows are encouraged to use
* `sentry_options_add_attachmentw` instead.
*/
SENTRY_API void sentry_options_add_attachment(
sentry_options_t *opts, const char *path);
Expand Down
5 changes: 3 additions & 2 deletions src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ sentry__crashpad_backend_user_consent_changed(sentry_backend_t *backend)
}

static void
sentry__crashpad_backend_flush_scope(sentry_backend_t *backend)
sentry__crashpad_backend_flush_scope(
sentry_backend_t *backend, const sentry_options_t *options)
{
const crashpad_state_t *data = (crashpad_state_t *)backend->data;
if (!data->event_path) {
Expand All @@ -78,7 +79,7 @@ sentry__crashpad_backend_flush_scope(sentry_backend_t *backend)
sentry_value_t event = sentry_value_new_object();
SENTRY_WITH_SCOPE (scope) {
// we want the scope without any modules or breadcrumbs
sentry__scope_apply_to_event(scope, event, SENTRY_SCOPE_NONE);
sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_NONE);
}

size_t mpack_size;
Expand Down
26 changes: 12 additions & 14 deletions src/sentry_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,22 @@
* can ensure that any captured crash contains the sentry scope and other
* information.
*/
struct sentry_backend_s;
typedef struct sentry_backend_s {
int (*startup_func)(
struct sentry_backend_s *, const sentry_options_t *options);
void (*shutdown_func)(struct sentry_backend_s *);
void (*free_func)(struct sentry_backend_s *);
void (*except_func)(
struct sentry_backend_s *, const struct sentry_ucontext_s *);
void (*flush_scope_func)(struct sentry_backend_s *);
typedef struct sentry_backend_s sentry_backend_t;
struct sentry_backend_s {
int (*startup_func)(sentry_backend_t *, const sentry_options_t *options);
void (*shutdown_func)(sentry_backend_t *);
void (*free_func)(sentry_backend_t *);
void (*except_func)(sentry_backend_t *, const struct sentry_ucontext_s *);
void (*flush_scope_func)(
sentry_backend_t *, const sentry_options_t *options);
// NOTE: The breadcrumb is not moved into the hook and does not need to be
// `decref`-d internally.
void (*add_breadcrumb_func)(
struct sentry_backend_s *, sentry_value_t breadcrumb);
void (*user_consent_changed_func)(struct sentry_backend_s *);
uint64_t (*get_last_crash_func)(struct sentry_backend_s *);
void (*add_breadcrumb_func)(sentry_backend_t *, sentry_value_t breadcrumb);
void (*user_consent_changed_func)(sentry_backend_t *);
uint64_t (*get_last_crash_func)(sentry_backend_t *);
void *data;
bool can_capture_after_shutdown;
} sentry_backend_t;
};

/**
* This will free a previously allocated backend.
Expand Down
2 changes: 1 addition & 1 deletion src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event,
if (!options->symbolize_stacktraces) {
mode &= ~SENTRY_SCOPE_STACKTRACES;
}
sentry__scope_apply_to_event(scope, event, mode);
sentry__scope_apply_to_event(scope, options, event, mode);
}

if (options->before_send_func) {
Expand Down
5 changes: 0 additions & 5 deletions src/sentry_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ sentry_value_t sentry__ensure_event_id(
*/
const sentry_options_t *sentry__options_getref(void);

/**
* Release the lock on the global options.
*/
void sentry__options_unlock(void);

#define SENTRY_WITH_OPTIONS(Options) \
for (const sentry_options_t *Options = sentry__options_getref(); Options; \
sentry_options_free((sentry_options_t *)Options), Options = NULL)
Expand Down
26 changes: 26 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ sentry_options_free(sentry_options_t *opts)
sentry_transport_free(opts->transport);
sentry__backend_free(opts->backend);

sentry_string_list_t *next_in_app = opts->in_app_includes;
while (next_in_app) {
sentry_string_list_t *in_app = next_in_app;
next_in_app = in_app->next;

sentry_free(in_app->str);
sentry_free(in_app);
}

sentry_attachment_t *next_attachment = opts->attachments;
while (next_attachment) {
sentry_attachment_t *attachment = next_attachment;
Expand Down Expand Up @@ -290,6 +299,23 @@ sentry_options_get_symbolize_stacktraces(const sentry_options_t *opts)
return opts->symbolize_stacktraces;
}

void
sentry_options_add_in_app_include(sentry_options_t *opts, const char *pattern)
{
char *owned_pattern = sentry__string_clone(pattern);
if (!owned_pattern) {
return;
}
sentry_string_list_t *list = SENTRY_MAKE(sentry_string_list_t);
if (!list) {
sentry_free(owned_pattern);
return;
}
list->str = owned_pattern;
list->next = opts->in_app_includes;
opts->in_app_includes = list;
}

void
sentry_options_set_system_crash_reporter_enabled(
sentry_options_t *opts, int enabled)
Expand Down
10 changes: 10 additions & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ struct sentry_attachment_s {
sentry_attachment_t *next;
};

/**
* A linked list of strings, used for *in-app* patterns.
*/
typedef struct sentry_string_list_s sentry_string_list_t;
struct sentry_string_list_s {
char *str;
sentry_string_list_t *next;
};

/**
* This is the main options struct, which is being accessed throughout all of
* the sentry internals.
Expand All @@ -47,6 +56,7 @@ typedef struct sentry_options_s {
bool symbolize_stacktraces;
bool system_crash_reporter_enabled;

sentry_string_list_t *in_app_includes;
sentry_attachment_t *attachments;
sentry_run_t *run;

Expand Down
61 changes: 45 additions & 16 deletions src/sentry_scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ sentry__scope_flush_unlock(const sentry_scope_t *scope)
// we try to unlock the scope/session lock as soon as possible. The
// backend will do its own `WITH_SCOPE` internally.
if (options->backend && options->backend->flush_scope_func) {
options->backend->flush_scope_func(options->backend);
options->backend->flush_scope_func(options->backend, options);
}
}
if (!did_unlock) {
Expand All @@ -136,8 +136,9 @@ sentry__scope_flush_unlock(const sentry_scope_t *scope)
}

static void
sentry__foreach_stacktrace(
sentry_value_t event, void (*func)(sentry_value_t stacktrace))
sentry__foreach_stacktrace(const sentry_options_t *options,
sentry_value_t event,
void (*func)(const sentry_options_t *options, sentry_value_t stacktrace))
{
// We have stacktraces at the following locations:
// * `exception[.values].X.stacktrace`:
Expand All @@ -155,7 +156,7 @@ sentry__foreach_stacktrace(
sentry_value_t stacktrace = sentry_value_get_by_key(
sentry_value_get_by_index(exception, i), "stacktrace");
if (!sentry_value_is_null(stacktrace)) {
func(stacktrace);
func(options, stacktrace);
}
}
}
Expand All @@ -170,17 +171,24 @@ sentry__foreach_stacktrace(
sentry_value_t stacktrace = sentry_value_get_by_key(
sentry_value_get_by_index(threads, i), "stacktrace");
if (!sentry_value_is_null(stacktrace)) {
func(stacktrace);
func(options, stacktrace);
}
}
}
}

typedef struct {
sentry_value_t frame;
const sentry_options_t *options;
} sentry__symbolize_frame_data_t;

static void
sentry__symbolize_frame(const sentry_frame_info_t *info, void *data)
{
// See https://develop.sentry.dev/sdk/event-payloads/stacktrace/
sentry_value_t frame = *(sentry_value_t *)data;
sentry__symbolize_frame_data_t *symbolize_data
= (sentry__symbolize_frame_data_t *)data;
sentry_value_t frame = symbolize_data->frame;

if (info->symbol
&& sentry_value_is_null(sentry_value_get_by_key(frame, "function"))) {
Expand All @@ -206,10 +214,28 @@ sentry__symbolize_frame(const sentry_frame_info_t *info, void *data)
sentry_value_set_by_key(frame, "image_addr",
sentry__value_new_addr((uint64_t)(size_t)info->load_addr));
}

const char *symbol_name
= sentry_value_as_string(sentry_value_get_by_key(frame, "function"));
const char *object_name
= sentry_value_as_string(sentry_value_get_by_key(frame, "package"));

sentry_string_list_t *in_app = symbolize_data->options->in_app_includes;
while (in_app) {
if (strstr(symbol_name, in_app->str) != NULL

@jan-auer jan-auer Apr 19, 2021

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on if we go with a prefix match, you can use strncmp here.

|| strstr(object_name, in_app->str) != NULL) {
sentry_value_set_by_key(
frame, "in_app", sentry_value_new_bool(true));
break;
}

in_app = in_app->next;
}
}

static void
sentry__symbolize_stacktrace(sentry_value_t stacktrace)
sentry__symbolize_stacktrace(
const sentry_options_t *options, sentry_value_t stacktrace)
{
sentry_value_t frames = sentry_value_get_by_key(stacktrace, "frames");
if (sentry_value_get_type(frames) != SENTRY_VALUE_TYPE_LIST) {
Expand All @@ -232,13 +258,17 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace)
if (!addr) {
continue;
}
sentry__symbolize((void *)addr, sentry__symbolize_frame, &frame);
sentry__symbolize_frame_data_t data;
data.frame = frame;
data.options = options;
sentry__symbolize((void *)addr, sentry__symbolize_frame, &data);
}
}

void
sentry__scope_apply_to_event(
const sentry_scope_t *scope, sentry_value_t event, sentry_scope_mode_t mode)
sentry__scope_apply_to_event(const sentry_scope_t *scope,
const sentry_options_t *options, sentry_value_t event,
sentry_scope_mode_t mode)
{
#define IS_NULL(Key) sentry_value_is_null(sentry_value_get_by_key(event, Key))
#define SET(Key, Value) sentry_value_set_by_key(event, Key, Value)
Expand All @@ -264,11 +294,9 @@ sentry__scope_apply_to_event(

PLACE_STRING("platform", "native");

SENTRY_WITH_OPTIONS (options) {
PLACE_STRING("release", options->release);
PLACE_STRING("dist", options->dist);
PLACE_STRING("environment", options->environment);
}
PLACE_STRING("release", options->release);
PLACE_STRING("dist", options->dist);
PLACE_STRING("environment", options->environment);

if (IS_NULL("level")) {
SET("level", sentry__value_new_level(scope->level));
Expand Down Expand Up @@ -298,7 +326,8 @@ sentry__scope_apply_to_event(
}

if (mode & SENTRY_SCOPE_STACKTRACES) {
sentry__foreach_stacktrace(event, sentry__symbolize_stacktrace);
sentry__foreach_stacktrace(
options, event, sentry__symbolize_stacktrace);
}

#undef PLACE_STRING
Expand Down
3 changes: 2 additions & 1 deletion src/sentry_scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ void sentry__scope_flush_unlock(const sentry_scope_t *scope);
* attached.
*/
void sentry__scope_apply_to_event(const sentry_scope_t *scope,
sentry_value_t event, sentry_scope_mode_t mode);
const sentry_options_t *options, sentry_value_t event,
sentry_scope_mode_t mode);

/**
* This will update a sessions `distinct_id`, which is generated out of other
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/test_mpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ SENTRY_TEST(mpack_removed_tags)
sentry_set_extra("int", sentry_value_new_int32(1234));
sentry_set_extra("double", sentry_value_new_double(12.34));

sentry_options_t *options = sentry_options_new();
SENTRY_WITH_SCOPE (scope) {
sentry__scope_apply_to_event(scope, obj, SENTRY_SCOPE_NONE);
sentry__scope_apply_to_event(scope, options, obj, SENTRY_SCOPE_NONE);
}

size_t size;
char *buf = sentry_value_to_msgpack(obj, &size);

sentry_options_free(options);
sentry_value_decref(obj);
sentry_free(buf);
sentry__scope_cleanup();
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/test_unwinder.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "sentry_scope.h"
#include "sentry_symbolizer.h"
#include "sentry_testsupport.h"
#include <sentry.h>
Expand Down Expand Up @@ -65,3 +66,55 @@ SENTRY_TEST(unwinder)
}
}
}

TEST_VISIBLE sentry_value_t
capture_inapp_event()
{
sentry_value_t event = sentry_value_new_event();
sentry_event_value_add_stacktrace(event, NULL, 0);
return event;
}

SENTRY_TEST(inapp_stacktrace)
{
sentry_options_t *options = sentry_options_new();
sentry_options_add_in_app_include(options, "capture_inapp_event");

sentry_value_t event = capture_inapp_event();
SENTRY_WITH_SCOPE (scope) {
// this will symbolize all the stacktraces, and flag `in_app`.
sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_ALL);
}

size_t in_app = 0;
bool found = false;

sentry_value_t threads = sentry_value_get_by_key(event, "threads");
sentry_value_t values = sentry_value_get_by_key(threads, "values");
sentry_value_t thread = sentry_value_get_by_index(values, 0);
sentry_value_t stacktrace = sentry_value_get_by_key(thread, "stacktrace");
sentry_value_t frames = sentry_value_get_by_key(stacktrace, "frames");

size_t len = sentry_value_get_length(frames);
for (size_t i = 0; i < len; i++) {
sentry_value_t frame = sentry_value_get_by_index(frames, i);

const char *symbol = sentry_value_as_string(
sentry_value_get_by_key(frame, "function"));
bool is_in_app
= sentry_value_is_true(sentry_value_get_by_key(frame, "in_app"));
if (strcmp(symbol, "capture_inapp_event") == 0) {
found = true;
TEST_CHECK(is_in_app);
}

in_app += is_in_app;
}

TEST_CHECK(found);
TEST_CHECK_INT_EQUAL(in_app, 1);

sentry_options_free(options);
sentry_value_decref(event);
sentry__scope_cleanup();
}
1 change: 1 addition & 0 deletions tests/unit/tests.inc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ XX(dsn_store_url_with_path)
XX(dsn_store_url_without_path)
XX(empty_transport)
XX(fuzz_json)
XX(inapp_stacktrace)
XX(init_failure)
XX(invalid_dsn)
XX(invalid_proxy)
Expand Down