xpcom/ds/TimeStamp_posix.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:7c051a61cba4
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

mercurial