1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : /*
8 : * NaCl Server Runtime time abstraction layer.
9 : * This is the host-OS-dependent implementation.
10 : */
11 :
12 : #include <windows.h>
13 : #include <mmsystem.h>
14 : #include <sys/timeb.h>
15 : #include <time.h>
16 :
17 : #include "native_client/src/include/nacl_macros.h"
18 : #include "native_client/src/shared/platform/nacl_time.h"
19 : #include "native_client/src/shared/platform/win/nacl_time_types.h"
20 :
21 : #include "native_client/src/shared/platform/nacl_log.h"
22 : #include "native_client/src/shared/platform/nacl_sync.h"
23 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
24 : #include "native_client/src/shared/platform/win/xlate_system_error.h"
25 :
26 : #define kMillisecondsPerSecond 1000
27 : #define kMicrosecondsPerMillisecond 1000
28 : #define kMicrosecondsPerSecond 1000000
29 : #define kCalibrateTimeoutMs 1000
30 : #define kCoarseMks 10
31 :
32 : /*
33 : * Here's some usefull links related to timing on Windows:
34 : * http://svn.wildfiregames.com/public/ps/trunk/docs/timing_pitfalls.pdf
35 : * Rob Arnold (mozilla) blog:
36 : * http://robarnold.org/measuring-performance/
37 : * https://bugzilla.mozilla.org/show_bug.cgi?id=563082
38 : */
39 :
40 : /*
41 : * To get a 1ms resolution time-of-day clock, we have to jump thrugh
42 : * some hoops. This approach has the following defect: ntp
43 : * adjustments will be nullified, until the difference is larger than
44 : * a recalibration threshold (below), at which point time may jump
45 : * (until we introduce a mechanism to slowly phase in time changes).
46 : * A system suspend/resume will generate a clock recalibration with
47 : * high probability.
48 : *
49 : * The algorithm is as follows:
50 : *
51 : * At start up, we sample the system time (coarse timer) via
52 : * GetSystemTimeAsFileTime until it changes. At the state transition,
53 : * we record the value of the time-after-boot counter via timeGetTime.
54 : *
55 : * At subsequent gettimeofday syscall, we query the system time via
56 : * GetSystemTimeAsFileTime. This is converted to an
57 : * expected/corresponding return value from timeGetTime. The
58 : * difference from the actual return value is the time that elapsed
59 : * since the state transition of the system time counter, and we add
60 : * that difference (assuming it is smaller than the max-drift
61 : * threshold) to the system time and return that (minus the Unix
62 : * epoch, etc), as the result of the gettimeofday syscall. If the
63 : * difference is larger than the max-drift threshold, we invoke the
64 : * calibration function.
65 : */
66 :
67 : struct NaClTimeState g_NaCl_time_state;
68 :
69 : static uint32_t const kMaxMillsecondDriftBeforeRecalibration = 60;
70 : static uint32_t const kMaxCalibrationDiff = 4;
71 :
72 : /*
73 : * Translate Windows system time to milliseconds. Windows System time
74 : * is in units of 100nS. Windows time is in 100e-9s == 1e-7s, and we
75 : * want units of 1e-3s, so ms/wt = 1e-4.
76 : */
77 18 : static uint64_t NaClFileTimeToMs(FILETIME *ftp) {
78 : ULARGE_INTEGER t;
79 18 : t.u.HighPart = ftp->dwHighDateTime;
80 18 : t.u.LowPart = ftp->dwLowDateTime;
81 18 : return t.QuadPart / NACL_100_NANOS_PER_MILLI;
82 18 : }
83 :
84 : #define kCpuInfoPageSize 4 /* Not in bytes, but in sizeof(uint32_t). */
85 :
86 18 : static char* CPU_GetBrandString(void) {
87 18 : const int kBaseInfoPage = 0x80000000;
88 18 : const int kCpuBrandStringPage1 = 0x80000002;
89 18 : const int kCpuBrandStringPage2 = 0x80000003;
90 18 : const int kCpuBrandStringPage3 = 0x80000004;
91 :
92 : static int cpu_info[kCpuInfoPageSize * 4] = {0};
93 18 : __cpuid(cpu_info, kBaseInfoPage);
94 18 : if (cpu_info[0] < kCpuBrandStringPage3)
95 0 : return "";
96 :
97 18 : __cpuid(&cpu_info[0], kCpuBrandStringPage1);
98 18 : __cpuid(&cpu_info[kCpuInfoPageSize], kCpuBrandStringPage2);
99 18 : __cpuid(&cpu_info[kCpuInfoPageSize * 2], kCpuBrandStringPage3);
100 18 : return (char*)cpu_info;
101 18 : }
102 :
103 0 : static int CPU_GetFamily(void) {
104 0 : const int kFeaturesPage = 1;
105 : int cpu_info[kCpuInfoPageSize];
106 0 : __cpuid(cpu_info, kFeaturesPage);
107 0 : return (cpu_info[0] >> 8) & 0xf;
108 0 : }
109 :
110 : /*
111 : * Returns 1 if success, 0 in case of QueryPerformanceCounter is not supported.
112 : *
113 : * Calibration is done as described above.
114 : *
115 : * To ensure that the calibration is accurate, we interleave sampling
116 : * the microsecond resolution counter via QueryPerformanceCounter with the 1
117 : * millisecond resolution system clock via GetSystemTimeAsFileTime.
118 : * When we see the edge transition where the system clock ticks, we
119 : * compare the before and after microsecond counter values. If it is
120 : * within a calibration threshold (kMaxCalibrationDiff), then we
121 : * record the instantaneous system time (1 msresolution) and the
122 : * 64-bit counter value (~0.5 mks reslution). Since we have a before and
123 : * after counter value, we interpolate it to get the "average" counter
124 : * value to associate with the system time.
125 : */
126 18 : static int NaClCalibrateWindowsClockQpc(struct NaClTimeState *ntsp) {
127 : FILETIME ft_start;
128 : FILETIME ft_prev;
129 : FILETIME ft_now;
130 : LARGE_INTEGER counter_before;
131 : LARGE_INTEGER counter_after;
132 : int64_t counter_diff;
133 : int64_t counter_diff_ms;
134 : uint64_t end_of_calibrate;
135 : int sys_time_changed;
136 18 : int calibration_success = 0;
137 :
138 18 : NaClLog(5, "Entered NaClCalibrateWindowsClockQpc\n");
139 :
140 18 : GetSystemTimeAsFileTime(&ft_start);
141 18 : ft_prev = ft_start;
142 18 : end_of_calibrate = NaClFileTimeToMs(&ft_start) + kCalibrateTimeoutMs;
143 :
144 : do {
145 18 : if (!QueryPerformanceCounter(&counter_before))
146 0 : return 0;
147 18 : GetSystemTimeAsFileTime(&ft_now);
148 18 : if (!QueryPerformanceCounter(&counter_after))
149 0 : return 0;
150 :
151 18 : counter_diff = counter_after.QuadPart - counter_before.QuadPart;
152 : counter_diff_ms =
153 18 : (counter_diff * kMillisecondsPerSecond) / ntsp->qpc_frequency;
154 : sys_time_changed = (ft_now.dwHighDateTime != ft_prev.dwHighDateTime) ||
155 18 : (ft_now.dwLowDateTime != ft_prev.dwLowDateTime);
156 :
157 : if ((counter_diff >= 0) &&
158 : (counter_diff_ms <= kMaxCalibrationDiff) &&
159 18 : sys_time_changed) {
160 18 : calibration_success = 1;
161 18 : break;
162 : }
163 18 : if (sys_time_changed)
164 0 : ft_prev = ft_now;
165 18 : } while (NaClFileTimeToMs(&ft_now) < end_of_calibrate);
166 :
167 18 : ntsp->system_time_start_ms = NaClFileTimeToMs(&ft_now);
168 18 : ntsp->qpc_start = counter_before.QuadPart + (counter_diff / 2);
169 18 : ntsp->last_qpc = counter_after.QuadPart;
170 :
171 : NaClLog(5,
172 : "Leaving NaClCalibrateWindowsClockQpc : %d\n",
173 18 : calibration_success);
174 18 : return calibration_success;
175 18 : }
176 :
177 : /*
178 : * Calibration is done as described above.
179 : *
180 : * To ensure that the calibration is accurate, we interleave sampling
181 : * the millisecond resolution counter via timeGetTime with the 10-55
182 : * millisecond resolution system clock via GetSystemTimeAsFileTime.
183 : * When we see the edge transition where the system clock ticks, we
184 : * compare the before and after millisecond counter values. If it is
185 : * within a calibration threshold (kMaxCalibrationDiff), then we
186 : * record the instantaneous system time (10-55 msresolution) and the
187 : * 32-bit counter value (1ms reslution). Since we have a before and
188 : * after counter value, we interpolate it to get the "average" counter
189 : * value to associate with the system time.
190 : */
191 0 : static void NaClCalibrateWindowsClockMu(struct NaClTimeState *ntsp) {
192 : FILETIME ft_start;
193 : FILETIME ft_now;
194 : DWORD ms_counter_before;
195 : DWORD ms_counter_after;
196 : uint32_t ms_counter_diff;
197 :
198 0 : NaClLog(5, "Entered NaClCalibrateWindowsClockMu\n");
199 0 : GetSystemTimeAsFileTime(&ft_start);
200 0 : ms_counter_before = timeGetTime();
201 : for (;;) {
202 0 : GetSystemTimeAsFileTime(&ft_now);
203 0 : ms_counter_after = timeGetTime();
204 0 : ms_counter_diff = ms_counter_after - (uint32_t) ms_counter_before;
205 0 : NaClLog(5, "ms_counter_diff %u\n", ms_counter_diff);
206 : if (ms_counter_diff <= kMaxCalibrationDiff &&
207 : (ft_now.dwHighDateTime != ft_start.dwHighDateTime ||
208 0 : ft_now.dwLowDateTime != ft_start.dwLowDateTime)) {
209 0 : break;
210 : }
211 0 : ms_counter_before = ms_counter_after;
212 0 : }
213 0 : ntsp->system_time_start_ms = NaClFileTimeToMs(&ft_now);
214 : /*
215 : * Average the counter values. Note unsigned computation of
216 : * ms_counter_diff, so that was mod 2**32 arithmetic, and the
217 : * addition of half the difference is numerically correct, whereas
218 : * (ms_counter_before + ms_counter_after)/2 is wrong due to
219 : * overflow.
220 : */
221 0 : ntsp->ms_counter_start = (DWORD) (ms_counter_before + (ms_counter_diff / 2));
222 :
223 0 : NaClLog(5, "Leaving NaClCalibrateWindowsClockMu\n");
224 0 : }
225 :
226 0 : void NaClAllowLowResolutionTimeOfDay(void) {
227 0 : g_NaCl_time_state.allow_low_resolution = 1;
228 0 : }
229 :
230 18 : void NaClTimeInternalInit(struct NaClTimeState *ntsp) {
231 : TIMECAPS tc;
232 : SYSTEMTIME st;
233 : FILETIME ft;
234 : LARGE_INTEGER qpc_freq;
235 :
236 : /*
237 : * Maximize timer/Sleep resolution.
238 : */
239 18 : timeGetDevCaps(&tc, sizeof tc);
240 :
241 18 : if (ntsp->allow_low_resolution) {
242 : /* Set resolution to max so we don't over-promise. */
243 0 : ntsp->wPeriodMin = tc.wPeriodMax;
244 0 : } else {
245 18 : ntsp->wPeriodMin = tc.wPeriodMin;
246 18 : timeBeginPeriod(ntsp->wPeriodMin);
247 18 : NaClLog(4, "NaClTimeInternalInit: timeBeginPeriod(%u)\n", ntsp->wPeriodMin);
248 : }
249 18 : ntsp->time_resolution_ns = ntsp->wPeriodMin * NACL_NANOS_PER_MILLI;
250 :
251 : /*
252 : * Compute Unix epoch start; calibrate high resolution clock.
253 : */
254 18 : st.wYear = 1970;
255 18 : st.wMonth = 1;
256 18 : st.wDay = 1;
257 18 : st.wHour = 0;
258 18 : st.wMinute = 0;
259 18 : st.wSecond = 0;
260 18 : st.wMilliseconds = 0;
261 18 : SystemTimeToFileTime(&st, &ft);
262 18 : ntsp->epoch_start_ms = NaClFileTimeToMs(&ft);
263 : NaClLog(4, "Unix epoch start is %"NACL_PRIu64"ms in Windows epoch time\n",
264 18 : ntsp->epoch_start_ms);
265 :
266 18 : NaClMutexCtor(&ntsp->mu);
267 :
268 : /*
269 : * We don't actually grab the lock, since the module initializer
270 : * should be called before going threaded.
271 : */
272 18 : ntsp->can_use_qpc = 0;
273 18 : if (!ntsp->allow_low_resolution) {
274 18 : ntsp->can_use_qpc = QueryPerformanceFrequency(&qpc_freq);
275 : /*
276 : * On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is
277 : * unreliable. Fallback to low-res clock.
278 : */
279 18 : if (strstr(CPU_GetBrandString(), "AuthenticAMD") && (CPU_GetFamily() == 15))
280 0 : ntsp->can_use_qpc = 0;
281 :
282 : NaClLog(4,
283 : "CPU_GetBrandString->[%s] ntsp->can_use_qpc=%d\n",
284 : CPU_GetBrandString(),
285 18 : ntsp->can_use_qpc);
286 :
287 18 : if (ntsp->can_use_qpc) {
288 18 : ntsp->qpc_frequency = qpc_freq.QuadPart;
289 : NaClLog(4, "qpc_frequency = %"NACL_PRId64" (counts/s)\n",
290 18 : ntsp->qpc_frequency);
291 18 : if (!NaClCalibrateWindowsClockQpc(ntsp))
292 0 : ntsp->can_use_qpc = 0;
293 : }
294 18 : if (!ntsp->can_use_qpc)
295 0 : NaClCalibrateWindowsClockMu(ntsp);
296 : }
297 18 : }
298 :
299 4 : uint64_t NaClTimerResolutionNsInternal(struct NaClTimeState *ntsp) {
300 4 : return ntsp->time_resolution_ns;
301 4 : }
302 :
303 13 : void NaClTimeInternalFini(struct NaClTimeState *ntsp) {
304 13 : NaClMutexDtor(&ntsp->mu);
305 13 : if (!ntsp->allow_low_resolution)
306 13 : timeEndPeriod(ntsp->wPeriodMin);
307 13 : }
308 :
309 18 : void NaClTimeInit(void) {
310 18 : NaClTimeInternalInit(&g_NaCl_time_state);
311 18 : }
312 :
313 13 : void NaClTimeFini(void) {
314 13 : NaClTimeInternalFini(&g_NaCl_time_state);
315 13 : }
316 :
317 4 : uint64_t NaClTimerResolutionNanoseconds(void) {
318 4 : return NaClTimerResolutionNsInternal(&g_NaCl_time_state);
319 4 : }
320 :
321 : int NaClGetTimeOfDayInternQpc(struct nacl_abi_timeval *tv,
322 : struct NaClTimeState *ntsp,
323 16 : int allow_calibration) {
324 : FILETIME ft_now;
325 : int64_t sys_now_mks;
326 : LARGE_INTEGER qpc;
327 : int64_t qpc_diff;
328 : int64_t qpc_diff_mks;
329 : int64_t qpc_now_mks;
330 : int64_t drift_mks;
331 : int64_t drift_ms;
332 :
333 16 : NaClLog(5, "Entered NaClGetTimeOfDayInternQpc\n");
334 :
335 16 : NaClXMutexLock(&ntsp->mu);
336 :
337 16 : GetSystemTimeAsFileTime(&ft_now);
338 16 : QueryPerformanceCounter(&qpc);
339 16 : sys_now_mks = NaClFileTimeToMs(&ft_now) * kMicrosecondsPerMillisecond;
340 16 : NaClLog(5, " sys_now_mks = %"NACL_PRId64" (us)\n", sys_now_mks);
341 16 : qpc_diff = qpc.QuadPart - ntsp->qpc_start;
342 16 : NaClLog(5, " qpc_diff = %"NACL_PRId64" (counts)\n", qpc_diff);
343 : /*
344 : * Coarse qpc_now_mks to 10 microseconds resolution,
345 : * to match the other platforms and not make a side-channel
346 : * attack any easier than it needs to be.
347 : */
348 : qpc_diff_mks = ((qpc_diff * (kMicrosecondsPerSecond / kCoarseMks)) /
349 16 : ntsp->qpc_frequency) * kCoarseMks;
350 16 : NaClLog(5, " qpc_diff_mks = %"NACL_PRId64" (us)\n", qpc_diff_mks);
351 :
352 : qpc_now_mks = (ntsp->system_time_start_ms * kMicrosecondsPerMillisecond) +
353 16 : qpc_diff_mks;
354 : NaClLog(5, " system_time_start_ms %"NACL_PRIu64"\n",
355 16 : ntsp->system_time_start_ms);
356 16 : NaClLog(5, " qpc_now_mks = %"NACL_PRId64" (us)\n", qpc_now_mks);
357 :
358 16 : if ((qpc_diff < 0) || (qpc.QuadPart < ntsp->last_qpc)) {
359 0 : NaClLog(5, " need recalibration\n");
360 0 : if (allow_calibration) {
361 0 : NaClCalibrateWindowsClockQpc(ntsp);
362 0 : NaClXMutexUnlock(&ntsp->mu);
363 0 : return NaClGetTimeOfDayInternQpc(tv, ntsp, 0);
364 : } else {
365 0 : NaClLog(5, " ... but using coarse, system time instead.\n");
366 : /* use GetSystemTimeAsFileTime(), not QPC */
367 0 : qpc_now_mks = sys_now_mks;
368 : }
369 : }
370 16 : ntsp->last_qpc = qpc.QuadPart;
371 16 : drift_mks = sys_now_mks - qpc_now_mks;
372 16 : if (qpc_now_mks > sys_now_mks)
373 6 : drift_mks = qpc_now_mks - sys_now_mks;
374 :
375 16 : drift_ms = drift_mks / kMicrosecondsPerMillisecond;
376 16 : NaClLog(5, " drift_ms = %"NACL_PRId64"\n", drift_ms);
377 :
378 : if (allow_calibration &&
379 16 : (drift_ms > kMaxMillsecondDriftBeforeRecalibration)) {
380 0 : NaClLog(5, "drift_ms recalibration\n");
381 0 : NaClCalibrateWindowsClockQpc(ntsp);
382 0 : NaClXMutexUnlock(&ntsp->mu);
383 0 : return NaClGetTimeOfDayInternQpc(tv, ntsp, 0);
384 : }
385 :
386 16 : NaClXMutexUnlock(&ntsp->mu);
387 :
388 : /* translate to unix time base */
389 : qpc_now_mks = qpc_now_mks
390 16 : - ntsp->epoch_start_ms * kMicrosecondsPerMillisecond;
391 :
392 : tv->nacl_abi_tv_sec =
393 16 : (nacl_abi_time_t)(qpc_now_mks / kMicrosecondsPerSecond);
394 : tv->nacl_abi_tv_usec =
395 16 : (nacl_abi_suseconds_t)(qpc_now_mks % kMicrosecondsPerSecond);
396 16 : return 0;
397 16 : }
398 :
399 : int NaClGetTimeOfDayIntern(struct nacl_abi_timeval *tv,
400 16 : struct NaClTimeState *ntsp) {
401 : FILETIME ft_now;
402 : DWORD ms_counter_now;
403 : uint64_t t_ms;
404 : DWORD ms_counter_at_ft_now;
405 : uint32_t ms_counter_diff;
406 : uint64_t unix_time_ms;
407 :
408 16 : if (ntsp->can_use_qpc)
409 16 : return NaClGetTimeOfDayInternQpc(tv, ntsp, 1);
410 :
411 0 : GetSystemTimeAsFileTime(&ft_now);
412 0 : ms_counter_now = timeGetTime();
413 0 : t_ms = NaClFileTimeToMs(&ft_now);
414 :
415 0 : NaClXMutexLock(&ntsp->mu);
416 :
417 0 : if (!ntsp->allow_low_resolution) {
418 : NaClLog(5, "ms_counter_now %"NACL_PRIu32"\n",
419 0 : (uint32_t) ms_counter_now);
420 0 : NaClLog(5, "t_ms %"NACL_PRId64"\n", t_ms);
421 : NaClLog(5, "system_time_start_ms %"NACL_PRIu64"\n",
422 0 : ntsp->system_time_start_ms);
423 :
424 : ms_counter_at_ft_now = (DWORD)
425 : (ntsp->ms_counter_start +
426 0 : (uint32_t) (t_ms - ntsp->system_time_start_ms));
427 :
428 : NaClLog(5, "ms_counter_at_ft_now %"NACL_PRIu32"\n",
429 0 : (uint32_t) ms_counter_at_ft_now);
430 :
431 0 : ms_counter_diff = ms_counter_now - (uint32_t) ms_counter_at_ft_now;
432 :
433 0 : NaClLog(5, "ms_counter_diff %"NACL_PRIu32"\n", ms_counter_diff);
434 :
435 0 : if (ms_counter_diff <= kMaxMillsecondDriftBeforeRecalibration) {
436 0 : t_ms = t_ms + ms_counter_diff;
437 0 : } else {
438 0 : NaClCalibrateWindowsClockMu(ntsp);
439 0 : t_ms = ntsp->system_time_start_ms;
440 : }
441 :
442 0 : NaClLog(5, "adjusted t_ms = %"NACL_PRIu64"\n", t_ms);
443 : }
444 :
445 0 : unix_time_ms = t_ms - ntsp->epoch_start_ms;
446 :
447 0 : NaClXMutexUnlock(&ntsp->mu);
448 :
449 0 : NaClLog(5, "unix_time_ms = %"NACL_PRId64"\n", unix_time_ms);
450 : /*
451 : * Unix time is measured relative to a different epoch, Jan 1, 1970.
452 : * See the module initialization for epoch_start_ms.
453 : */
454 :
455 0 : tv->nacl_abi_tv_sec = (nacl_abi_time_t) (unix_time_ms / 1000);
456 0 : tv->nacl_abi_tv_usec = (nacl_abi_suseconds_t) ((unix_time_ms % 1000) * 1000);
457 0 : return 0;
458 16 : }
459 :
460 16 : int NaClGetTimeOfDay(struct nacl_abi_timeval *tv) {
461 16 : return NaClGetTimeOfDayIntern(tv, &g_NaCl_time_state);
462 16 : }
463 :
464 : int NaClNanosleep(struct nacl_abi_timespec const *req,
465 4 : struct nacl_abi_timespec *rem) {
466 : DWORD sleep_ms;
467 : uint64_t resolution;
468 4 : DWORD resolution_gap = 0;
469 :
470 : UNREFERENCED_PARAMETER(rem);
471 :
472 : /* round up from ns resolution to ms resolution */
473 : /* TODO(bsy): report an error or loop if req->tv_sec does not fit in DWORD */
474 : sleep_ms = ((DWORD) req->tv_sec * NACL_MILLIS_PER_UNIT +
475 4 : NACL_UNIT_CONVERT_ROUND(req->tv_nsec, NACL_NANOS_PER_MILLI));
476 :
477 : /* round up to minimum timer resolution */
478 4 : resolution = NaClTimerResolutionNanoseconds();
479 4 : NaClLog(4, "Resolution %"NACL_PRId64"\n", resolution);
480 4 : if (0 != resolution) {
481 4 : resolution = NACL_UNIT_CONVERT_ROUND(resolution, NACL_NANOS_PER_MILLI);
482 4 : resolution_gap = (DWORD) (sleep_ms % resolution);
483 4 : if (0 != resolution_gap) {
484 0 : resolution_gap = (DWORD) (resolution - resolution_gap);
485 : }
486 : }
487 4 : NaClLog(4, "Resolution gap %d\n", resolution_gap);
488 4 : sleep_ms += resolution_gap;
489 :
490 4 : NaClLog(4, "Sleep(%d)\n", sleep_ms);
491 4 : Sleep(sleep_ms);
492 :
493 4 : return 0;
494 4 : }
|