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