|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
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 mach_absolute_time |
|
9 // |
|
10 // The "tick" unit for mach_absolute_time is defined using mach_timebase_info() which |
|
11 // gives a conversion ratio to nanoseconds. For more information see Apple's QA1398. |
|
12 // |
|
13 // This code is inspired by Chromium's time_mac.cc. The biggest |
|
14 // differences are that we explicitly initialize using |
|
15 // TimeStamp::Initialize() instead of lazily in Now() and that |
|
16 // we store the time value in ticks and convert when needed instead |
|
17 // of storing the time value in nanoseconds. |
|
18 |
|
19 #include <mach/mach_time.h> |
|
20 #include <sys/time.h> |
|
21 #include <sys/sysctl.h> |
|
22 #include <time.h> |
|
23 #include <unistd.h> |
|
24 |
|
25 #include "mozilla/TimeStamp.h" |
|
26 #include "nsDebug.h" |
|
27 |
|
28 // Estimate of the smallest duration of time we can measure. |
|
29 static uint64_t sResolution; |
|
30 static uint64_t sResolutionSigDigs; |
|
31 |
|
32 static const uint64_t kNsPerMs = 1000000; |
|
33 static const uint64_t kUsPerSec = 1000000; |
|
34 static const double kNsPerMsd = 1000000.0; |
|
35 static const double kNsPerSecd = 1000000000.0; |
|
36 |
|
37 static bool gInitialized = false; |
|
38 static double sNsPerTick; |
|
39 |
|
40 static uint64_t |
|
41 ClockTime() |
|
42 { |
|
43 // mach_absolute_time is it when it comes to ticks on the Mac. Other calls |
|
44 // with less precision (such as TickCount) just call through to |
|
45 // mach_absolute_time. |
|
46 // |
|
47 // At the time of writing mach_absolute_time returns the number of nanoseconds |
|
48 // since boot. This won't overflow 64bits for 500+ years so we aren't going |
|
49 // to worry about that possiblity |
|
50 return mach_absolute_time(); |
|
51 } |
|
52 |
|
53 static uint64_t |
|
54 ClockResolutionNs() |
|
55 { |
|
56 uint64_t start = ClockTime(); |
|
57 uint64_t end = ClockTime(); |
|
58 uint64_t minres = (end - start); |
|
59 |
|
60 // 10 total trials is arbitrary: what we're trying to avoid by |
|
61 // looping is getting unlucky and being interrupted by a context |
|
62 // switch or signal, or being bitten by paging/cache effects |
|
63 for (int i = 0; i < 9; ++i) { |
|
64 start = ClockTime(); |
|
65 end = ClockTime(); |
|
66 |
|
67 uint64_t candidate = (start - end); |
|
68 if (candidate < minres) |
|
69 minres = candidate; |
|
70 } |
|
71 |
|
72 if (0 == minres) { |
|
73 // measurable resolution is either incredibly low, ~1ns, or very |
|
74 // high. fall back on NSPR's resolution assumption |
|
75 minres = 1 * kNsPerMs; |
|
76 } |
|
77 |
|
78 return minres; |
|
79 } |
|
80 |
|
81 namespace mozilla { |
|
82 |
|
83 double |
|
84 TimeDuration::ToSeconds() const |
|
85 { |
|
86 NS_ABORT_IF_FALSE(gInitialized, "calling TimeDuration too early"); |
|
87 return (mValue * sNsPerTick) / kNsPerSecd; |
|
88 } |
|
89 |
|
90 double |
|
91 TimeDuration::ToSecondsSigDigits() const |
|
92 { |
|
93 NS_ABORT_IF_FALSE(gInitialized, "calling TimeDuration too early"); |
|
94 // don't report a value < mResolution ... |
|
95 int64_t valueSigDigs = sResolution * (mValue / sResolution); |
|
96 // and chop off insignificant digits |
|
97 valueSigDigs = sResolutionSigDigs * (valueSigDigs / sResolutionSigDigs); |
|
98 return (valueSigDigs * sNsPerTick) / kNsPerSecd; |
|
99 } |
|
100 |
|
101 TimeDuration |
|
102 TimeDuration::FromMilliseconds(double aMilliseconds) |
|
103 { |
|
104 NS_ABORT_IF_FALSE(gInitialized, "calling TimeDuration too early"); |
|
105 return TimeDuration::FromTicks(int64_t((aMilliseconds * kNsPerMsd) / sNsPerTick)); |
|
106 } |
|
107 |
|
108 TimeDuration |
|
109 TimeDuration::Resolution() |
|
110 { |
|
111 NS_ABORT_IF_FALSE(gInitialized, "calling TimeDuration too early"); |
|
112 return TimeDuration::FromTicks(int64_t(sResolution)); |
|
113 } |
|
114 |
|
115 nsresult |
|
116 TimeStamp::Startup() |
|
117 { |
|
118 if (gInitialized) |
|
119 return NS_OK; |
|
120 |
|
121 mach_timebase_info_data_t timebaseInfo; |
|
122 // Apple's QA1398 suggests that the output from mach_timebase_info |
|
123 // will not change while a program is running, so it should be safe |
|
124 // to cache the result. |
|
125 kern_return_t kr = mach_timebase_info(&timebaseInfo); |
|
126 if (kr != KERN_SUCCESS) |
|
127 NS_RUNTIMEABORT("mach_timebase_info failed"); |
|
128 |
|
129 sNsPerTick = double(timebaseInfo.numer) / timebaseInfo.denom; |
|
130 |
|
131 sResolution = ClockResolutionNs(); |
|
132 |
|
133 // find the number of significant digits in sResolution, for the |
|
134 // sake of ToSecondsSigDigits() |
|
135 for (sResolutionSigDigs = 1; |
|
136 !(sResolutionSigDigs == sResolution |
|
137 || 10*sResolutionSigDigs > sResolution); |
|
138 sResolutionSigDigs *= 10); |
|
139 |
|
140 gInitialized = true; |
|
141 |
|
142 return NS_OK; |
|
143 } |
|
144 |
|
145 void |
|
146 TimeStamp::Shutdown() |
|
147 { |
|
148 } |
|
149 |
|
150 TimeStamp |
|
151 TimeStamp::Now(bool aHighResolution) |
|
152 { |
|
153 return TimeStamp(ClockTime()); |
|
154 } |
|
155 |
|
156 // Computes and returns the process uptime in microseconds. |
|
157 // Returns 0 if an error was encountered. |
|
158 |
|
159 uint64_t |
|
160 TimeStamp::ComputeProcessUptime() |
|
161 { |
|
162 struct timeval tv; |
|
163 int rv = gettimeofday(&tv, nullptr); |
|
164 |
|
165 if (rv == -1) { |
|
166 return 0; |
|
167 } |
|
168 |
|
169 int mib[] = { |
|
170 CTL_KERN, |
|
171 KERN_PROC, |
|
172 KERN_PROC_PID, |
|
173 getpid(), |
|
174 }; |
|
175 u_int mibLen = sizeof(mib) / sizeof(mib[0]); |
|
176 |
|
177 struct kinfo_proc proc; |
|
178 size_t bufferSize = sizeof(proc); |
|
179 rv = sysctl(mib, mibLen, &proc, &bufferSize, nullptr, 0); |
|
180 |
|
181 if (rv == -1) |
|
182 return 0; |
|
183 |
|
184 uint64_t startTime = |
|
185 ((uint64_t)proc.kp_proc.p_un.__p_starttime.tv_sec * kUsPerSec) + |
|
186 proc.kp_proc.p_un.__p_starttime.tv_usec; |
|
187 uint64_t now = (tv.tv_sec * kUsPerSec) + tv.tv_usec; |
|
188 |
|
189 if (startTime > now) |
|
190 return 0; |
|
191 |
|
192 return now - startTime; |
|
193 } |
|
194 |
|
195 } // namespace mozilla |