diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f981c4a..d8437c16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +**Features**: + +- Change the timestamp resolution to microseconds. ([#995](https://github.com/getsentry/sentry-native/pull/995)) + ## 0.7.4 **Fixes**: diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index ad7d5846a..19df8bf81 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -554,7 +554,7 @@ report_crash_time( // can have a session that starts at, e.g. `0.471`, whereas the crashpad // report will be `0`, which would mean our heuristic does not trigger due // to rounding. - uint64_t time = ((uint64_t)report.creation_time + 1) * 1000; + uint64_t time = (static_cast(report.creation_time) + 1) * 1000000; if (time > *crash_time) { *crash_time = time; } diff --git a/src/sentry_core.c b/src/sentry_core.c index c9ef8cd45..55241aa75 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -869,7 +869,7 @@ sentry_transaction_start( sentry_value_set_by_key(tx, "start_timestamp", sentry__value_new_string_owned( - sentry__msec_time_to_iso8601(sentry__msec_time()))); + sentry__usec_time_to_iso8601(sentry__usec_time()))); sentry__transaction_context_free(opaque_tx_cxt); return sentry__transaction_new(tx); @@ -914,7 +914,7 @@ sentry_transaction_finish(sentry_transaction_t *opaque_tx) sentry_value_set_by_key(tx, "type", sentry_value_new_string("transaction")); sentry_value_set_by_key(tx, "timestamp", sentry__value_new_string_owned( - sentry__msec_time_to_iso8601(sentry__msec_time()))); + sentry__usec_time_to_iso8601(sentry__usec_time()))); // TODO: This might not actually be necessary. Revisit after talking to // the relay team about this. sentry_value_set_by_key(tx, "level", sentry_value_new_string("info")); @@ -1111,7 +1111,7 @@ sentry_span_finish(sentry_span_t *opaque_span) sentry_value_set_by_key(span, "timestamp", sentry__value_new_string_owned( - sentry__msec_time_to_iso8601(sentry__msec_time()))); + sentry__usec_time_to_iso8601(sentry__usec_time()))); sentry_value_remove_by_key(span, "sampled"); size_t max_spans = SENTRY_SPANS_MAX; diff --git a/src/sentry_database.c b/src/sentry_database.c index d9874fcdb..6ebe89008 100644 --- a/src/sentry_database.c +++ b/src/sentry_database.c @@ -203,10 +203,10 @@ sentry__process_old_runs(const sentry_options_t *options, uint64_t last_crash) // time. if (session->status == SENTRY_SESSION_STATUS_OK) { bool was_crash - = last_crash && last_crash > session->started_ms; + = last_crash && last_crash > session->started_us; if (was_crash) { - session->duration_ms - = last_crash - session->started_ms; + session->duration_us + = last_crash - session->started_us; session->errors += 1; // we only set at most one unclosed session as // crashed @@ -248,7 +248,7 @@ static const char *g_last_crash_filename = "last_crash"; bool sentry__write_crash_marker(const sentry_options_t *options) { - char *iso_time = sentry__msec_time_to_iso8601(sentry__msec_time()); + char *iso_time = sentry__usec_time_to_iso8601(sentry__usec_time()); if (!iso_time) { return false; } diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index b71242c1d..615197654 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -288,7 +288,7 @@ sentry__envelope_add_transaction( sentry_value_t now = sentry_value_new_string("2021-12-16T05:53:59.343Z"); #else sentry_value_t now = sentry__value_new_string_owned( - sentry__msec_time_to_iso8601(sentry__msec_time())); + sentry__usec_time_to_iso8601(sentry__usec_time())); #endif sentry__envelope_set_header(envelope, "sent_at", now); diff --git a/src/sentry_json.c b/src/sentry_json.c index f83cd1886..904ee4825 100644 --- a/src/sentry_json.c +++ b/src/sentry_json.c @@ -278,9 +278,9 @@ sentry__jsonwriter_write_uuid( } void -sentry__jsonwriter_write_msec_timestamp(sentry_jsonwriter_t *jw, uint64_t time) +sentry__jsonwriter_write_usec_timestamp(sentry_jsonwriter_t *jw, uint64_t time) { - char *formatted = sentry__msec_time_to_iso8601(time); + char *formatted = sentry__usec_time_to_iso8601(time); sentry__jsonwriter_write_str(jw, formatted); sentry_free(formatted); } diff --git a/src/sentry_json.h b/src/sentry_json.h index 1ff9a11c7..8379bf021 100644 --- a/src/sentry_json.h +++ b/src/sentry_json.h @@ -58,11 +58,11 @@ void sentry__jsonwriter_write_uuid( sentry_jsonwriter_t *jw, const sentry_uuid_t *uuid); /** - * This will write a millisecond resolution timestamp formattad as an ISO8601 + * This will write a microsecond resolution timestamp formattad as an ISO8601 * string. - * See `sentry__msec_time_to_iso8601`. + * See `sentry__usec_time_to_iso8601`. */ -void sentry__jsonwriter_write_msec_timestamp( +void sentry__jsonwriter_write_usec_timestamp( sentry_jsonwriter_t *jw, uint64_t time); /** diff --git a/src/sentry_session.c b/src/sentry_session.c index e782d385f..6c8589497 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -76,8 +76,8 @@ sentry__session_new(void) rv->status = SENTRY_SESSION_STATUS_OK; rv->init = true; rv->errors = 0; - rv->started_ms = sentry__msec_time(); - rv->duration_ms = (uint64_t)-1; + rv->started_us = sentry__usec_time(); + rv->duration_us = (uint64_t)-1; return rv; } @@ -119,17 +119,18 @@ sentry__session_to_json( sentry__jsonwriter_write_int32(jw, (int32_t)session->errors); sentry__jsonwriter_write_key(jw, "started"); - sentry__jsonwriter_write_msec_timestamp(jw, session->started_ms); + sentry__jsonwriter_write_usec_timestamp(jw, session->started_us); // if there is a duration stored on the struct (that happens after // reading back from disk) we use that, otherwise we calculate the // difference to the start time. sentry__jsonwriter_write_key(jw, "duration"); double duration; - if (session->duration_ms != (uint64_t)-1) { - duration = (double)session->duration_ms / 1000.0; + if (session->duration_us != (uint64_t)-1) { + duration = (double)session->duration_us / 1000000.0; } else { - duration = (double)(sentry__msec_time() - session->started_ms) / 1000.0; + duration + = (double)(sentry__usec_time() - session->started_us) / 1000000.0; } sentry__jsonwriter_write_double(jw, duration); @@ -184,12 +185,12 @@ sentry__session_from_json(const char *buf, size_t buflen) rv->errors = (int64_t)sentry_value_as_int32( sentry_value_get_by_key(value, "errors")); - rv->started_ms = sentry__iso8601_to_msec( + rv->started_us = sentry__iso8601_to_usec( sentry_value_as_string(sentry_value_get_by_key(value, "started"))); double duration = sentry_value_as_double(sentry_value_get_by_key(value, "duration")); - rv->duration_ms = (uint64_t)(duration * 1000); + rv->duration_us = (uint64_t)(duration * 1000000); sentry_value_decref(value); diff --git a/src/sentry_session.h b/src/sentry_session.h index 2a18edb4c..d95cc6871 100644 --- a/src/sentry_session.h +++ b/src/sentry_session.h @@ -17,8 +17,8 @@ typedef struct sentry_session_s { char *environment; sentry_uuid_t session_id; sentry_value_t distinct_id; - uint64_t started_ms; - uint64_t duration_ms; + uint64_t started_us; + uint64_t duration_us; uint64_t errors; sentry_session_status_t status; long init; diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index bbc7f00b4..5f3ae7c69 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -327,7 +327,7 @@ sentry__value_span_new_n(size_t max_spans, sentry_value_t parent, 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()))); + sentry__usec_time_to_iso8601(sentry__usec_time()))); return child; fail: diff --git a/src/sentry_utils.c b/src/sentry_utils.c index 45f32081e..f2980b728 100644 --- a/src/sentry_utils.c +++ b/src/sentry_utils.c @@ -374,11 +374,11 @@ sentry__dsn_get_minidump_url(const sentry_dsn_t *dsn, const char *user_agent) } char * -sentry__msec_time_to_iso8601(uint64_t time) +sentry__usec_time_to_iso8601(uint64_t time) { char buf[64]; size_t buf_len = sizeof(buf); - time_t secs = time / 1000; + time_t secs = time / 1000000; struct tm *tm; #ifdef SENTRY_PLATFORM_WINDOWS tm = gmtime(&secs); @@ -397,10 +397,10 @@ sentry__msec_time_to_iso8601(uint64_t time) return NULL; } - int msecs = time % 1000; - if (msecs) { + int usecs = time % 1000000; + if (usecs) { size_t rv = (size_t)snprintf( - buf + written, buf_len - written, ".%03d", msecs); + buf + written, buf_len - written, ".%06d", usecs); if (rv >= buf_len - written) { return NULL; } @@ -416,14 +416,14 @@ sentry__msec_time_to_iso8601(uint64_t time) } uint64_t -sentry__iso8601_to_msec(const char *iso) +sentry__iso8601_to_usec(const char *iso) { size_t len = strlen(iso); - if (len != 20 && len != 24) { + if (len != 20 && len != 27) { return 0; } // The code is adapted from: https://stackoverflow.com/a/26896792 - int y, M, d, h, m, s, msec = 0; + int y, M, d, h, m, s, usec = 0; int consumed = 0; if (sscanf(iso, "%d-%d-%dT%d:%d:%d%n", &y, &M, &d, &h, &m, &s, &consumed) < 6 @@ -431,9 +431,10 @@ sentry__iso8601_to_msec(const char *iso) return 0; } iso += consumed; - // we optionally have millisecond precision + // we optionally have microsecond precision if (iso[0] == '.') { - if (sscanf(iso, ".%d%n", &msec, &consumed) < 1 || consumed != 4) { + if (sscanf(iso, ".%d%n", &usec, &consumed) < 1 || consumed != 7) { + printf("consumed = %d\n", consumed); return 0; } iso += consumed; @@ -482,7 +483,7 @@ sentry__iso8601_to_msec(const char *iso) return 0; } - return (uint64_t)time * 1000 + msec; + return (uint64_t)time * 1000000 + usec; } #ifdef SENTRY_PLATFORM_WINDOWS diff --git a/src/sentry_utils.h b/src/sentry_utils.h index b5b8e9d3e..41678d7d4 100644 --- a/src/sentry_utils.h +++ b/src/sentry_utils.h @@ -97,10 +97,10 @@ char *sentry__dsn_get_minidump_url( const sentry_dsn_t *dsn, const char *user_agent); /** - * Returns the number of milliseconds since the unix epoch. + * Returns the number of microseconds since the unix epoch. */ static inline uint64_t -sentry__msec_time(void) +sentry__usec_time(void) { #ifdef SENTRY_PLATFORM_WINDOWS // Contains a 64-bit value representing the number of 100-nanosecond @@ -113,13 +113,13 @@ sentry__msec_time(void) uint64_t timestamp = (uint64_t)file_time.dwLowDateTime + ((uint64_t)file_time.dwHighDateTime << 32); timestamp -= 116444736000000000LL; // convert to unix epoch - timestamp /= 10000LL; // 100ns -> 1ms + timestamp /= 10LL; // 100ns -> 1us return timestamp; #else struct timeval tv; return (gettimeofday(&tv, NULL) == 0) - ? (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000 + ? (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec : 0; #endif } @@ -180,16 +180,16 @@ sentry__monotonic_time(void) } /** - * Formats a timestamp (milliseconds since epoch) into ISO8601 format. + * Formats a timestamp (microseconds since epoch) into ISO8601 format. */ -char *sentry__msec_time_to_iso8601(uint64_t time); +char *sentry__usec_time_to_iso8601(uint64_t time); /** - * Parses a ISO8601 formatted string into a millisecond resolution timestamp. - * This only accepts the format `YYYY-MM-DD'T'hh:mm:ss(.zzz)'Z'`, which is - * produced by the `sentry__msec_time_to_iso8601` function. + * Parses a ISO8601 formatted string into a microsecond resolution timestamp. + * This only accepts the format `YYYY-MM-DD'T'hh:mm:ss(.zzzzzz)'Z'`, which is + * produced by the `sentry__usec_time_to_iso8601` function. */ -uint64_t sentry__iso8601_to_msec(const char *iso); +uint64_t sentry__iso8601_to_usec(const char *iso); /** * Locale independent (or rather, using "C" locale) `strtod`. diff --git a/src/sentry_value.c b/src/sentry_value.c index 3c3e344c9..e2315914b 100644 --- a/src/sentry_value.c +++ b/src/sentry_value.c @@ -1121,7 +1121,7 @@ sentry_value_new_event(void) sentry_value_set_by_key(rv, "timestamp", sentry__value_new_string_owned( - sentry__msec_time_to_iso8601(sentry__msec_time()))); + sentry__usec_time_to_iso8601(sentry__usec_time()))); sentry_value_set_by_key(rv, "platform", sentry_value_new_string("native")); @@ -1162,7 +1162,7 @@ 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__usec_time_to_iso8601(sentry__usec_time()))); } sentry_value_t diff --git a/tests/unit/test_utils.c b/tests/unit/test_utils.c index 232303415..9fb5421f7 100644 --- a/tests/unit/test_utils.c +++ b/tests/unit/test_utils.c @@ -9,22 +9,22 @@ SENTRY_TEST(iso_time) { - uint64_t msec; + uint64_t usec; char *str; - msec = sentry__iso8601_to_msec("1970-01-01T00:00:10Z"); - TEST_CHECK_INT_EQUAL(msec, 10 * 1000); - msec = sentry__iso8601_to_msec("2020-04-27T11:02:36.050Z"); - TEST_CHECK_INT_EQUAL(msec, 1587985356050); - str = sentry__msec_time_to_iso8601(msec); - TEST_CHECK_STRING_EQUAL(str, "2020-04-27T11:02:36.050Z"); + usec = sentry__iso8601_to_usec("1970-01-01T00:00:10Z"); + TEST_CHECK_INT_EQUAL(usec, 10 * 1000000); + usec = sentry__iso8601_to_usec("2020-04-27T11:02:36.050505Z"); + TEST_CHECK_INT_EQUAL(usec, 1587985356050505); + str = sentry__usec_time_to_iso8601(usec); + TEST_CHECK_STRING_EQUAL(str, "2020-04-27T11:02:36.050505Z"); sentry_free(str); - msec = sentry__msec_time(); - str = sentry__msec_time_to_iso8601(msec); - uint64_t roundtrip = sentry__iso8601_to_msec(str); + usec = sentry__usec_time(); + str = sentry__usec_time_to_iso8601(usec); + uint64_t roundtrip = sentry__iso8601_to_usec(str); sentry_free(str); - TEST_CHECK_INT_EQUAL(roundtrip, msec); + TEST_CHECK_INT_EQUAL(roundtrip, usec); } SENTRY_TEST(url_parsing_complete)