1 //===-- Timer.cpp -----------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 #include "lldb/Core/Timer.h" 10 11 #include <map> 12 #include <vector> 13 #include <algorithm> 14 15 #include "lldb/Core/Stream.h" 16 #include "lldb/Host/Mutex.h" 17 18 #include <stdio.h> 19 20 using namespace lldb_private; 21 22 #define TIMER_INDENT_AMOUNT 2 23 static bool g_quiet = true; 24 uint32_t Timer::g_depth = 0; 25 uint32_t Timer::g_display_depth = 0; 26 FILE * Timer::g_file = NULL; 27 typedef std::vector<Timer *> TimerStack; 28 typedef std::map<const char *, uint64_t> TimerCategoryMap; 29 static pthread_key_t g_key; 30 31 static Mutex & 32 GetCategoryMutex() 33 { 34 static Mutex g_category_mutex(Mutex::eMutexTypeNormal); 35 return g_category_mutex; 36 } 37 38 static TimerCategoryMap & 39 GetCategoryMap() 40 { 41 static TimerCategoryMap g_category_map; 42 return g_category_map; 43 } 44 45 46 static TimerStack * 47 GetTimerStackForCurrentThread () 48 { 49 void *timer_stack = ::pthread_getspecific (g_key); 50 if (timer_stack == NULL) 51 { 52 ::pthread_setspecific (g_key, new TimerStack); 53 timer_stack = ::pthread_getspecific (g_key); 54 } 55 return (TimerStack *)timer_stack; 56 } 57 58 void 59 ThreadSpecificCleanup (void *p) 60 { 61 delete (TimerStack *)p; 62 } 63 64 void 65 Timer::SetQuiet (bool value) 66 { 67 g_quiet = value; 68 } 69 70 void 71 Timer::Initialize () 72 { 73 Timer::g_file = stdout; 74 ::pthread_key_create (&g_key, ThreadSpecificCleanup); 75 76 } 77 78 Timer::Timer (const char *category, const char *format, ...) : 79 m_category (category), 80 m_total_start (), 81 m_timer_start (), 82 m_total_ticks (0), 83 m_timer_ticks (0) 84 { 85 if (g_depth++ < g_display_depth) 86 { 87 if (g_quiet == false) 88 { 89 // Indent 90 ::fprintf (g_file, "%*s", g_depth * TIMER_INDENT_AMOUNT, ""); 91 // Print formatted string 92 va_list args; 93 va_start (args, format); 94 ::vfprintf (g_file, format, args); 95 va_end (args); 96 97 // Newline 98 ::fprintf (g_file, "\n"); 99 } 100 TimeValue start_time(TimeValue::Now()); 101 m_total_start = start_time; 102 m_timer_start = start_time; 103 TimerStack *stack = GetTimerStackForCurrentThread (); 104 if (stack) 105 { 106 if (stack->empty() == false) 107 stack->back()->ChildStarted (start_time); 108 stack->push_back(this); 109 } 110 } 111 } 112 113 114 Timer::~Timer() 115 { 116 if (m_total_start.IsValid()) 117 { 118 TimeValue stop_time = TimeValue::Now(); 119 if (m_total_start.IsValid()) 120 { 121 m_total_ticks += (stop_time - m_total_start); 122 m_total_start.Clear(); 123 } 124 if (m_timer_start.IsValid()) 125 { 126 m_timer_ticks += (stop_time - m_timer_start); 127 m_timer_start.Clear(); 128 } 129 130 TimerStack *stack = GetTimerStackForCurrentThread (); 131 if (stack) 132 { 133 assert (stack->back() == this); 134 stack->pop_back(); 135 if (stack->empty() == false) 136 stack->back()->ChildStopped(stop_time); 137 } 138 139 const uint64_t total_nsec_uint = GetTotalElapsedNanoSeconds(); 140 const uint64_t timer_nsec_uint = GetTimerElapsedNanoSeconds(); 141 const double total_nsec = total_nsec_uint; 142 const double timer_nsec = timer_nsec_uint; 143 144 if (g_quiet == false) 145 { 146 147 ::fprintf (g_file, 148 "%*s%.9f sec (%.9f sec)\n", 149 (g_depth - 1) *TIMER_INDENT_AMOUNT, "", 150 total_nsec / 1000000000.0, 151 timer_nsec / 1000000000.0); 152 } 153 154 // Keep total results for each category so we can dump results. 155 Mutex::Locker locker (GetCategoryMutex()); 156 TimerCategoryMap &category_map = GetCategoryMap(); 157 category_map[m_category] += timer_nsec_uint; 158 } 159 if (g_depth > 0) 160 --g_depth; 161 } 162 163 uint64_t 164 Timer::GetTotalElapsedNanoSeconds() 165 { 166 uint64_t total_ticks = m_total_ticks; 167 168 // If we are currently running, we need to add the current 169 // elapsed time of the running timer... 170 if (m_total_start.IsValid()) 171 total_ticks += (TimeValue::Now() - m_total_start); 172 173 return total_ticks; 174 } 175 176 uint64_t 177 Timer::GetTimerElapsedNanoSeconds() 178 { 179 uint64_t timer_ticks = m_timer_ticks; 180 181 // If we are currently running, we need to add the current 182 // elapsed time of the running timer... 183 if (m_timer_start.IsValid()) 184 timer_ticks += (TimeValue::Now() - m_timer_start); 185 186 return timer_ticks; 187 } 188 189 void 190 Timer::ChildStarted (const TimeValue& start_time) 191 { 192 if (m_timer_start.IsValid()) 193 { 194 m_timer_ticks += (start_time - m_timer_start); 195 m_timer_start.Clear(); 196 } 197 } 198 199 void 200 Timer::ChildStopped (const TimeValue& stop_time) 201 { 202 if (!m_timer_start.IsValid()) 203 m_timer_start = stop_time; 204 } 205 206 void 207 Timer::SetDisplayDepth (uint32_t depth) 208 { 209 g_display_depth = depth; 210 } 211 212 213 /* binary function predicate: 214 * - returns whether a person is less than another person 215 */ 216 static bool 217 CategoryMapIteratorSortCriterion (const TimerCategoryMap::const_iterator& lhs, const TimerCategoryMap::const_iterator& rhs) 218 { 219 return lhs->second > rhs->second; 220 } 221 222 223 void 224 Timer::ResetCategoryTimes () 225 { 226 Mutex::Locker locker (GetCategoryMutex()); 227 TimerCategoryMap &category_map = GetCategoryMap(); 228 category_map.clear(); 229 } 230 231 void 232 Timer::DumpCategoryTimes (Stream *s) 233 { 234 Mutex::Locker locker (GetCategoryMutex()); 235 TimerCategoryMap &category_map = GetCategoryMap(); 236 std::vector<TimerCategoryMap::const_iterator> sorted_iterators; 237 TimerCategoryMap::const_iterator pos, end = category_map.end(); 238 for (pos = category_map.begin(); pos != end; ++pos) 239 { 240 sorted_iterators.push_back (pos); 241 } 242 std::sort (sorted_iterators.begin(), sorted_iterators.end(), CategoryMapIteratorSortCriterion); 243 244 const size_t count = sorted_iterators.size(); 245 for (size_t i=0; i<count; ++i) 246 { 247 const double timer_nsec = sorted_iterators[i]->second; 248 s->Printf("%.9f sec for %s\n", timer_nsec / 1000000000.0, sorted_iterators[i]->first); 249 } 250 } 251