1 : // Copyright 2013 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/trace/common/clock.h"
16 :
17 : #include <WinBase.h>
18 : #include <type_traits>
19 :
20 : #include "base/logging.h"
21 : #include "base/win/registry.h"
22 :
23 : namespace trace {
24 : namespace common {
25 :
26 : namespace {
27 :
28 : union LargeInteger {
29 : LARGE_INTEGER li;
30 : uint64 ui64;
31 : COMPILE_ASSERT(sizeof(LARGE_INTEGER) == sizeof(uint64),
32 : LARGE_INTEGER_and_uint64_must_have_same_size);
33 : };
34 :
35 : typedef ULONGLONG (*GetTickCount64Ptr)();
36 :
37 : } // namespace
38 :
39 E : void GetTickTimerInfo(TimerInfo* timer_info) {
40 E : DCHECK(timer_info != NULL);
41 :
42 : // Ticks are in milliseconds.
43 E : timer_info->frequency = 1000;
44 :
45 : // The resolution of the tick counter varies, but is documented to have a
46 : // worst case of 16 ms.
47 E : timer_info->resolution = 16;
48 E : }
49 :
50 E : void GetTscTimerInfo(TimerInfo* timer_info) {
51 E : DCHECK(timer_info != NULL);
52 :
53 E : ::memset(timer_info, 0, sizeof(TimerInfo));
54 :
55 : // Check the TscInvariant flag to see if we can rely on TSC as a constant
56 : // rate timer that is synchronous across all cores. This is in
57 : // CPUID.80000007.EDX[8].
58 : int info[4];
59 E : ::__cpuid(info, 0x80000007);
60 E : if ((info[3] & (1 << 8)) == 0)
61 i : return;
62 :
63 : // Get the CPU frequency. If all is well, this is the frequency of the TSC
64 : // timer.
65 E : base::win::RegKey cpureg;
66 E : DWORD mhz = 0;
67 : if (cpureg.Open(HKEY_LOCAL_MACHINE,
68 : L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
69 : KEY_READ) != 0 ||
70 E : cpureg.ReadValueDW(L"~MHz", &mhz) != 0) {
71 i : LOG(WARNING) << "Unable to get CPU frequency from registry.";
72 i : return;
73 : }
74 :
75 : // An invariant TSC is documented to run at the fastest clock speed of the
76 : // CPU.
77 E : timer_info->frequency = mhz * 1000000;
78 E : timer_info->resolution = 1;
79 E : }
80 :
81 : bool TimerToFileTime(const FILETIME& file_time_ref,
82 : const TimerInfo& timer_info,
83 : const uint64& timer_ref,
84 : const uint64& timer_value,
85 E : FILETIME* file_time) {
86 E : DCHECK(file_time != NULL);
87 :
88 : // This only works if we have valid timer information.
89 E : if (timer_info.frequency == 0 || timer_info.resolution == 0)
90 E : return false;
91 :
92 : uint64 t = (static_cast<uint64>(file_time_ref.dwHighDateTime) << 32) |
93 E : file_time_ref.dwLowDateTime;
94 :
95 : // The filetime is expressed in 100ns intervals.
96 E : double cycles_per_100ns = 1.0e-7 * timer_info.frequency;
97 : double elapsed_100ns_intervals =
98 E : (static_cast<double>(timer_value) - timer_ref) / cycles_per_100ns;
99 E : double new_file_time = t + elapsed_100ns_intervals;
100 E : if (new_file_time < 0)
101 E : return false;
102 :
103 E : t = static_cast<uint64>(new_file_time);
104 E : file_time->dwLowDateTime = t & 0xFFFFFFFF;
105 E : file_time->dwHighDateTime = t >> 32;
106 :
107 E : return true;
108 E : }
109 :
110 E : uint64 GetTicks() {
111 : // We can't explicitly invoke GetTickCount64 as it doesn't exist in Windows
112 : // XP. This would make all of our trace code unable to be run on XP systems.
113 : const GetTickCount64Ptr kUninitialized =
114 E : reinterpret_cast<GetTickCount64Ptr>(1);
115 E : static GetTickCount64Ptr get_tick_count64 = kUninitialized;
116 :
117 : // This is racy but safe. Worst case scenario multiple threads do the lookup,
118 : // each of them writing the same value to |get_tick_count64|. Since writes are
119 : // atomic all will be well by the time it is dereferenced.
120 E : if (get_tick_count64 == kUninitialized) {
121 E : HMODULE kernel32 = ::GetModuleHandleA("kernel32.dll");
122 E : DCHECK(kernel32 != NULL);
123 :
124 : get_tick_count64 = reinterpret_cast<GetTickCount64Ptr>(
125 E : ::GetProcAddress(kernel32, "GetTickCount64"));
126 : }
127 E : DCHECK(get_tick_count64 != kUninitialized);
128 :
129 E : if (get_tick_count64 != NULL)
130 E : return (*get_tick_count64)();
131 :
132 : // Fall back to using the 32-bit counter if the 64-bit one is not available.
133 i : return ::GetTickCount();
134 E : }
135 :
136 E : void GetClockInfo(ClockInfo* clock_info) {
137 E : DCHECK(clock_info != NULL);
138 E : ::memset(clock_info, 0, sizeof(ClockInfo));
139 E : GetTickTimerInfo(&clock_info->ticks_info);
140 E : GetTscTimerInfo(&clock_info->tsc_info);
141 :
142 E : ::GetSystemTimeAsFileTime(&clock_info->file_time);
143 :
144 : // The TSC timer may not always be valid/available.
145 E : if (clock_info->tsc_info.frequency)
146 E : clock_info->tsc_reference = GetTsc();
147 :
148 : // The tick counter is always valid.
149 E : clock_info->ticks_reference = GetTicks();
150 E : }
151 :
152 : bool TicksToFileTime(const ClockInfo& clock_info,
153 : uint64 ticks,
154 E : FILETIME* file_time) {
155 E : DCHECK(file_time != NULL);
156 : return TimerToFileTime(clock_info.file_time,
157 : clock_info.ticks_info,
158 : clock_info.ticks_reference,
159 : ticks,
160 E : file_time);
161 E : }
162 :
163 : bool TscToFileTime(const ClockInfo& clock_info,
164 : uint64 tsc,
165 E : FILETIME* file_time) {
166 E : DCHECK(file_time != NULL);
167 : return TimerToFileTime(clock_info.file_time,
168 : clock_info.tsc_info,
169 : clock_info.tsc_reference,
170 : tsc,
171 E : file_time);
172 E : }
173 :
174 : } // namespace common
175 : } // namespace trace
|