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 JG |
11 | |
12 | #include <common/time.hpp> | |
13 | #include <common/exception.hpp> | |
14 | ||
d7bfb9b0 JG |
15 | #include <lttng/ust-clock.h> |
16 | ||
b3647fb8 JG |
17 | #define CLOCK_OFFSET_SAMPLE_COUNT 10 |
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 | ||
69 | return nonstd::optional<lttng_uuid>{uuid}; | |
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) { | |
82 | LTTNG_THROW_ERROR("Invalid clock name returned by LTTng-UST `lttng_ust_clock_name_function`"); | |
83 | } | |
84 | ||
85 | return name; | |
86 | } | |
87 | ||
88 | const char *sample_clock_description() | |
89 | { | |
90 | lttng_ust_clock_description_function get_description_cb; | |
91 | ||
92 | if (lttng_ust_trace_clock_get_description_cb(&get_description_cb)) { | |
93 | LTTNG_THROW_ERROR("Failed to get clock description callback"); | |
94 | } | |
95 | ||
96 | const auto description = get_description_cb(); | |
97 | if (!description) { | |
98 | LTTNG_THROW_ERROR("Invalid clock description returned by LTTng-UST `lttng_ust_clock_description_function`"); | |
99 | } | |
100 | ||
101 | return description; | |
102 | } | |
103 | ||
104 | /* | |
105 | * The offset between monotonic and realtime clock can be negative if | |
106 | * the system sets the REALTIME clock to 0 after boot. | |
107 | */ | |
108 | void measure_single_clock_offset(struct offset_sample *sample) | |
109 | { | |
d7bfb9b0 | 110 | lst::clock_class::cycles_t monotonic_avg, monotonic[2], measure_delta, |
b3647fb8 JG |
111 | realtime; |
112 | const auto tcf = sample_clock_frequency(); | |
113 | struct timespec rts = { 0, 0 }; | |
114 | ||
115 | monotonic[0] = sample_clock_read64(); | |
116 | if (lttng_clock_gettime(CLOCK_REALTIME, &rts)) { | |
117 | LTTNG_THROW_POSIX("Failed to sample time from clock", errno); | |
118 | } | |
119 | ||
120 | monotonic[1] = sample_clock_read64(); | |
121 | measure_delta = monotonic[1] - monotonic[0]; | |
122 | if (measure_delta > sample->measure_delta) { | |
123 | /* | |
124 | * Discard value if it took longer to read than the best | |
125 | * sample so far. | |
126 | */ | |
127 | return; | |
128 | } | |
129 | ||
130 | monotonic_avg = (monotonic[0] + monotonic[1]) >> 1; | |
d7bfb9b0 | 131 | realtime = (lst::clock_class::cycles_t) rts.tv_sec * tcf; |
b3647fb8 JG |
132 | if (tcf == NSEC_PER_SEC) { |
133 | realtime += rts.tv_nsec; | |
134 | } else { | |
d7bfb9b0 | 135 | realtime += (lst::clock_class::cycles_t) rts.tv_nsec * tcf / |
b3647fb8 JG |
136 | NSEC_PER_SEC; |
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 | */ | |
d7bfb9b0 | 149 | lst::clock_class::scycles_t measure_clock_offset(void) |
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(), | |
166 | sample_clock_description(), | |
167 | sample_clock_uuid(), | |
168 | measure_clock_offset(), | |
169 | sample_clock_frequency()) | |
b3647fb8 JG |
170 | { |
171 | } |