xpcom/ds/TimeStamp_posix.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial