xpcom/ds/TimeStamp_posix.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/ds/TimeStamp_posix.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,345 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +//
    1.11 +// Implement TimeStamp::Now() with POSIX clocks.
    1.12 +//
    1.13 +// The "tick" unit for POSIX clocks is simply a nanosecond, as this is
    1.14 +// the smallest unit of time representable by struct timespec.  That
    1.15 +// doesn't mean that a nanosecond is the resolution of TimeDurations
    1.16 +// obtained with this API; see TimeDuration::Resolution;
    1.17 +//
    1.18 +
    1.19 +#include <sys/syscall.h>
    1.20 +#include <time.h>
    1.21 +#include <unistd.h>
    1.22 +
    1.23 +#if defined(__DragonFly__) || defined(__FreeBSD__) \
    1.24 +    || defined(__NetBSD__) || defined(__OpenBSD__)
    1.25 +#include <sys/param.h>
    1.26 +#include <sys/sysctl.h>
    1.27 +#endif
    1.28 +
    1.29 +#if defined(__DragonFly__) || defined(__FreeBSD__)
    1.30 +#include <sys/user.h>
    1.31 +#endif
    1.32 +
    1.33 +#if defined(__NetBSD__)
    1.34 +#undef KERN_PROC
    1.35 +#define KERN_PROC KERN_PROC2
    1.36 +#define KINFO_PROC struct kinfo_proc2
    1.37 +#else
    1.38 +#define KINFO_PROC struct kinfo_proc
    1.39 +#endif
    1.40 +
    1.41 +#if defined(__DragonFly__)
    1.42 +#define KP_START_SEC kp_start.tv_sec
    1.43 +#define KP_START_USEC kp_start.tv_usec
    1.44 +#elif defined(__FreeBSD__)
    1.45 +#define KP_START_SEC ki_start.tv_sec
    1.46 +#define KP_START_USEC ki_start.tv_usec
    1.47 +#else
    1.48 +#define KP_START_SEC p_ustart_sec
    1.49 +#define KP_START_USEC p_ustart_usec
    1.50 +#endif
    1.51 +
    1.52 +#include "mozilla/TimeStamp.h"
    1.53 +#include "nsCRT.h"
    1.54 +#include "prprf.h"
    1.55 +#include "prthread.h"
    1.56 +#include "nsDebug.h"
    1.57 +
    1.58 +// Estimate of the smallest duration of time we can measure.
    1.59 +static uint64_t sResolution;
    1.60 +static uint64_t sResolutionSigDigs;
    1.61 +
    1.62 +static const uint16_t kNsPerUs   =       1000;
    1.63 +static const uint64_t kNsPerMs   =    1000000;
    1.64 +static const uint64_t kNsPerSec  = 1000000000; 
    1.65 +static const double kNsPerMsd    =    1000000.0;
    1.66 +static const double kNsPerSecd   = 1000000000.0;
    1.67 +
    1.68 +static uint64_t
    1.69 +TimespecToNs(const struct timespec& ts)
    1.70 +{
    1.71 +  uint64_t baseNs = uint64_t(ts.tv_sec) * kNsPerSec;
    1.72 +  return baseNs + uint64_t(ts.tv_nsec);
    1.73 +}
    1.74 +
    1.75 +static uint64_t
    1.76 +ClockTimeNs()
    1.77 +{
    1.78 +  struct timespec ts;
    1.79 +  // this can't fail: we know &ts is valid, and TimeStamp::Startup()
    1.80 +  // checks that CLOCK_MONOTONIC is supported (and aborts if not)
    1.81 +  clock_gettime(CLOCK_MONOTONIC, &ts);
    1.82 +
    1.83 +  // tv_sec is defined to be relative to an arbitrary point in time,
    1.84 +  // but it would be madness for that point in time to be earlier than
    1.85 +  // the Epoch.  So we can safely assume that even if time_t is 32
    1.86 +  // bits, tv_sec won't overflow while the browser is open.  Revisit
    1.87 +  // this argument if we're still building with 32-bit time_t around
    1.88 +  // the year 2037.
    1.89 +  return TimespecToNs(ts);
    1.90 +}
    1.91 +
    1.92 +static uint64_t
    1.93 +ClockResolutionNs()
    1.94 +{
    1.95 +  // NB: why not rely on clock_getres()?  Two reasons: (i) it might
    1.96 +  // lie, and (ii) it might return an "ideal" resolution that while
    1.97 +  // theoretically true, could never be measured in practice.  Since
    1.98 +  // clock_gettime() likely involves a system call on your platform,
    1.99 +  // the "actual" timing resolution shouldn't be lower than syscall
   1.100 +  // overhead.
   1.101 +
   1.102 +  uint64_t start = ClockTimeNs();
   1.103 +  uint64_t end = ClockTimeNs();
   1.104 +  uint64_t minres = (end - start);
   1.105 +
   1.106 +  // 10 total trials is arbitrary: what we're trying to avoid by
   1.107 +  // looping is getting unlucky and being interrupted by a context
   1.108 +  // switch or signal, or being bitten by paging/cache effects
   1.109 +  for (int i = 0; i < 9; ++i) {
   1.110 +    start = ClockTimeNs();
   1.111 +    end = ClockTimeNs();
   1.112 +
   1.113 +    uint64_t candidate = (start - end);
   1.114 +    if (candidate < minres)
   1.115 +      minres = candidate;
   1.116 +  }
   1.117 +
   1.118 +  if (0 == minres) {
   1.119 +    // measurable resolution is either incredibly low, ~1ns, or very
   1.120 +    // high.  fall back on clock_getres()
   1.121 +    struct timespec ts;
   1.122 +    if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) {
   1.123 +      minres = TimespecToNs(ts);
   1.124 +    }
   1.125 +  }
   1.126 +
   1.127 +  if (0 == minres) {
   1.128 +    // clock_getres probably failed.  fall back on NSPR's resolution
   1.129 +    // assumption
   1.130 +    minres = 1 * kNsPerMs;
   1.131 +  }
   1.132 +
   1.133 +  return minres;
   1.134 +}
   1.135 +
   1.136 +namespace mozilla {
   1.137 +
   1.138 +double
   1.139 +TimeDuration::ToSeconds() const
   1.140 +{
   1.141 +  return double(mValue) / kNsPerSecd;
   1.142 +}
   1.143 +
   1.144 +double
   1.145 +TimeDuration::ToSecondsSigDigits() const
   1.146 +{
   1.147 +  // don't report a value < mResolution ...
   1.148 +  int64_t valueSigDigs = sResolution * (mValue / sResolution);
   1.149 +  // and chop off insignificant digits
   1.150 +  valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs);
   1.151 +  return double(valueSigDigs) / kNsPerSecd;
   1.152 +}
   1.153 +
   1.154 +TimeDuration
   1.155 +TimeDuration::FromMilliseconds(double aMilliseconds)
   1.156 +{
   1.157 +  return TimeDuration::FromTicks(aMilliseconds * kNsPerMsd);
   1.158 +}
   1.159 +
   1.160 +TimeDuration
   1.161 +TimeDuration::Resolution()
   1.162 +{
   1.163 +  return TimeDuration::FromTicks(int64_t(sResolution));
   1.164 +}
   1.165 +
   1.166 +static bool gInitialized = false;
   1.167 +
   1.168 +nsresult
   1.169 +TimeStamp::Startup()
   1.170 +{
   1.171 +  if (gInitialized)
   1.172 +    return NS_OK;
   1.173 +
   1.174 +  struct timespec dummy;
   1.175 +  if (0 != clock_gettime(CLOCK_MONOTONIC, &dummy))
   1.176 +      NS_RUNTIMEABORT("CLOCK_MONOTONIC is absent!");
   1.177 +
   1.178 +  sResolution = ClockResolutionNs();
   1.179 +
   1.180 +  // find the number of significant digits in sResolution, for the
   1.181 +  // sake of ToSecondsSigDigits()
   1.182 +  for (sResolutionSigDigs = 1;
   1.183 +       !(sResolutionSigDigs == sResolution
   1.184 +         || 10*sResolutionSigDigs > sResolution);
   1.185 +       sResolutionSigDigs *= 10);
   1.186 +
   1.187 +  gInitialized = true;
   1.188 +
   1.189 +  return NS_OK;
   1.190 +}
   1.191 +
   1.192 +void
   1.193 +TimeStamp::Shutdown()
   1.194 +{
   1.195 +}
   1.196 +
   1.197 +TimeStamp
   1.198 +TimeStamp::Now(bool aHighResolution)
   1.199 +{
   1.200 +  return TimeStamp(ClockTimeNs());
   1.201 +}
   1.202 +
   1.203 +#if defined(LINUX) || defined(ANDROID)
   1.204 +
   1.205 +// Calculates the amount of jiffies that have elapsed since boot and up to the
   1.206 +// starttime value of a specific process as found in its /proc/*/stat file.
   1.207 +// Returns 0 if an error occurred.
   1.208 +
   1.209 +static uint64_t
   1.210 +JiffiesSinceBoot(const char *aFile)
   1.211 +{
   1.212 +  char stat[512];
   1.213 +
   1.214 +  FILE *f = fopen(aFile, "r");
   1.215 +  if (!f)
   1.216 +    return 0;
   1.217 +
   1.218 +  int n = fread(&stat, 1, sizeof(stat) - 1, f);
   1.219 +
   1.220 +  fclose(f);
   1.221 +
   1.222 +  if (n <= 0)
   1.223 +    return 0;
   1.224 +
   1.225 +  stat[n] = 0;
   1.226 +
   1.227 +  long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet
   1.228 +  char *s = strrchr(stat, ')');
   1.229 +
   1.230 +  if (!s)
   1.231 +    return 0;
   1.232 +
   1.233 +  int rv = sscanf(s + 2,
   1.234 +                  "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
   1.235 +                  "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu",
   1.236 +                  &startTime);
   1.237 +
   1.238 +  if (rv != 1 || !startTime)
   1.239 +    return 0;
   1.240 +
   1.241 +  return startTime;
   1.242 +}
   1.243 +
   1.244 +// Computes the interval that has elapsed between the thread creation and the
   1.245 +// process creation by comparing the starttime fields in the respective
   1.246 +// /proc/*/stat files. The resulting value will be a good approximation of the
   1.247 +// process uptime. This value will be stored at the address pointed by aTime;
   1.248 +// if an error occurred 0 will be stored instead.
   1.249 +
   1.250 +static void
   1.251 +ComputeProcessUptimeThread(void *aTime)
   1.252 +{
   1.253 +  uint64_t *uptime = static_cast<uint64_t *>(aTime);
   1.254 +  long hz = sysconf(_SC_CLK_TCK);
   1.255 +
   1.256 +  *uptime = 0;
   1.257 +
   1.258 +  if (!hz)
   1.259 +    return;
   1.260 +
   1.261 +  char threadStat[40];
   1.262 +  sprintf(threadStat, "/proc/self/task/%d/stat", (pid_t) syscall(__NR_gettid));
   1.263 +
   1.264 +  uint64_t threadJiffies = JiffiesSinceBoot(threadStat);
   1.265 +  uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat");
   1.266 +
   1.267 +  if (!threadJiffies || !selfJiffies)
   1.268 +    return;
   1.269 +
   1.270 +  *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz;
   1.271 +}
   1.272 +
   1.273 +// Computes and returns the process uptime in us on Linux & its derivatives.
   1.274 +// Returns 0 if an error was encountered.
   1.275 +
   1.276 +uint64_t
   1.277 +TimeStamp::ComputeProcessUptime()
   1.278 +{
   1.279 +  uint64_t uptime = 0;
   1.280 +  PRThread *thread = PR_CreateThread(PR_USER_THREAD,
   1.281 +                                     ComputeProcessUptimeThread,
   1.282 +                                     &uptime,
   1.283 +                                     PR_PRIORITY_NORMAL,
   1.284 +                                     PR_GLOBAL_THREAD,
   1.285 +                                     PR_JOINABLE_THREAD,
   1.286 +                                     0);
   1.287 +
   1.288 +  PR_JoinThread(thread);
   1.289 +
   1.290 +  return uptime / kNsPerUs;
   1.291 +}
   1.292 +
   1.293 +#elif defined(__DragonFly__) || defined(__FreeBSD__) \
   1.294 +      || defined(__NetBSD__) || defined(__OpenBSD__)
   1.295 +
   1.296 +// Computes and returns the process uptime in us on various BSD flavors.
   1.297 +// Returns 0 if an error was encountered.
   1.298 +
   1.299 +uint64_t
   1.300 +TimeStamp::ComputeProcessUptime()
   1.301 +{
   1.302 +  struct timespec ts;
   1.303 +  int rv = clock_gettime(CLOCK_REALTIME, &ts);
   1.304 +
   1.305 +  if (rv == -1) {
   1.306 +    return 0;
   1.307 +  }
   1.308 +
   1.309 +  int mib[] = {
   1.310 +    CTL_KERN,
   1.311 +    KERN_PROC,
   1.312 +    KERN_PROC_PID,
   1.313 +    getpid(),
   1.314 +#if defined(__NetBSD__) || defined(__OpenBSD__)
   1.315 +    sizeof(KINFO_PROC),
   1.316 +    1,
   1.317 +#endif
   1.318 +  };
   1.319 +  u_int mibLen = sizeof(mib) / sizeof(mib[0]);
   1.320 +
   1.321 +  KINFO_PROC proc;
   1.322 +  size_t bufferSize = sizeof(proc);
   1.323 +  rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
   1.324 +
   1.325 +  if (rv == -1)
   1.326 +    return 0;
   1.327 +
   1.328 +  uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec)
   1.329 +    + (proc.KP_START_USEC * kNsPerUs);
   1.330 +  uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec;
   1.331 +
   1.332 +  if (startTime > now)
   1.333 +    return 0;
   1.334 +
   1.335 +  return (now - startTime) / kNsPerUs;
   1.336 +}
   1.337 +
   1.338 +#else
   1.339 +
   1.340 +uint64_t
   1.341 +TimeStamp::ComputeProcessUptime()
   1.342 +{
   1.343 +  return 0;
   1.344 +}
   1.345 +
   1.346 +#endif
   1.347 +
   1.348 +} // namespace mozilla

mercurial