blob: 41bc133a5fc7a8a5fcca4bd0b55b7f402dd71fbc [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/trace/common/clock.h"
#include <WinBase.h>
#include <type_traits>
#include "base/logging.h"
#include "base/win/registry.h"
namespace trace {
namespace common {
namespace {
union LargeInteger {
LARGE_INTEGER li;
uint64 ui64;
COMPILE_ASSERT(sizeof(LARGE_INTEGER) == sizeof(uint64),
LARGE_INTEGER_and_uint64_must_have_same_size);
};
typedef ULONGLONG (*GetTickCount64Ptr)();
} // namespace
void GetTickTimerInfo(TimerInfo* timer_info) {
DCHECK(timer_info != NULL);
// Ticks are in milliseconds.
timer_info->frequency = 1000;
// The resolution of the tick counter varies, but is documented to have a
// worst case of 16 ms.
timer_info->resolution = 16;
}
void GetTscTimerInfo(TimerInfo* timer_info) {
DCHECK(timer_info != NULL);
::memset(timer_info, 0, sizeof(TimerInfo));
// Check the TscInvariant flag to see if we can rely on TSC as a constant
// rate timer that is synchronous across all cores. This is in
// CPUID.80000007.EDX[8].
int info[4];
::__cpuid(info, 0x80000007);
if ((info[3] & (1 << 8)) == 0)
return;
// Get the CPU frequency. If all is well, this is the frequency of the TSC
// timer.
base::win::RegKey cpureg;
DWORD mhz = 0;
if (cpureg.Open(HKEY_LOCAL_MACHINE,
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
KEY_READ) != 0 ||
cpureg.ReadValueDW(L"~MHz", &mhz) != 0) {
LOG(WARNING) << "Unable to get CPU frequency from registry.";
return;
}
// An invariant TSC is documented to run at the fastest clock speed of the
// CPU.
timer_info->frequency = mhz * 1000000;
timer_info->resolution = 1;
}
bool TimerToFileTime(const FILETIME& file_time_ref,
const TimerInfo& timer_info,
const uint64& timer_ref,
const uint64& timer_value,
FILETIME* file_time) {
DCHECK(file_time != NULL);
// This only works if we have valid timer information.
if (timer_info.frequency == 0 || timer_info.resolution == 0)
return false;
uint64 t = (static_cast<uint64>(file_time_ref.dwHighDateTime) << 32) |
file_time_ref.dwLowDateTime;
// The filetime is expressed in 100ns intervals.
double cycles_per_100ns = 1.0e-7 * timer_info.frequency;
double elapsed_100ns_intervals =
(static_cast<double>(timer_value) - timer_ref) / cycles_per_100ns;
double new_file_time = t + elapsed_100ns_intervals;
if (new_file_time < 0)
return false;
t = static_cast<uint64>(new_file_time);
file_time->dwLowDateTime = t & 0xFFFFFFFF;
file_time->dwHighDateTime = t >> 32;
return true;
}
uint64 GetTicks() {
// We can't explicitly invoke GetTickCount64 as it doesn't exist in Windows
// XP. This would make all of our trace code unable to be run on XP systems.
const GetTickCount64Ptr kUninitialized =
reinterpret_cast<GetTickCount64Ptr>(1);
static GetTickCount64Ptr get_tick_count64 = kUninitialized;
// This is racy but safe. Worst case scenario multiple threads do the lookup,
// each of them writing the same value to |get_tick_count64|. Since writes are
// atomic all will be well by the time it is dereferenced.
if (get_tick_count64 == kUninitialized) {
HMODULE kernel32 = ::GetModuleHandleA("kernel32.dll");
DCHECK(kernel32 != NULL);
get_tick_count64 = reinterpret_cast<GetTickCount64Ptr>(
::GetProcAddress(kernel32, "GetTickCount64"));
}
DCHECK(get_tick_count64 != kUninitialized);
if (get_tick_count64 != NULL)
return (*get_tick_count64)();
// Fall back to using the 32-bit counter if the 64-bit one is not available.
return ::GetTickCount();
}
void GetClockInfo(ClockInfo* clock_info) {
DCHECK(clock_info != NULL);
::memset(clock_info, 0, sizeof(ClockInfo));
GetTickTimerInfo(&clock_info->ticks_info);
GetTscTimerInfo(&clock_info->tsc_info);
::GetSystemTimeAsFileTime(&clock_info->file_time);
// The TSC timer may not always be valid/available.
if (clock_info->tsc_info.frequency)
clock_info->tsc_reference = GetTsc();
// The tick counter is always valid.
clock_info->ticks_reference = GetTicks();
}
bool TicksToFileTime(const ClockInfo& clock_info,
uint64 ticks,
FILETIME* file_time) {
DCHECK(file_time != NULL);
return TimerToFileTime(clock_info.file_time,
clock_info.ticks_info,
clock_info.ticks_reference,
ticks,
file_time);
}
bool TscToFileTime(const ClockInfo& clock_info,
uint64 tsc,
FILETIME* file_time) {
DCHECK(file_time != NULL);
return TimerToFileTime(clock_info.file_time,
clock_info.tsc_info,
clock_info.tsc_reference,
tsc,
file_time);
}
} // namespace common
} // namespace trace