hphp/util/timer.cpp (152 lines of code) (raw):

/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/util/timer.h" #include <cassert> #include <folly/ClockGettimeWrappers.h> #include <folly/portability/SysResource.h> #include <folly/portability/SysTime.h> #include "hphp/util/logger.h" #include "hphp/util/trace.h" #ifdef FACEBOOK #include "common/time/ClockGettimeNS.h" // nolint #endif namespace HPHP { /////////////////////////////////////////////////////////////////////////////// __thread int64_t s_extra_request_nanoseconds; namespace { /////////////////////////////////////////////////////////////////////////////// #define PRINT_MSG(...) \ switch (m_report) { \ case Log: \ Logger::Info(__VA_ARGS__); \ break; \ case Stderr: \ fprintf(stderr, __VA_ARGS__); \ break; \ case Trace: \ Trace::traceRelease(__VA_ARGS__); \ break; \ default: not_reached(); \ } /////////////////////////////////////////////////////////////////////////////// } Timer::Timer(Type type, const char *name /* = NULL */, ReportType r) : m_type(type), m_report(r) { if (name) { m_name = name; PRINT_MSG("%s...", name); } m_start = measure(); } Timer::~Timer() { if (!m_name.empty()) { report(); } } int64_t Timer::getMicroSeconds() const { return measure() - m_start; } void Timer::report() const { int64_t ms = getMicroSeconds(); int seconds = ms / 1000000; PRINT_MSG("%s took %d'%02d\" (%" PRId64 " us) %s", m_name.c_str(), seconds / 60, seconds % 60, ms, getName()); } void Timer::GetMonotonicTime(timespec &ts) { gettime(CLOCK_MONOTONIC, &ts); } void Timer::GetRealtimeTime(timespec &ts) { gettime(CLOCK_REALTIME, &ts); } static int64_t to_usec(const timeval& tv) { return (int64_t(tv.tv_sec) * 1000000) + tv.tv_usec; } int64_t Timer::GetCurrentTimeMicros() { timeval tv; gettimeofday(&tv, 0); return to_usec(tv); } int64_t Timer::GetRusageMicros(Type t, Who who) { assert(t != WallTime); rusage ru; memset(&ru, 0, sizeof(ru)); auto DEBUG_ONLY ret = getrusage(who, &ru); assert(ret == 0); switch (t) { case SystemCPU: return to_usec(ru.ru_stime); case UserCPU: return to_usec(ru.ru_utime); case TotalCPU: return to_usec(ru.ru_stime) + to_usec(ru.ru_utime); default: always_assert(false); } } int64_t Timer::GetThreadCPUTimeNanos() { return gettime_ns(CLOCK_THREAD_CPUTIME_ID); } int64_t Timer::measure() const { if (m_type == WallTime) { return GetCurrentTimeMicros(); } return GetRusageMicros(m_type, Timer::Self); } const char *Timer::getName() const { switch (m_type) { case WallTime: return "wall time"; case SystemCPU: return "system cpu"; case UserCPU: return "user cpu"; case TotalCPU: return "total cpu"; } always_assert(false); } /////////////////////////////////////////////////////////////////////////////// SlowTimer::SlowTimer(int64_t msThreshold, const char *location, const char *info) : m_timer(Timer::WallTime), m_msThreshold(msThreshold) { if (location) m_location = location; if (info) m_info = info; } SlowTimer::~SlowTimer() { int64_t msec = getTime(); if (msec >= m_msThreshold && m_msThreshold != 0) { Logger::Error("SlowTimer [%" PRId64 "ms] at %s: %s", msec, m_location.c_str(), m_info.c_str()); } } int64_t SlowTimer::getTime() const { return m_timer.getMicroSeconds() / 1000; } /////////////////////////////////////////////////////////////////////////////// int gettime(clockid_t clock, timespec* ts) { if (clock != CLOCK_THREAD_CPUTIME_ID) { return folly::chrono::clock_gettime(clock, ts); } constexpr uint64_t sec_to_ns = 1000000000; #ifdef FACEBOOK uint64_t time; if (!fb_perf_get_thread_cputime_ns(&time)) { time += s_extra_request_nanoseconds; ts->tv_sec = time / sec_to_ns; ts->tv_nsec = time % sec_to_ns; return 0; } #endif auto const ret = folly::chrono::clock_gettime(clock, ts); always_assert(ts->tv_nsec < sec_to_ns); ts->tv_sec += s_extra_request_nanoseconds / sec_to_ns; auto res = ts->tv_nsec + s_extra_request_nanoseconds % sec_to_ns; if (res > sec_to_ns) { res -= sec_to_ns; ts->tv_sec += 1; } ts->tv_nsec = res; return ret; } int64_t gettime_ns(clockid_t clock) { if (clock != CLOCK_THREAD_CPUTIME_ID) { return folly::chrono::clock_gettime_ns(clock); } #ifdef FACEBOOK uint64_t time; if (!fb_perf_get_thread_cputime_ns(&time)) { return time + s_extra_request_nanoseconds; } #endif return folly::chrono::clock_gettime_ns(clock) + s_extra_request_nanoseconds; } int64_t gettime_diff_us(const timespec& start, const timespec& end) { int64_t dsec = end.tv_sec - start.tv_sec; int64_t dnsec = end.tv_nsec - start.tv_nsec; return dsec * 1000000 + dnsec / 1000; } /////////////////////////////////////////////////////////////////////////////// }