tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TimeStamp_posix.cpp (7638B)


      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__) || defined(__NetBSD__) || \
     22    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 
     53 #if !defined(__wasi__)
     54 #  include <pthread.h>
     55 #endif
     56 
     57 #ifdef CLOCK_MONOTONIC_COARSE
     58 static bool sSupportsMonotonicCoarseClock = false;
     59 #endif
     60 
     61 #if !defined(__wasi__)
     62 static const uint16_t kNsPerUs = 1000;
     63 #endif
     64 
     65 static const uint64_t kNsPerSec = 1000000000;
     66 static const double kNsPerMsd = 1000000.0;
     67 static const double kNsPerSecd = 1000000000.0;
     68 
     69 static uint64_t TimespecToNs(const struct timespec& aTs) {
     70  uint64_t baseNs = uint64_t(aTs.tv_sec) * kNsPerSec;
     71  return baseNs + uint64_t(aTs.tv_nsec);
     72 }
     73 
     74 static uint64_t ClockTimeNs(const clockid_t aClockId = CLOCK_MONOTONIC) {
     75  struct timespec ts;
     76 #ifdef CLOCK_MONOTONIC_COARSE
     77  MOZ_RELEASE_ASSERT(
     78      aClockId == CLOCK_MONOTONIC ||
     79      (sSupportsMonotonicCoarseClock && aClockId == CLOCK_MONOTONIC_COARSE));
     80 #else
     81  MOZ_RELEASE_ASSERT(aClockId == CLOCK_MONOTONIC);
     82 #endif
     83  // this can't fail: we know &ts is valid, and TimeStamp::Startup()
     84  // checks that CLOCK_MONOTONIC / CLOCK_MONOTONIC_COARSE are
     85  // supported (and aborts if the former is not).
     86  clock_gettime(aClockId, &ts);
     87 
     88  // tv_sec is defined to be relative to an arbitrary point in time,
     89  // but it would be madness for that point in time to be earlier than
     90  // the Epoch.  So we can safely assume that even if time_t is 32
     91  // bits, tv_sec won't overflow while the browser is open.  Revisit
     92  // this argument if we're still building with 32-bit time_t around
     93  // the year 2037.
     94  return TimespecToNs(ts);
     95 }
     96 
     97 namespace mozilla {
     98 
     99 double BaseTimeDurationPlatformUtils::ToSeconds(int64_t aTicks) {
    100  return double(aTicks) / kNsPerSecd;
    101 }
    102 
    103 int64_t BaseTimeDurationPlatformUtils::TicksFromMilliseconds(
    104    double aMilliseconds) {
    105  double result = aMilliseconds * kNsPerMsd;
    106  // NOTE: this MUST be a >= test, because int64_t(double(INT64_MAX))
    107  // overflows and gives INT64_MIN.
    108  if (result >= double(INT64_MAX)) {
    109    return INT64_MAX;
    110  }
    111  if (result <= INT64_MIN) {
    112    return INT64_MIN;
    113  }
    114 
    115  return result;
    116 }
    117 
    118 static bool gInitialized = false;
    119 
    120 void TimeStamp::Startup() {
    121  if (gInitialized) {
    122    return;
    123  }
    124 
    125  struct timespec dummy;
    126  if (clock_gettime(CLOCK_MONOTONIC, &dummy) != 0) {
    127    MOZ_CRASH("CLOCK_MONOTONIC is absent!");
    128  }
    129 
    130 #ifdef CLOCK_MONOTONIC_COARSE
    131  if (clock_gettime(CLOCK_MONOTONIC_COARSE, &dummy) == 0) {
    132    sSupportsMonotonicCoarseClock = true;
    133  }
    134 #endif
    135 
    136  gInitialized = true;
    137 }
    138 
    139 void TimeStamp::Shutdown() {}
    140 
    141 TimeStamp TimeStamp::Now(bool aHighResolution) {
    142 #ifdef CLOCK_MONOTONIC_COARSE
    143  if (!aHighResolution && sSupportsMonotonicCoarseClock) {
    144    return TimeStamp(ClockTimeNs(CLOCK_MONOTONIC_COARSE));
    145  }
    146 #endif
    147  return TimeStamp(ClockTimeNs(CLOCK_MONOTONIC));
    148 }
    149 
    150 #if defined(XP_LINUX) || defined(ANDROID)
    151 
    152 // Calculates the amount of jiffies that have elapsed since boot and up to the
    153 // starttime value of a specific process as found in its /proc/*/stat file.
    154 // Returns 0 if an error occurred.
    155 
    156 static uint64_t JiffiesSinceBoot(const char* aFile) {
    157  char stat[512];
    158 
    159  FILE* f = fopen(aFile, "r");
    160  if (!f) {
    161    return 0;
    162  }
    163 
    164  int n = fread(&stat, 1, sizeof(stat) - 1, f);
    165 
    166  fclose(f);
    167 
    168  if (n <= 0) {
    169    return 0;
    170  }
    171 
    172  stat[n] = 0;
    173 
    174  long long unsigned startTime = 0;  // instead of uint64_t to keep GCC quiet
    175  char* s = strrchr(stat, ')');
    176 
    177  if (!s) {
    178    return 0;
    179  }
    180 
    181  int rv = sscanf(s + 2,
    182                  "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
    183                  "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu",
    184                  &startTime);
    185 
    186  if (rv != 1 || !startTime) {
    187    return 0;
    188  }
    189 
    190  return startTime;
    191 }
    192 
    193 // Computes the interval that has elapsed between the thread creation and the
    194 // process creation by comparing the starttime fields in the respective
    195 // /proc/*/stat files. The resulting value will be a good approximation of the
    196 // process uptime. This value will be stored at the address pointed by aTime;
    197 // if an error occurred 0 will be stored instead.
    198 
    199 static void* ComputeProcessUptimeThread(void* aTime) {
    200  uint64_t* uptime = static_cast<uint64_t*>(aTime);
    201  long hz = sysconf(_SC_CLK_TCK);
    202 
    203  *uptime = 0;
    204 
    205  if (!hz) {
    206    return nullptr;
    207  }
    208 
    209  char threadStat[40];
    210  SprintfLiteral(threadStat, "/proc/self/task/%d/stat",
    211                 (pid_t)syscall(__NR_gettid));
    212 
    213  uint64_t threadJiffies = JiffiesSinceBoot(threadStat);
    214  uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat");
    215 
    216  if (!threadJiffies || !selfJiffies) {
    217    return nullptr;
    218  }
    219 
    220  *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz;
    221  return nullptr;
    222 }
    223 
    224 // Computes and returns the process uptime in us on Linux & its derivatives.
    225 // Returns 0 if an error was encountered.
    226 
    227 uint64_t TimeStamp::ComputeProcessUptime() {
    228  uint64_t uptime = 0;
    229  pthread_t uptime_pthread;
    230 
    231  if (pthread_create(&uptime_pthread, nullptr, ComputeProcessUptimeThread,
    232                     &uptime)) {
    233    MOZ_CRASH("Failed to create process uptime thread.");
    234    return 0;
    235  }
    236 
    237  pthread_join(uptime_pthread, NULL);
    238 
    239  return uptime / kNsPerUs;
    240 }
    241 
    242 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
    243    defined(__OpenBSD__)
    244 
    245 // Computes and returns the process uptime in us on various BSD flavors.
    246 // Returns 0 if an error was encountered.
    247 
    248 uint64_t TimeStamp::ComputeProcessUptime() {
    249  struct timespec ts;
    250  int rv = clock_gettime(CLOCK_REALTIME, &ts);
    251 
    252  if (rv == -1) {
    253    return 0;
    254  }
    255 
    256  int mib[] = {
    257      // clang-format off
    258      CTL_KERN,
    259      KERN_PROC,
    260      KERN_PROC_PID,
    261      getpid(),
    262 #  if defined(__NetBSD__) || defined(__OpenBSD__)
    263      sizeof(KINFO_PROC),
    264      1,
    265 #  endif
    266      // clang-format on
    267  };
    268  u_int mibLen = sizeof(mib) / sizeof(mib[0]);
    269 
    270  KINFO_PROC proc;
    271  size_t bufferSize = sizeof(proc);
    272  rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0);
    273 
    274  if (rv == -1) {
    275    return 0;
    276  }
    277 
    278  uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) +
    279                       (proc.KP_START_USEC * kNsPerUs);
    280  uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec;
    281 
    282  if (startTime > now) {
    283    return 0;
    284  }
    285 
    286  return (now - startTime) / kNsPerUs;
    287 }
    288 
    289 #else
    290 
    291 uint64_t TimeStamp::ComputeProcessUptime() { return 0; }
    292 
    293 #endif
    294 
    295 }  // namespace mozilla