Coverage Report

Created: 2018-09-25 14:53

/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