blob: e7995426fb05896fe58ac5fcf5d26520ae5552fd [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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 <atomic>
#include "perfetto/base/time.h"
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <Windows.h>
#else
#include <unistd.h>
#endif
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
namespace {
// Returns the current value of the performance counter.
int64_t QPCNowRaw() {
LARGE_INTEGER perf_counter_now = {};
// According to the MSDN documentation for QueryPerformanceCounter(), this
// will never fail on systems that run XP or later.
// https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
::QueryPerformanceCounter(&perf_counter_now);
return perf_counter_now.QuadPart;
}
double TSCTicksPerSecond() {
// The value returned by QueryPerformanceFrequency() cannot be used as the TSC
// frequency, because there is no guarantee that the TSC frequency is equal to
// the performance counter frequency.
// The TSC frequency is cached in a static variable because it takes some time
// to compute it.
static std::atomic<double> tsc_ticks_per_second = 0;
double value = tsc_ticks_per_second.load(std::memory_order_relaxed);
if (value != 0)
return value;
// Increase the thread priority to reduces the chances of having a context
// switch during a reading of the TSC and the performance counter.
const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
// The first time that this function is called, make an initial reading of the
// TSC and the performance counter. Initialization of static variable is
// thread-safe. Threads can race initializing tsc_initial vs
// perf_counter_initial, although they should be storing very similar values.
static const uint64_t tsc_initial = __rdtsc();
static const int64_t perf_counter_initial = QPCNowRaw();
// Make a another reading of the TSC and the performance counter every time
// that this function is called.
const uint64_t tsc_now = __rdtsc();
const int64_t perf_counter_now = QPCNowRaw();
// Reset the thread priority.
::SetThreadPriority(::GetCurrentThread(), previous_priority);
// Make sure that at least 50 ms elapsed between the 2 readings. The first
// time that this function is called, we don't expect this to be the case.
// Note: The longer the elapsed time between the 2 readings is, the more
// accurate the computed TSC frequency will be. The 50 ms value was
// chosen because local benchmarks show that it allows us to get a
// stddev of less than 1 tick/us between multiple runs.
// Note: According to the MSDN documentation for QueryPerformanceFrequency(),
// this will never fail on systems that run XP or later.
// https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
LARGE_INTEGER perf_counter_frequency = {};
::QueryPerformanceFrequency(&perf_counter_frequency);
PERFETTO_CHECK(perf_counter_now >= perf_counter_initial);
const int64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
const double elapsed_time_seconds =
static_cast<double>(perf_counter_ticks) /
static_cast<double>(perf_counter_frequency.QuadPart);
constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
return 0;
// Compute the frequency of the TSC.
PERFETTO_CHECK(tsc_now >= tsc_initial);
const uint64_t tsc_ticks = tsc_now - tsc_initial;
// Racing with another thread to write |tsc_ticks_per_second| is benign
// because both threads will write a valid result.
tsc_ticks_per_second.store(
static_cast<double>(tsc_ticks) / elapsed_time_seconds,
std::memory_order_relaxed);
return tsc_ticks_per_second.load(std::memory_order_relaxed);
}
} // namespace
#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
TimeNanos GetWallTimeNs() {
LARGE_INTEGER freq;
::QueryPerformanceFrequency(&freq);
LARGE_INTEGER counter;
::QueryPerformanceCounter(&counter);
double elapsed_nanoseconds = (1e9 * static_cast<double>(counter.QuadPart)) /
static_cast<double>(freq.QuadPart);
return TimeNanos(static_cast<uint64_t>(elapsed_nanoseconds));
}
TimeNanos GetThreadCPUTimeNs() {
#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
// QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
// actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
// backed by the actual number of CPU cycles executed, rather than a
// constant-rate timer like Intel. To work around this, use GetThreadTimes
// (which isn't as accurate but is meaningful as a measure of elapsed
// per-thread time).
FILETIME dummy, kernel_ftime, user_ftime;
::GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel_ftime,
&user_ftime);
uint64_t kernel_time =
kernel_ftime.dwHighDateTime * 0x100000000 + kernel_ftime.dwLowDateTime;
uint64_t user_time =
user_ftime.dwHighDateTime * 0x100000000 + user_ftime.dwLowDateTime;
return TimeNanos((kernel_time + user_time) * 100);
#else // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
// Get the number of TSC ticks used by the current thread.
ULONG64 thread_cycle_time = 0;
::QueryThreadCycleTime(GetCurrentThread(), &thread_cycle_time);
// Get the frequency of the TSC.
const double tsc_ticks_per_second = TSCTicksPerSecond();
if (tsc_ticks_per_second == 0)
return TimeNanos();
// Return the CPU time of the current thread.
const double thread_time_seconds =
static_cast<double>(thread_cycle_time) / tsc_ticks_per_second;
constexpr int64_t kNanosecondsPerSecond = 1000 * 1000 * 1000;
return TimeNanos(
static_cast<int64_t>(thread_time_seconds * kNanosecondsPerSecond));
#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
}
void SleepMicroseconds(unsigned interval_us) {
// The Windows Sleep function takes a millisecond count. Round up so that
// short sleeps don't turn into a busy wait. Note that the sleep granularity
// on Windows can dynamically vary from 1 ms to ~16 ms, so don't count on this
// being a short sleep.
::Sleep(static_cast<DWORD>((interval_us + 999) / 1000));
}
void InitializeTime() {
#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
// Make an early first call to TSCTicksPerSecond() to start 50 ms elapsed time
// (see comment in TSCTicksPerSecond()).
TSCTicksPerSecond();
#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
}
#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
void SleepMicroseconds(unsigned interval_us) {
::usleep(static_cast<useconds_t>(interval_us));
}
void InitializeTime() {}
#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
std::string GetTimeFmt(const std::string& fmt) {
time_t raw_time;
time(&raw_time);
struct tm* local_tm;
local_tm = localtime(&raw_time);
char buf[128];
PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), local_tm) > 0);
return buf;
}
std::optional<int32_t> GetTimezoneOffsetMins() {
std::string tz = GetTimeFmt("%z");
if (tz.size() != 5 || (tz[0] != '+' && tz[0] != '-'))
return std::nullopt;
char sign = '\0';
int32_t hh = 0;
int32_t mm = 0;
if (sscanf(tz.c_str(), "%c%2d%2d", &sign, &hh, &mm) != 3)
return std::nullopt;
return (hh * 60 + mm) * (sign == '-' ? -1 : 1);
}
} // namespace base
} // namespace perfetto