1 | /* |
2 | * Copyright 2016-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/portability/Time.h> |
18 | |
19 | #include <folly/CPortability.h> |
20 | #include <folly/Likely.h> |
21 | |
22 | #include <assert.h> |
23 | |
24 | #include <chrono> |
25 | |
26 | template <typename _Rep, typename _Period> |
27 | static void duration_to_ts( |
28 | std::chrono::duration<_Rep, _Period> d, |
29 | struct timespec* ts) { |
30 | ts->tv_sec = |
31 | time_t(std::chrono::duration_cast<std::chrono::seconds>(d).count()); |
32 | ts->tv_nsec = long(std::chrono::duration_cast<std::chrono::nanoseconds>( |
33 | d % std::chrono::seconds(1)) |
34 | .count()); |
35 | } |
36 | |
37 | #if !FOLLY_HAVE_CLOCK_GETTIME || FOLLY_FORCE_CLOCK_GETTIME_DEFINITION |
38 | #if __MACH__ |
39 | #include <errno.h> |
40 | #include <mach/mach_init.h> // @manual |
41 | #include <mach/mach_port.h> // @manual |
42 | #include <mach/mach_time.h> // @manual |
43 | #include <mach/mach_types.h> // @manual |
44 | #include <mach/task.h> // @manual |
45 | #include <mach/thread_act.h> // @manual |
46 | #include <mach/vm_map.h> // @manual |
47 | |
48 | static std::chrono::nanoseconds time_value_to_ns(time_value_t t) { |
49 | return std::chrono::seconds(t.seconds) + |
50 | std::chrono::microseconds(t.microseconds); |
51 | } |
52 | |
53 | static int clock_process_cputime(struct timespec* ts) { |
54 | // Get CPU usage for live threads. |
55 | task_thread_times_info thread_times_info; |
56 | mach_msg_type_number_t thread_times_info_count = TASK_THREAD_TIMES_INFO_COUNT; |
57 | kern_return_t kern_result = task_info( |
58 | mach_task_self(), |
59 | TASK_THREAD_TIMES_INFO, |
60 | (thread_info_t)&thread_times_info, |
61 | &thread_times_info_count); |
62 | if (UNLIKELY(kern_result != KERN_SUCCESS)) { |
63 | return -1; |
64 | } |
65 | |
66 | // Get CPU usage for terminated threads. |
67 | mach_task_basic_info task_basic_info; |
68 | mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT; |
69 | kern_result = task_info( |
70 | mach_task_self(), |
71 | MACH_TASK_BASIC_INFO, |
72 | (thread_info_t)&task_basic_info, |
73 | &task_basic_info_count); |
74 | if (UNLIKELY(kern_result != KERN_SUCCESS)) { |
75 | return -1; |
76 | } |
77 | |
78 | auto cputime = time_value_to_ns(thread_times_info.user_time) + |
79 | time_value_to_ns(thread_times_info.system_time) + |
80 | time_value_to_ns(task_basic_info.user_time) + |
81 | time_value_to_ns(task_basic_info.system_time); |
82 | duration_to_ts(cputime, ts); |
83 | return 0; |
84 | } |
85 | |
86 | static int clock_thread_cputime(struct timespec* ts) { |
87 | mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; |
88 | thread_basic_info_data_t thread_info_data; |
89 | thread_act_t thread = mach_thread_self(); |
90 | kern_return_t kern_result = thread_info( |
91 | thread, THREAD_BASIC_INFO, (thread_info_t)&thread_info_data, &count); |
92 | mach_port_deallocate(mach_task_self(), thread); |
93 | if (UNLIKELY(kern_result != KERN_SUCCESS)) { |
94 | return -1; |
95 | } |
96 | auto cputime = time_value_to_ns(thread_info_data.system_time) + |
97 | time_value_to_ns(thread_info_data.user_time); |
98 | duration_to_ts(cputime, ts); |
99 | return 0; |
100 | } |
101 | |
102 | FOLLY_ATTR_WEAK int clock_gettime(clockid_t clk_id, struct timespec* ts) { |
103 | switch (clk_id) { |
104 | case CLOCK_REALTIME: { |
105 | auto now = std::chrono::system_clock::now().time_since_epoch(); |
106 | duration_to_ts(now, ts); |
107 | return 0; |
108 | } |
109 | case CLOCK_MONOTONIC: { |
110 | auto now = std::chrono::steady_clock::now().time_since_epoch(); |
111 | duration_to_ts(now, ts); |
112 | return 0; |
113 | } |
114 | case CLOCK_PROCESS_CPUTIME_ID: |
115 | return clock_process_cputime(ts); |
116 | case CLOCK_THREAD_CPUTIME_ID: |
117 | return clock_thread_cputime(ts); |
118 | default: |
119 | errno = EINVAL; |
120 | return -1; |
121 | } |
122 | } |
123 | |
124 | int clock_getres(clockid_t clk_id, struct timespec* ts) { |
125 | if (clk_id != CLOCK_MONOTONIC) { |
126 | return -1; |
127 | } |
128 | |
129 | static auto info = [] { |
130 | static mach_timebase_info_data_t info; |
131 | auto result = (mach_timebase_info(&info) == KERN_SUCCESS) ? &info : nullptr; |
132 | assert(result); |
133 | return result; |
134 | }(); |
135 | |
136 | ts->tv_sec = 0; |
137 | ts->tv_nsec = info->numer / info->denom; |
138 | |
139 | return 0; |
140 | } |
141 | #elif defined(_WIN32) |
142 | #include <errno.h> |
143 | #include <locale.h> |
144 | #include <stdint.h> |
145 | #include <stdlib.h> |
146 | |
147 | #include <folly/portability/Windows.h> |
148 | |
149 | using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>; |
150 | |
151 | static unsigned_nanos filetimeToUnsignedNanos(FILETIME ft) { |
152 | ULARGE_INTEGER i; |
153 | i.HighPart = ft.dwHighDateTime; |
154 | i.LowPart = ft.dwLowDateTime; |
155 | |
156 | // FILETIMEs are in units of 100ns. |
157 | return unsigned_nanos(i.QuadPart * 100); |
158 | }; |
159 | |
160 | static LARGE_INTEGER performanceFrequency() { |
161 | static auto result = [] { |
162 | LARGE_INTEGER freq; |
163 | // On Windows XP or later, this will never fail. |
164 | BOOL res = QueryPerformanceFrequency(&freq); |
165 | assert(res); |
166 | return freq; |
167 | }(); |
168 | return result; |
169 | } |
170 | |
171 | extern "C" int clock_getres(clockid_t clock_id, struct timespec* res) { |
172 | if (!res) { |
173 | errno = EFAULT; |
174 | return -1; |
175 | } |
176 | |
177 | static constexpr size_t kNsPerSec = 1000000000; |
178 | switch (clock_id) { |
179 | case CLOCK_REALTIME: { |
180 | constexpr auto perSec = double(std::chrono::system_clock::period::num) / |
181 | std::chrono::system_clock::period::den; |
182 | res->tv_sec = time_t(perSec); |
183 | res->tv_nsec = time_t(perSec * kNsPerSec); |
184 | return 0; |
185 | } |
186 | case CLOCK_MONOTONIC: { |
187 | constexpr auto perSec = double(std::chrono::steady_clock::period::num) / |
188 | std::chrono::steady_clock::period::den; |
189 | res->tv_sec = time_t(perSec); |
190 | res->tv_nsec = time_t(perSec * kNsPerSec); |
191 | return 0; |
192 | } |
193 | case CLOCK_PROCESS_CPUTIME_ID: |
194 | case CLOCK_THREAD_CPUTIME_ID: { |
195 | DWORD adj, timeIncrement; |
196 | BOOL adjDisabled; |
197 | if (!GetSystemTimeAdjustment(&adj, &timeIncrement, &adjDisabled)) { |
198 | errno = EINVAL; |
199 | return -1; |
200 | } |
201 | |
202 | res->tv_sec = 0; |
203 | res->tv_nsec = long(timeIncrement * 100); |
204 | return 0; |
205 | } |
206 | |
207 | default: |
208 | errno = EINVAL; |
209 | return -1; |
210 | } |
211 | } |
212 | |
213 | extern "C" int clock_gettime(clockid_t clock_id, struct timespec* tp) { |
214 | if (!tp) { |
215 | errno = EFAULT; |
216 | return -1; |
217 | } |
218 | |
219 | const auto unanosToTimespec = [](timespec* tp, unsigned_nanos t) -> int { |
220 | static constexpr unsigned_nanos one_sec{std::chrono::seconds(1)}; |
221 | tp->tv_sec = |
222 | time_t(std::chrono::duration_cast<std::chrono::seconds>(t).count()); |
223 | tp->tv_nsec = long((t % one_sec).count()); |
224 | return 0; |
225 | }; |
226 | |
227 | FILETIME createTime, exitTime, kernalTime, userTime; |
228 | switch (clock_id) { |
229 | case CLOCK_REALTIME: { |
230 | auto now = std::chrono::system_clock::now().time_since_epoch(); |
231 | duration_to_ts(now, tp); |
232 | return 0; |
233 | } |
234 | case CLOCK_MONOTONIC: { |
235 | auto now = std::chrono::steady_clock::now().time_since_epoch(); |
236 | duration_to_ts(now, tp); |
237 | return 0; |
238 | } |
239 | case CLOCK_PROCESS_CPUTIME_ID: { |
240 | if (!GetProcessTimes( |
241 | GetCurrentProcess(), |
242 | &createTime, |
243 | &exitTime, |
244 | &kernalTime, |
245 | &userTime)) { |
246 | errno = EINVAL; |
247 | return -1; |
248 | } |
249 | |
250 | return unanosToTimespec( |
251 | tp, |
252 | filetimeToUnsignedNanos(kernalTime) + |
253 | filetimeToUnsignedNanos(userTime)); |
254 | } |
255 | case CLOCK_THREAD_CPUTIME_ID: { |
256 | if (!GetThreadTimes( |
257 | GetCurrentThread(), |
258 | &createTime, |
259 | &exitTime, |
260 | &kernalTime, |
261 | &userTime)) { |
262 | errno = EINVAL; |
263 | return -1; |
264 | } |
265 | |
266 | return unanosToTimespec( |
267 | tp, |
268 | filetimeToUnsignedNanos(kernalTime) + |
269 | filetimeToUnsignedNanos(userTime)); |
270 | } |
271 | |
272 | default: |
273 | errno = EINVAL; |
274 | return -1; |
275 | } |
276 | } |
277 | #else |
278 | #error No clock_gettime(3) compatibility wrapper available for this platform. |
279 | #endif |
280 | #endif |
281 | |
282 | #ifdef _WIN32 |
283 | #include <iomanip> |
284 | #include <sstream> |
285 | |
286 | #include <folly/portability/Windows.h> |
287 | |
288 | extern "C" { |
289 | char* asctime_r(const tm* tm, char* buf) { |
290 | char tmpBuf[64]; |
291 | if (asctime_s(tmpBuf, tm)) { |
292 | return nullptr; |
293 | } |
294 | // Nothing we can do if the buff is to small :( |
295 | return strcpy(buf, tmpBuf); |
296 | } |
297 | |
298 | char* ctime_r(const time_t* t, char* buf) { |
299 | char tmpBuf[64]; |
300 | if (ctime_s(tmpBuf, 64, t)) { |
301 | return nullptr; |
302 | } |
303 | // Nothing we can do if the buff is to small :( |
304 | return strcpy(buf, tmpBuf); |
305 | } |
306 | |
307 | tm* gmtime_r(const time_t* t, tm* res) { |
308 | if (!gmtime_s(res, t)) { |
309 | return res; |
310 | } |
311 | return nullptr; |
312 | } |
313 | |
314 | tm* localtime_r(const time_t* t, tm* o) { |
315 | if (!localtime_s(o, t)) { |
316 | return o; |
317 | } |
318 | return nullptr; |
319 | } |
320 | |
321 | int nanosleep(const struct timespec* request, struct timespec* remain) { |
322 | Sleep((DWORD)((request->tv_sec * 1000) + (request->tv_nsec / 1000000))); |
323 | if (remain != nullptr) { |
324 | remain->tv_nsec = 0; |
325 | remain->tv_sec = 0; |
326 | } |
327 | return 0; |
328 | } |
329 | |
330 | char* strptime( |
331 | const char* __restrict s, |
332 | const char* __restrict f, |
333 | struct tm* __restrict tm) { |
334 | // Isn't the C++ standard lib nice? std::get_time is defined such that its |
335 | // format parameters are the exact same as strptime. Of course, we have to |
336 | // create a string stream first, and imbue it with the current C locale, and |
337 | // we also have to make sure we return the right things if it fails, or |
338 | // if it succeeds, but this is still far simpler an implementation than any |
339 | // of the versions in any of the C standard libraries. |
340 | std::istringstream input(s); |
341 | input.imbue(std::locale(setlocale(LC_ALL, nullptr))); |
342 | input >> std::get_time(tm, f); |
343 | if (input.fail()) { |
344 | return nullptr; |
345 | } |
346 | return const_cast<char*>(s + input.tellg()); |
347 | } |
348 | } |
349 | #endif |
350 | |