Commit | Line | Data |
---|---|---|
b3647fb8 JG |
1 | /* |
2 | * Copyright (C) 2010 Pierre-Marc Fournier | |
3 | * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
4 | * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0-only | |
7 | * | |
8 | */ | |
9 | ||
10 | #include "ust-clock.hpp" | |
11 | ||
12 | #include <common/time.hpp> | |
13 | #include <common/exception.hpp> | |
14 | ||
15 | #define CLOCK_OFFSET_SAMPLE_COUNT 10 | |
16 | ||
17 | namespace { | |
18 | struct offset_sample { | |
19 | /* correlation offset */ | |
20 | lttng::ust::clock_attributes_sample::scycles_t offset; | |
21 | /* lower is better */ | |
22 | lttng::ust::clock_attributes_sample::cycles_t measure_delta; | |
23 | }; | |
24 | ||
25 | lttng::ust::clock_attributes_sample::cycles_t sample_clock_read64() | |
26 | { | |
27 | lttng_ust_clock_read64_function read64_cb; | |
28 | ||
29 | if (lttng_ust_trace_clock_get_read64_cb(&read64_cb)) { | |
30 | LTTNG_THROW_ERROR("Failed to get clock sample callback"); | |
31 | } | |
32 | ||
33 | return read64_cb(); | |
34 | } | |
35 | ||
36 | lttng::ust::clock_attributes_sample::cycles_t sample_clock_frequency() | |
37 | { | |
38 | lttng_ust_clock_freq_function get_freq_cb; | |
39 | ||
40 | if (lttng_ust_trace_clock_get_freq_cb(&get_freq_cb)) { | |
41 | LTTNG_THROW_ERROR("Failed to get clock frequency callback"); | |
42 | } | |
43 | ||
44 | return get_freq_cb(); | |
45 | } | |
46 | ||
47 | nonstd::optional<lttng_uuid> sample_clock_uuid() | |
48 | { | |
49 | lttng_ust_clock_uuid_function get_uuid_cb; | |
50 | ||
51 | if (lttng_ust_trace_clock_get_uuid_cb(&get_uuid_cb)) { | |
52 | return nonstd::nullopt; | |
53 | } | |
54 | ||
55 | char uuid_str[LTTNG_UUID_STR_LEN]; | |
56 | if (get_uuid_cb(uuid_str)) { | |
57 | return nonstd::nullopt; | |
58 | } | |
59 | ||
60 | lttng_uuid uuid; | |
61 | if (lttng_uuid_from_str(uuid_str, uuid)) { | |
62 | LTTNG_THROW_ERROR("Failed to parse UUID from string"); | |
63 | } | |
64 | ||
65 | return nonstd::optional<lttng_uuid>{uuid}; | |
66 | } | |
67 | ||
68 | const char *sample_clock_name() | |
69 | { | |
70 | lttng_ust_clock_name_function get_name_cb; | |
71 | ||
72 | if (lttng_ust_trace_clock_get_name_cb(&get_name_cb)) { | |
73 | LTTNG_THROW_ERROR("Failed to get clock name callback"); | |
74 | } | |
75 | ||
76 | const auto name = get_name_cb(); | |
77 | if (!name) { | |
78 | LTTNG_THROW_ERROR("Invalid clock name returned by LTTng-UST `lttng_ust_clock_name_function`"); | |
79 | } | |
80 | ||
81 | return name; | |
82 | } | |
83 | ||
84 | const char *sample_clock_description() | |
85 | { | |
86 | lttng_ust_clock_description_function get_description_cb; | |
87 | ||
88 | if (lttng_ust_trace_clock_get_description_cb(&get_description_cb)) { | |
89 | LTTNG_THROW_ERROR("Failed to get clock description callback"); | |
90 | } | |
91 | ||
92 | const auto description = get_description_cb(); | |
93 | if (!description) { | |
94 | LTTNG_THROW_ERROR("Invalid clock description returned by LTTng-UST `lttng_ust_clock_description_function`"); | |
95 | } | |
96 | ||
97 | return description; | |
98 | } | |
99 | ||
100 | /* | |
101 | * The offset between monotonic and realtime clock can be negative if | |
102 | * the system sets the REALTIME clock to 0 after boot. | |
103 | */ | |
104 | void measure_single_clock_offset(struct offset_sample *sample) | |
105 | { | |
106 | lttng::ust::clock_attributes_sample::cycles_t monotonic_avg, monotonic[2], measure_delta, | |
107 | realtime; | |
108 | const auto tcf = sample_clock_frequency(); | |
109 | struct timespec rts = { 0, 0 }; | |
110 | ||
111 | monotonic[0] = sample_clock_read64(); | |
112 | if (lttng_clock_gettime(CLOCK_REALTIME, &rts)) { | |
113 | LTTNG_THROW_POSIX("Failed to sample time from clock", errno); | |
114 | } | |
115 | ||
116 | monotonic[1] = sample_clock_read64(); | |
117 | measure_delta = monotonic[1] - monotonic[0]; | |
118 | if (measure_delta > sample->measure_delta) { | |
119 | /* | |
120 | * Discard value if it took longer to read than the best | |
121 | * sample so far. | |
122 | */ | |
123 | return; | |
124 | } | |
125 | ||
126 | monotonic_avg = (monotonic[0] + monotonic[1]) >> 1; | |
127 | realtime = (lttng::ust::clock_attributes_sample::cycles_t) rts.tv_sec * tcf; | |
128 | if (tcf == NSEC_PER_SEC) { | |
129 | realtime += rts.tv_nsec; | |
130 | } else { | |
131 | realtime += (lttng::ust::clock_attributes_sample::cycles_t) rts.tv_nsec * tcf / | |
132 | NSEC_PER_SEC; | |
133 | } | |
134 | ||
135 | sample->offset = (lttng::ust::clock_attributes_sample::scycles_t) realtime - monotonic_avg; | |
136 | sample->measure_delta = measure_delta; | |
137 | } | |
138 | ||
139 | /* | |
140 | * Approximation of NTP time of day to clock monotonic correlation, | |
141 | * taken at start of trace. Keep the measurement that took the less time | |
142 | * to complete, thus removing imprecision caused by preemption. | |
143 | * May return a negative offset. | |
144 | */ | |
145 | lttng::ust::clock_attributes_sample::scycles_t measure_clock_offset(void) | |
146 | { | |
147 | struct offset_sample offset_best_sample = { | |
148 | .offset = 0, | |
149 | .measure_delta = UINT64_MAX, | |
150 | }; | |
151 | ||
152 | for (auto i = 0; i < CLOCK_OFFSET_SAMPLE_COUNT; i++) { | |
153 | measure_single_clock_offset(&offset_best_sample); | |
154 | } | |
155 | ||
156 | return offset_best_sample.offset; | |
157 | } | |
158 | } | |
159 | ||
160 | lttng::ust::clock_attributes_sample::clock_attributes_sample() : | |
161 | _name{sample_clock_name()}, | |
162 | _description{sample_clock_description()}, | |
163 | _uuid{sample_clock_uuid()}, | |
164 | _offset{measure_clock_offset()}, | |
165 | _frequency{sample_clock_frequency()} | |
166 | { | |
167 | } |