michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Implement TimeStamp::Now() with POSIX clocks. michael@0: // michael@0: // The "tick" unit for POSIX clocks is simply a nanosecond, as this is michael@0: // the smallest unit of time representable by struct timespec. That michael@0: // doesn't mean that a nanosecond is the resolution of TimeDurations michael@0: // obtained with this API; see TimeDuration::Resolution; michael@0: // michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #if defined(__DragonFly__) || defined(__FreeBSD__) \ michael@0: || defined(__NetBSD__) || defined(__OpenBSD__) michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(__DragonFly__) || defined(__FreeBSD__) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(__NetBSD__) michael@0: #undef KERN_PROC michael@0: #define KERN_PROC KERN_PROC2 michael@0: #define KINFO_PROC struct kinfo_proc2 michael@0: #else michael@0: #define KINFO_PROC struct kinfo_proc michael@0: #endif michael@0: michael@0: #if defined(__DragonFly__) michael@0: #define KP_START_SEC kp_start.tv_sec michael@0: #define KP_START_USEC kp_start.tv_usec michael@0: #elif defined(__FreeBSD__) michael@0: #define KP_START_SEC ki_start.tv_sec michael@0: #define KP_START_USEC ki_start.tv_usec michael@0: #else michael@0: #define KP_START_SEC p_ustart_sec michael@0: #define KP_START_USEC p_ustart_usec michael@0: #endif michael@0: michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "nsCRT.h" michael@0: #include "prprf.h" michael@0: #include "prthread.h" michael@0: #include "nsDebug.h" michael@0: michael@0: // Estimate of the smallest duration of time we can measure. michael@0: static uint64_t sResolution; michael@0: static uint64_t sResolutionSigDigs; michael@0: michael@0: static const uint16_t kNsPerUs = 1000; michael@0: static const uint64_t kNsPerMs = 1000000; michael@0: static const uint64_t kNsPerSec = 1000000000; michael@0: static const double kNsPerMsd = 1000000.0; michael@0: static const double kNsPerSecd = 1000000000.0; michael@0: michael@0: static uint64_t michael@0: TimespecToNs(const struct timespec& ts) michael@0: { michael@0: uint64_t baseNs = uint64_t(ts.tv_sec) * kNsPerSec; michael@0: return baseNs + uint64_t(ts.tv_nsec); michael@0: } michael@0: michael@0: static uint64_t michael@0: ClockTimeNs() michael@0: { michael@0: struct timespec ts; michael@0: // this can't fail: we know &ts is valid, and TimeStamp::Startup() michael@0: // checks that CLOCK_MONOTONIC is supported (and aborts if not) michael@0: clock_gettime(CLOCK_MONOTONIC, &ts); michael@0: michael@0: // tv_sec is defined to be relative to an arbitrary point in time, michael@0: // but it would be madness for that point in time to be earlier than michael@0: // the Epoch. So we can safely assume that even if time_t is 32 michael@0: // bits, tv_sec won't overflow while the browser is open. Revisit michael@0: // this argument if we're still building with 32-bit time_t around michael@0: // the year 2037. michael@0: return TimespecToNs(ts); michael@0: } michael@0: michael@0: static uint64_t michael@0: ClockResolutionNs() michael@0: { michael@0: // NB: why not rely on clock_getres()? Two reasons: (i) it might michael@0: // lie, and (ii) it might return an "ideal" resolution that while michael@0: // theoretically true, could never be measured in practice. Since michael@0: // clock_gettime() likely involves a system call on your platform, michael@0: // the "actual" timing resolution shouldn't be lower than syscall michael@0: // overhead. michael@0: michael@0: uint64_t start = ClockTimeNs(); michael@0: uint64_t end = ClockTimeNs(); michael@0: uint64_t minres = (end - start); michael@0: michael@0: // 10 total trials is arbitrary: what we're trying to avoid by michael@0: // looping is getting unlucky and being interrupted by a context michael@0: // switch or signal, or being bitten by paging/cache effects michael@0: for (int i = 0; i < 9; ++i) { michael@0: start = ClockTimeNs(); michael@0: end = ClockTimeNs(); michael@0: michael@0: uint64_t candidate = (start - end); michael@0: if (candidate < minres) michael@0: minres = candidate; michael@0: } michael@0: michael@0: if (0 == minres) { michael@0: // measurable resolution is either incredibly low, ~1ns, or very michael@0: // high. fall back on clock_getres() michael@0: struct timespec ts; michael@0: if (0 == clock_getres(CLOCK_MONOTONIC, &ts)) { michael@0: minres = TimespecToNs(ts); michael@0: } michael@0: } michael@0: michael@0: if (0 == minres) { michael@0: // clock_getres probably failed. fall back on NSPR's resolution michael@0: // assumption michael@0: minres = 1 * kNsPerMs; michael@0: } michael@0: michael@0: return minres; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: double michael@0: TimeDuration::ToSeconds() const michael@0: { michael@0: return double(mValue) / kNsPerSecd; michael@0: } michael@0: michael@0: double michael@0: TimeDuration::ToSecondsSigDigits() const michael@0: { michael@0: // don't report a value < mResolution ... michael@0: int64_t valueSigDigs = sResolution * (mValue / sResolution); michael@0: // and chop off insignificant digits michael@0: valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs); michael@0: return double(valueSigDigs) / kNsPerSecd; michael@0: } michael@0: michael@0: TimeDuration michael@0: TimeDuration::FromMilliseconds(double aMilliseconds) michael@0: { michael@0: return TimeDuration::FromTicks(aMilliseconds * kNsPerMsd); michael@0: } michael@0: michael@0: TimeDuration michael@0: TimeDuration::Resolution() michael@0: { michael@0: return TimeDuration::FromTicks(int64_t(sResolution)); michael@0: } michael@0: michael@0: static bool gInitialized = false; michael@0: michael@0: nsresult michael@0: TimeStamp::Startup() michael@0: { michael@0: if (gInitialized) michael@0: return NS_OK; michael@0: michael@0: struct timespec dummy; michael@0: if (0 != clock_gettime(CLOCK_MONOTONIC, &dummy)) michael@0: NS_RUNTIMEABORT("CLOCK_MONOTONIC is absent!"); michael@0: michael@0: sResolution = ClockResolutionNs(); michael@0: michael@0: // find the number of significant digits in sResolution, for the michael@0: // sake of ToSecondsSigDigits() michael@0: for (sResolutionSigDigs = 1; michael@0: !(sResolutionSigDigs == sResolution michael@0: || 10*sResolutionSigDigs > sResolution); michael@0: sResolutionSigDigs *= 10); michael@0: michael@0: gInitialized = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: TimeStamp::Shutdown() michael@0: { michael@0: } michael@0: michael@0: TimeStamp michael@0: TimeStamp::Now(bool aHighResolution) michael@0: { michael@0: return TimeStamp(ClockTimeNs()); michael@0: } michael@0: michael@0: #if defined(LINUX) || defined(ANDROID) michael@0: michael@0: // Calculates the amount of jiffies that have elapsed since boot and up to the michael@0: // starttime value of a specific process as found in its /proc/*/stat file. michael@0: // Returns 0 if an error occurred. michael@0: michael@0: static uint64_t michael@0: JiffiesSinceBoot(const char *aFile) michael@0: { michael@0: char stat[512]; michael@0: michael@0: FILE *f = fopen(aFile, "r"); michael@0: if (!f) michael@0: return 0; michael@0: michael@0: int n = fread(&stat, 1, sizeof(stat) - 1, f); michael@0: michael@0: fclose(f); michael@0: michael@0: if (n <= 0) michael@0: return 0; michael@0: michael@0: stat[n] = 0; michael@0: michael@0: long long unsigned startTime = 0; // instead of uint64_t to keep GCC quiet michael@0: char *s = strrchr(stat, ')'); michael@0: michael@0: if (!s) michael@0: return 0; michael@0: michael@0: int rv = sscanf(s + 2, michael@0: "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u " michael@0: "%*u %*u %*u %*d %*d %*d %*d %*d %*d %llu", michael@0: &startTime); michael@0: michael@0: if (rv != 1 || !startTime) michael@0: return 0; michael@0: michael@0: return startTime; michael@0: } michael@0: michael@0: // Computes the interval that has elapsed between the thread creation and the michael@0: // process creation by comparing the starttime fields in the respective michael@0: // /proc/*/stat files. The resulting value will be a good approximation of the michael@0: // process uptime. This value will be stored at the address pointed by aTime; michael@0: // if an error occurred 0 will be stored instead. michael@0: michael@0: static void michael@0: ComputeProcessUptimeThread(void *aTime) michael@0: { michael@0: uint64_t *uptime = static_cast(aTime); michael@0: long hz = sysconf(_SC_CLK_TCK); michael@0: michael@0: *uptime = 0; michael@0: michael@0: if (!hz) michael@0: return; michael@0: michael@0: char threadStat[40]; michael@0: sprintf(threadStat, "/proc/self/task/%d/stat", (pid_t) syscall(__NR_gettid)); michael@0: michael@0: uint64_t threadJiffies = JiffiesSinceBoot(threadStat); michael@0: uint64_t selfJiffies = JiffiesSinceBoot("/proc/self/stat"); michael@0: michael@0: if (!threadJiffies || !selfJiffies) michael@0: return; michael@0: michael@0: *uptime = ((threadJiffies - selfJiffies) * kNsPerSec) / hz; michael@0: } michael@0: michael@0: // Computes and returns the process uptime in us on Linux & its derivatives. michael@0: // Returns 0 if an error was encountered. michael@0: michael@0: uint64_t michael@0: TimeStamp::ComputeProcessUptime() michael@0: { michael@0: uint64_t uptime = 0; michael@0: PRThread *thread = PR_CreateThread(PR_USER_THREAD, michael@0: ComputeProcessUptimeThread, michael@0: &uptime, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: michael@0: PR_JoinThread(thread); michael@0: michael@0: return uptime / kNsPerUs; michael@0: } michael@0: michael@0: #elif defined(__DragonFly__) || defined(__FreeBSD__) \ michael@0: || defined(__NetBSD__) || defined(__OpenBSD__) michael@0: michael@0: // Computes and returns the process uptime in us on various BSD flavors. michael@0: // Returns 0 if an error was encountered. michael@0: michael@0: uint64_t michael@0: TimeStamp::ComputeProcessUptime() michael@0: { michael@0: struct timespec ts; michael@0: int rv = clock_gettime(CLOCK_REALTIME, &ts); michael@0: michael@0: if (rv == -1) { michael@0: return 0; michael@0: } michael@0: michael@0: int mib[] = { michael@0: CTL_KERN, michael@0: KERN_PROC, michael@0: KERN_PROC_PID, michael@0: getpid(), michael@0: #if defined(__NetBSD__) || defined(__OpenBSD__) michael@0: sizeof(KINFO_PROC), michael@0: 1, michael@0: #endif michael@0: }; michael@0: u_int mibLen = sizeof(mib) / sizeof(mib[0]); michael@0: michael@0: KINFO_PROC proc; michael@0: size_t bufferSize = sizeof(proc); michael@0: rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0); michael@0: michael@0: if (rv == -1) michael@0: return 0; michael@0: michael@0: uint64_t startTime = ((uint64_t)proc.KP_START_SEC * kNsPerSec) michael@0: + (proc.KP_START_USEC * kNsPerUs); michael@0: uint64_t now = ((uint64_t)ts.tv_sec * kNsPerSec) + ts.tv_nsec; michael@0: michael@0: if (startTime > now) michael@0: return 0; michael@0: michael@0: return (now - startTime) / kNsPerUs; michael@0: } michael@0: michael@0: #else michael@0: michael@0: uint64_t michael@0: TimeStamp::ComputeProcessUptime() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: } // namespace mozilla