/Users/deen/code/yugabyte-db/src/yb/gutil/walltime.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2012 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // The following only applies to changes made to this file as part of YugaByte development. |
4 | | // |
5 | | // Portions Copyright (c) YugaByte, Inc. |
6 | | // |
7 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
8 | | // in compliance with the License. You may obtain a copy of the License at |
9 | | // |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // |
12 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
13 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
14 | | // or implied. See the License for the specific language governing permissions and limitations |
15 | | // under the License. |
16 | | // |
17 | | // Licensed to the Apache Software Foundation (ASF) under one |
18 | | // or more contributor license agreements. See the NOTICE file |
19 | | // distributed with this work for additional information |
20 | | // regarding copyright ownership. The ASF licenses this file |
21 | | // to you under the Apache License, Version 2.0 (the |
22 | | // "License"); you may not use this file except in compliance |
23 | | // with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, |
28 | | // software distributed under the License is distributed on an |
29 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
30 | | // KIND, either express or implied. See the License for the |
31 | | // specific language governing permissions and limitations |
32 | | // under the License. |
33 | | // |
34 | | // Author: tkaftal@google.com (Tomasz Kaftal) |
35 | | // |
36 | | // The implementation of walltime functionalities. |
37 | | #ifndef _GNU_SOURCE // gcc3 at least defines it on the command line |
38 | | #define _GNU_SOURCE // Linux wants that for strptime in time.h |
39 | | #endif |
40 | | |
41 | | #include "yb/gutil/walltime.h" |
42 | | |
43 | | #if defined(__APPLE__) |
44 | | #include <mach/clock.h> |
45 | | #include <mach/mach.h> |
46 | | #endif // defined(__APPLE__) |
47 | | |
48 | | #include <memory> |
49 | | |
50 | | #include <glog/logging.h> |
51 | | |
52 | | #if defined(__APPLE__) |
53 | | namespace walltime_internal { |
54 | | |
55 | | GoogleOnceType timebase_info_once = GOOGLE_ONCE_INIT; |
56 | | mach_timebase_info_data_t timebase_info; |
57 | | |
58 | 13.4k | void InitializeTimebaseInfo() { |
59 | 0 | CHECK_EQ(KERN_SUCCESS, mach_timebase_info(&timebase_info)) |
60 | 0 | << "unable to initialize mach_timebase_info"; |
61 | 13.4k | } |
62 | | } // namespace walltime_internal |
63 | | #endif |
64 | | |
65 | | // This is exactly like mktime() except it is guaranteed to return -1 on |
66 | | // failure. Some versions of glibc allow mktime() to return negative |
67 | | // values which the standard says are undefined. See the standard at |
68 | | // http://www.opengroup.org/onlinepubs/007904875/basedefs/xbd_chap04.html |
69 | | // under the heading "Seconds Since the Epoch". |
70 | 0 | static inline time_t gmktime(struct tm *tm) { |
71 | 0 | time_t rt = mktime(tm); |
72 | 0 | return rt < 0 ? time_t(-1) : rt; |
73 | 0 | } |
74 | | |
75 | | void StringAppendStrftime(std::string* dst, |
76 | | const char* format, |
77 | 30.6k | const struct tm* tm) { |
78 | 30.6k | char space[1024]; |
79 | | |
80 | 30.6k | size_t result = strftime(space, sizeof(space), format, tm); |
81 | | |
82 | 30.6k | if (result > 0) { |
83 | | // It fit |
84 | 30.6k | dst->append(space, result); |
85 | 30.6k | return; |
86 | 30.6k | } |
87 | | |
88 | 0 | size_t length = sizeof(space); |
89 | 0 | for (int sanity = 0; sanity < 5; ++sanity) { |
90 | 0 | length *= 2; |
91 | 0 | std::unique_ptr<char[]> buf(new char[length]); |
92 | |
|
93 | 0 | result = strftime(buf.get(), length, format, tm); |
94 | 0 | if (result > 0) { |
95 | | // It fit |
96 | 0 | dst->append(buf.get(), result); |
97 | 0 | return; |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | // sanity failure |
102 | 0 | return; |
103 | 0 | } |
104 | | |
105 | | // Convert a "struct tm" interpreted as *GMT* into a time_t (technically |
106 | | // a long since we can't include header files in header files bla bla bla). |
107 | | // This is basically filling a hole in the standard library. |
108 | | // |
109 | | // There are several approaches to mkgmtime() implementation on the net, |
110 | | // many of them wrong. Simply reimplementing the logic seems to be the |
111 | | // simplest and most efficient, though it does reimplement calendar logic. |
112 | | // The calculation is mostly straightforward; leap years are the main issue. |
113 | | // |
114 | | // Like gmktime() this method returns -1 on failure. Negative results |
115 | | // are considered undefined by the standard so these cases are |
116 | | // considered failures and thus return -1. |
117 | 0 | time_t mkgmtime(const struct tm *tm) { |
118 | | // Month-to-day offset for non-leap-years. |
119 | 0 | static const int month_day[12] = |
120 | 0 | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; |
121 | | |
122 | | // Most of the calculation is easy; leap years are the main difficulty. |
123 | 0 | int month = tm->tm_mon % 12; |
124 | 0 | int year = tm->tm_year + tm->tm_mon / 12; |
125 | 0 | if (month < 0) { // Negative values % 12 are still negative. |
126 | 0 | month += 12; |
127 | 0 | --year; |
128 | 0 | } |
129 | | |
130 | | // This is the number of Februaries since 1900. |
131 | 0 | const int year_for_leap = (month > 1) ? year + 1 : year; |
132 | |
|
133 | 0 | time_t rt = tm->tm_sec // Seconds |
134 | 0 | + 60 * (tm->tm_min // Minute = 60 seconds |
135 | 0 | + 60 * (tm->tm_hour // Hour = 60 minutes |
136 | 0 | + 24 * (month_day[month] + tm->tm_mday - 1 // Day = 24 hours |
137 | 0 | + 365 * (year - 70) // Year = 365 days |
138 | 0 | + (year_for_leap - 69) / 4 // Every 4 years is leap... |
139 | 0 | - (year_for_leap - 1) / 100 // Except centuries... |
140 | 0 | + (year_for_leap + 299) / 400))); // Except 400s. |
141 | 0 | return rt < 0 ? -1 : rt; |
142 | 0 | } |
143 | | |
144 | | bool WallTime_Parse_Timezone(const char* time_spec, |
145 | | const char* format, |
146 | | const struct tm* default_time, |
147 | | bool local, |
148 | 0 | WallTime* result) { |
149 | 0 | struct tm split_time; |
150 | 0 | if (default_time) { |
151 | 0 | split_time = *default_time; |
152 | 0 | } else { |
153 | 0 | memset(&split_time, 0, sizeof(split_time)); |
154 | 0 | } |
155 | 0 | const char* parsed = strptime(time_spec, format, &split_time); |
156 | 0 | if (parsed == nullptr) return false; |
157 | | |
158 | | // If format ends with "%S", match fractional seconds |
159 | 0 | double fraction = 0.0; |
160 | 0 | char junk; |
161 | 0 | if ((*parsed == '.') && |
162 | 0 | (strcmp(format + strlen(format) - 2, "%S") == 0) && |
163 | 0 | (sscanf(parsed, "%lf%c", // NOLINT(runtime/printf) |
164 | 0 | &fraction, &junk) == 1)) { |
165 | 0 | parsed = format + strlen(format); // Parsed it all! |
166 | 0 | } |
167 | 0 | if (*parsed != '\0') return false; |
168 | | |
169 | | // Convert into seconds since epoch. Adjust so it is interpreted |
170 | | // w.r.t. the daylight-saving-state at the specified time. |
171 | 0 | split_time.tm_isdst = -1; // Ask gmktime() to find dst imfo |
172 | 0 | time_t ptime; |
173 | 0 | if (local) { |
174 | 0 | ptime = gmktime(&split_time); |
175 | 0 | } else { |
176 | 0 | ptime = mkgmtime(&split_time); // Returns time in GMT instead of local. |
177 | 0 | } |
178 | |
|
179 | 0 | if (ptime == -1) return false; |
180 | | |
181 | 0 | *result = ptime; |
182 | 0 | *result += fraction; |
183 | 0 | return true; |
184 | 0 | } |
185 | | |
186 | 20 | WallTime WallTime_Now() { |
187 | 20 | #if defined(__APPLE__) |
188 | 20 | mach_timespec_t ts; |
189 | 20 | walltime_internal::GetCurrentTime(&ts); |
190 | 20 | return ts.tv_sec + ts.tv_nsec / static_cast<double>(1e9); |
191 | | #else |
192 | | timespec ts; |
193 | | clock_gettime(CLOCK_REALTIME, &ts); |
194 | | return ts.tv_sec + ts.tv_nsec / static_cast<double>(1e9); |
195 | | #endif // defined(__APPLE__) |
196 | 20 | } |
197 | | |
198 | | void StringAppendStrftime(std::string* dst, |
199 | | const char* format, |
200 | | time_t when, |
201 | 30.2k | bool local) { |
202 | 30.2k | struct tm tm; |
203 | 30.2k | bool conversion_error; |
204 | 30.2k | if (local) { |
205 | 18.1k | conversion_error = (localtime_r(&when, &tm) == nullptr); |
206 | 12.0k | } else { |
207 | 12.0k | conversion_error = (gmtime_r(&when, &tm) == nullptr); |
208 | 12.0k | } |
209 | 30.2k | if (conversion_error) { |
210 | | // If we couldn't convert the time, don't append anything. |
211 | 0 | return; |
212 | 0 | } |
213 | 30.2k | StringAppendStrftime(dst, format, &tm); |
214 | 30.2k | } |
215 | | |
216 | 18.1k | std::string LocalTimeAsString() { |
217 | 18.1k | std::string ret; |
218 | 18.1k | StringAppendStrftime(&ret, "%Y-%m-%d %H:%M:%S %Z", time(nullptr), true); |
219 | 18.1k | return ret; |
220 | 18.1k | } |
221 | | |
222 | 39.7M | void GetThreadUserAndSysCpuTimeMicros(MicrosecondsInt64 *user, MicrosecondsInt64 *sys) { |
223 | 39.7M | #if defined(__APPLE__) |
224 | | // See https://www.gnu.org/software/hurd/gnumach-doc/Thread-Information.html |
225 | | // and Chromium base/time/time_mac.cc. |
226 | 39.7M | task_t thread = mach_thread_self(); |
227 | 39.7M | if (thread == MACH_PORT_NULL) { |
228 | 0 | LOG(WARNING) << "Failed to get mach_thread_self()"; |
229 | 0 | return; |
230 | 0 | } |
231 | | |
232 | 39.7M | mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT; |
233 | 39.7M | thread_basic_info_data_t thread_info_data; |
234 | | |
235 | 39.7M | kern_return_t result = thread_info( |
236 | 39.7M | thread, |
237 | 39.7M | THREAD_BASIC_INFO, |
238 | 39.7M | reinterpret_cast<thread_info_t>(&thread_info_data), |
239 | 39.7M | &thread_info_count); |
240 | | |
241 | 39.7M | if (result != KERN_SUCCESS) { |
242 | 0 | LOG(WARNING) << "Failed to get thread_info()"; |
243 | 0 | return; |
244 | 0 | } |
245 | | |
246 | 39.7M | if (user) { |
247 | 39.7M | *user = thread_info_data.user_time.seconds * 1e6L + thread_info_data.user_time.microseconds; |
248 | 39.7M | } |
249 | 39.7M | if (sys) { |
250 | 39.4M | *sys = thread_info_data.system_time.seconds * 1e6L + thread_info_data.system_time.microseconds; |
251 | 39.4M | } |
252 | | #else |
253 | | struct rusage usage; |
254 | | CHECK_EQ(0, getrusage(RUSAGE_THREAD, &usage)); |
255 | | if (user) { |
256 | | *user = usage.ru_utime.tv_sec * 1e6L + usage.ru_utime.tv_usec; |
257 | | } |
258 | | if (sys) { |
259 | | *sys = usage.ru_stime.tv_sec * 1e6L + usage.ru_stime.tv_usec; |
260 | | } |
261 | | #endif |
262 | 39.7M | } |