Kill rarely used std::sort.
[oota-llvm.git] / lib / Support / Timer.cpp
1 //===-- Timer.cpp - Interval Timing Support -------------------------------===//
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 //
10 // Interval Timing implementation.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/Support/Timer.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/Debug.h"
17 #include "llvm/Support/ManagedStatic.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include "llvm/Support/Format.h"
20 #include "llvm/System/Mutex.h"
21 #include "llvm/System/Process.h"
22 #include "llvm/ADT/OwningPtr.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/StringMap.h"
25 using namespace llvm;
26
27 // CreateInfoOutputFile - Return a file stream to print our output on.
28 namespace llvm { extern raw_ostream *CreateInfoOutputFile(); }
29
30 // getLibSupportInfoOutputFilename - This ugly hack is brought to you courtesy
31 // of constructor/destructor ordering being unspecified by C++.  Basically the
32 // problem is that a Statistic object gets destroyed, which ends up calling
33 // 'GetLibSupportInfoOutputFile()' (below), which calls this function.
34 // LibSupportInfoOutputFilename used to be a global variable, but sometimes it
35 // would get destroyed before the Statistic, causing havoc to ensue.  We "fix"
36 // this by creating the string the first time it is needed and never destroying
37 // it.
38 static ManagedStatic<std::string> LibSupportInfoOutputFilename;
39 static std::string &getLibSupportInfoOutputFilename() {
40   return *LibSupportInfoOutputFilename;
41 }
42
43 static ManagedStatic<sys::SmartMutex<true> > TimerLock;
44
45 namespace {
46   static cl::opt<bool>
47   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
48                                       "tracking (this may be slow)"),
49              cl::Hidden);
50
51   static cl::opt<std::string, true>
52   InfoOutputFilename("info-output-file", cl::value_desc("filename"),
53                      cl::desc("File to append -stats and -timer output to"),
54                    cl::Hidden, cl::location(getLibSupportInfoOutputFilename()));
55 }
56
57 // CreateInfoOutputFile - Return a file stream to print our output on.
58 raw_ostream *llvm::CreateInfoOutputFile() {
59   const std::string &OutputFilename = getLibSupportInfoOutputFilename();
60   if (OutputFilename.empty())
61     return new raw_fd_ostream(2, false); // stderr.
62   if (OutputFilename == "-")
63     return new raw_fd_ostream(1, false); // stdout.
64   
65   // Append mode is used because the info output file is opened and closed
66   // each time -stats or -time-passes wants to print output to it. To
67   // compensate for this, the test-suite Makefiles have code to delete the
68   // info output file before running commands which write to it.
69   std::string Error;
70   raw_ostream *Result = new raw_fd_ostream(OutputFilename.c_str(),
71                                            Error, raw_fd_ostream::F_Append);
72   if (Error.empty())
73     return Result;
74   
75   errs() << "Error opening info-output-file '"
76     << OutputFilename << " for appending!\n";
77   delete Result;
78   return new raw_fd_ostream(2, false); // stderr.
79 }
80
81
82 static TimerGroup *DefaultTimerGroup = 0;
83 static TimerGroup *getDefaultTimerGroup() {
84   TimerGroup *tmp = DefaultTimerGroup;
85   sys::MemoryFence();
86   if (tmp) return tmp;
87   
88   llvm_acquire_global_lock();
89   tmp = DefaultTimerGroup;
90   if (!tmp) {
91     tmp = new TimerGroup("Miscellaneous Ungrouped Timers");
92     sys::MemoryFence();
93     DefaultTimerGroup = tmp;
94   }
95   llvm_release_global_lock();
96
97   return tmp;
98 }
99
100 //===----------------------------------------------------------------------===//
101 // Timer Implementation
102 //===----------------------------------------------------------------------===//
103
104 void Timer::init(StringRef N) {
105   assert(TG == 0 && "Timer already initialized");
106   Name.assign(N.begin(), N.end());
107   Started = false;
108   TG = getDefaultTimerGroup();
109   TG->addTimer(*this);
110 }
111
112 void Timer::init(StringRef N, TimerGroup &tg) {
113   assert(TG == 0 && "Timer already initialized");
114   Name.assign(N.begin(), N.end());
115   Started = false;
116   TG = &tg;
117   TG->addTimer(*this);
118 }
119
120 Timer::~Timer() {
121   if (!TG) return;  // Never initialized, or already cleared.
122   TG->removeTimer(*this);
123 }
124
125 static inline size_t getMemUsage() {
126   if (!TrackSpace) return 0;
127   return sys::Process::GetMallocUsage();
128 }
129
130 TimeRecord TimeRecord::getCurrentTime(bool Start) {
131   TimeRecord Result;
132   sys::TimeValue now(0,0), user(0,0), sys(0,0);
133   
134   if (Start) {
135     Result.MemUsed = getMemUsage();
136     sys::Process::GetTimeUsage(now, user, sys);
137   } else {
138     sys::Process::GetTimeUsage(now, user, sys);
139     Result.MemUsed = getMemUsage();
140   }
141
142   Result.WallTime   =  now.seconds() +  now.microseconds() / 1000000.0;
143   Result.UserTime   = user.seconds() + user.microseconds() / 1000000.0;
144   Result.SystemTime =  sys.seconds() +  sys.microseconds() / 1000000.0;
145   return Result;
146 }
147
148 static ManagedStatic<std::vector<Timer*> > ActiveTimers;
149
150 void Timer::startTimer() {
151   Started = true;
152   ActiveTimers->push_back(this);
153   Time -= TimeRecord::getCurrentTime(true);
154 }
155
156 void Timer::stopTimer() {
157   Time += TimeRecord::getCurrentTime(false);
158
159   if (ActiveTimers->back() == this) {
160     ActiveTimers->pop_back();
161   } else {
162     std::vector<Timer*>::iterator I =
163       std::find(ActiveTimers->begin(), ActiveTimers->end(), this);
164     assert(I != ActiveTimers->end() && "stop but no startTimer?");
165     ActiveTimers->erase(I);
166   }
167 }
168
169 static void printVal(double Val, double Total, raw_ostream &OS) {
170   if (Total < 1e-7)   // Avoid dividing by zero.
171     OS << "        -----     ";
172   else {
173     OS << "  " << format("%7.4f", Val) << " (";
174     OS << format("%5.1f", Val*100/Total) << "%)";
175   }
176 }
177
178 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
179   if (Total.getUserTime())
180     printVal(getUserTime(), Total.getUserTime(), OS);
181   if (Total.getSystemTime())
182     printVal(getSystemTime(), Total.getSystemTime(), OS);
183   if (Total.getProcessTime())
184     printVal(getProcessTime(), Total.getProcessTime(), OS);
185   printVal(getWallTime(), Total.getWallTime(), OS);
186   
187   OS << "  ";
188   
189   if (Total.getMemUsed())
190     OS << format("%9lld", (long long)getMemUsed()) << "  ";
191 }
192
193
194 //===----------------------------------------------------------------------===//
195 //   NamedRegionTimer Implementation
196 //===----------------------------------------------------------------------===//
197
198 namespace {
199
200 typedef StringMap<Timer> Name2TimerMap;
201
202 class Name2PairMap {
203   StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
204 public:
205   ~Name2PairMap() {
206     for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
207          I = Map.begin(), E = Map.end(); I != E; ++I)
208       delete I->second.first;
209   }
210   
211   Timer &get(StringRef Name, StringRef GroupName) {
212     sys::SmartScopedLock<true> L(*TimerLock);
213     
214     std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
215     
216     if (!GroupEntry.first)
217       GroupEntry.first = new TimerGroup(GroupName);
218     
219     Timer &T = GroupEntry.second[Name];
220     if (!T.isInitialized())
221       T.init(Name, *GroupEntry.first);
222     return T;
223   }
224 };
225
226 }
227
228 static ManagedStatic<Name2TimerMap> NamedTimers;
229 static ManagedStatic<Name2PairMap> NamedGroupedTimers;
230
231 static Timer &getNamedRegionTimer(StringRef Name) {
232   sys::SmartScopedLock<true> L(*TimerLock);
233   
234   Timer &T = (*NamedTimers)[Name];
235   if (!T.isInitialized())
236     T.init(Name);
237   return T;
238 }
239
240 NamedRegionTimer::NamedRegionTimer(StringRef Name,
241                                    bool Enabled)
242   : TimeRegion(!Enabled ? 0 : &getNamedRegionTimer(Name)) {}
243
244 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName,
245                                    bool Enabled)
246   : TimeRegion(!Enabled ? 0 : &NamedGroupedTimers->get(Name, GroupName)) {}
247
248 //===----------------------------------------------------------------------===//
249 //   TimerGroup Implementation
250 //===----------------------------------------------------------------------===//
251
252 /// TimerGroupList - This is the global list of TimerGroups, maintained by the
253 /// TimerGroup ctor/dtor and is protected by the TimerLock lock.
254 static TimerGroup *TimerGroupList = 0;
255
256 TimerGroup::TimerGroup(StringRef name)
257   : Name(name.begin(), name.end()), FirstTimer(0) {
258     
259   // Add the group to TimerGroupList.
260   sys::SmartScopedLock<true> L(*TimerLock);
261   if (TimerGroupList)
262     TimerGroupList->Prev = &Next;
263   Next = TimerGroupList;
264   Prev = &TimerGroupList;
265   TimerGroupList = this;
266 }
267
268 TimerGroup::~TimerGroup() {
269   // If the timer group is destroyed before the timers it owns, accumulate and
270   // print the timing data.
271   while (FirstTimer != 0)
272     removeTimer(*FirstTimer);
273   
274   // Remove the group from the TimerGroupList.
275   sys::SmartScopedLock<true> L(*TimerLock);
276   *Prev = Next;
277   if (Next)
278     Next->Prev = Prev;
279 }
280
281
282 void TimerGroup::removeTimer(Timer &T) {
283   sys::SmartScopedLock<true> L(*TimerLock);
284   
285   // If the timer was started, move its data to TimersToPrint.
286   if (T.Started)
287     TimersToPrint.push_back(std::make_pair(T.Time, T.Name));
288
289   T.TG = 0;
290   
291   // Unlink the timer from our list.
292   *T.Prev = T.Next;
293   if (T.Next)
294     T.Next->Prev = T.Prev;
295   
296   // Print the report when all timers in this group are destroyed if some of
297   // them were started.
298   if (FirstTimer != 0 || TimersToPrint.empty())
299     return;
300   
301   raw_ostream *OutStream = CreateInfoOutputFile();
302   PrintQueuedTimers(*OutStream);
303   delete OutStream;   // Close the file.
304 }
305
306 void TimerGroup::addTimer(Timer &T) {
307   sys::SmartScopedLock<true> L(*TimerLock);
308   
309   // Add the timer to our list.
310   if (FirstTimer)
311     FirstTimer->Prev = &T.Next;
312   T.Next = FirstTimer;
313   T.Prev = &FirstTimer;
314   FirstTimer = &T;
315 }
316
317 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
318   // Sort the timers in descending order by amount of time taken.
319   array_pod_sort(TimersToPrint.begin(), TimersToPrint.end());
320
321   TimeRecord Total;
322   for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
323     Total += TimersToPrint[i].first;
324   
325   // Print out timing header.
326   OS << "===" << std::string(73, '-') << "===\n";
327   // Figure out how many spaces to indent TimerGroup name.
328   unsigned Padding = (80-Name.length())/2;
329   if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
330   OS.indent(Padding) << Name << '\n';
331   OS << "===" << std::string(73, '-') << "===\n";
332   
333   // If this is not an collection of ungrouped times, print the total time.
334   // Ungrouped timers don't really make sense to add up.  We still print the
335   // TOTAL line to make the percentages make sense.
336   if (this != DefaultTimerGroup) {
337     OS << "  Total Execution Time: ";
338     OS << format("%5.4f", Total.getProcessTime()) << " seconds (";
339     OS << format("%5.4f", Total.getWallTime()) << " wall clock)\n";
340   }
341   OS << '\n';
342   
343   if (Total.getUserTime())
344     OS << "   ---User Time---";
345   if (Total.getSystemTime())
346     OS << "   --System Time--";
347   if (Total.getProcessTime())
348     OS << "   --User+System--";
349   OS << "   ---Wall Time---";
350   if (Total.getMemUsed())
351     OS << "  ---Mem---";
352   OS << "  --- Name ---\n";
353   
354   // Loop through all of the timing data, printing it out.
355   for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) {
356     const std::pair<TimeRecord, std::string> &Entry = TimersToPrint[e-i-1];
357     Entry.first.print(Total, OS);
358     OS << Entry.second << '\n';
359   }
360   
361   Total.print(Total, OS);
362   OS << "Total\n\n";
363   OS.flush();
364   
365   TimersToPrint.clear();
366 }
367
368 /// print - Print any started timers in this group and zero them.
369 void TimerGroup::print(raw_ostream &OS) {
370   sys::SmartScopedLock<true> L(*TimerLock);
371
372   // See if any of our timers were started, if so add them to TimersToPrint and
373   // reset them.
374   for (Timer *T = FirstTimer; T; T = T->Next) {
375     if (!T->Started) continue;
376     TimersToPrint.push_back(std::make_pair(T->Time, T->Name));
377     
378     // Clear out the time.
379     T->Started = 0;
380     T->Time = TimeRecord();
381   }
382
383   // If any timers were started, print the group.
384   if (!TimersToPrint.empty())
385     PrintQueuedTimers(OS);
386 }
387
388 /// printAll - This static method prints all timers and clears them all out.
389 void TimerGroup::printAll(raw_ostream &OS) {
390   sys::SmartScopedLock<true> L(*TimerLock);
391
392   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
393     TG->print(OS);
394 }