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