CppCommon  1.0.4.1
C++ Common Library
timestamp.cpp
Go to the documentation of this file.
1 
9 #include "time/timestamp.h"
10 
11 #include "math/math.h"
12 
13 #if defined(__APPLE__)
14 #include <mach/mach.h>
15 #include <mach/mach_time.h>
16 #include <sys/time.h>
17 #include <math.h>
18 #include <time.h>
19 #elif defined(unix) || defined(__unix) || defined(__unix__)
20 #include <time.h>
21 #elif defined(_WIN32) || defined(_WIN64)
22 #include <windows.h>
23 #endif
24 
25 namespace CppCommon {
26 
28 namespace Internals {
29 
30 #if defined(__APPLE__)
31 
32 uint32_t CeilLog2(uint32_t x)
33 {
34  uint32_t result = 0;
35 
36  --x;
37  while (x > 0)
38  {
39  ++result;
40  x >>= 1;
41  }
42 
43  return result;
44 }
45 
46 // This function returns the rational number inside the given interval with
47 // the smallest denominator (and smallest numerator breaks ties; correctness
48 // proof neglects floating-point errors).
49 mach_timebase_info_data_t BestFrac(double a, double b)
50 {
51  if (floor(a) < floor(b))
52  {
53  mach_timebase_info_data_t rv = { (uint32_t)ceil(a), 1 };
54  return rv;
55  }
56 
57  double m = floor(a);
58  mach_timebase_info_data_t next = BestFrac(1 / (b - m), 1 / (a - m));
59  mach_timebase_info_data_t rv = { (int)m * next.numer + next.denom, next.numer };
60  return rv;
61 }
62 
63 // This is just less than the smallest thing we can multiply numer by without
64 // overflowing. CeilLog2(numer) = 64 - number of leading zeros of numer
65 uint64_t GetExpressibleSpan(uint32_t numer, uint32_t denom)
66 {
67  uint64_t maxDiffWithoutOverflow = ((uint64_t)1 << (64 - CeilLog2(numer))) - 1;
68  return maxDiffWithoutOverflow * numer / denom;
69 }
70 
71 // The clock may run up to 0.1% faster or slower than the "exact" tick count.
72 // However, although the bound on the error is the same as for the pragmatic
73 // answer, the error is actually minimized over the given accuracy bound.
74 uint64_t PrepareTimebaseInfo(mach_timebase_info_data_t& tb)
75 {
76  tb.numer = 0;
77  tb.denom = 1;
78 
79  kern_return_t mtiStatus = mach_timebase_info(&tb);
80  if (mtiStatus != KERN_SUCCESS)
81  return 0;
82 
83  double frac = (double)tb.numer / tb.denom;
84  uint64_t spanTarget = 315360000000000000llu;
85  if (GetExpressibleSpan(tb.numer, tb.denom) >= spanTarget)
86  return 0;
87 
88  for (double errorTarget = 1 / 1024.0; errorTarget > 0.000001;)
89  {
90  mach_timebase_info_data_t newFrac = BestFrac((1 - errorTarget) * frac, (1 + errorTarget) * frac);
91  if (GetExpressibleSpan(newFrac.numer, newFrac.denom) < spanTarget)
92  break;
93  tb = newFrac;
94  errorTarget = fabs((double)tb.numer / tb.denom - frac) / frac / 8;
95  }
96 
97  return 0;
98 }
99 
100 #endif
101 
102 } // namespace Internals
104 
105 uint64_t Timestamp::utc()
106 {
107 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
108  struct timespec timestamp;
109  if (clock_gettime(CLOCK_REALTIME, &timestamp) != 0)
110  throwex SystemException("Cannot get value of CLOCK_REALTIME timer!");
111  return (timestamp.tv_sec * 1000000000) + timestamp.tv_nsec;
112 #elif defined(_WIN32) || defined(_WIN64)
113  FILETIME ft;
114  GetSystemTimePreciseAsFileTime(&ft);
115 
116  ULARGE_INTEGER result;
117  result.LowPart = ft.dwLowDateTime;
118  result.HighPart = ft.dwHighDateTime;
119  return (result.QuadPart - 116444736000000000ull) * 100;
120 #endif
121 }
122 
124 {
125 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
126  uint64_t timestamp = utc();
127 
128  // Adjust UTC time with local timezone offset
129  struct tm local;
130  time_t seconds = timestamp / (1000000000);
131  if (localtime_r(&seconds, &local) != &local)
132  throwex SystemException("Cannot convert CLOCK_REALTIME time to local date & time structure!");
133  return timestamp + (local.tm_gmtoff * 1000000000);
134 #elif defined(_WIN32) || defined(_WIN64)
135  FILETIME ft;
136 #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
137  GetSystemTimePreciseAsFileTime(&ft);
138 #else
139  GetSystemTimeAsFileTime(&ft);
140 #endif
141 
142  FILETIME ft_local;
143  if (!FileTimeToLocalFileTime(&ft, &ft_local))
144  throwex SystemException("Cannot convert UTC file time to local file time structure!");
145 
146  ULARGE_INTEGER result;
147  result.LowPart = ft_local.dwLowDateTime;
148  result.HighPart = ft_local.dwHighDateTime;
149  return (result.QuadPart - 116444736000000000ull) * 100;
150 #endif
151 }
152 
153 uint64_t Timestamp::nano()
154 {
155 #if defined(__APPLE__)
156  static mach_timebase_info_data_t info;
157  static uint64_t bias = Internals::PrepareTimebaseInfo(info);
158  return ((mach_absolute_time() - bias) * info.numer) / info.denom;
159 #elif defined(unix) || defined(__unix) || defined(__unix__)
160  struct timespec timestamp = { 0 };
161  if (clock_gettime(CLOCK_MONOTONIC, &timestamp) != 0)
162  throwex SystemException("Cannot get value of CLOCK_MONOTONIC timer!");
163  return (timestamp.tv_sec * 1000000000) + timestamp.tv_nsec;
164 #elif defined(_WIN32) || defined(_WIN64)
165  static uint64_t offset = 0;
166  static LARGE_INTEGER first = { 0 };
167  static LARGE_INTEGER frequency = { 0 };
168  static bool initialized = false;
169  static bool qpc = true;
170 
171  if (!initialized)
172  {
173  // Calculate timestamp offset
174  FILETIME timestamp;
175  GetSystemTimePreciseAsFileTime(&timestamp);
176 
177  ULARGE_INTEGER result;
178  result.LowPart = timestamp.dwLowDateTime;
179  result.HighPart = timestamp.dwHighDateTime;
180 
181  // Convert 01.01.1601 to 01.01.1970
182  result.QuadPart -= 116444736000000000ll;
183  offset = result.QuadPart * 100;
184 
185  // Setup performance counter
186  qpc = QueryPerformanceFrequency(&frequency) && QueryPerformanceCounter(&first);
187 
188  initialized = true;
189  }
190 
191  if (qpc)
192  {
193  LARGE_INTEGER timestamp = { 0 };
194  QueryPerformanceCounter(&timestamp);
195  timestamp.QuadPart -= first.QuadPart;
196  return offset + Math::MulDiv64(timestamp.QuadPart, 1000000000, frequency.QuadPart);
197  }
198  else
199  return offset;
200 #else
201  #error Unsupported platform
202 #endif
203 }
204 
205 uint64_t Timestamp::rdts()
206 {
207 #if defined(__APPLE__)
208  // This goes at the top because we need ALL Macs, regardless of
209  // architecture, to return the number of "mach time units" that
210  // have passed since startup. See sysinfo.cc where
211  // InitializeSystemInfo() sets the supposed cpu clock frequency of
212  // macs to the number of mach time units per second, not actual
213  // CPU clock frequency (which can change in the face of CPU
214  // frequency scaling). Also note that when the Mac sleeps, this
215  // counter pauses; it does not continue counting, nor does it
216  // reset to zero.
217  return mach_absolute_time();
218 #elif defined(_MSC_VER)
219  return __rdtsc();
220 #elif defined(__i386__)
221  int64_t ret;
222  __asm__ volatile("rdtsc" : "=A"(ret));
223  return ret;
224 #elif defined(__x86_64__) || defined(__amd64__)
225  uint64_t low, high;
226  __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
227  return (high << 32) | low;
228 #elif defined(__powerpc__) || defined(__ppc__)
229  // This returns a time-base, which is not always precisely a cycle-count.
230  int64_t tbl, tbu0, tbu1;
231  asm("mftbu %0" : "=r"(tbu0));
232  asm("mftb %0" : "=r"(tbl));
233  asm("mftbu %0" : "=r"(tbu1));
234  tbl &= -static_cast<int64>(tbu0 == tbu1);
235  // High 32 bits in tbu1;
236  // How 32 bits in tbl (tbu0 is garbage)
237  return (tbu1 << 32) | tbl;
238 #elif defined(__sparc__)
239  int64_t tick;
240  asm(".byte 0x83, 0x41, 0x00, 0x00");
241  asm("mov %%g1, %0" : "=r"(tick));
242  return tick;
243 #elif defined(__ia64__)
244  int64_t itc;
245  asm("mov %0 = ar.itc" : "=r"(itc));
246  return itc;
247 #elif defined(COMPILER_MSVC) && defined(_M_IX86)
248  // Older MSVC compilers (like 7.x) don't seem to support the
249  // __rdtsc intrinsic properly, so I prefer to use _asm instead
250  // when I know it will work. Otherwise, I'll use __rdtsc and hope
251  // the code is being compiled with a non-ancient compiler.
252  _asm rdtsc
253 #elif defined(__aarch64__)
254  // System timer of ARMv8 runs at a different frequency than the CPU's.
255  // The frequency is fixed, typically in the range 1-50MHz. It can be
256  // read at CNTFRQ special register. We assume the OS has set up
257  // the virtual timer properly.
258  int64_t virtual_timer_value;
259  asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
260  return virtual_timer_value;
261 #elif defined(__ARM_ARCH)
262 
263  // V6 is the earliest arch that has a standard cyclecount
264  #if (__ARM_ARCH >= 6)
265  uint32_t pmccntr;
266  uint32_t pmuseren;
267  uint32_t pmcntenset;
268  // Read the user mode perf monitor counter access permissions.
269  asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
270  if (pmuseren & 1)
271  {
272  // Allows reading perfmon counters for user mode code.
273  asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
274  if (pmcntenset & 0x80000000ul)
275  {
276  // Is it counting?
277  asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
278  // The counter is set up to count every 64th cycle
279  return static_cast<int64_t>(pmccntr) * 64;
280  }
281  }
282  #endif
283 
284  struct timeval tv;
285  gettimeofday(&tv, nullptr);
286  return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
287 #elif defined(__mips__)
288  // mips apparently only allows rdtsc for superusers, so we fall back to gettimeofday.
289  // It's possible clock_gettime would be better.
290  struct timeval tv;
291  gettimeofday(&tv, nullptr);
292  return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
293 #else
294  #error Unsupported platform
295 #endif
296 }
297 
298 } // namespace CppCommon
static uint64_t MulDiv64(uint64_t operant, uint64_t multiplier, uint64_t divider)
Calculate (operant * multiplier / divider) with 64-bit unsigned integer values.
Definition: math.cpp:17
System exception.
Definition: exceptions.h:107
static uint64_t local()
Get the local timestamp.
Definition: timestamp.cpp:123
static uint64_t utc()
Get the UTC timestamp.
Definition: timestamp.cpp:105
static uint64_t nano()
Get the high resolution timestamp.
Definition: timestamp.cpp:153
uint64_t seconds() const noexcept
Get total seconds of the current timestamp.
Definition: timestamp.h:143
static uint64_t rdts()
Get the current value of RDTS (Read Time Stamp Counter)
Definition: timestamp.cpp:205
#define throwex
Throw extended exception macro.
Definition: exceptions.h:23
Math definition.
C++ Common project definitions.
Definition: token_bucket.h:15
Time definition.
Timestamp definition.