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