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
26template <typename _Rep, typename _Period>
27static 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
48static 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
53static 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
86static 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
102FOLLY_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
124int 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
149using unsigned_nanos = std::chrono::duration<uint64_t, std::nano>;
150
151static 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
160static 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
171extern "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
213extern "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
288extern "C" {
289char* 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
298char* 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
307tm* gmtime_r(const time_t* t, tm* res) {
308 if (!gmtime_s(res, t)) {
309 return res;
310 }
311 return nullptr;
312}
313
314tm* localtime_r(const time_t* t, tm* o) {
315 if (!localtime_s(o, t)) {
316 return o;
317 }
318 return nullptr;
319}
320
321int 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
330char* 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