Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: change the timestamp resolution to microseconds #995

Merged
merged 3 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading