// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #include #include #include #include #include #include using namespace hilti::rt; using namespace hilti::rt::profiler; // Helper to get a platform-specific, monotonic high-resolution clock. inline static uint64_t _getClock() { #if defined(__APPLE__) return clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW); #else struct timespec t; #if defined(__linux__) clock_gettime(CLOCK_MONOTONIC_RAW, &t); #elif defined(__FreeBSD__) clock_gettime(CLOCK_MONOTONIC_PRECISE, &t); #else clock_gettime(CLOCK_MONOTONIC, &t); // POSIX #endif return (static_cast(1000000000) * t.tv_sec) + t.tv_nsec; #endif } void Profiler::_register() const { ++detail::globalState()->profilers[_name].instances; } profiler::Measurement Profiler::snapshot(std::optional volume) { if ( ! detail::globalState()->profiling_enabled ) return Measurement(); Measurement m; m.time = _getClock(); m.volume = volume; return m; } void Profiler::record(const Measurement& end) { if ( ! detail::globalState()->profiling_enabled ) return; if ( ! *this ) return; // already recorded auto& p = detail::globalState()->profilers[_name]; assert(p.instances > 0); ++p.m.count; // With recursive calls, we only time the top-level. if ( p.instances-- == 1 ) p.m += (end - _start); _name.clear(); } void profiler::detail::init() { if ( ! configuration::get().enable_profiling ) return; rt::detail::globalState()->profiling_enabled = true; auto& p = rt::detail::globalState()->profilers["hilti/total"]; p.m = Profiler::snapshot(); } void profiler::detail::done() { if ( ! rt::detail::globalState()->profiling_enabled ) return; auto& p = rt::detail::globalState()->profilers["hilti/total"]; p.m = (Profiler::snapshot() - p.m); ++p.m.count; report(); } std::optional profiler::get(const std::string& name) { const auto& profilers = rt::detail::globalState()->profilers; if ( auto i = profilers.find(name); i != profilers.end() ) return i->second.m; else return {}; } void profiler::report() { static const auto fmt_header = "#%-49s %10s %10s %10s %10s %15s\n"; static const auto fmt_data = "%-50s %10" PRIu64 " %10" PRIu64 " %10.2f %10.2f %15s\n"; const auto& profilers = rt::detail::globalState()->profilers; std::cerr << "#\n# Profiling results\n#\n"; std::cerr << fmt(fmt_header, "name", "count", "time", "avg-%", "total-%", "volume"); std::set names; for ( const auto& [name, _] : profilers ) names.insert(name); auto total_time = static_cast(profilers.at("hilti/total").m.time); for ( const auto& name : names ) { const auto& p = profilers.at(name).m; if ( p.count == 0 ) continue; auto percent = static_cast(p.time) * 100.0 / total_time; std::string volume = "-"; if ( p.volume ) volume = fmt("%" PRIu64, *p.volume); std::cerr << fmt(fmt_data, name, p.count, p.time, percent / static_cast(p.count), percent, volume); } }