diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa8c1bb9a8..a5d03e81af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - run: make style + - run: | + sudo apt update + sudo apt install clang-format-15 + make style-15 build-ios: name: Xcode Build for iOS diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c253cc5a..952f1c4977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +**Features**: + +- Extend API with ptr/len-string interfaces. ([#827](https://github.com/getsentry/sentry-native/pull/827)) + ## 0.6.1 **Fixes**: diff --git a/Makefile b/Makefile index 6925df596c..a936b143fd 100644 --- a/Makefile +++ b/Makefile @@ -83,3 +83,9 @@ style: setup-venv @.venv/bin/python ./scripts/check-clang-format.py -r examples include src tests/unit @.venv/bin/black --diff --check tests .PHONY: style + +# TODO: workaround for clang-format 15+ where local formatting breaks with clang-format-14 based style checks on CI +style-15: setup-venv + @.venv/bin/python ./scripts/check-clang-format.py --clang-format-executable /usr/bin/clang-format-15 -r examples include src tests/unit + @.venv/bin/black --diff --check tests +.PHONY: style-15 diff --git a/include/sentry.h b/include/sentry.h index 088f81dffc..32cecb63a2 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -207,6 +207,8 @@ SENTRY_API sentry_value_t sentry_value_new_bool(int value); * Creates a new null terminated string. */ SENTRY_API sentry_value_t sentry_value_new_string(const char *value); +SENTRY_API sentry_value_t sentry_value_new_string_n( + const char *value, size_t value_len); /** * Creates a new list value. @@ -232,10 +234,15 @@ SENTRY_API sentry_value_type_t sentry_value_get_type(sentry_value_t value); SENTRY_API int sentry_value_set_by_key( sentry_value_t value, const char *k, sentry_value_t v); +SENTRY_API int sentry_value_set_by_key_n( + sentry_value_t value, const char *k, size_t k_len, sentry_value_t v); + /** * This removes a value from the map by key. */ SENTRY_API int sentry_value_remove_by_key(sentry_value_t value, const char *k); +SENTRY_API int sentry_value_remove_by_key_n( + sentry_value_t value, const char *k, size_t k_len); /** * Appends a value to a list. @@ -268,6 +275,8 @@ SENTRY_API int sentry_value_remove_by_index(sentry_value_t value, size_t index); */ SENTRY_API sentry_value_t sentry_value_get_by_key( sentry_value_t value, const char *k); +SENTRY_API sentry_value_t sentry_value_get_by_key_n( + sentry_value_t value, const char *k, size_t k_len); /** * Looks up a value in a map by key. If missing a null value is returned. @@ -278,6 +287,8 @@ SENTRY_API sentry_value_t sentry_value_get_by_key( */ SENTRY_API sentry_value_t sentry_value_get_by_key_owned( sentry_value_t value, const char *k); +SENTRY_API sentry_value_t sentry_value_get_by_key_owned_n( + sentry_value_t value, const char *k, size_t k_len); /** * Looks up a value in a list by index. If missing a null value is returned. @@ -365,6 +376,8 @@ SENTRY_API sentry_value_t sentry_value_new_event(void); */ SENTRY_API sentry_value_t sentry_value_new_message_event( sentry_level_t level, const char *logger, const char *text); +SENTRY_API sentry_value_t sentry_value_new_message_event_n(sentry_level_t level, + const char *logger, size_t logger_len, const char *text, size_t text_len); /** * Creates a new Breadcrumb with a specific type and message. @@ -375,6 +388,8 @@ SENTRY_API sentry_value_t sentry_value_new_message_event( */ SENTRY_API sentry_value_t sentry_value_new_breadcrumb( const char *type, const char *message); +SENTRY_API sentry_value_t sentry_value_new_breadcrumb_n( + const char *type, size_t type_len, const char *message, size_t message_len); /** * Creates a new Exception value. @@ -390,6 +405,8 @@ SENTRY_API sentry_value_t sentry_value_new_breadcrumb( */ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_exception( const char *type, const char *value); +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_exception_n( + const char *type, size_t type_len, const char *value, size_t value_len); /** * Creates a new Thread value. @@ -403,6 +420,8 @@ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_exception( */ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread( uint64_t id, const char *name); +SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread_n( + uint64_t id, const char *name, size_t name_len); /** * Creates a new Stack Trace conforming to the Stack Trace Interface. @@ -534,6 +553,8 @@ SENTRY_API sentry_uuid_t sentry_uuid_new_v4(void); * Parses a uuid from a string. */ SENTRY_API sentry_uuid_t sentry_uuid_from_string(const char *str); +SENTRY_API sentry_uuid_t sentry_uuid_from_string_n( + const char *str, size_t str_len); /** * Creates a uuid from bytes. @@ -603,6 +624,8 @@ SENTRY_API char *sentry_envelope_serialize( */ SENTRY_API int sentry_envelope_write_to_file( const sentry_envelope_t *envelope, const char *path); +SENTRY_API int sentry_envelope_write_to_file_n( + const sentry_envelope_t *envelope, const char *path, size_t path_len); /** * The Sentry Client Options. @@ -866,6 +889,8 @@ SENTRY_API void sentry_options_set_on_crash( * Sets the DSN. */ SENTRY_API void sentry_options_set_dsn(sentry_options_t *opts, const char *dsn); +SENTRY_API void sentry_options_set_dsn_n( + sentry_options_t *opts, const char *dsn, size_t dsn_len); /** * Gets the DSN. @@ -903,6 +928,8 @@ SENTRY_API double sentry_options_get_sample_rate(const sentry_options_t *opts); */ SENTRY_API void sentry_options_set_release( sentry_options_t *opts, const char *release); +SENTRY_API void sentry_options_set_release_n( + sentry_options_t *opts, const char *release, size_t release_len); /** * Gets the release. @@ -914,6 +941,8 @@ SENTRY_API const char *sentry_options_get_release(const sentry_options_t *opts); */ SENTRY_API void sentry_options_set_environment( sentry_options_t *opts, const char *environment); +SENTRY_API void sentry_options_set_environment_n( + sentry_options_t *opts, const char *environment, size_t environment_len); /** * Gets the environment. @@ -926,6 +955,8 @@ SENTRY_API const char *sentry_options_get_environment( */ SENTRY_API void sentry_options_set_dist( sentry_options_t *opts, const char *dist); +SENTRY_API void sentry_options_set_dist_n( + sentry_options_t *opts, const char *dist, size_t dist_len); /** * Gets the dist. @@ -939,6 +970,8 @@ SENTRY_API const char *sentry_options_get_dist(const sentry_options_t *opts); */ SENTRY_API void sentry_options_set_http_proxy( sentry_options_t *opts, const char *proxy); +SENTRY_API void sentry_options_set_http_proxy_n( + sentry_options_t *opts, const char *proxy, size_t proxy_len); /** * Returns the configured http proxy. @@ -952,6 +985,8 @@ SENTRY_API const char *sentry_options_get_http_proxy( */ SENTRY_API void sentry_options_set_ca_certs( sentry_options_t *opts, const char *path); +SENTRY_API void sentry_options_set_ca_certs_n( + sentry_options_t *opts, const char *path, size_t path_len); /** * Returns the configured path for ca certificates. @@ -964,6 +999,8 @@ SENTRY_API const char *sentry_options_get_ca_certs( */ SENTRY_API void sentry_options_set_transport_thread_name( sentry_options_t *opts, const char *name); +SENTRY_API void sentry_options_set_transport_thread_name_n( + sentry_options_t *opts, const char *name, size_t name_len); /** * Returns the configured http transport thread name. @@ -1069,6 +1106,8 @@ SENTRY_API int sentry_options_get_symbolize_stacktraces( */ SENTRY_API void sentry_options_add_attachment( sentry_options_t *opts, const char *path); +SENTRY_API void sentry_options_add_attachment_n( + sentry_options_t *opts, const char *path, size_t path_len); /** * Sets the path to the crashpad handler if the crashpad backend is used. @@ -1086,6 +1125,8 @@ SENTRY_API void sentry_options_add_attachment( */ SENTRY_API void sentry_options_set_handler_path( sentry_options_t *opts, const char *path); +SENTRY_API void sentry_options_set_handler_path_n( + sentry_options_t *opts, const char *path, size_t path_len); /** * Sets the path to the Sentry Database Directory. @@ -1118,6 +1159,8 @@ SENTRY_API void sentry_options_set_handler_path( */ SENTRY_API void sentry_options_set_database_path( sentry_options_t *opts, const char *path); +SENTRY_API void sentry_options_set_database_path_n( + sentry_options_t *opts, const char *path, size_t path_len); #ifdef SENTRY_PLATFORM_WINDOWS /** @@ -1125,18 +1168,24 @@ SENTRY_API void sentry_options_set_database_path( */ SENTRY_API void sentry_options_add_attachmentw( sentry_options_t *opts, const wchar_t *path); +SENTRY_API void sentry_options_add_attachmentw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len); /** * Wide char version of `sentry_options_set_handler_path`. */ SENTRY_API void sentry_options_set_handler_pathw( sentry_options_t *opts, const wchar_t *path); +SENTRY_API void sentry_options_set_handler_pathw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len); /** * Wide char version of `sentry_options_set_database_path`. */ SENTRY_API void sentry_options_set_database_pathw( sentry_options_t *opts, const wchar_t *path); +SENTRY_API void sentry_options_set_database_pathw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len); #endif /** @@ -1297,31 +1346,40 @@ SENTRY_API void sentry_remove_user(void); * Sets a tag. */ SENTRY_API void sentry_set_tag(const char *key, const char *value); +SENTRY_API void sentry_set_tag_n( + const char *key, size_t key_len, const char *value, size_t value_len); /** * Removes the tag with the specified key. */ SENTRY_API void sentry_remove_tag(const char *key); +SENTRY_API void sentry_remove_tag_n(const char *key, size_t key_len); /** * Sets extra information. */ SENTRY_API void sentry_set_extra(const char *key, sentry_value_t value); +SENTRY_API void sentry_set_extra_n( + const char *key, size_t key_len, sentry_value_t value); /** * Removes the extra with the specified key. */ SENTRY_API void sentry_remove_extra(const char *key); +SENTRY_API void sentry_remove_extra_n(const char *key, size_t key_len); /** * Sets a context object. */ SENTRY_API void sentry_set_context(const char *key, sentry_value_t value); +SENTRY_API void sentry_set_context_n( + const char *key, size_t key_len, sentry_value_t value); /** * Removes the context object with the specified key. */ SENTRY_API void sentry_remove_context(const char *key); +SENTRY_API void sentry_remove_context_n(const char *key, size_t key_len); /** * Sets the event fingerprint. @@ -1330,6 +1388,8 @@ SENTRY_API void sentry_remove_context(const char *key); * trailing `NULL`. */ SENTRY_API void sentry_set_fingerprint(const char *fingerprint, ...); +SENTRY_API void sentry_set_fingerprint_n( + const char *fingerprint, size_t fingerprint_len, ...); /** * Removes the fingerprint. @@ -1340,6 +1400,8 @@ SENTRY_API void sentry_remove_fingerprint(void); * Sets the transaction. */ SENTRY_API void sentry_set_transaction(const char *transaction); +SENTRY_API void sentry_set_transaction_n( + const char *transaction, size_t transaction_len); /** * Sets the event level. @@ -1448,6 +1510,9 @@ typedef struct sentry_span_s sentry_span_t; */ SENTRY_EXPERIMENTAL_API sentry_transaction_context_t * sentry_transaction_context_new(const char *name, const char *operation); +SENTRY_EXPERIMENTAL_API sentry_transaction_context_t * +sentry_transaction_context_new_n(const char *name, size_t name_len, + const char *operation, size_t operation_len); /** * Sets the `name` on a Transaction Context, which will be used in the @@ -1458,6 +1523,8 @@ sentry_transaction_context_new(const char *name, const char *operation); */ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name( sentry_transaction_context_t *tx_cxt, const char *name); +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name_n( + sentry_transaction_context_t *tx_cxt, const char *name, size_t name_len); /** * Sets the `operation` on a Transaction Context, which will be used in the @@ -1471,6 +1538,9 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation( sentry_transaction_context_t *tx_cxt, const char *operation); +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation_n( + sentry_transaction_context_t *tx_cxt, const char *operation, + size_t operation_len); /** * Sets the `sampled` field on a Transaction Context, which will be used in the @@ -1508,6 +1578,9 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header( sentry_transaction_context_t *tx_cxt, const char *key, const char *value); +SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header_n( + sentry_transaction_context_t *tx_cxt, const char *key, size_t key_len, + const char *value, size_t value_len); /** * Starts a new Transaction based on the provided context, restored from an @@ -1618,7 +1691,11 @@ SENTRY_EXPERIMENTAL_API void sentry_set_span(sentry_span_t *span); * in a thread-safe way. */ SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_transaction_start_child( - sentry_transaction_t *parent, char *operation, char *description); + sentry_transaction_t *parent, const char *operation, + const char *description); +SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_transaction_start_child_n( + sentry_transaction_t *parent, const char *operation, size_t operation_len, + const char *description, size_t description_len); /** * Starts a new Span. @@ -1651,7 +1728,10 @@ SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_transaction_start_child( * in a thread-safe way. */ SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_span_start_child( - sentry_span_t *parent, char *operation, char *description); + sentry_span_t *parent, const char *operation, const char *description); +SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_span_start_child_n( + sentry_span_t *parent, const char *operation, size_t operation_len, + const char *description, size_t description_len); /** * Finishes a Span. @@ -1675,6 +1755,9 @@ SENTRY_EXPERIMENTAL_API void sentry_span_finish(sentry_span_t *span); */ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_tag( sentry_transaction_t *transaction, const char *tag, const char *value); +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_tag_n( + sentry_transaction_t *transaction, const char *tag, size_t tag_len, + const char *value, size_t value_len); /** * Removes a tag from a Transaction. @@ -1684,6 +1767,8 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_tag( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_tag( sentry_transaction_t *transaction, const char *tag); +SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_tag_n( + sentry_transaction_t *transaction, const char *tag, size_t tag_len); /** * Sets the given key in a Transaction's "data" section to the given value. @@ -1693,6 +1778,9 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_tag( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_data( sentry_transaction_t *transaction, const char *key, sentry_value_t value); +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_data_n( + sentry_transaction_t *transaction, const char *key, size_t key_len, + sentry_value_t value); /** * Removes a key from a Transaction's "data" section. @@ -1702,6 +1790,8 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_data( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_data( sentry_transaction_t *transaction, const char *key); +SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_data_n( + sentry_transaction_t *transaction, const char *key, size_t key_len); /** * Sets a tag on a Span to the given string value. @@ -1713,6 +1803,8 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_data( */ SENTRY_EXPERIMENTAL_API void sentry_span_set_tag( sentry_span_t *span, const char *tag, const char *value); +SENTRY_EXPERIMENTAL_API void sentry_span_set_tag_n(sentry_span_t *span, + const char *tag, size_t tag_len, const char *value, size_t value_len); /** * Removes a tag from a Span. @@ -1722,6 +1814,8 @@ SENTRY_EXPERIMENTAL_API void sentry_span_set_tag( */ SENTRY_EXPERIMENTAL_API void sentry_span_remove_tag( sentry_span_t *span, const char *tag); +SENTRY_EXPERIMENTAL_API void sentry_span_remove_tag_n( + sentry_span_t *span, const char *tag, size_t tag_len); /** * Sets the given key in a Span's "data" section to the given value. @@ -1731,6 +1825,8 @@ SENTRY_EXPERIMENTAL_API void sentry_span_remove_tag( */ SENTRY_EXPERIMENTAL_API void sentry_span_set_data( sentry_span_t *span, const char *key, sentry_value_t value); +SENTRY_EXPERIMENTAL_API void sentry_span_set_data_n( + sentry_span_t *span, const char *key, size_t key_len, sentry_value_t value); /** * Removes a key from a Span's "data" section. @@ -1740,6 +1836,8 @@ SENTRY_EXPERIMENTAL_API void sentry_span_set_data( */ SENTRY_EXPERIMENTAL_API void sentry_span_remove_data( sentry_span_t *span, const char *key); +SENTRY_EXPERIMENTAL_API void sentry_span_remove_data_n( + sentry_span_t *span, const char *key, size_t key_len); /** * Sets a Transaction's name. @@ -1749,6 +1847,8 @@ SENTRY_EXPERIMENTAL_API void sentry_span_remove_data( */ SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name( sentry_transaction_t *transaction, const char *name); +SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name_n( + sentry_transaction_t *transaction, const char *name, size_t name_len); /** * The status of a Span or Transaction. diff --git a/src/path/sentry_path_unix.c b/src/path/sentry_path_unix.c index d75a0eb8f1..298913996b 100644 --- a/src/path/sentry_path_unix.c +++ b/src/path/sentry_path_unix.c @@ -169,9 +169,9 @@ sentry__path_dir(const sentry_path_t *path) } sentry_path_t * -sentry__path_from_str(const char *s) +sentry__path_from_str_n(const char *s, size_t s_len) { - char *path = sentry__string_clone(s); + char *path = sentry__string_clone_n(s, s_len); if (!path) { return NULL; } @@ -179,6 +179,12 @@ sentry__path_from_str(const char *s) return sentry__path_from_str_owned(path); } +sentry_path_t * +sentry__path_from_str(const char *s) +{ + return s ? sentry__path_from_str_n(s, strlen(s)) : NULL; +} + sentry_path_t * sentry__path_from_str_owned(char *s) { diff --git a/src/path/sentry_path_windows.c b/src/path/sentry_path_windows.c index ea2a7097e7..b953af9a6b 100644 --- a/src/path/sentry_path_windows.c +++ b/src/path/sentry_path_windows.c @@ -135,16 +135,28 @@ sentry__path_dir(const sentry_path_t *path) } sentry_path_t * -sentry__path_from_wstr(const wchar_t *s) +sentry__path_from_wstr_n(const wchar_t *s, size_t s_len) { - size_t len = wcslen(s) + 1; - sentry_path_t *rv = path_with_len(len); + if (!s) { + return NULL; + } + sentry_path_t *rv = path_with_len(s_len + 1); if (rv) { - memcpy(rv->path, s, len * sizeof(wchar_t)); + memcpy(rv->path, s, s_len * sizeof(wchar_t)); + rv->path[s_len] = 0; } return rv; } +sentry_path_t * +sentry__path_from_wstr(const wchar_t *s) +{ + if (!s) { + return NULL; + } + return sentry__path_from_wstr_n(s, wcslen(s)); +} + sentry_path_t * sentry__path_join_wstr(const sentry_path_t *base, const wchar_t *other) { @@ -189,20 +201,42 @@ sentry__path_join_wstr(const sentry_path_t *base, const wchar_t *other) } sentry_path_t * -sentry__path_from_str(const char *s) +sentry__path_from_str_n(const char *s, size_t s_len) { - size_t len = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0); + if (!s) { + return NULL; + } sentry_path_t *rv = SENTRY_MAKE(sentry_path_t); if (!rv) { return NULL; } - rv->path = sentry_malloc(sizeof(wchar_t) * len); + size_t src_size = sizeof(char) * s_len; + size_t dst_size = sizeof(wchar_t) * (s_len + 1); + rv->path = sentry_malloc(dst_size); if (!rv->path) { - sentry_free(rv); - return NULL; + goto error; + } + int conv_len = MultiByteToWideChar( + CP_ACP, 0, s, (int)src_size, rv->path, (int)s_len); + if (conv_len == 0) { + goto error; } - MultiByteToWideChar(CP_ACP, 0, s, -1, rv->path, (int)len); + rv->path[conv_len] = 0; return rv; + +error: + sentry_free(rv); + return NULL; +} + +sentry_path_t * +sentry__path_from_str(const char *s) +{ + if (!s) { + return NULL; + } + + return sentry__path_from_str_n(s, strlen(s)); } sentry_path_t * diff --git a/src/sentry_core.c b/src/sentry_core.c index b10d6065df..8cbb39a031 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1,10 +1,8 @@ #include "sentry_boot.h" #include -#include #include -#include "sentry_alloc.h" #include "sentry_backend.h" #include "sentry_core.h" #include "sentry_database.h" @@ -640,6 +638,16 @@ sentry_set_tag(const char *key, const char *value) } } +void +sentry_set_tag_n( + const char *key, size_t key_len, const char *value, size_t value_len) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_set_by_key_n(scope->tags, key, key_len, + sentry_value_new_string_n(value, value_len)); + } +} + void sentry_remove_tag(const char *key) { @@ -648,6 +656,14 @@ sentry_remove_tag(const char *key) } } +void +sentry_remove_tag_n(const char *key, size_t key_len) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_remove_by_key_n(scope->tags, key, key_len); + } +} + void sentry_set_extra(const char *key, sentry_value_t value) { @@ -656,6 +672,14 @@ sentry_set_extra(const char *key, sentry_value_t value) } } +void +sentry_set_extra_n(const char *key, size_t key_len, sentry_value_t value) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_set_by_key_n(scope->extra, key, key_len, value); + } +} + void sentry_remove_extra(const char *key) { @@ -664,6 +688,14 @@ sentry_remove_extra(const char *key) } } +void +sentry_remove_extra_n(const char *key, size_t key_len) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_remove_by_key_n(scope->extra, key, key_len); + } +} + void sentry_set_context(const char *key, sentry_value_t value) { @@ -672,6 +704,14 @@ sentry_set_context(const char *key, sentry_value_t value) } } +void +sentry_set_context_n(const char *key, size_t key_len, sentry_value_t value) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_set_by_key_n(scope->contexts, key, key_len, value); + } +} + void sentry_remove_context(const char *key) { @@ -680,6 +720,33 @@ sentry_remove_context(const char *key) } } +void +sentry_remove_context_n(const char *key, size_t key_len) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_remove_by_key_n(scope->contexts, key, key_len); + } +} + +void +sentry_set_fingerprint_n(const char *fingerprint, size_t fingerprint_len, ...) +{ + sentry_value_t fingerprint_value = sentry_value_new_list(); + + va_list va; + va_start(va, fingerprint_len); + for (; fingerprint; fingerprint = va_arg(va, const char *)) { + sentry_value_append(fingerprint_value, + sentry_value_new_string_n(fingerprint, fingerprint_len)); + } + va_end(va); + + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_value_decref(scope->fingerprint); + scope->fingerprint = fingerprint_value; + } +} + void sentry_set_fingerprint(const char *fingerprint, ...) { @@ -696,7 +763,7 @@ sentry_set_fingerprint(const char *fingerprint, ...) SENTRY_WITH_SCOPE_MUT (scope) { sentry_value_decref(scope->fingerprint); scope->fingerprint = fingerprint_value; - }; + } } void @@ -705,7 +772,7 @@ sentry_remove_fingerprint(void) SENTRY_WITH_SCOPE_MUT (scope) { sentry_value_decref(scope->fingerprint); scope->fingerprint = sentry_value_new_null(); - }; + } } void @@ -721,6 +788,21 @@ sentry_set_transaction(const char *transaction) } } +void +sentry_set_transaction_n(const char *transaction, size_t transaction_len) +{ + SENTRY_WITH_SCOPE_MUT (scope) { + sentry_free(scope->transaction); + scope->transaction + = sentry__string_clone_n(transaction, transaction_len); + + if (scope->transaction_object) { + sentry_transaction_set_name_n( + scope->transaction_object, transaction, transaction_len); + } + } +} + void sentry_set_level(sentry_level_t level) { @@ -870,8 +952,9 @@ sentry_set_span(sentry_span_t *span) } sentry_span_t * -sentry_transaction_start_child( - sentry_transaction_t *opaque_parent, char *operation, char *description) +sentry_transaction_start_child_n(sentry_transaction_t *opaque_parent, + const char *operation, size_t operation_len, const char *description, + size_t description_len) { if (!opaque_parent || sentry_value_is_null(opaque_parent->inner)) { SENTRY_DEBUG("no transaction available to create a child under"); @@ -886,14 +969,25 @@ sentry_transaction_start_child( max_spans = options->max_spans; } - sentry_value_t span - = sentry__value_span_new(max_spans, parent, operation, description); + sentry_value_t span = sentry__value_span_new_n(max_spans, parent, + (sentry_slice_t) { operation, operation_len }, + (sentry_slice_t) { description, description_len }); return sentry__span_new(opaque_parent, span); } sentry_span_t * -sentry_span_start_child( - sentry_span_t *opaque_parent, char *operation, char *description) +sentry_transaction_start_child(sentry_transaction_t *opaque_parent, + const char *operation, const char *description) +{ + const size_t operation_len = operation ? strlen(operation) : 0; + const size_t description_len = description ? strlen(description) : 0; + return sentry_transaction_start_child_n( + opaque_parent, operation, operation_len, description, description_len); +} + +sentry_span_t * +sentry_span_start_child_n(sentry_span_t *opaque_parent, const char *operation, + size_t operation_len, const char *description, size_t description_len) { if (!opaque_parent || sentry_value_is_null(opaque_parent->inner)) { SENTRY_DEBUG("no parent span available to create a child span under"); @@ -912,12 +1006,23 @@ sentry_span_start_child( max_spans = options->max_spans; } - sentry_value_t span - = sentry__value_span_new(max_spans, parent, operation, description); + sentry_value_t span = sentry__value_span_new_n(max_spans, parent, + (sentry_slice_t) { operation, operation_len }, + (sentry_slice_t) { description, description_len }); return sentry__span_new(opaque_parent->transaction, span); } +sentry_span_t * +sentry_span_start_child(sentry_span_t *opaque_parent, const char *operation, + const char *description) +{ + size_t operation_len = operation ? strlen(operation) : 0; + size_t description_len = description ? strlen(description) : 0; + return sentry_span_start_child_n( + opaque_parent, operation, operation_len, description, description_len); +} + void sentry_span_finish(sentry_span_t *opaque_span) { @@ -1010,7 +1115,6 @@ sentry_span_finish(sentry_span_t *opaque_span) fail: sentry__span_decref(opaque_span); - return; } int diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index cfcf5af824..4b68a27871 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -322,7 +322,7 @@ sentry__envelope_add_from_buffer(sentry_envelope_t *envelope, const char *buf, // NOTE: function will check for the clone of `buf` internally and free it // on error return envelope_add_from_owned_buffer( - envelope, sentry__string_clonen(buf, buf_len), buf_len, type); + envelope, sentry__string_clone_n(buf, buf_len), buf_len, type); } sentry_envelope_item_t * @@ -459,10 +459,13 @@ sentry_envelope_write_to_path( } int -sentry_envelope_write_to_file( - const sentry_envelope_t *envelope, const char *path) +sentry_envelope_write_to_file_n( + const sentry_envelope_t *envelope, const char *path, size_t path_len) { - sentry_path_t *path_obj = sentry__path_from_str(path); + if (!envelope || !path) { + return 1; + } + sentry_path_t *path_obj = sentry__path_from_str_n(path, path_len); int rv = sentry_envelope_write_to_path(envelope, path_obj); @@ -471,6 +474,17 @@ sentry_envelope_write_to_file( return rv; } +int +sentry_envelope_write_to_file( + const sentry_envelope_t *envelope, const char *path) +{ + if (!envelope || !path) { + return 1; + } + + return sentry_envelope_write_to_file_n(envelope, path, strlen(path)); +} + #ifdef SENTRY_UNITTEST size_t sentry__envelope_get_item_count(const sentry_envelope_t *envelope) diff --git a/src/sentry_json.c b/src/sentry_json.c index cf02c7f1b9..f83cd1886d 100644 --- a/src/sentry_json.c +++ b/src/sentry_json.c @@ -471,8 +471,8 @@ tokens_to_value(jsmntok_t *tokens, size_t token_count, const char *buf, break; } case JSMN_STRING: { - char *string - = sentry__string_clonen(buf + root->start, root->end - root->start); + char *string = sentry__string_clone_n_unchecked( + buf + root->start, root->end - root->start); if (decode_string_inplace(string)) { rv = sentry__value_new_string_owned(string); } else { @@ -492,7 +492,7 @@ tokens_to_value(jsmntok_t *tokens, size_t token_count, const char *buf, sentry_value_t child; NESTED_PARSE(&child); - char *key = sentry__string_clonen( + char *key = sentry__string_clone_n_unchecked( buf + token->start, token->end - token->start); if (decode_string_inplace(key)) { sentry_value_set_by_key(rv, key, child); diff --git a/src/sentry_options.c b/src/sentry_options.c index b0a6fe8aa6..4814bfefd2 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -130,6 +130,14 @@ sentry_options_set_on_crash( opts->on_crash_data = data; } +void +sentry_options_set_dsn_n( + sentry_options_t *opts, const char *raw_dsn, size_t raw_dsn_len) +{ + sentry__dsn_decref(opts->dsn); + opts->dsn = sentry__dsn_new_n(raw_dsn, raw_dsn_len); +} + void sentry_options_set_dsn(sentry_options_t *opts, const char *raw_dsn) { @@ -160,6 +168,14 @@ sentry_options_get_sample_rate(const sentry_options_t *opts) return opts->sample_rate; } +void +sentry_options_set_release_n( + sentry_options_t *opts, const char *release, size_t release_len) +{ + sentry_free(opts->release); + opts->release = sentry__string_clone_n(release, release_len); +} + void sentry_options_set_release(sentry_options_t *opts, const char *release) { @@ -173,6 +189,14 @@ sentry_options_get_release(const sentry_options_t *opts) return opts->release; } +void +sentry_options_set_environment_n( + sentry_options_t *opts, const char *environment, size_t environment_len) +{ + sentry_free(opts->environment); + opts->environment = sentry__string_clone_n(environment, environment_len); +} + void sentry_options_set_environment(sentry_options_t *opts, const char *environment) { @@ -186,6 +210,14 @@ sentry_options_get_environment(const sentry_options_t *opts) return opts->environment; } +void +sentry_options_set_dist_n( + sentry_options_t *opts, const char *dist, size_t dist_len) +{ + sentry_free(opts->dist); + opts->dist = sentry__string_clone_n(dist, dist_len); +} + void sentry_options_set_dist(sentry_options_t *opts, const char *dist) { @@ -199,6 +231,14 @@ sentry_options_get_dist(const sentry_options_t *opts) return opts->dist; } +void +sentry_options_set_http_proxy_n( + sentry_options_t *opts, const char *proxy, size_t proxy_len) +{ + sentry_free(opts->http_proxy); + opts->http_proxy = sentry__string_clone_n(proxy, proxy_len); +} + void sentry_options_set_http_proxy(sentry_options_t *opts, const char *proxy) { @@ -219,6 +259,14 @@ sentry_options_set_ca_certs(sentry_options_t *opts, const char *path) opts->ca_certs = sentry__string_clone(path); } +void +sentry_options_set_ca_certs_n( + sentry_options_t *opts, const char *path, size_t path_len) +{ + sentry_free(opts->ca_certs); + opts->ca_certs = sentry__string_clone_n(path, path_len); +} + const char * sentry_options_get_ca_certs(const sentry_options_t *opts) { @@ -233,6 +281,14 @@ sentry_options_set_transport_thread_name( opts->transport_thread_name = sentry__string_clone(name); } +void +sentry_options_set_transport_thread_name_n( + sentry_options_t *opts, const char *name, size_t name_len) +{ + sentry_free(opts->transport_thread_name); + opts->transport_thread_name = sentry__string_clone_n(name, name_len); +} + const char * sentry_options_get_transport_thread_name(const sentry_options_t *opts) { @@ -350,6 +406,13 @@ sentry_options_add_attachment(sentry_options_t *opts, const char *path) add_attachment(opts, sentry__path_from_str(path)); } +void +sentry_options_add_attachment_n( + sentry_options_t *opts, const char *path, size_t path_len) +{ + add_attachment(opts, sentry__path_from_str_n(path, path_len)); +} + void sentry_options_set_handler_path(sentry_options_t *opts, const char *path) { @@ -357,6 +420,14 @@ sentry_options_set_handler_path(sentry_options_t *opts, const char *path) opts->handler_path = sentry__path_from_str(path); } +void +sentry_options_set_handler_path_n( + sentry_options_t *opts, const char *path, size_t path_len) +{ + sentry__path_free(opts->handler_path); + opts->handler_path = sentry__path_from_str_n(path, path_len); +} + void sentry_options_set_database_path(sentry_options_t *opts, const char *path) { @@ -364,25 +435,57 @@ sentry_options_set_database_path(sentry_options_t *opts, const char *path) opts->database_path = sentry__path_from_str(path); } +void +sentry_options_set_database_path_n( + sentry_options_t *opts, const char *path, size_t path_len) +{ + sentry__path_free(opts->database_path); + opts->database_path = sentry__path_from_str_n(path, path_len); +} + #ifdef SENTRY_PLATFORM_WINDOWS +void +sentry_options_add_attachmentw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len) +{ + add_attachment(opts, sentry__path_from_wstr_n(path, path_len)); +} + void sentry_options_add_attachmentw(sentry_options_t *opts, const wchar_t *path) { - add_attachment(opts, sentry__path_from_wstr(path)); + size_t path_len = path ? wcslen(path) : 0; + sentry_options_add_attachmentw_n(opts, path, path_len); } void -sentry_options_set_handler_pathw(sentry_options_t *opts, const wchar_t *path) +sentry_options_set_handler_pathw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len) { sentry__path_free(opts->handler_path); - opts->handler_path = sentry__path_from_wstr(path); + opts->handler_path = sentry__path_from_wstr_n(path, path_len); } void -sentry_options_set_database_pathw(sentry_options_t *opts, const wchar_t *path) +sentry_options_set_handler_pathw(sentry_options_t *opts, const wchar_t *path) +{ + size_t path_len = path ? wcslen(path) : 0; + sentry_options_set_handler_pathw_n(opts, path, path_len); +} + +void +sentry_options_set_database_pathw_n( + sentry_options_t *opts, const wchar_t *path, size_t path_len) { sentry__path_free(opts->database_path); - opts->database_path = sentry__path_from_wstr(path); + opts->database_path = sentry__path_from_wstr_n(path, path_len); +} + +void +sentry_options_set_database_pathw(sentry_options_t *opts, const wchar_t *path) +{ + size_t path_len = path ? wcslen(path) : 0; + sentry_options_set_database_pathw_n(opts, path, path_len); } #endif diff --git a/src/sentry_path.h b/src/sentry_path.h index 8cedd1d213..02e42d5fe0 100644 --- a/src/sentry_path.h +++ b/src/sentry_path.h @@ -53,6 +53,7 @@ sentry_path_t *sentry__path_dir(const sentry_path_t *path); * Create a new path from the given string. */ sentry_path_t *sentry__path_from_str(const char *s); +sentry_path_t *sentry__path_from_str_n(const char *s, size_t s_len); /** * Create a new path from the given string. @@ -205,6 +206,7 @@ void sentry__filelock_free(sentry_filelock_t *lock); * Create a new path from a Wide String. */ sentry_path_t *sentry__path_from_wstr(const wchar_t *s); +sentry_path_t *sentry__path_from_wstr_n(const wchar_t *s, size_t s_len); /** * Create another path by appending a new path segment. diff --git a/src/sentry_slice.c b/src/sentry_slice.c index 79894b7ded..0f68311b22 100644 --- a/src/sentry_slice.c +++ b/src/sentry_slice.c @@ -15,7 +15,7 @@ sentry__slice_from_str(const char *str) char * sentry__slice_to_owned(sentry_slice_t slice) { - return sentry__string_clonen(slice.ptr, slice.len); + return sentry__string_clone_n_unchecked(slice.ptr, slice.len); } bool diff --git a/src/sentry_string.h b/src/sentry_string.h index 255d55d171..95e1e4f614 100644 --- a/src/sentry_string.h +++ b/src/sentry_string.h @@ -110,10 +110,11 @@ size_t sentry__stringbuilder_len(const sentry_stringbuilder_t *sb); void sentry__stringbuilder_set_len(sentry_stringbuilder_t *sb, size_t len); /** - * Duplicates a zero terminated string with a length limit. + * Duplicates a zero terminated string with a length limit. Does not check + * if `str` is NULL. */ static inline char * -sentry__string_clonen(const char *str, size_t n) +sentry__string_clone_n_unchecked(const char *str, size_t n) { size_t len = n + 1; char *rv = (char *)sentry_malloc(len); @@ -124,13 +125,32 @@ sentry__string_clonen(const char *str, size_t n) return rv; } +/** + * Duplicates a ptr/len string into a zero terminated string. + */ +static inline char * +sentry__string_clone_n(const char *str, size_t n) +{ + return str ? sentry__string_clone_n_unchecked(str, n) : NULL; +} + /** * Duplicates a zero terminated string. */ static inline char * sentry__string_clone(const char *str) { - return str ? sentry__string_clonen(str, strlen(str)) : NULL; + return str ? sentry__string_clone_n_unchecked(str, strlen(str)) : NULL; +} + +static inline char * +sentry__string_clone_max_n(const char *str, size_t str_len, size_t max_len) +{ + if (!str) { + return NULL; + } + size_t min_len = str_len < max_len ? str_len : max_len; + return sentry__string_clone_n_unchecked(str, min_len); } /** diff --git a/src/sentry_sync.h b/src/sentry_sync.h index 0741c30702..73531235b5 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -167,7 +167,10 @@ typedef HANDLE sentry_threadid_t; typedef struct sentry__winmutex_s sentry_mutex_t; # define SENTRY__MUTEX_INIT \ { \ - INIT_ONCE_STATIC_INIT, { 0 } \ + INIT_ONCE_STATIC_INIT, \ + { \ + 0 \ + } \ } # define sentry__mutex_init(Lock) sentry__winmutex_init(Lock) # define sentry__mutex_lock(Lock) sentry__winmutex_lock(Lock) diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index 9b9139a175..b32c30496a 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -1,17 +1,20 @@ #include "sentry_tracing.h" +#include "sentry.h" #include "sentry_alloc.h" #include "sentry_logger.h" +#include "sentry_slice.h" #include "sentry_string.h" #include "sentry_utils.h" #include "sentry_value.h" #include sentry_value_t -sentry__value_new_span(sentry_value_t parent, const char *operation) +sentry__value_new_span_n(sentry_value_t parent, sentry_slice_t operation) { sentry_value_t span = sentry_value_new_object(); - sentry_value_set_by_key(span, "op", sentry_value_new_string(operation)); + sentry_value_set_by_key( + span, "op", sentry_value_new_string_n(operation.ptr, operation.len)); sentry_uuid_t span_id = sentry_uuid_new_v4(); sentry_value_set_by_key( @@ -32,30 +35,34 @@ sentry__value_new_span(sentry_value_t parent, const char *operation) } sentry_value_t -sentry__value_transaction_context_new(const char *name, const char *operation) +sentry__value_transaction_context_new_n( + sentry_slice_t name, sentry_slice_t operation) { sentry_value_t transaction_context - = sentry__value_new_span(sentry_value_new_null(), operation); + = sentry__value_new_span_n(sentry_value_new_null(), operation); sentry_uuid_t trace_id = sentry_uuid_new_v4(); sentry_value_set_by_key(transaction_context, "trace_id", sentry__value_new_internal_uuid(&trace_id)); - sentry_value_set_by_key( - transaction_context, "transaction", sentry_value_new_string(name)); + sentry_value_set_by_key(transaction_context, "transaction", + sentry_value_new_string_n(name.ptr, name.len)); return transaction_context; } sentry_transaction_context_t * -sentry_transaction_context_new(const char *name, const char *operation) +sentry_transaction_context_new_n(const char *name, size_t name_len, + const char *operation, size_t operation_len) { sentry_transaction_context_t *tx_cxt = SENTRY_MAKE(sentry_transaction_context_t); if (!tx_cxt) { return NULL; } - tx_cxt->inner = sentry__value_transaction_context_new(name, operation); + tx_cxt->inner = sentry__value_transaction_context_new_n( + (sentry_slice_t) { name, name_len }, + (sentry_slice_t) { operation, operation_len }); if (sentry_value_is_null(tx_cxt->inner)) { sentry_free(tx_cxt); @@ -65,6 +72,16 @@ sentry_transaction_context_new(const char *name, const char *operation) return tx_cxt; } +sentry_transaction_context_t * +sentry_transaction_context_new(const char *name, const char *operation) +{ + size_t name_len = name ? strlen(name) : 0; + size_t operation_len = operation ? strlen(operation) : 0; + + return sentry_transaction_context_new_n( + name, name_len, operation, operation_len); +} + void sentry__transaction_context_free(sentry_transaction_context_t *tx_cxt) { @@ -76,7 +93,7 @@ sentry__transaction_context_free(sentry_transaction_context_t *tx_cxt) sentry_free(tx_cxt); } else { sentry_value_decref(tx_cxt->inner); - }; + } } void @@ -89,6 +106,16 @@ sentry_transaction_context_set_name( } } +void +sentry_transaction_context_set_name_n( + sentry_transaction_context_t *tx_cxt, const char *name, size_t name_len) +{ + if (tx_cxt) { + sentry_value_set_by_key(tx_cxt->inner, "transaction", + sentry_value_new_string_n(name, name_len)); + } +} + void sentry_transaction_context_set_operation( sentry_transaction_context_t *tx_cxt, const char *operation) @@ -99,6 +126,16 @@ sentry_transaction_context_set_operation( } } +void +sentry_transaction_context_set_operation_n(sentry_transaction_context_t *tx_cxt, + const char *operation, size_t operation_len) +{ + if (tx_cxt) { + sentry_value_set_by_key(tx_cxt->inner, "op", + sentry_value_new_string_n(operation, operation_len)); + } +} + void sentry_transaction_context_set_sampled( sentry_transaction_context_t *tx_cxt, int sampled) @@ -118,8 +155,9 @@ sentry_transaction_context_remove_sampled(sentry_transaction_context_t *tx_cxt) } void -sentry_transaction_context_update_from_header( - sentry_transaction_context_t *tx_cxt, const char *key, const char *value) +sentry_transaction_context_update_from_header_n( + sentry_transaction_context_t *tx_cxt, const char *key, size_t key_len, + const char *value, size_t value_len) { if (!tx_cxt) { return; @@ -127,7 +165,11 @@ sentry_transaction_context_update_from_header( // do case-insensitive header key comparison const char sentry_trace[] = "sentry-trace"; - for (size_t i = 0; i < sizeof(sentry_trace); i++) { + const size_t sentry_trace_len = sizeof(sentry_trace) - 1; + if (key_len != sentry_trace_len) { + return; + } + for (size_t i = 0; i < sentry_trace_len; i++) { if (tolower(key[i]) != sentry_trace[i]) { return; } @@ -136,7 +178,7 @@ sentry_transaction_context_update_from_header( // https://develop.sentry.dev/sdk/performance/#header-sentry-trace // sentry-trace = traceid-spanid(-sampled)? const char *trace_id_start = value; - const char *trace_id_end = strchr(trace_id_start, '-'); + const char *trace_id_end = memchr(trace_id_start, '-', value_len); if (!trace_id_end) { return; } @@ -144,7 +186,7 @@ sentry_transaction_context_update_from_header( sentry_value_t inner = tx_cxt->inner; char *s - = sentry__string_clonen(trace_id_start, trace_id_end - trace_id_start); + = sentry__string_clone_n(trace_id_start, trace_id_end - trace_id_start); sentry_value_t trace_id = sentry__value_new_string_owned(s); sentry_value_set_by_key(inner, "trace_id", trace_id); @@ -158,7 +200,7 @@ sentry_transaction_context_update_from_header( } // else: we have a sampled flag - s = sentry__string_clonen(span_id_start, span_id_end - span_id_start); + s = sentry__string_clone_n(span_id_start, span_id_end - span_id_start); sentry_value_t parent_span_id = sentry__value_new_string_owned(s); sentry_value_set_by_key(inner, "parent_span_id", parent_span_id); @@ -166,6 +208,17 @@ sentry_transaction_context_update_from_header( sentry_value_set_by_key(inner, "sampled", sentry_value_new_bool(sampled)); } +void +sentry_transaction_context_update_from_header( + sentry_transaction_context_t *tx_cxt, const char *key, const char *value) +{ + size_t key_len = key ? strlen(key) : 0; + size_t value_len = value ? strlen(value) : 0; + + sentry_transaction_context_update_from_header_n( + tx_cxt, key, key_len, value, value_len); +} + sentry_transaction_t * sentry__transaction_new(sentry_value_t inner) { @@ -203,7 +256,7 @@ sentry__transaction_decref(sentry_transaction_t *tx) sentry_free(tx); } else { sentry_value_decref(tx->inner); - }; + } } void @@ -227,7 +280,7 @@ sentry__span_decref(sentry_span_t *span) sentry_free(span); } else { sentry_value_decref(span->inner); - }; + } } sentry_span_t * @@ -251,8 +304,8 @@ sentry__span_new(sentry_transaction_t *tx, sentry_value_t inner) } sentry_value_t -sentry__value_span_new( - size_t max_spans, sentry_value_t parent, char *operation, char *description) +sentry__value_span_new_n(size_t max_spans, sentry_value_t parent, + sentry_slice_t operation, sentry_slice_t description) { if (!sentry_value_is_null(sentry_value_get_by_key(parent, "timestamp"))) { SENTRY_DEBUG("span's parent is already finished, not creating span"); @@ -269,9 +322,9 @@ sentry__value_span_new( goto fail; } - sentry_value_t child = sentry__value_new_span(parent, operation); - sentry_value_set_by_key( - child, "description", sentry_value_new_string(description)); + sentry_value_t child = sentry__value_new_span_n(parent, operation); + sentry_value_set_by_key(child, "description", + sentry_value_new_string_n(description.ptr, description.len)); sentry_value_set_by_key(child, "start_timestamp", sentry__value_new_string_owned( sentry__msec_time_to_iso8601(sentry__msec_time()))); @@ -281,6 +334,17 @@ sentry__value_span_new( return sentry_value_new_null(); } +sentry_value_t +sentry__value_span_new(size_t max_spans, sentry_value_t parent, + const char *operation, const char *description) +{ + const size_t operation_len = operation ? strlen(operation) : 0; + const size_t description_len = description ? strlen(description) : 0; + return sentry__value_span_new_n(max_spans, parent, + (sentry_slice_t) { operation, operation_len }, + (sentry_slice_t) { description, description_len }); +} + sentry_value_t sentry__value_get_trace_context(sentry_value_t span) { @@ -326,21 +390,37 @@ sentry_transaction_set_name(sentry_transaction_t *tx, const char *name) } } +void +sentry_transaction_set_name_n( + sentry_transaction_t *tx, const char *name, size_t name_len) +{ + if (tx) { + sentry_value_set_by_key(tx->inner, "transaction", + sentry_value_new_string_n(name, name_len)); + } +} + static void -set_tag(sentry_value_t item, const char *tag, const char *value) +set_tag_n(sentry_value_t item, sentry_slice_t tag, sentry_slice_t value) { sentry_value_t tags = sentry_value_get_by_key(item, "tags"); if (sentry_value_is_null(tags)) { tags = sentry_value_new_object(); sentry_value_set_by_key(item, "tags", tags); } + char *s = sentry__string_clone_max_n(value.ptr, value.len, 200); + sentry_value_t tag_value + = s ? sentry__value_new_string_owned(s) : sentry_value_new_null(); + sentry_value_set_by_key_n(tags, tag.ptr, tag.len, tag_value); +} - char *s = sentry__string_clonen(value, 200); - if (s) { - sentry_value_set_by_key(tags, tag, sentry__value_new_string_owned(s)); - } else { - sentry_value_set_by_key(tags, tag, sentry_value_new_null()); - } +static void +set_tag(sentry_value_t item, const char *tag, const char *value) +{ + const size_t tag_len = tag ? strlen(tag) : 0; + const size_t value_len = value ? strlen(value) : 0; + set_tag_n(item, (sentry_slice_t) { tag, tag_len }, + (sentry_slice_t) { value, value_len }); } void @@ -352,6 +432,16 @@ sentry_transaction_set_tag( } } +void +sentry_transaction_set_tag_n(sentry_transaction_t *tx, const char *tag, + size_t tag_len, const char *value, size_t value_len) +{ + if (tx) { + set_tag_n(tx->inner, (sentry_slice_t) { tag, tag_len }, + (sentry_slice_t) { value, value_len }); + } +} + void sentry_span_set_tag(sentry_span_t *span, const char *tag, const char *value) { @@ -360,6 +450,16 @@ sentry_span_set_tag(sentry_span_t *span, const char *tag, const char *value) } } +void +sentry_span_set_tag_n(sentry_span_t *span, const char *tag, size_t tag_len, + const char *value, size_t value_len) +{ + if (span) { + set_tag_n(span->inner, (sentry_slice_t) { tag, tag_len }, + (sentry_slice_t) { value, value_len }); + } +} + static void remove_tag(sentry_value_t item, const char *tag) { @@ -369,6 +469,15 @@ remove_tag(sentry_value_t item, const char *tag) } } +static void +remove_tag_n(sentry_value_t item, const char *tag, size_t tag_len) +{ + sentry_value_t tags = sentry_value_get_by_key(item, "tags"); + if (!sentry_value_is_null(tags)) { + sentry_value_remove_by_key_n(tags, tag, tag_len); + } +} + void sentry_transaction_remove_tag(sentry_transaction_t *tx, const char *tag) { @@ -377,6 +486,15 @@ sentry_transaction_remove_tag(sentry_transaction_t *tx, const char *tag) } } +void +sentry_transaction_remove_tag_n( + sentry_transaction_t *tx, const char *tag, size_t tag_len) +{ + if (tx) { + remove_tag_n(tx->inner, tag, tag_len); + } +} + void sentry_span_remove_tag(sentry_span_t *span, const char *tag) { @@ -385,6 +503,14 @@ sentry_span_remove_tag(sentry_span_t *span, const char *tag) } } +void +sentry_span_remove_tag_n(sentry_span_t *span, const char *tag, size_t tag_len) +{ + if (span) { + remove_tag_n(span->inner, tag, tag_len); + } +} + static void set_data(sentry_value_t item, const char *key, sentry_value_t value) { @@ -396,6 +522,18 @@ set_data(sentry_value_t item, const char *key, sentry_value_t value) sentry_value_set_by_key(data, key, value); } +static void +set_data_n( + sentry_value_t item, const char *key, size_t key_len, sentry_value_t value) +{ + sentry_value_t data = sentry_value_get_by_key(item, "data"); + if (sentry_value_is_null(data)) { + data = sentry_value_new_object(); + sentry_value_set_by_key(item, "data", data); + } + sentry_value_set_by_key_n(data, key, key_len, value); +} + void sentry_transaction_set_data( sentry_transaction_t *tx, const char *key, sentry_value_t value) @@ -405,6 +543,15 @@ sentry_transaction_set_data( } } +void +sentry_transaction_set_data_n(sentry_transaction_t *tx, const char *key, + size_t key_len, sentry_value_t value) +{ + if (tx) { + set_data_n(tx->inner, key, key_len, value); + } +} + void sentry_span_set_data(sentry_span_t *span, const char *key, sentry_value_t value) { @@ -413,6 +560,15 @@ sentry_span_set_data(sentry_span_t *span, const char *key, sentry_value_t value) } } +void +sentry_span_set_data_n( + sentry_span_t *span, const char *key, size_t key_len, sentry_value_t value) +{ + if (span) { + set_data_n(span->inner, key, key_len, value); + } +} + static void remove_data(sentry_value_t item, const char *key) { @@ -422,6 +578,15 @@ remove_data(sentry_value_t item, const char *key) } } +static void +remove_data_n(sentry_value_t item, const char *key, size_t key_len) +{ + sentry_value_t data = sentry_value_get_by_key(item, "data"); + if (!sentry_value_is_null(data)) { + sentry_value_remove_by_key_n(data, key, key_len); + } +} + void sentry_transaction_remove_data(sentry_transaction_t *tx, const char *key) { @@ -430,6 +595,15 @@ sentry_transaction_remove_data(sentry_transaction_t *tx, const char *key) } } +void +sentry_transaction_remove_data_n( + sentry_transaction_t *tx, const char *key, size_t key_len) +{ + if (tx) { + remove_data_n(tx->inner, key, key_len); + } +} + void sentry_span_remove_data(sentry_span_t *span, const char *key) { @@ -438,6 +612,14 @@ sentry_span_remove_data(sentry_span_t *span, const char *key) } } +void +sentry_span_remove_data_n(sentry_span_t *span, const char *key, size_t key_len) +{ + if (span) { + remove_data_n(span->inner, key, key_len); + } +} + sentry_value_t sentry_status_to_string(sentry_span_status_t status) { @@ -481,7 +663,7 @@ sentry_status_to_string(sentry_span_status_t status) } } -void +static void set_status(sentry_value_t item, sentry_span_status_t status) { sentry_value_set_by_key(item, "status", sentry_status_to_string(status)); diff --git a/src/sentry_tracing.h b/src/sentry_tracing.h index e04e5ee2fd..d312b1cd41 100644 --- a/src/sentry_tracing.h +++ b/src/sentry_tracing.h @@ -1,6 +1,7 @@ #ifndef SENTRY_TRACING_H_INCLUDED #define SENTRY_TRACING_H_INCLUDED +#include "sentry_slice.h" #include "sentry_value.h" /** @@ -36,7 +37,10 @@ void sentry__span_incref(sentry_span_t *span); void sentry__span_decref(sentry_span_t *span); sentry_value_t sentry__value_span_new(size_t max_spans, sentry_value_t parent, - char *operation, char *description); + const char *operation, const char *description); +sentry_value_t sentry__value_span_new_n(size_t max_spans, sentry_value_t parent, + sentry_slice_t operation, sentry_slice_t description); + sentry_span_t *sentry__span_new( sentry_transaction_t *parent_tx, sentry_value_t inner); diff --git a/src/sentry_utils.c b/src/sentry_utils.c index 2319fc9889..bb80a98f0a 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -4,7 +4,6 @@ #include "sentry_boot.h" #include "sentry_alloc.h" -#include "sentry_core.h" #include "sentry_string.h" #include "sentry_sync.h" #include "sentry_utils.h" @@ -64,7 +63,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) if (!tmp) { goto error; } - url_out->scheme = sentry__string_clonen(ptr, tmp - ptr); + url_out->scheme = sentry__string_clone_n_unchecked(ptr, tmp - ptr); if (!url_out->scheme || !is_scheme_valid(url_out->scheme)) { goto error; @@ -97,13 +96,14 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) tmp = ptr; if (has_username) { SKIP_WHILE_NOT2(tmp, '@', ':'); - url_out->username = sentry__string_clonen(ptr, tmp - ptr); + url_out->username = sentry__string_clone_n_unchecked(ptr, tmp - ptr); ptr = tmp; if (*ptr == ':') { ptr++; tmp = ptr; SKIP_WHILE_NOT(tmp, '@'); - url_out->password = sentry__string_clonen(ptr, tmp - ptr); + url_out->password + = sentry__string_clone_n_unchecked(ptr, tmp - ptr); ptr = tmp; } if (*ptr != '@') { @@ -126,7 +126,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) tmp++; } - url_out->host = sentry__string_clonen(ptr, tmp - ptr); + url_out->host = sentry__string_clone_n_unchecked(ptr, tmp - ptr); /* port */ ptr = tmp; @@ -134,7 +134,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) ptr++; tmp = ptr; SKIP_WHILE_NOT(tmp, '/'); - aux_buf = sentry__string_clonen(ptr, tmp - ptr); + aux_buf = sentry__string_clone_n_unchecked(ptr, tmp - ptr); char *end; url_out->port = (int)strtol(aux_buf, &end, 10); if (end != aux_buf + strlen(aux_buf)) { @@ -157,7 +157,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) /* path */ tmp = ptr; SKIP_WHILE_NOT2(tmp, '#', '?'); - url_out->path = sentry__string_clonen(ptr, tmp - ptr); + url_out->path = sentry__string_clone_n_unchecked(ptr, tmp - ptr); ptr = tmp; /* query */ @@ -165,7 +165,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) ptr++; tmp = ptr; SKIP_WHILE_NOT(tmp, '#'); - url_out->query = sentry__string_clonen(ptr, tmp - ptr); + url_out->query = sentry__string_clone_n_unchecked(ptr, tmp - ptr); ptr = tmp; } @@ -174,7 +174,7 @@ sentry__url_parse(sentry_url_t *url_out, const char *url) ptr++; tmp = ptr; SKIP_WHILE_NOT(tmp, 0); - url_out->fragment = sentry__string_clonen(ptr, tmp - ptr); + url_out->fragment = sentry__string_clone_n_unchecked(ptr, tmp - ptr); } if (url_out->port == 0) { @@ -213,7 +213,7 @@ sentry__url_cleanup(sentry_url_t *url) } sentry_dsn_t * -sentry__dsn_new(const char *raw_dsn) +sentry__dsn_new_n(const char *raw_dsn, size_t raw_dsn_len) { sentry_url_t url; memset(&url, 0, sizeof(sentry_url_t)); @@ -227,7 +227,7 @@ sentry__dsn_new(const char *raw_dsn) memset(dsn, 0, sizeof(sentry_dsn_t)); dsn->refcount = 1; - dsn->raw = sentry__string_clone(raw_dsn); + dsn->raw = sentry__string_clone_n(raw_dsn, raw_dsn_len); if (!dsn->raw || !dsn->raw[0] || sentry__url_parse(&url, dsn->raw) != 0) { goto exit; } @@ -274,6 +274,16 @@ sentry__dsn_new(const char *raw_dsn) return dsn; } +sentry_dsn_t * +sentry__dsn_new(const char *raw_dsn) +{ + if (!raw_dsn) { + return NULL; + } + + return sentry__dsn_new_n(raw_dsn, strlen(raw_dsn)); +} + sentry_dsn_t * sentry__dsn_incref(sentry_dsn_t *dsn) { diff --git a/src/sentry_utils.h b/src/sentry_utils.h index 703b53c3f2..6f96eea647 100644 --- a/src/sentry_utils.h +++ b/src/sentry_utils.h @@ -63,6 +63,7 @@ typedef struct sentry_dsn_s { * DSN has been successfully parsed. */ sentry_dsn_t *sentry__dsn_new(const char *dsn); +sentry_dsn_t *sentry__dsn_new_n(const char *dsn, size_t raw_dsn_len); /** * Increases the reference-count of the DSN. diff --git a/src/sentry_uuid.c b/src/sentry_uuid.c index ad5e349996..bd1ee66dae 100644 --- a/src/sentry_uuid.c +++ b/src/sentry_uuid.c @@ -25,13 +25,13 @@ sentry_uuid_new_v4(void) } sentry_uuid_t -sentry_uuid_from_string(const char *str) +sentry_uuid_from_string_n(const char *str, size_t str_len) { sentry_uuid_t rv; memset(&rv, 0, sizeof(rv)); size_t i = 0; - size_t len = strlen(str); + size_t len = str_len; size_t pos = 0; bool is_nibble = true; char nibble = 0; @@ -65,6 +65,12 @@ sentry_uuid_from_string(const char *str) return rv; } +sentry_uuid_t +sentry_uuid_from_string(const char *str) +{ + return str ? sentry_uuid_from_string_n(str, strlen(str)) + : sentry_uuid_nil(); +} sentry_uuid_t sentry_uuid_from_bytes(const char bytes[16]) { diff --git a/src/sentry_value.c b/src/sentry_value.c index 9d78efd9ec..e46eb79f5e 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -5,7 +5,6 @@ #include #include #include -#include #if defined(_MSC_VER) # pragma warning(push) @@ -21,6 +20,7 @@ #include "sentry_alloc.h" #include "sentry_core.h" #include "sentry_json.h" +#include "sentry_slice.h" #include "sentry_string.h" #include "sentry_sync.h" #include "sentry_utils.h" @@ -320,15 +320,22 @@ sentry_value_new_bool(int value) } sentry_value_t -sentry_value_new_string(const char *value) +sentry_value_new_string_n(const char *value, size_t value_len) { - char *s = sentry__string_clone(value); + char *s = sentry__string_clone_n(value, value_len); if (!s) { return sentry_value_new_null(); } return sentry__value_new_string_owned(s); } +sentry_value_t +sentry_value_new_string(const char *value) +{ + return value ? sentry_value_new_string_n(value, strlen(value)) + : sentry_value_new_null(); +} + sentry_value_t sentry_value_new_list(void) { @@ -440,8 +447,13 @@ sentry_value_get_type(sentry_value_t value) } int -sentry_value_set_by_key(sentry_value_t value, const char *k, sentry_value_t v) +sentry_value_set_by_key_n( + sentry_value_t value, const char *k, size_t k_len, sentry_value_t v) { + if (!k) { + goto fail; + } + sentry_slice_t k_slice = { k, k_len }; thing_t *thing = value_as_unfrozen_thing(value); if (!thing || thing_get_type(thing) != THING_TYPE_OBJECT) { goto fail; @@ -449,7 +461,7 @@ sentry_value_set_by_key(sentry_value_t value, const char *k, sentry_value_t v) obj_t *o = thing->payload._ptr; for (size_t i = 0; i < o->len; i++) { obj_pair_t *pair = &o->pairs[i]; - if (sentry__string_eq(pair->k, k)) { + if (sentry__slice_eqs(k_slice, pair->k)) { sentry_value_decref(pair->v); pair->v = v; return 0; @@ -462,7 +474,7 @@ sentry_value_set_by_key(sentry_value_t value, const char *k, sentry_value_t v) } obj_pair_t pair; - pair.k = sentry__string_clone(k); + pair.k = sentry__slice_to_owned(k_slice); if (!pair.k) { goto fail; } @@ -476,8 +488,23 @@ sentry_value_set_by_key(sentry_value_t value, const char *k, sentry_value_t v) } int -sentry_value_remove_by_key(sentry_value_t value, const char *k) +sentry_value_set_by_key(sentry_value_t value, const char *k, sentry_value_t v) { + if (k) { + return sentry_value_set_by_key_n(value, k, strlen(k), v); + } + + sentry_value_decref(v); + return 1; +} + +int +sentry_value_remove_by_key_n(sentry_value_t value, const char *k, size_t k_len) +{ + if (!k) { + return 1; + } + sentry_slice_t k_slice = { k, k_len }; thing_t *thing = value_as_unfrozen_thing(value); if (!thing || thing_get_type(thing) != THING_TYPE_OBJECT) { return 1; @@ -485,7 +512,7 @@ sentry_value_remove_by_key(sentry_value_t value, const char *k) obj_t *o = thing->payload._ptr; for (size_t i = 0; i < o->len; i++) { obj_pair_t *pair = &o->pairs[i]; - if (sentry__string_eq(pair->k, k)) { + if (sentry__slice_eqs(k_slice, pair->k)) { sentry_free(pair->k); sentry_value_decref(pair->v); memmove(o->pairs + i, o->pairs + i + 1, @@ -497,6 +524,16 @@ sentry_value_remove_by_key(sentry_value_t value, const char *k) return 1; } +int +sentry_value_remove_by_key(sentry_value_t value, const char *k) +{ + if (k) { + return sentry_value_remove_by_key_n(value, k, strlen(k)); + } + + return 1; +} + int sentry_value_append(sentry_value_t value, sentry_value_t v) { @@ -684,14 +721,17 @@ sentry_value_remove_by_index(sentry_value_t value, size_t index) } sentry_value_t -sentry_value_get_by_key(sentry_value_t value, const char *k) +sentry_value_get_by_key_n(sentry_value_t value, const char *k, size_t k_len) { + if (!k) { + return sentry_value_new_null(); + } const thing_t *thing = value_as_thing(value); if (thing && thing_get_type(thing) == THING_TYPE_OBJECT) { obj_t *o = thing->payload._ptr; for (size_t i = 0; i < o->len; i++) { obj_pair_t *pair = &o->pairs[i]; - if (sentry__string_eq(pair->k, k)) { + if (sentry__slice_eqs((sentry_slice_t) { k, k_len }, pair->k)) { return pair->v; } } @@ -699,10 +739,30 @@ sentry_value_get_by_key(sentry_value_t value, const char *k) return sentry_value_new_null(); } +sentry_value_t +sentry_value_get_by_key(sentry_value_t value, const char *k) +{ + const size_t k_len = k ? strlen(k) : 0; + return sentry_value_get_by_key_n(value, k, k_len); +} + +sentry_value_t +sentry_value_get_by_key_owned_n( + sentry_value_t value, const char *k, size_t k_len) +{ + sentry_value_t rv = sentry_value_get_by_key_n(value, k, k_len); + sentry_value_incref(rv); + return rv; +} + sentry_value_t sentry_value_get_by_key_owned(sentry_value_t value, const char *k) { - sentry_value_t rv = sentry_value_get_by_key(value, k); + if (k) { + return sentry_value_get_by_key_owned_n(value, k, strlen(k)); + } + + sentry_value_t rv = sentry_value_new_null(); sentry_value_incref(rv); return rv; } @@ -1069,52 +1129,90 @@ sentry_value_new_event(void) } sentry_value_t -sentry_value_new_message_event( - sentry_level_t level, const char *logger, const char *text) +sentry_value_new_message_event_n(sentry_level_t level, const char *logger, + size_t logger_len, const char *text, size_t text_len) { sentry_value_t rv = sentry_value_new_event(); sentry_value_set_by_key(rv, "level", sentry__value_new_level(level)); if (logger) { - sentry_value_set_by_key(rv, "logger", sentry_value_new_string(logger)); + sentry_value_set_by_key( + rv, "logger", sentry_value_new_string_n(logger, logger_len)); } if (text) { sentry_value_t container = sentry_value_new_object(); sentry_value_set_by_key( - container, "formatted", sentry_value_new_string(text)); + container, "formatted", sentry_value_new_string_n(text, text_len)); sentry_value_set_by_key(rv, "message", container); } return rv; } sentry_value_t -sentry_value_new_breadcrumb(const char *type, const char *message) +sentry_value_new_message_event( + sentry_level_t level, const char *logger, const char *text) { - sentry_value_t rv = sentry_value_new_object(); - sentry_value_set_by_key(rv, "timestamp", + size_t logger_len = logger ? strlen(logger) : 0; + size_t text_len = text ? strlen(text) : 0; + return sentry_value_new_message_event_n( + level, logger, logger_len, text, text_len); +} + +static void +timestamp_value(sentry_value_t value) +{ + sentry_value_set_by_key(value, "timestamp", sentry__value_new_string_owned( sentry__msec_time_to_iso8601(sentry__msec_time()))); +} + +sentry_value_t +sentry_value_new_breadcrumb_n( + const char *type, size_t type_len, const char *message, size_t message_len) +{ + sentry_value_t rv = sentry_value_new_object(); + timestamp_value(rv); if (type) { - sentry_value_set_by_key(rv, "type", sentry_value_new_string(type)); + sentry_value_set_by_key( + rv, "type", sentry_value_new_string_n(type, type_len)); } if (message) { sentry_value_set_by_key( - rv, "message", sentry_value_new_string(message)); + rv, "message", sentry_value_new_string_n(message, message_len)); } return rv; } sentry_value_t -sentry_value_new_exception(const char *type, const char *value) +sentry_value_new_breadcrumb(const char *type, const char *message) +{ + const size_t type_len = type ? strlen(type) : 0; + const size_t message_len = message ? strlen(message) : 0; + return sentry_value_new_breadcrumb_n(type, type_len, message, message_len); +} + +sentry_value_t +sentry_value_new_exception_n( + const char *type, size_t type_len, const char *value, size_t value_len) { sentry_value_t exc = sentry_value_new_object(); - sentry_value_set_by_key(exc, "type", sentry_value_new_string(type)); - sentry_value_set_by_key(exc, "value", sentry_value_new_string(value)); + sentry_value_set_by_key( + exc, "type", sentry_value_new_string_n(type, type_len)); + sentry_value_set_by_key( + exc, "value", sentry_value_new_string_n(value, value_len)); return exc; } sentry_value_t -sentry_value_new_thread(uint64_t id, const char *name) +sentry_value_new_exception(const char *type, const char *value) +{ + const size_t type_len = type ? strlen(type) : 0; + const size_t value_len = value ? strlen(value) : 0; + return sentry_value_new_exception_n(type, type_len, value, value_len); +} + +sentry_value_t +sentry_value_new_thread_n(uint64_t id, const char *name, size_t name_len) { sentry_value_t thread = sentry_value_new_object(); @@ -1128,12 +1226,20 @@ sentry_value_new_thread(uint64_t id, const char *name) } if (name) { - sentry_value_set_by_key(thread, "name", sentry_value_new_string(name)); + sentry_value_set_by_key( + thread, "name", sentry_value_new_string_n(name, name_len)); } return thread; } +sentry_value_t +sentry_value_new_thread(uint64_t id, const char *name) +{ + const size_t name_len = name ? strlen(name) : 0; + return sentry_value_new_thread_n(id, name, name_len); +} + sentry_value_t sentry_value_new_stacktrace(void **ips, size_t len) { diff --git a/src/transports/sentry_transport_curl.c b/src/transports/sentry_transport_curl.c index cb30d432cf..b9f52c8813 100644 --- a/src/transports/sentry_transport_curl.c +++ b/src/transports/sentry_transport_curl.c @@ -47,6 +47,7 @@ sentry__curl_bgworker_state_free(void *_state) curl_bgworker_state_t *state = _state; if (state->curl_handle) { curl_easy_cleanup(state->curl_handle); + curl_global_cleanup(); } sentry__dsn_decref(state->dsn); sentry__rate_limiter_free(state->ratelimiter); @@ -140,7 +141,7 @@ header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { size_t bytes = size * nitems; struct header_info *info = userdata; - char *header = sentry__string_clonen(buffer, bytes); + char *header = sentry__string_clone_n(buffer, bytes); if (!header) { return bytes; } diff --git a/src/transports/sentry_transport_winhttp.c b/src/transports/sentry_transport_winhttp.c index 9d509f23d8..93848c3452 100644 --- a/src/transports/sentry_transport_winhttp.c +++ b/src/transports/sentry_transport_winhttp.c @@ -74,7 +74,7 @@ sentry__winhttp_transport_start( const char *ptr = opts->http_proxy + 7; const char *slash = strchr(ptr, '/'); if (slash) { - char *copy = sentry__string_clonen(ptr, slash - ptr); + char *copy = sentry__string_clone_n(ptr, slash - ptr); state->proxy = sentry__string_to_wstr(copy); sentry_free(copy); } else { diff --git a/tests/assertions.py b/tests/assertions.py index 89fe1eec98..6bae5f60d6 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -7,7 +7,7 @@ from .conditions import is_android -VERSION_RE = re.compile(r"(\d+\.\d+\.\d+)(?:[-\.]?)(.*)") +VERSION_RE = re.compile(r"(\d+\.\d+\.\d+)[-.]?(.*)") def matches(actual, expected): @@ -95,7 +95,7 @@ def assert_meta( ) assert event["contexts"]["os"]["build"] is not None - if sdk_override != None: + if sdk_override is not None: expected_sdk["name"] = sdk_override assert_matches(event, expected) diff --git a/tests/leaks.txt b/tests/leaks.txt index 2ba3e9dc8b..4ca4e4b684 100644 --- a/tests/leaks.txt +++ b/tests/leaks.txt @@ -3,3 +3,4 @@ # Adding a manual `[paths release]` "fixes" the `crashpad_handler` leak, but leads to a use-after-free in sentry. # https://github.com/getsentry/crashpad/blob/9cd1a4dadb51b31665f5e50c5ffc25bb9d10571a/client/crash_report_database_mac.mm#L705 leak:contentsOfDirectoryAtPath +leak:SCDynamicStoreCopyProxiesWithOptions diff --git a/tests/requirements.txt b/tests/requirements.txt index dd2fd18e86..4d181eb9d0 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ -black==22.3.0 -pytest==6.2.5 -pytest-httpserver==1.0.1 +black==23.3.0 +pytest==7.2.2 +pytest-httpserver==1.0.6 diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 2bca41e6d9..400b639bdf 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -1,13 +1,10 @@ -import time import pytest -import subprocess -import sys import os import time import itertools import uuid import json -from . import make_dsn, check_output, run, Envelope +from . import make_dsn, run, Envelope from .conditions import has_http, has_breakpad, has_files from .assertions import ( assert_attachment, @@ -217,7 +214,7 @@ def test_inproc_crash_http(cmake, httpserver): ["log", "start-session", "attachment", "crash"], env=env, ) - assert child.returncode # well, its a crash after all + assert child.returncode # well, it's a crash after all run( tmp_path, @@ -254,7 +251,7 @@ def test_inproc_reinstall(cmake, httpserver): ["log", "reinstall", "crash"], env=env, ) - assert child.returncode # well, its a crash after all + assert child.returncode # well, it's a crash after all run( tmp_path, @@ -279,8 +276,7 @@ def test_inproc_dump_inflight(cmake, httpserver): child = run( tmp_path, "sentry_example", ["log", "capture-multiple", "crash"], env=env ) - assert child.returncode # well, its a crash after all - + assert child.returncode # well, it's a crash after all run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env) # we trigger 10 normal events, and 1 crash @@ -303,7 +299,7 @@ def test_breakpad_crash_http(cmake, httpserver): ["log", "start-session", "attachment", "crash"], env=env, ) - assert child.returncode # well, its a crash after all + assert child.returncode # well, it's a crash after all run( tmp_path, @@ -341,7 +337,7 @@ def test_breakpad_reinstall(cmake, httpserver): ["log", "reinstall", "crash"], env=env, ) - assert child.returncode # well, its a crash after all + assert child.returncode # well, it's a crash after all run( tmp_path, @@ -367,7 +363,7 @@ def test_breakpad_dump_inflight(cmake, httpserver): child = run( tmp_path, "sentry_example", ["log", "capture-multiple", "crash"], env=env ) - assert child.returncode # well, its a crash after all + assert child.returncode # well, it's a crash after all run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env) @@ -405,6 +401,7 @@ def delayed(req): env=env, check=True, ) + assert child.returncode == 0 httpserver.clear_all_handlers() httpserver.clear_log() @@ -419,6 +416,9 @@ def delayed(req): assert len(httpserver.log) == 10 +RFC3339_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" + + def test_transaction_only(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) @@ -450,10 +450,10 @@ def test_transaction_only(cmake, httpserver): (event,) = envelope.items assert event.headers["type"] == "transaction" - json = event.payload.json + payload = event.payload.json # See https://develop.sentry.dev/sdk/performance/trace-context/#trace-context - trace_context = json["contexts"]["trace"] + trace_context = payload["contexts"]["trace"] assert ( trace_context["op"] == "Short and stout here is my handle and here is my spout" @@ -469,8 +469,7 @@ def test_transaction_only(cmake, httpserver): assert trace_context["span_id"] assert trace_context["status"] == "ok" - RFC3339_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" - start_timestamp = time.strptime(json["start_timestamp"], RFC3339_FORMAT) + start_timestamp = time.strptime(payload["start_timestamp"], RFC3339_FORMAT) assert start_timestamp - timestamp = time.strptime(json["timestamp"], RFC3339_FORMAT) + timestamp = time.strptime(payload["timestamp"], RFC3339_FORMAT) assert timestamp >= start_timestamp diff --git a/tests/unit/sentry_testsupport.h b/tests/unit/sentry_testsupport.h index 81927db1b7..bd6f112a76 100644 --- a/tests/unit/sentry_testsupport.h +++ b/tests/unit/sentry_testsupport.h @@ -23,6 +23,13 @@ TEST_MSG("Received: %s", Val); \ } while (0) +#define TEST_CHECK_WSTRING_EQUAL(Val, ReferenceVal) \ + do { \ + TEST_CHECK(wcscmp(Val, ReferenceVal) == 0); \ + TEST_MSG("Expected: %s", ReferenceVal); \ + TEST_MSG("Received: %s", Val); \ + } while (0) + #define TEST_CHECK_JSON_VALUE(Val, ReferenceJson) \ do { \ char *json = sentry_value_to_json(Val); \ diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index bd4d2d065d..62dcf0af72 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -35,7 +35,8 @@ SENTRY_TEST(lazy_attachments) sentry_options_set_transport(options, sentry_new_function_transport( send_envelope_test_attachments, &testdata)); - sentry_options_set_release(options, "prod"); + char rel[] = { 't', 'e', 's', 't' }; + sentry_options_set_release_n(options, rel, sizeof(rel)); sentry_options_add_attachment(options, PREFIX ".existing-file-attachment"); sentry_options_add_attachment( @@ -53,6 +54,7 @@ SENTRY_TEST(lazy_attachments) char *serialized = sentry_stringbuilder_take_string(&testdata.serialized_envelope); + TEST_CHECK(strstr(serialized, "\"release\":\"test\"") != NULL); TEST_CHECK(strstr(serialized, "{\"type\":\"attachment\",\"length\":3," "\"filename\":\".existing-file-attachment\"}\n" diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index d8a51e4618..fc508bbd20 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -176,14 +176,19 @@ SENTRY_TEST(crashed_last_run) TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), -1); options = sentry_options_new(); - sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + const char *dsn_str = "https://foo@sentry.invalid/42"; + const char dsn[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'f', 'o', 'o', + '@', 's', 'e', 'n', 't', 'r', 'y', '.', 'i', 'n', 'v', 'a', 'l', 'i', + 'd', '/', '4', '2' }; + sentry_options_set_dsn_n(options, dsn, sizeof(dsn)); + TEST_CHECK_STRING_EQUAL(sentry_options_get_dsn(options), dsn_str); 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"); + sentry_options_set_dsn_n(options, dsn, sizeof(dsn)); // simulate a crash TEST_CHECK(sentry__write_crash_marker(options)); @@ -201,7 +206,7 @@ SENTRY_TEST(crashed_last_run) TEST_CHECK_INT_EQUAL(sentry_get_crashed_last_run(), 1); options = sentry_options_new(); - sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + sentry_options_set_dsn_n(options, dsn, sizeof(dsn)); TEST_CHECK_INT_EQUAL(sentry_init(options), 0); sentry_close(); diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 2e6128a9cb..e0032706cd 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -1,9 +1,21 @@ #include "sentry_envelope.h" +#include "sentry_path.h" #include "sentry_testsupport.h" #include "sentry_transport.h" #include "sentry_utils.h" #include "sentry_value.h" +static char *const SERIALIZED_ENVELOPE_STR + = "{\"dsn\":\"https://foo@sentry.invalid/42\"," + "\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\"}\n" + "{\"type\":\"event\",\"length\":71}\n" + "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\",\"some-" + "context\":null}\n" + "{\"type\":\"minidump\",\"length\":4}\n" + "MDMP\n" + "{\"type\":\"attachment\",\"length\":12}\n" + "Hello World!"; + SENTRY_TEST(basic_http_request_preparation_for_event) { sentry_dsn_t *dsn = sentry__dsn_new("https://foo@sentry.invalid/42"); @@ -124,7 +136,8 @@ SENTRY_TEST(basic_http_request_preparation_for_minidump) sentry__dsn_decref(dsn); } -SENTRY_TEST(serialize_envelope) +sentry_envelope_t * +create_test_envelope() { sentry_options_t *options = sentry_options_new(); sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); @@ -145,25 +158,60 @@ SENTRY_TEST(serialize_envelope) char msg[] = "Hello World!"; sentry__envelope_add_from_buffer( envelope, msg, sizeof(msg) - 1, "attachment"); + return envelope; +} + +SENTRY_TEST(serialize_envelope) +{ + sentry_envelope_t *envelope = create_test_envelope(); sentry_stringbuilder_t sb; sentry__stringbuilder_init(&sb); sentry__envelope_serialize_into_stringbuilder(envelope, &sb); char *str = sentry__stringbuilder_into_string(&sb); - TEST_CHECK_STRING_EQUAL(str, - "{\"dsn\":\"https://foo@sentry.invalid/42\"," - "\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\"}\n" - "{\"type\":\"event\",\"length\":71}\n" - "{\"event_id\":\"c993afb6-b4ac-48a6-b61b-2558e601d65d\",\"some-" - "context\":null}\n" - "{\"type\":\"minidump\",\"length\":4}\n" - "MDMP\n" - "{\"type\":\"attachment\",\"length\":12}\n" - "Hello World!"); + TEST_CHECK_STRING_EQUAL(str, SERIALIZED_ENVELOPE_STR); sentry_envelope_free(envelope); sentry_free(str); sentry_close(); } + +SENTRY_TEST(basic_write_envelope_to_file) +{ + sentry_envelope_t *envelope = create_test_envelope(); + const char *test_file_str = "sentry_test_envelope"; + sentry_path_t *test_file_path = sentry__path_from_str(test_file_str); + int rv = sentry_envelope_write_to_file(envelope, test_file_str); + TEST_CHECK_INT_EQUAL(rv, 0); + TEST_ASSERT(sentry__path_is_file(test_file_path)); + + size_t test_file_size; + char *test_file_content + = sentry__path_read_to_buffer(test_file_path, &test_file_size); + TEST_CHECK_INT_EQUAL(test_file_size, strlen(SERIALIZED_ENVELOPE_STR)); + TEST_CHECK_STRING_EQUAL(test_file_content, SERIALIZED_ENVELOPE_STR); + + sentry_free(test_file_content); + sentry__path_remove(test_file_path); + sentry__path_free(test_file_path); + sentry_envelope_free(envelope); + sentry_close(); +} + +SENTRY_TEST(write_envelope_to_file_null) +{ + sentry_envelope_t *empty_envelope = sentry__envelope_new(); + + TEST_CHECK_INT_EQUAL( + sentry_envelope_write_to_file(NULL, "irrelevant/path"), 1); + TEST_CHECK_INT_EQUAL( + sentry_envelope_write_to_file(empty_envelope, NULL), 1); + TEST_CHECK_INT_EQUAL( + sentry_envelope_write_to_file_n(NULL, "irrelevant/path", 0), 1); + TEST_CHECK_INT_EQUAL( + sentry_envelope_write_to_file_n(empty_envelope, NULL, 0), 1); + + sentry_envelope_free(empty_envelope); +} diff --git a/tests/unit/test_path.c b/tests/unit/test_path.c index 3f25d77a52..8424d1f0b3 100644 --- a/tests/unit/test_path.c +++ b/tests/unit/test_path.c @@ -58,6 +58,26 @@ SENTRY_TEST(path_joining_unix) #endif } +SENTRY_TEST(path_from_str_null) +{ + TEST_CHECK(NULL == sentry__path_from_str(NULL)); + TEST_CHECK(NULL == sentry__path_from_str_n(NULL, 0)); + TEST_CHECK(NULL == sentry__path_from_str_n(NULL, 10)); +} + +SENTRY_TEST(path_from_str_n_wo_null_termination) +{ + // provide non-null-terminated path string with buffer character at the end. + char path_str[] = { 't', 'e', 's', 't', 'X' }; + sentry_path_t *test_path = sentry__path_from_str_n(path_str, 4); +#ifdef SENTRY_PLATFORM_WINDOWS + TEST_CHECK_WSTRING_EQUAL(test_path->path, L"test"); +#else + TEST_CHECK_STRING_EQUAL(test_path->path, "test"); +#endif + sentry__path_free(test_path); +} + SENTRY_TEST(path_joining_windows) { #ifndef SENTRY_PLATFORM_WINDOWS diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index fbe586a02a..ebaaed389b 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -47,7 +47,7 @@ send_envelope(const sentry_envelope_t *envelope, void *data) "my_release"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(attrs, "environment")), - "my_environment"); + "test"); sentry_value_decref(session); } @@ -67,6 +67,10 @@ SENTRY_TEST(session_basics) TEST_CHECK_STRING_EQUAL( sentry_options_get_environment(options), "production"); sentry_options_set_environment(options, "my_environment"); + TEST_CHECK_STRING_EQUAL( + sentry_options_get_environment(options), "my_environment"); + char env[] = { 't', 'e', 's', 't' }; + sentry_options_set_environment_n(options, env, sizeof(env)); sentry_init(options); // a session was already started by automatic session tracking diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index a960273e03..bdac0921fd 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -94,9 +94,19 @@ SENTRY_TEST(basic_transaction) sentry_transaction_context_set_name(opaque_tx_cxt, ""); CHECK_STRING_PROPERTY(tx_cxt, "transaction", ""); + char txn_ctx_name[] = { 'h', 'o', 'n', 'k', '.', 'b', 'e', 'e', 'p' }; + sentry_transaction_context_set_name_n( + opaque_tx_cxt, txn_ctx_name, sizeof(txn_ctx_name)); + CHECK_STRING_PROPERTY(tx_cxt, "transaction", "honk.beep"); + sentry_transaction_context_set_operation(opaque_tx_cxt, ""); CHECK_STRING_PROPERTY(tx_cxt, "op", ""); + char txn_ctx_op[] = { 'b', 'e', 'e', 'p', 'b', 'e', 'e', 'p' }; + sentry_transaction_context_set_operation_n( + opaque_tx_cxt, txn_ctx_op, sizeof(txn_ctx_op)); + CHECK_STRING_PROPERTY(tx_cxt, "op", "beepbeep"); + sentry_transaction_context_set_sampled(opaque_tx_cxt, 1); TEST_CHECK( sentry_value_is_true(sentry_value_get_by_key(tx_cxt, "sampled")) @@ -195,9 +205,13 @@ SENTRY_TEST(basic_function_transport_transaction) // consent was not given TEST_CHECK(!sentry_uuid_is_nil(&event_id)); sentry_user_consent_give(); - - tx_cxt = sentry_transaction_context_new("honk", "beep"); + char name[] = { 'h', 'o', 'n', 'k' }; + char op[] = { 'b', 'e', 'e', 'p' }; + tx_cxt + = sentry_transaction_context_new_n(name, sizeof(name), op, sizeof(op)); tx = sentry_transaction_start(tx_cxt, sentry_value_new_null()); + CHECK_STRING_PROPERTY(tx->inner, "transaction", "honk"); + CHECK_STRING_PROPERTY(tx->inner, "op", "beep"); event_id = sentry_transaction_finish(tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); @@ -697,6 +711,43 @@ forward_headers_to(const char *key, const char *value, void *userdata) sentry_transaction_context_update_from_header(tx_ctx, key, value); } +SENTRY_TEST(update_from_header_null_ctx) +{ + sentry_transaction_context_update_from_header( + NULL, "irrelevant-key", "irrelevant-value"); +} + +SENTRY_TEST(update_from_header_no_sampled_flag) +{ + sentry_options_t *options = sentry_options_new(); + sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + + sentry_options_set_traces_sample_rate(options, 1.0); + sentry_options_set_max_spans(options, 2); + sentry_init(options); + + sentry_transaction_context_update_from_header( + NULL, "irrelevant-key", "irrelevant-value"); + const char *trace_header + = "2674eb52d5874b13b560236d6c79ce8a-a0f9fdf04f1a63df"; + sentry_transaction_context_t *tx_ctx + = sentry_transaction_context_new("wow!", NULL); + sentry_transaction_context_update_from_header( + tx_ctx, "sentry-trace", trace_header); + sentry_transaction_t *tx + = sentry_transaction_start(tx_ctx, sentry_value_new_null()); + + CHECK_STRING_PROPERTY( + tx->inner, "trace_id", "2674eb52d5874b13b560236d6c79ce8a"); + CHECK_STRING_PROPERTY(tx->inner, "parent_span_id", "a0f9fdf04f1a63df"); + sentry_value_t sampled = sentry_value_get_by_key(tx->inner, "sampled"); + TEST_CHECK(sentry_value_get_type(sampled) == SENTRY_VALUE_TYPE_BOOL); + TEST_CHECK(sentry_value_is_true(sampled)); + + sentry__transaction_decref(tx); + sentry_close(); +} + SENTRY_TEST(distributed_headers) { sentry_options_t *options = sentry_options_new(); @@ -715,9 +766,14 @@ SENTRY_TEST(distributed_headers) sentry_transaction_context_t *tx_ctx = sentry_transaction_context_new("wow!", NULL); - // check case insensitive headers, and bogus header names + // check case-insensitive headers, and bogus header names sentry_transaction_context_update_from_header( tx_ctx, "SeNtry-TrAcE", trace_header); + sentry_transaction_context_update_from_header( + tx_ctx, "sentry_trace", not_expected_header); + sentry_transaction_context_update_from_header( + tx_ctx, NULL, not_expected_header); + sentry_transaction_context_update_from_header(tx_ctx, "sentry-trace", NULL); sentry_transaction_context_update_from_header( tx_ctx, "nop", not_expected_header); sentry_transaction_context_update_from_header( @@ -807,5 +863,246 @@ SENTRY_TEST(distributed_headers) sentry_close(); } +void +check_after_set(sentry_value_t inner, const char *inner_key, + const char *item_key, const char *expected) +{ + sentry_value_t inner_tags = sentry_value_get_by_key(inner, inner_key); + TEST_CHECK_INT_EQUAL(1, sentry_value_get_length(inner_tags)); + TEST_CHECK( + sentry_value_get_type(sentry_value_get_by_key(inner_tags, item_key)) + == SENTRY_VALUE_TYPE_STRING); + CHECK_STRING_PROPERTY(inner_tags, item_key, expected); +} + +void +check_after_remove( + sentry_value_t inner, const char *inner_key, const char *item_key) +{ + sentry_value_t inner_tags = sentry_value_get_by_key(inner, inner_key); + TEST_CHECK_INT_EQUAL(0, sentry_value_get_length(inner_tags)); + TEST_CHECK(IS_NULL(inner_tags, item_key)); +} + +SENTRY_TEST(txn_tagging) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + + sentry_transaction_set_tag(txn, "os.name", "Linux"); + check_after_set(txn->inner, "tags", "os.name", "Linux"); + + sentry_transaction_remove_tag(txn, "os.name"); + check_after_remove(txn->inner, "tags", "os.name"); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(span_tagging) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_span_t *span = sentry__span_new(txn, sentry_value_new_object()); + + sentry_span_set_tag(span, "os.name", "Linux"); + check_after_set(span->inner, "tags", "os.name", "Linux"); + + sentry_span_remove_tag(span, "os.name"); + check_after_remove(span->inner, "tags", "os.name"); + + sentry__span_decref(span); + sentry__transaction_decref(txn); +} + +SENTRY_TEST(txn_tagging_n) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + + char tag[] = { 'o', 's', '.', 'n', 'a', 'm', 'e' }; + char tag_val[] = { 'L', 'i', 'n', 'u', 'x' }; + sentry_transaction_set_tag_n( + txn, tag, sizeof(tag), tag_val, sizeof(tag_val)); + check_after_set(txn->inner, "tags", "os.name", "Linux"); + + sentry_transaction_remove_tag_n(txn, tag, sizeof(tag)); + check_after_remove(txn->inner, "tags", "os.name"); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(span_tagging_n) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_span_t *span = sentry__span_new(txn, sentry_value_new_object()); + + char tag[] = { 'o', 's', '.', 'n', 'a', 'm', 'e' }; + char tag_val[] = { 'L', 'i', 'n', 'u', 'x' }; + sentry_span_set_tag_n(span, tag, sizeof(tag), tag_val, sizeof(tag_val)); + check_after_set(span->inner, "tags", "os.name", "Linux"); + + sentry_span_remove_tag_n(span, tag, sizeof(tag)); + check_after_remove(span->inner, "tags", "os.name"); + + sentry__span_decref(span); + sentry__transaction_decref(txn); +} + +SENTRY_TEST(txn_name) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + + char *txn_name = "the_txn"; + sentry_transaction_set_name(txn, txn_name); + sentry_value_t txn_name_value + = sentry_value_get_by_key(txn->inner, "transaction"); + TEST_CHECK( + sentry_value_get_type(txn_name_value) == SENTRY_VALUE_TYPE_STRING); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(txn_name_value), txn_name); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(txn_data) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + + sentry_transaction_set_data( + txn, "os.name", sentry_value_new_string("Linux")); + check_after_set(txn->inner, "data", "os.name", "Linux"); + + sentry_transaction_remove_data(txn, "os.name"); + check_after_remove(txn->inner, "data", "os.name"); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(span_data) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_span_t *span = sentry__span_new(txn, sentry_value_new_object()); + + sentry_span_set_data(span, "os.name", sentry_value_new_string("Linux")); + check_after_set(span->inner, "data", "os.name", "Linux"); + + sentry_span_remove_data(span, "os.name"); + check_after_remove(span->inner, "data", "os.name"); + + sentry__span_decref(span); + sentry__transaction_decref(txn); +} + +SENTRY_TEST(txn_name_n) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + char txn_name[] = { 't', 'h', 'e', '_', 't', 'x', 'n' }; + sentry_transaction_set_name_n(txn, txn_name, sizeof(txn_name)); + + sentry_value_t txn_name_value + = sentry_value_get_by_key(txn->inner, "transaction"); + TEST_CHECK( + sentry_value_get_type(txn_name_value) == SENTRY_VALUE_TYPE_STRING); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(txn_name_value), "the_txn"); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(txn_data_n) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + + char data_k[] = { 'o', 's', '.', 'n', 'a', 'm', 'e' }; + char data_v[] = { 'L', 'i', 'n', 'u', 'x' }; + sentry_value_t data_value + = sentry_value_new_string_n(data_v, sizeof(data_v)); + sentry_transaction_set_data_n(txn, data_k, sizeof(data_k), data_value); + check_after_set(txn->inner, "data", "os.name", "Linux"); + + sentry_transaction_remove_data_n(txn, data_k, sizeof(data_k)); + check_after_remove(txn->inner, "data", "os.name"); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(span_data_n) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_span_t *span = sentry__span_new(txn, sentry_value_new_object()); + + char data_k[] = { 'o', 's', '.', 'n', 'a', 'm', 'e' }; + char data_v[] = { 'L', 'i', 'n', 'u', 'x' }; + sentry_value_t data_value + = sentry_value_new_string_n(data_v, sizeof(data_v)); + sentry_span_set_data_n(span, data_k, sizeof(data_k), data_value); + check_after_set(span->inner, "data", "os.name", "Linux"); + + sentry_span_remove_data_n(span, data_k, sizeof(data_k)); + check_after_remove(span->inner, "data", "os.name"); + + sentry__span_decref(span); + sentry__transaction_decref(txn); +} + +SENTRY_TEST(sentry__value_span_new_requires_unfinished_parent) +{ + sentry_value_t parent = sentry_value_new_object(); + // timestamps are typically iso8601 strings, but this is irrelevant to + // `sentry__value_span_new` which just wants `timestamp` to not be null. + sentry_value_set_by_key(parent, "timestamp", sentry_value_new_object()); + sentry_value_t inner_span = sentry__value_span_new(0, parent, NULL, NULL); + TEST_CHECK(sentry_value_is_null(inner_span)); + + sentry_value_decref(parent); +} + +SENTRY_TEST(set_tag_allows_null_tag_and_value) +{ + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_transaction_set_tag(txn, NULL, NULL); + sentry_value_t tags = sentry_value_get_by_key(txn->inner, "tags"); + TEST_CHECK(!sentry_value_is_null(tags)); + TEST_CHECK(sentry_value_get_type(tags) == SENTRY_VALUE_TYPE_OBJECT); + TEST_CHECK(sentry_value_get_length(tags) == 0); + + sentry_transaction_set_tag(txn, "os.name", NULL); + tags = sentry_value_get_by_key(txn->inner, "tags"); + TEST_CHECK(!sentry_value_is_null(tags)); + TEST_CHECK(sentry_value_get_type(tags) == SENTRY_VALUE_TYPE_OBJECT); + TEST_CHECK(sentry_value_get_length(tags) == 1); + TEST_CHECK(IS_NULL(tags, "os.name")); + + sentry__transaction_decref(txn); +} + +SENTRY_TEST(set_tag_cuts_value_at_length_200) +{ + const char test_value[] + = "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789" + "012345678901234567890123456789012345678901234567890123456789"; + + sentry_transaction_t *txn + = sentry__transaction_new(sentry_value_new_object()); + sentry_transaction_set_tag(txn, "cut-off", test_value); + sentry_value_t tags = sentry_value_get_by_key(txn->inner, "tags"); + TEST_CHECK(!sentry_value_is_null(tags)); + TEST_CHECK(sentry_value_get_type(tags) == SENTRY_VALUE_TYPE_OBJECT); + TEST_CHECK(sentry_value_get_length(tags) == 1); + TEST_CHECK_INT_EQUAL(strlen(sentry_value_as_string( + sentry_value_get_by_key(tags, "cut-off"))), + 200); + + sentry__transaction_decref(txn); +} + #undef IS_NULL #undef CHECK_STRING_PROPERTY diff --git a/tests/unit/test_utils.c b/tests/unit/test_utils.c index 855f59d3d7..57f1ffb4ce 100644 --- a/tests/unit/test_utils.c +++ b/tests/unit/test_utils.c @@ -249,4 +249,36 @@ SENTRY_TEST(check_version) TEST_CHECK(!sentry__check_min_version( (sentry_version_t) { .major = 7, .minor = 10, .patch = 6 }, (sentry_version_t) { .major = 7, .minor = 10, .patch = 7 })); -} \ No newline at end of file +} + +SENTRY_TEST(dsn_without_url_scheme_is_invalid) +{ + sentry_dsn_t *dsn = sentry__dsn_new("//without-scheme-separator"); + TEST_CHECK(dsn->is_valid == false); + sentry__dsn_decref(dsn); +} + +SENTRY_TEST(dsn_with_non_http_scheme_is_invalid) +{ + sentry_dsn_t *dsn = sentry__dsn_new("ftp://ftp-server/"); + TEST_CHECK(dsn->is_valid == false); + sentry__dsn_decref(dsn); +} + +SENTRY_TEST(dsn_without_project_id_is_invalid) +{ + sentry_dsn_t *dsn = sentry__dsn_new("https://foo@sentry.io/"); + TEST_CHECK(dsn->is_valid == false); + sentry__dsn_decref(dsn); +} + +SENTRY_TEST(dsn_with_ending_forward_slash_will_be_cleaned) +{ + sentry_dsn_t *dsn = sentry__dsn_new("https://foo@sentry.io/42/43/44////"); + + TEST_CHECK_STRING_EQUAL(dsn->path, "/42/43"); + TEST_CHECK_STRING_EQUAL(dsn->project_id, "44"); + TEST_CHECK(dsn->is_valid == true); + + sentry__dsn_decref(dsn); +} diff --git a/tests/unit/test_value.c b/tests/unit/test_value.c index 6a4044c13c..92e195d580 100644 --- a/tests/unit/test_value.c +++ b/tests/unit/test_value.c @@ -102,16 +102,31 @@ SENTRY_TEST(value_string) sentry_value_decref(val); } +SENTRY_TEST(value_string_n) +{ + sentry_value_t val = sentry_value_new_string_n(NULL, 0); + TEST_CHECK(sentry_value_is_null(val)); + TEST_CHECK(sentry_value_get_type(val) == SENTRY_VALUE_TYPE_NULL); + TEST_CHECK(sentry_value_is_true(val) == false); + sentry_value_decref(val); + + char non_null_term_empty_str[] = { 'h', 'e', 'l', 'l', 'o' }; + val = sentry_value_new_string_n( + non_null_term_empty_str, sizeof(non_null_term_empty_str)); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(val), "hello"); + TEST_CHECK(sentry_value_get_type(val) == SENTRY_VALUE_TYPE_STRING); + TEST_CHECK(sentry_value_is_true(val) == true); + sentry_value_decref(val); +} + SENTRY_TEST(value_unicode) { // https://xkcd.com/1813/ :-) - sentry_value_t val - = sentry_value_new_string("őá…–🤮🚀¿ 한글 테스트 \a\v"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(val), - "őá…–🤮🚀¿ 한글 테스트 \a\v"); + sentry_value_t val = sentry_value_new_string("őá…–🤮🚀¿ 한글 테스트 \a\v"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(val), "őá…–🤮🚀¿ 한글 테스트 \a\v"); // json does not need to escape unicode, except for control characters - TEST_CHECK_JSON_VALUE( - val, "\"őá…–🤮🚀¿ 한글 테스트 \\u0007\\u000b\""); + TEST_CHECK_JSON_VALUE(val, "\"őá…–🤮🚀¿ 한글 테스트 \\u0007\\u000b\""); sentry_value_decref(val); char zalgo[] = "z̴̢̈͜ä̴̺̟́ͅl̸̛̦͎̺͂̃̚͝g̷̦̲͊͋̄̌͝o̸͇̞̪͙̞͌̇̀̓̏͜"; val = sentry_value_new_string(zalgo); @@ -161,7 +176,7 @@ SENTRY_TEST(value_list) sentry_value_decref(val); val = sentry_value_new_list(); - for (uint32_t i = 1; i <= 10; i++) { + for (int32_t i = 1; i <= 10; i++) { sentry_value_append(val, sentry_value_new_int32(i)); } sentry__value_append_bounded(val, sentry_value_new_int32(1010), 5); @@ -547,6 +562,89 @@ SENTRY_TEST(value_collections_leak) sentry_value_decref(obj); } +SENTRY_TEST(value_set_by_null_key) +{ + sentry_value_t value = sentry_value_new_object(); + sentry_value_t payload = sentry_value_new_object(); + + TEST_CHECK(sentry_value_refcount(payload) == 1); + TEST_CHECK_INT_EQUAL(1, sentry_value_set_by_key(value, NULL, payload)); + TEST_CHECK(sentry_value_get_length(value) == 0); + + payload = sentry_value_new_object(); + TEST_CHECK(sentry_value_refcount(payload) == 1); + TEST_CHECK_INT_EQUAL(1, sentry_value_set_by_key_n(value, NULL, 0, payload)); + TEST_CHECK(sentry_value_get_length(value) == 0); + + payload = sentry_value_new_object(); + TEST_CHECK(sentry_value_refcount(payload) == 1); + TEST_CHECK_INT_EQUAL( + 1, sentry_value_set_by_key_n(value, NULL, 10, payload)); + TEST_CHECK(sentry_value_get_length(value) == 0); + + sentry_value_decref(value); +} + +SENTRY_TEST(value_remove_by_null_key) +{ + sentry_value_t value = sentry_value_new_object(); + + TEST_CHECK_INT_EQUAL(0, + sentry_value_set_by_key(value, "some_key", sentry_value_new_object())); + TEST_CHECK(sentry_value_get_length(value) == 1); + + TEST_CHECK_INT_EQUAL(1, sentry_value_remove_by_key(value, NULL)); + TEST_CHECK_INT_EQUAL(1, sentry_value_get_length(value)); + TEST_CHECK_INT_EQUAL(1, sentry_value_remove_by_key_n(value, NULL, 0)); + TEST_CHECK_INT_EQUAL(1, sentry_value_get_length(value)); + TEST_CHECK_INT_EQUAL(1, sentry_value_remove_by_key_n(value, NULL, 10)); + TEST_CHECK_INT_EQUAL(1, sentry_value_get_length(value)); + + sentry_value_decref(value); +} + +SENTRY_TEST(value_get_by_null_key) +{ + sentry_value_t value = sentry_value_new_object(); + + const char *some_key = "some_key"; + TEST_CHECK_INT_EQUAL( + 0, sentry_value_set_by_key(value, some_key, sentry_value_new_object())); + TEST_CHECK(sentry_value_get_length(value) == 1); + + sentry_value_t rv = sentry_value_get_by_key(value, NULL); + TEST_CHECK(sentry_value_is_null(rv)); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + + rv = sentry_value_get_by_key_owned(value, NULL); + TEST_CHECK(sentry_value_is_null(rv)); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + sentry_value_decref(rv); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + + rv = sentry_value_get_by_key_owned(value, some_key); + TEST_CHECK(!sentry_value_is_null(rv)); + TEST_CHECK_INT_EQUAL(2, sentry_value_refcount(rv)); + sentry_value_decref(rv); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + + // if `k_len` != any length of keys stored in the object this won't + // segfault because the `sentry_slice_t` equality check already fails due to + // the length-inequality and never reaches `memcmp()`. + TEST_CHECK(sentry_value_is_null(sentry_value_get_by_key_n(value, NULL, 0))); + // If `k_len' == any key-length, we'd segfault without a NULL-check. + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key_n(value, NULL, strlen(some_key)))); + + rv = sentry_value_get_by_key_owned_n(value, NULL, strlen(some_key)); + TEST_CHECK(sentry_value_is_null(rv)); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + sentry_value_decref(rv); + TEST_CHECK_INT_EQUAL(1, sentry_value_refcount(rv)); + + sentry_value_decref(value); +} + SENTRY_TEST(value_set_stacktrace) { sentry_value_t exc @@ -564,3 +662,113 @@ SENTRY_TEST(value_set_stacktrace) sentry_value_decref(exc); } + +SENTRY_TEST(message_with_null_text_is_valid) +{ + sentry_value_t message_event = sentry_value_new_message_event( + SENTRY_LEVEL_WARNING, "some-logger", NULL); + + TEST_CHECK(!sentry_value_is_null(message_event)); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + message_event, "logger")), + "some-logger"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(message_event, "level")), + "warning"); + + sentry_value_decref(message_event); +} + +SENTRY_TEST(breadcrumb_without_type_or_message_still_valid) +{ + sentry_value_t breadcrumb = sentry_value_new_breadcrumb(NULL, NULL); + TEST_CHECK(!sentry_value_is_null(breadcrumb)); + TEST_CHECK(!sentry_value_is_null( + sentry_value_get_by_key(breadcrumb, "timestamp"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(breadcrumb, "type"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(breadcrumb, "message"))); + sentry_value_decref(breadcrumb); + + char *const test_type = "navigation"; + breadcrumb = sentry_value_new_breadcrumb(test_type, NULL); + TEST_CHECK(!sentry_value_is_null(breadcrumb)); + TEST_CHECK(!sentry_value_is_null( + sentry_value_get_by_key(breadcrumb, "timestamp"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(breadcrumb, "type")), + test_type); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(breadcrumb, "message"))); + sentry_value_decref(breadcrumb); + + char *const test_message = "a fork in the road, take it"; + breadcrumb = sentry_value_new_breadcrumb(NULL, test_message); + TEST_CHECK(!sentry_value_is_null(breadcrumb)); + TEST_CHECK(!sentry_value_is_null( + sentry_value_get_by_key(breadcrumb, "timestamp"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(breadcrumb, "type"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(breadcrumb, "message")), + test_message); + sentry_value_decref(breadcrumb); +} + +SENTRY_TEST(exception_without_type_or_value_still_valid) +{ + sentry_value_t exception = sentry_value_new_exception(NULL, NULL); + TEST_CHECK(!sentry_value_is_null(exception)); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(exception, "type"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(exception, "value"))); + sentry_value_decref(exception); + + char *const test_type = "EXC_BAD_ACCESS / KERN_INVALID_ADDRESS / 0x61"; + exception = sentry_value_new_exception(test_type, NULL); + TEST_CHECK(!sentry_value_is_null(exception)); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(exception, "type")), + test_type); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(exception, "value"))); + sentry_value_decref(exception); + + char *const test_value + = "Fatal Error: EXC_BAD_ACCESS / KERN_INVALID_ADDRESS / 0x61"; + exception = sentry_value_new_exception(NULL, test_value); + TEST_CHECK(!sentry_value_is_null(exception)); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(exception, "type"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(exception, "value")), + test_value); + sentry_value_decref(exception); +} + +SENTRY_TEST(thread_without_name_still_valid) +{ + sentry_value_t thread = sentry_value_new_thread(0xFF00FF00FF00FF00, NULL); + TEST_CHECK(!sentry_value_is_null(thread)); + TEST_CHECK(!sentry_value_is_null(sentry_value_get_by_key(thread, "id"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(thread, "id")), + "18374966859414961920"); + TEST_CHECK(sentry_value_is_null(sentry_value_get_by_key(thread, "name"))); + sentry_value_decref(thread); + + char *const test_name = "worker"; + thread = sentry_value_new_thread(0xAA00AA00AA00AA00, test_name); + TEST_CHECK(!sentry_value_is_null(thread)); + TEST_CHECK(!sentry_value_is_null(sentry_value_get_by_key(thread, "id"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(thread, "id")), + "12249977906276641280"); + TEST_CHECK(!sentry_value_is_null(sentry_value_get_by_key(thread, "name"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(thread, "name")), + test_name); + sentry_value_decref(thread); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index c60d465545..94b1cbca5f 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -12,7 +12,9 @@ XX(basic_http_request_preparation_for_transaction) XX(basic_spans) XX(basic_tracing_context) XX(basic_transaction) +XX(basic_write_envelope_to_file) XX(bgworker_flush) +XX(breadcrumb_without_type_or_message_still_valid) XX(build_id_parser) XX(check_version) XX(child_spans) @@ -29,7 +31,12 @@ XX(dsn_parsing_complete) XX(dsn_parsing_invalid) XX(dsn_store_url_with_path) XX(dsn_store_url_without_path) +XX(dsn_with_ending_forward_slash_will_be_cleaned) +XX(dsn_with_non_http_scheme_is_invalid) +XX(dsn_without_project_id_is_invalid) +XX(dsn_without_url_scheme_is_invalid) XX(empty_transport) +XX(exception_without_type_or_value_still_valid) XX(fuzz_json) XX(init_failure) XX(internal_uuid_api) @@ -37,6 +44,7 @@ XX(invalid_dsn) XX(invalid_proxy) XX(iso_time) XX(lazy_attachments) +XX(message_with_null_text_is_valid) XX(module_addr) XX(module_finder) XX(mpack_newlines) @@ -49,6 +57,8 @@ XX(page_allocator) XX(path_basics) XX(path_current_exe) XX(path_directory) +XX(path_from_str_n_wo_null_termination) +XX(path_from_str_null) XX(path_joining_unix) XX(path_joining_windows) XX(path_relative_filename) @@ -58,18 +68,34 @@ XX(recursive_paths) XX(sampling_before_send) XX(sampling_decision) XX(sampling_transaction) +XX(sentry__value_span_new_requires_unfinished_parent) XX(serialize_envelope) XX(session_basics) +XX(set_tag_allows_null_tag_and_value) +XX(set_tag_cuts_value_at_length_200) XX(slice) +XX(span_data) +XX(span_data_n) +XX(span_tagging) +XX(span_tagging_n) XX(spans_on_scope) XX(symbolizer) XX(task_queue) +XX(thread_without_name_still_valid) XX(transaction_name_backfill_on_finish) XX(transactions_skip_before_send) XX(transport_sampling_transactions) +XX(txn_data) +XX(txn_data_n) +XX(txn_name) +XX(txn_name_n) +XX(txn_tagging) +XX(txn_tagging_n) XX(uninitialized) XX(unsampled_spans) XX(unwinder) +XX(update_from_header_no_sampled_flag) +XX(update_from_header_null_ctx) XX(url_parsing_complete) XX(url_parsing_invalid) XX(url_parsing_partial) @@ -79,6 +105,7 @@ XX(value_bool) XX(value_collections_leak) XX(value_double) XX(value_freezing) +XX(value_get_by_null_key) XX(value_int32) XX(value_json_deeply_nested) XX(value_json_escaping) @@ -91,7 +118,11 @@ XX(value_null) XX(value_object) XX(value_object_merge) XX(value_object_merge_nested) +XX(value_remove_by_null_key) +XX(value_set_by_null_key) XX(value_set_stacktrace) XX(value_string) +XX(value_string_n) XX(value_unicode) XX(value_wrong_type) +XX(write_envelope_to_file_null)