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