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