Skip to content

Commit

Permalink
feat: change the timestamp resolution to microseconds (#995)
Browse files Browse the repository at this point in the history
  • Loading branch information
supervacuus authored May 27, 2024
1 parent 0be2fdf commit 235f837
Show file tree
Hide file tree
Showing 14 changed files with 67 additions and 59 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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**:
Expand Down
2 changes: 1 addition & 1 deletion src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint64_t>(report.creation_time) + 1) * 1000000;
if (time > *crash_time) {
*crash_time = time;
}
Expand Down
6 changes: 3 additions & 3 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions src/sentry_database.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/sentry_envelope.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions src/sentry_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions src/sentry_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
Expand Down
17 changes: 9 additions & 8 deletions src/sentry_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions src/sentry_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/sentry_tracing.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
23 changes: 12 additions & 11 deletions src/sentry_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -416,24 +416,25 @@ 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
|| consumed != 19) {
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;
Expand Down Expand Up @@ -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
Expand Down
20 changes: 10 additions & 10 deletions src/sentry_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions src/sentry_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

Expand Down Expand Up @@ -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
Expand Down
22 changes: 11 additions & 11 deletions tests/unit/test_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 235f837

Please sign in to comment.