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