15ffd83dbSDimitry Andric //===-- Timer.cpp ---------------------------------------------------------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric #include "lldb/Utility/Timer.h" 9*0b57cec5SDimitry Andric #include "lldb/Utility/Stream.h" 10*0b57cec5SDimitry Andric 11*0b57cec5SDimitry Andric #include <algorithm> 12*0b57cec5SDimitry Andric #include <map> 13*0b57cec5SDimitry Andric #include <mutex> 14*0b57cec5SDimitry Andric #include <utility> 15*0b57cec5SDimitry Andric #include <vector> 16*0b57cec5SDimitry Andric 17*0b57cec5SDimitry Andric #include <assert.h> 185ffd83dbSDimitry Andric #include <inttypes.h> 19*0b57cec5SDimitry Andric #include <stdarg.h> 20*0b57cec5SDimitry Andric #include <stdio.h> 21*0b57cec5SDimitry Andric 22*0b57cec5SDimitry Andric using namespace lldb_private; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric #define TIMER_INDENT_AMOUNT 2 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric namespace { 27*0b57cec5SDimitry Andric typedef std::vector<Timer *> TimerStack; 28*0b57cec5SDimitry Andric static std::atomic<Timer::Category *> g_categories; 29*0b57cec5SDimitry Andric } // end of anonymous namespace 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric std::atomic<bool> Timer::g_quiet(true); 32*0b57cec5SDimitry Andric std::atomic<unsigned> Timer::g_display_depth(0); 33*0b57cec5SDimitry Andric static std::mutex &GetFileMutex() { 34*0b57cec5SDimitry Andric static std::mutex *g_file_mutex_ptr = new std::mutex(); 35*0b57cec5SDimitry Andric return *g_file_mutex_ptr; 36*0b57cec5SDimitry Andric } 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric static TimerStack &GetTimerStackForCurrentThread() { 39*0b57cec5SDimitry Andric static thread_local TimerStack g_stack; 40*0b57cec5SDimitry Andric return g_stack; 41*0b57cec5SDimitry Andric } 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric Timer::Category::Category(const char *cat) : m_name(cat) { 44*0b57cec5SDimitry Andric m_nanos.store(0, std::memory_order_release); 45*0b57cec5SDimitry Andric m_nanos_total.store(0, std::memory_order_release); 46*0b57cec5SDimitry Andric m_count.store(0, std::memory_order_release); 47*0b57cec5SDimitry Andric Category *expected = g_categories; 48*0b57cec5SDimitry Andric do { 49*0b57cec5SDimitry Andric m_next = expected; 50*0b57cec5SDimitry Andric } while (!g_categories.compare_exchange_weak(expected, this)); 51*0b57cec5SDimitry Andric } 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric void Timer::SetQuiet(bool value) { g_quiet = value; } 54*0b57cec5SDimitry Andric 55*0b57cec5SDimitry Andric Timer::Timer(Timer::Category &category, const char *format, ...) 56*0b57cec5SDimitry Andric : m_category(category), m_total_start(std::chrono::steady_clock::now()) { 57*0b57cec5SDimitry Andric TimerStack &stack = GetTimerStackForCurrentThread(); 58*0b57cec5SDimitry Andric 59*0b57cec5SDimitry Andric stack.push_back(this); 60*0b57cec5SDimitry Andric if (g_quiet && stack.size() <= g_display_depth) { 61*0b57cec5SDimitry Andric std::lock_guard<std::mutex> lock(GetFileMutex()); 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric // Indent 64*0b57cec5SDimitry Andric ::fprintf(stdout, "%*s", int(stack.size() - 1) * TIMER_INDENT_AMOUNT, ""); 65*0b57cec5SDimitry Andric // Print formatted string 66*0b57cec5SDimitry Andric va_list args; 67*0b57cec5SDimitry Andric va_start(args, format); 68*0b57cec5SDimitry Andric ::vfprintf(stdout, format, args); 69*0b57cec5SDimitry Andric va_end(args); 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric // Newline 72*0b57cec5SDimitry Andric ::fprintf(stdout, "\n"); 73*0b57cec5SDimitry Andric } 74*0b57cec5SDimitry Andric } 75*0b57cec5SDimitry Andric 76*0b57cec5SDimitry Andric Timer::~Timer() { 77*0b57cec5SDimitry Andric using namespace std::chrono; 78*0b57cec5SDimitry Andric 79*0b57cec5SDimitry Andric auto stop_time = steady_clock::now(); 80*0b57cec5SDimitry Andric auto total_dur = stop_time - m_total_start; 81*0b57cec5SDimitry Andric auto timer_dur = total_dur - m_child_duration; 82*0b57cec5SDimitry Andric 83*0b57cec5SDimitry Andric TimerStack &stack = GetTimerStackForCurrentThread(); 84*0b57cec5SDimitry Andric if (g_quiet && stack.size() <= g_display_depth) { 85*0b57cec5SDimitry Andric std::lock_guard<std::mutex> lock(GetFileMutex()); 86*0b57cec5SDimitry Andric ::fprintf(stdout, "%*s%.9f sec (%.9f sec)\n", 87*0b57cec5SDimitry Andric int(stack.size() - 1) * TIMER_INDENT_AMOUNT, "", 88*0b57cec5SDimitry Andric duration<double>(total_dur).count(), 89*0b57cec5SDimitry Andric duration<double>(timer_dur).count()); 90*0b57cec5SDimitry Andric } 91*0b57cec5SDimitry Andric 92*0b57cec5SDimitry Andric assert(stack.back() == this); 93*0b57cec5SDimitry Andric stack.pop_back(); 94*0b57cec5SDimitry Andric if (!stack.empty()) 95*0b57cec5SDimitry Andric stack.back()->ChildDuration(total_dur); 96*0b57cec5SDimitry Andric 97*0b57cec5SDimitry Andric // Keep total results for each category so we can dump results. 98*0b57cec5SDimitry Andric m_category.m_nanos += std::chrono::nanoseconds(timer_dur).count(); 99*0b57cec5SDimitry Andric m_category.m_nanos_total += std::chrono::nanoseconds(total_dur).count(); 100*0b57cec5SDimitry Andric m_category.m_count++; 101*0b57cec5SDimitry Andric } 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric void Timer::SetDisplayDepth(uint32_t depth) { g_display_depth = depth; } 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric /* binary function predicate: 106*0b57cec5SDimitry Andric * - returns whether a person is less than another person 107*0b57cec5SDimitry Andric */ 108*0b57cec5SDimitry Andric namespace { 109*0b57cec5SDimitry Andric struct Stats { 110*0b57cec5SDimitry Andric const char *name; 111*0b57cec5SDimitry Andric uint64_t nanos; 112*0b57cec5SDimitry Andric uint64_t nanos_total; 113*0b57cec5SDimitry Andric uint64_t count; 114*0b57cec5SDimitry Andric }; 115*0b57cec5SDimitry Andric } // namespace 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric static bool CategoryMapIteratorSortCriterion(const Stats &lhs, 118*0b57cec5SDimitry Andric const Stats &rhs) { 119*0b57cec5SDimitry Andric return lhs.nanos > rhs.nanos; 120*0b57cec5SDimitry Andric } 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric void Timer::ResetCategoryTimes() { 123*0b57cec5SDimitry Andric for (Category *i = g_categories; i; i = i->m_next) { 124*0b57cec5SDimitry Andric i->m_nanos.store(0, std::memory_order_release); 125*0b57cec5SDimitry Andric i->m_nanos_total.store(0, std::memory_order_release); 126*0b57cec5SDimitry Andric i->m_count.store(0, std::memory_order_release); 127*0b57cec5SDimitry Andric } 128*0b57cec5SDimitry Andric } 129*0b57cec5SDimitry Andric 130*0b57cec5SDimitry Andric void Timer::DumpCategoryTimes(Stream *s) { 131*0b57cec5SDimitry Andric std::vector<Stats> sorted; 132*0b57cec5SDimitry Andric for (Category *i = g_categories; i; i = i->m_next) { 133*0b57cec5SDimitry Andric uint64_t nanos = i->m_nanos.load(std::memory_order_acquire); 134*0b57cec5SDimitry Andric if (nanos) { 135*0b57cec5SDimitry Andric uint64_t nanos_total = i->m_nanos_total.load(std::memory_order_acquire); 136*0b57cec5SDimitry Andric uint64_t count = i->m_count.load(std::memory_order_acquire); 137*0b57cec5SDimitry Andric Stats stats{i->m_name, nanos, nanos_total, count}; 138*0b57cec5SDimitry Andric sorted.push_back(stats); 139*0b57cec5SDimitry Andric } 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric if (sorted.empty()) 142*0b57cec5SDimitry Andric return; // Later code will break without any elements. 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric // Sort by time 145*0b57cec5SDimitry Andric llvm::sort(sorted.begin(), sorted.end(), CategoryMapIteratorSortCriterion); 146*0b57cec5SDimitry Andric 147*0b57cec5SDimitry Andric for (const auto &stats : sorted) 148*0b57cec5SDimitry Andric s->Printf("%.9f sec (total: %.3fs; child: %.3fs; count: %" PRIu64 149*0b57cec5SDimitry Andric ") for %s\n", 150*0b57cec5SDimitry Andric stats.nanos / 1000000000., stats.nanos_total / 1000000000., 151*0b57cec5SDimitry Andric (stats.nanos_total - stats.nanos) / 1000000000., stats.count, 152*0b57cec5SDimitry Andric stats.name); 153*0b57cec5SDimitry Andric } 154