/src/mozilla-central/mozglue/misc/TimeStamp_posix.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // |
8 | | // Implement TimeStamp::Now() with POSIX clocks. |
9 | | // |
10 | | // The "tick" unit for POSIX clocks is simply a nanosecond, as this is |
11 | | // the smallest unit of time representable by struct timespec. That |
12 | | // doesn't mean that a nanosecond is the resolution of TimeDurations |
13 | | // obtained with this API; see TimeDuration::Resolution; |
14 | | // |
15 | | |
16 | | #include <sys/syscall.h> |
17 | | #include <time.h> |
18 | | #include <unistd.h> |
19 | | #include <string.h> |
20 | | |
21 | | #if defined(__DragonFly__) || defined(__FreeBSD__) \ |
22 | | || defined(__NetBSD__) || defined(__OpenBSD__) |
23 | | #include <sys/param.h> |
24 | | #include <sys/sysctl.h> |
25 | | #endif |
26 | | |
27 | | #if defined(__DragonFly__) || defined(__FreeBSD__) |
28 | | #include <sys/user.h> |
29 | | #endif |
30 | | |
31 | | #if defined(__NetBSD__) |
32 | | #undef KERN_PROC |
33 | | #define KERN_PROC KERN_PROC2 |
34 | | #define KINFO_PROC struct kinfo_proc2 |
35 | | #else |
36 | | #define KINFO_PROC struct kinfo_proc |
37 | | #endif |
38 | | |
39 | | #if defined(__DragonFly__) |
40 | | #define KP_START_SEC kp_start.tv_sec |
41 | | #define KP_START_USEC kp_start.tv_usec |
42 | | #elif defined(__FreeBSD__) |
43 | | #define KP_START_SEC ki_start.tv_sec |
44 | | #define KP_START_USEC ki_start.tv_usec |
45 | | #else |
46 | | #define KP_START_SEC p_ustart_sec |
47 | | #define KP_START_USEC p_ustart_usec |
48 | | #endif |
49 | | |
50 | | #include "mozilla/Sprintf.h" |
51 | | #include "mozilla/TimeStamp.h" |
52 | | #include <pthread.h> |
53 | | |
54 | | // Estimate of the smallest duration of time we can measure. |
55 | | static uint64_t sResolution; |
56 | | static uint64_t sResolutionSigDigs; |
57 | | |
58 | | static const uint16_t kNsPerUs = 1000; |
59 | | static const uint64_t kNsPerMs = 1000000; |
60 | | static const uint64_t kNsPerSec = 1000000000; |
61 | | static const double kNsPerMsd = 1000000.0; |
62 | | static const double kNsPerSecd = 1000000000.0; |
63 | | |
64 | | static uint64_t |
65 | | TimespecToNs(const struct timespec& aTs) |
66 | 8.24k | { |
67 | 8.24k | uint64_t baseNs = uint64_t(aTs.tv_sec) * kNsPerSec; |
68 | 8.24k | return baseNs + uint64_t(aTs.tv_nsec); |
69 | 8.24k | } |
70 | | |
71 | | static uint64_t |
72 | | ClockTimeNs() |
73 | 8.24k | { |
74 | 8.24k | struct timespec ts; |
75 | 8.24k | // this can't fail: we know &ts is valid, and TimeStamp::Startup() |
76 | 8.24k | // checks that CLOCK_MONOTONIC is supported (and aborts if not) |
77 | 8.24k | clock_gettime(CLOCK_MONOTONIC, &ts); |
78 | 8.24k | |
79 | 8.24k | // tv_sec is defined to be relative to an arbitrary point in time, |
80 | 8.24k | // but it would be madness for that point in time to be earlier than |
81 | 8.24k | // the Epoch. So we can safely assume that even if time_t is 32 |
82 | 8.24k | // bits, tv_sec won't overflow while the browser is open. Revisit |
83 | 8.24k | // this argument if we're still building with 32-bit time_t around |
84 | 8.24k | // the year 2037. |
85 | 8.24k | return TimespecToNs(ts); |
86 | 8.24k | } |
87 | | |
88 | | static uint64_t |
89 | | ClockResolutionNs() |
90 | 3 | { |
91 | 3 | // NB: why not rely on clock_getres()? Two reasons: (i) it might |
92 | 3 | // lie, and (ii) it might return an "ideal" resolution that while |
93 | 3 | // theoretically true, could never be measured in practice. Since |
94 | 3 | // clock_gettime() likely involves a system call on your platform, |
95 | 3 | // the "actual" timing resolution shouldn't be lower than syscall |
96 | 3 | // overhead. |
97 | 3 | |
98 | 3 | uint64_t start = ClockTimeNs(); |
99 | 3 | uint64_t end = ClockTimeNs(); |
100 | 3 | uint64_t minres = (end - start); |
101 | 3 | |
102 | 3 | // 10 total trials is arbitrary: what we're trying to avoid by |
103 | 3 | // looping is getting unlucky and being interrupted by a context |
104 | 3 | // switch or signal, or being bitten by paging/cache effects |
105 | 30 | for (int i = 0; i < 9; ++i) { |
106 | 27 | start = ClockTimeNs(); |
107 | 27 | end = ClockTimeNs(); |
108 | 27 | |
109 | 27 | uint64_t candidate = (start - end); |
110 | 27 | if (candidate < minres) { |
111 | 0 | minres = candidate; |
112 | 0 | } |
113 | 27 | } |
114 | 3 | |
115 | 3 | if (0 == minres) { |
116 | 0 | // measurable resolution is either incredibly low, ~1ns, or very |
117 | 0 | // high. fall back on clock_getres() |
118 | 0 | struct timespec ts; |
119 | 0 | if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) { |
120 | 0 | minres = TimespecToNs(ts); |
121 | 0 | } |
122 | 0 | } |
123 | 3 | |
124 | 3 | if (0 == minres) { |
125 | 0 | // clock_getres probably failed. fall back on NSPR's resolution |
126 | 0 | // assumption |
127 | 0 | minres = 1 * kNsPerMs; |
128 | 0 | } |
129 | 3 | |
130 | 3 | return minres; |
131 | 3 | } |
132 | | |
133 | | namespace mozilla { |
134 | | |
135 | | double |
136 | | BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks) |
137 | 3.11k | { |
138 | 3.11k | return double(aTicks) / kNsPerSecd; |
139 | 3.11k | } |
140 | | |
141 | | double |
142 | | BaseTimeDurationPlatformUtils::ToSecondsSigDigits(int64_t aTicks) |
143 | 0 | { |
144 | 0 | // don't report a value < mResolution ... |
145 | 0 | int64_t valueSigDigs = sResolution * (aTicks / sResolution); |
146 | 0 | // and chop off insignificant digits |
147 | 0 | valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs); |
148 | 0 | return double(valueSigDigs) / kNsPerSecd; |
149 | 0 | } |
150 | | |
151 | | int64_t |
152 | | BaseTimeDurationPlatformUtils::TicksFromMilliseconds(double aMilliseconds) |
153 | 586 | { |
154 | 586 | double result = aMilliseconds * kNsPerMsd; |
155 | 586 | if (result > INT64_MAX) { |
156 | 0 | return INT64_MAX; |
157 | 0 | } |
158 | 586 | if (result < INT64_MIN) { |
159 | 0 | return INT64_MIN; |
160 | 0 | } |
161 | 586 | |
162 | 586 | return result; |
163 | 586 | } |
164 | | |
165 | | int64_t |
166 | | BaseTimeDurationPlatformUtils::ResolutionInTicks() |
167 | 0 | { |
168 | 0 | return static_cast<int64_t>(sResolution); |
169 | 0 | } |
170 | | |
171 | | static bool gInitialized = false; |
172 | | |
173 | | void |
174 | | TimeStamp::Startup() |
175 | 3 | { |
176 | 3 | if (gInitialized) { |
177 | 0 | return; |
178 | 0 | } |
179 | 3 | |
180 | 3 | struct timespec dummy; |
181 | 3 | if (clock_gettime(CLOCK_MONOTONIC, &dummy) != 0) { |
182 | 0 | MOZ_CRASH("CLOCK_MONOTONIC is absent!"); |
183 | 0 | } |
184 | 3 | |
185 | 3 | sResolution = ClockResolutionNs(); |
186 | 3 | |
187 | 3 | // find the number of significant digits in sResolution, for the |
188 | 3 | // sake of ToSecondsSigDigits() |
189 | 3 | for (sResolutionSigDigs = 1; |
190 | 7 | !(sResolutionSigDigs == sResolution || |
191 | 7 | 10 * sResolutionSigDigs > sResolution); |
192 | 4 | sResolutionSigDigs *= 10); |
193 | 3 | |
194 | 3 | gInitialized = true; |
195 | 3 | } |
196 | | |
197 | | void |
198 | | TimeStamp::Shutdown() |
199 | 0 | { |
200 | 0 | } |
201 | | |
202 | | TimeStamp |
203 | | TimeStamp::Now(bool aHighResolution) |
204 | 8.18k | { |
205 | 8.18k | return TimeStamp(ClockTimeNs()); |
206 | 8.18k | } |
207 | | |
208 | | #if defined(XP_LINUX) || defined(ANDROID) |
209 | | |
210 | | // Calculates the amount of jiffies that have elapsed since boot and up to the |
211 | | // starttime value of a specific process as found in its /proc/*/stat file. |
212 | | // Returns 0 if an error occurred. |
213 | | |
214 | | static uint64_t |
215 | | JiffiesSinceBoot(const char* aFile) |
216 | 6 | { |
217 | 6 | char stat[512]; |
218 | 6 | |
219 | 6 | FILE* f = fopen(aFile, "r"); |
220 | 6 | if (!f) { |
221 | 0 | return 0; |
222 | 0 | } |
223 | 6 | |
224 | 6 | int n = fread(&stat, 1, sizeof(stat) - 1, f); |
225 | 6 | |
226 | 6 | fclose(f); |
227 | 6 | |
228 | 6 | if (n <= 0) { |
229 | 0 | return 0; |
230 | 0 | } |
231 | 6 | |
232 | 6 | stat[n] = 0; |
233 | 6 | |
234 | 6 | long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet |
235 | 6 | char* s = strrchr(stat, ')'); |
236 | 6 | |
237 | 6 | if (!s) { |
238 | 0 | return 0; |
239 | 0 | } |
240 | 6 | |
241 | 6 | int rv = sscanf(s + 2, |
242 | 6 | "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u " |
243 | 6 | "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu", |
244 | 6 | &startTime); |
245 | 6 | |
246 | 6 | if (rv != 1 || !startTime) { |
247 | 0 | return 0; |
248 | 0 | } |
249 | 6 | |
250 | 6 | return startTime; |
251 | 6 | } |
252 | | |
253 | | // Computes the interval that has elapsed between the thread creation and the |
254 | | // process creation by comparing the starttime fields in the respective |
255 | | // /proc/*/stat files. The resulting value will be a good approximation of the |
256 | | // process uptime. This value will be stored at the address pointed by aTime; |
257 | | // if an error occurred 0 will be stored instead. |
258 | | |
259 | | static void* |
260 | | ComputeProcessUptimeThread(void* aTime) |
261 | 3 | { |
262 | 3 | uint64_t* uptime = static_cast<uint64_t*>(aTime); |
263 | 3 | long hz = sysconf(_SC_CLK_TCK); |
264 | 3 | |
265 | 3 | *uptime = 0; |
266 | 3 | |
267 | 3 | if (!hz) { |
268 | 0 | return nullptr; |
269 | 0 | } |
270 | 3 | |
271 | 3 | char threadStat[40]; |
272 | 3 | SprintfLiteral(threadStat, "/proc/self/task/%d/stat", (pid_t)syscall(__NR_gettid)); |
273 | 3 | |
274 | 3 | uint64_t threadJiffies = JiffiesSinceBoot(threadStat); |
275 | 3 | uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat"); |
276 | 3 | |
277 | 3 | if (!threadJiffies || !selfJiffies) { |
278 | 0 | return nullptr; |
279 | 0 | } |
280 | 3 | |
281 | 3 | *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz; |
282 | 3 | return nullptr; |
283 | 3 | } |
284 | | |
285 | | // Computes and returns the process uptime in us on Linux & its derivatives. |
286 | | // Returns 0 if an error was encountered. |
287 | | |
288 | | uint64_t |
289 | | TimeStamp::ComputeProcessUptime() |
290 | 3 | { |
291 | 3 | uint64_t uptime = 0; |
292 | 3 | pthread_t uptime_pthread; |
293 | 3 | |
294 | 3 | if (pthread_create(&uptime_pthread, nullptr, ComputeProcessUptimeThread, &uptime)) { |
295 | 0 | MOZ_CRASH("Failed to create process uptime thread."); |
296 | 0 | return 0; |
297 | 3 | } |
298 | 3 | |
299 | 3 | pthread_join(uptime_pthread, NULL); |
300 | 3 | |
301 | 3 | return uptime / kNsPerUs; |
302 | 3 | } |
303 | | |
304 | | #elif defined(__DragonFly__) || defined(__FreeBSD__) \ |
305 | | || defined(__NetBSD__) || defined(__OpenBSD__) |
306 | | |
307 | | // Computes and returns the process uptime in us on various BSD flavors. |
308 | | // Returns 0 if an error was encountered. |
309 | | |
310 | | uint64_t |
311 | | TimeStamp::ComputeProcessUptime() |
312 | | { |
313 | | struct timespec ts; |
314 | | int rv = clock_gettime(CLOCK_REALTIME, &ts); |
315 | | |
316 | | if (rv == -1) { |
317 | | return 0; |
318 | | } |
319 | | |
320 | | int mib[] = { |
321 | | CTL_KERN, |
322 | | KERN_PROC, |
323 | | KERN_PROC_PID, |
324 | | getpid(), |
325 | | #if defined(__NetBSD__) || defined(__OpenBSD__) |
326 | | sizeof(KINFO_PROC), |
327 | | 1, |
328 | | #endif |
329 | | }; |
330 | | u_int mibLen = sizeof(mib) / sizeof(mib[0]); |
331 | | |
332 | | KINFO_PROC proc; |
333 | | size_t bufferSize = sizeof(proc); |
334 | | rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0); |
335 | | |
336 | | if (rv == -1) { |
337 | | return 0; |
338 | | } |
339 | | |
340 | | uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) + |
341 | | (proc.KP_START_USEC * kNsPerUs); |
342 | | uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec; |
343 | | |
344 | | if (startTime > now) { |
345 | | return 0; |
346 | | } |
347 | | |
348 | | return (now - startTime) / kNsPerUs; |
349 | | } |
350 | | |
351 | | #else |
352 | | |
353 | | uint64_t |
354 | | TimeStamp::ComputeProcessUptime() |
355 | | { |
356 | | return 0; |
357 | | } |
358 | | |
359 | | #endif |
360 | | |
361 | | } // namespace mozilla |