c4920f0ba573ad4bee57d2119272f3d5f15c079f
[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/ManagedStatic.h"
17 #include "llvm/Support/Streams.h"
18 #include "llvm/System/Mutex.h"
19 #include "llvm/System/Process.h"
20 #include <algorithm>
21 #include <fstream>
22 #include <functional>
23 #include <map>
24 using namespace llvm;
25
26 // GetLibSupportInfoOutputFile - Return a file stream to print our output on.
27 namespace llvm { extern std::ostream *GetLibSupportInfoOutputFile(); }
28
29 // getLibSupportInfoOutputFilename - This ugly hack is brought to you courtesy
30 // of constructor/destructor ordering being unspecified by C++.  Basically the
31 // problem is that a Statistic object gets destroyed, which ends up calling
32 // 'GetLibSupportInfoOutputFile()' (below), which calls this function.
33 // LibSupportInfoOutputFilename used to be a global variable, but sometimes it
34 // would get destroyed before the Statistic, causing havoc to ensue.  We "fix"
35 // this by creating the string the first time it is needed and never destroying
36 // it.
37 static ManagedStatic<std::string> LibSupportInfoOutputFilename;
38 static std::string &getLibSupportInfoOutputFilename() {
39   return *LibSupportInfoOutputFilename;
40 }
41
42 namespace {
43   static cl::opt<bool>
44   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
45                                       "tracking (this may be slow)"),
46              cl::Hidden);
47
48   static cl::opt<std::string, true>
49   InfoOutputFilename("info-output-file", cl::value_desc("filename"),
50                      cl::desc("File to append -stats and -timer output to"),
51                    cl::Hidden, cl::location(getLibSupportInfoOutputFilename()));
52 }
53
54 static ManagedStatic<sys::SmartMutex<true> > TimerLock;
55 static ManagedStatic<TimerGroup> DefaultTimerGroup;
56 static TimerGroup *getDefaultTimerGroup() {
57   return &*DefaultTimerGroup;
58 }
59
60 Timer::Timer(const std::string &N)
61   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
62     Started(false), TG(getDefaultTimerGroup()) {
63   sys::SmartScopedLock<true> Lock(&*TimerLock);
64   TG->addTimer();
65 }
66
67 Timer::Timer(const std::string &N, TimerGroup &tg)
68   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
69     Started(false), TG(&tg) {
70   sys::SmartScopedLock<true> Lock(&*TimerLock);
71   TG->addTimer();
72 }
73
74 Timer::Timer(const Timer &T) {
75   sys::SmartScopedLock<true> Lock(&*TimerLock);
76   TG = T.TG;
77   if (TG) TG->addTimer();
78   operator=(T);
79 }
80
81
82 // Copy ctor, initialize with no TG member.
83 Timer::Timer(bool, const Timer &T) {
84   sys::SmartScopedLock<true> Lock(&*TimerLock);
85   TG = T.TG;     // Avoid assertion in operator=
86   operator=(T);  // Copy contents
87   TG = 0;
88 }
89
90
91 Timer::~Timer() {
92   sys::SmartScopedLock<true> Lock(&*TimerLock);
93   if (TG) {
94     if (Started) {
95       Started = false;
96       TG->addTimerToPrint(*this);
97     }
98     TG->removeTimer();
99   }
100 }
101
102 static inline size_t getMemUsage() {
103   if (TrackSpace)
104     return sys::Process::GetMallocUsage();
105   return 0;
106 }
107
108 struct TimeRecord {
109   double Elapsed, UserTime, SystemTime;
110   ssize_t MemUsed;
111 };
112
113 static TimeRecord getTimeRecord(bool Start) {
114   TimeRecord Result;
115
116   sys::TimeValue now(0,0);
117   sys::TimeValue user(0,0);
118   sys::TimeValue sys(0,0);
119
120   ssize_t MemUsed = 0;
121   if (Start) {
122     MemUsed = getMemUsage();
123     sys::Process::GetTimeUsage(now,user,sys);
124   } else {
125     sys::Process::GetTimeUsage(now,user,sys);
126     MemUsed = getMemUsage();
127   }
128
129   Result.Elapsed  = now.seconds()  + now.microseconds()  / 1000000.0;
130   Result.UserTime = user.seconds() + user.microseconds() / 1000000.0;
131   Result.SystemTime  = sys.seconds()  + sys.microseconds()  / 1000000.0;
132   Result.MemUsed  = MemUsed;
133
134   return Result;
135 }
136
137 static ManagedStatic<std::vector<Timer*> > ActiveTimers;
138 static ManagedStatic<sys::SmartMutex<true> > ActiveTimerLock;
139
140 void Timer::startTimer() {
141   sys::SmartScopedLock<true> Lock(&*ActiveTimerLock);
142   Started = true;
143   ActiveTimers->push_back(this);
144   TimeRecord TR = getTimeRecord(true);
145   Elapsed    -= TR.Elapsed;
146   UserTime   -= TR.UserTime;
147   SystemTime -= TR.SystemTime;
148   MemUsed    -= TR.MemUsed;
149   PeakMemBase = TR.MemUsed;
150 }
151
152 void Timer::stopTimer() {
153   sys::SmartScopedLock<true> Lock(&*ActiveTimerLock);
154   TimeRecord TR = getTimeRecord(false);
155   Elapsed    += TR.Elapsed;
156   UserTime   += TR.UserTime;
157   SystemTime += TR.SystemTime;
158   MemUsed    += TR.MemUsed;
159
160   if (ActiveTimers->back() == this) {
161     ActiveTimers->pop_back();
162   } else {
163     std::vector<Timer*>::iterator I =
164       std::find(ActiveTimers->begin(), ActiveTimers->end(), this);
165     assert(I != ActiveTimers->end() && "stop but no startTimer?");
166     ActiveTimers->erase(I);
167   }
168 }
169
170 void Timer::sum(const Timer &T) {
171   Elapsed    += T.Elapsed;
172   UserTime   += T.UserTime;
173   SystemTime += T.SystemTime;
174   MemUsed    += T.MemUsed;
175   PeakMem    += T.PeakMem;
176 }
177
178 /// addPeakMemoryMeasurement - This method should be called whenever memory
179 /// usage needs to be checked.  It adds a peak memory measurement to the
180 /// currently active timers, which will be printed when the timer group prints
181 ///
182 void Timer::addPeakMemoryMeasurement() {
183   sys::SmartScopedLock<true> Lock(&*ActiveTimerLock);
184   size_t MemUsed = getMemUsage();
185
186   for (std::vector<Timer*>::iterator I = ActiveTimers->begin(),
187          E = ActiveTimers->end(); I != E; ++I)
188     (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase);
189 }
190
191 //===----------------------------------------------------------------------===//
192 //   NamedRegionTimer Implementation
193 //===----------------------------------------------------------------------===//
194
195 namespace {
196
197 typedef std::map<std::string, Timer> Name2Timer;
198 typedef std::map<std::string, std::pair<TimerGroup, Name2Timer> > Name2Pair;
199
200 }
201
202 static ManagedStatic<Name2Timer> NamedTimers;
203
204 static ManagedStatic<Name2Pair> NamedGroupedTimers;
205
206 static ManagedStatic<sys::SmartMutex<true> > NamedTimerLock;
207
208 static Timer &getNamedRegionTimer(const std::string &Name) {
209   sys::SmartScopedLock<true> Lock(&*NamedTimerLock);
210   Name2Timer::iterator I = NamedTimers->find(Name);
211   if (I != NamedTimers->end())
212     return I->second;
213
214   return NamedTimers->insert(I, std::make_pair(Name, Timer(Name)))->second;
215 }
216
217 static Timer &getNamedRegionTimer(const std::string &Name,
218                                   const std::string &GroupName) {
219   sys::SmartScopedLock<true> Lock(&*NamedTimerLock);
220
221   Name2Pair::iterator I = NamedGroupedTimers->find(GroupName);
222   if (I == NamedGroupedTimers->end()) {
223     TimerGroup TG(GroupName);
224     std::pair<TimerGroup, Name2Timer> Pair(TG, Name2Timer());
225     I = NamedGroupedTimers->insert(I, std::make_pair(GroupName, Pair));
226   }
227
228   Name2Timer::iterator J = I->second.second.find(Name);
229   if (J == I->second.second.end())
230     J = I->second.second.insert(J,
231                                 std::make_pair(Name,
232                                                Timer(Name,
233                                                      I->second.first)));
234
235   return J->second;
236 }
237
238 NamedRegionTimer::NamedRegionTimer(const std::string &Name)
239   : TimeRegion(getNamedRegionTimer(Name)) {}
240
241 NamedRegionTimer::NamedRegionTimer(const std::string &Name,
242                                    const std::string &GroupName)
243   : TimeRegion(getNamedRegionTimer(Name, GroupName)) {}
244
245 //===----------------------------------------------------------------------===//
246 //   TimerGroup Implementation
247 //===----------------------------------------------------------------------===//
248
249 // printAlignedFP - Simulate the printf "%A.Bf" format, where A is the
250 // TotalWidth size, and B is the AfterDec size.
251 //
252 static void printAlignedFP(double Val, unsigned AfterDec, unsigned TotalWidth,
253                            std::ostream &OS) {
254   assert(TotalWidth >= AfterDec+1 && "Bad FP Format!");
255   OS.width(TotalWidth-AfterDec-1);
256   char OldFill = OS.fill();
257   OS.fill(' ');
258   OS << (int)Val;  // Integer part;
259   OS << ".";
260   OS.width(AfterDec);
261   OS.fill('0');
262   unsigned ResultFieldSize = 1;
263   while (AfterDec--) ResultFieldSize *= 10;
264   OS << (int)(Val*ResultFieldSize) % ResultFieldSize;
265   OS.fill(OldFill);
266 }
267
268 static void printVal(double Val, double Total, std::ostream &OS) {
269   if (Total < 1e-7)   // Avoid dividing by zero...
270     OS << "        -----     ";
271   else {
272     OS << "  ";
273     printAlignedFP(Val, 4, 7, OS);
274     OS << " (";
275     printAlignedFP(Val*100/Total, 1, 5, OS);
276     OS << "%)";
277   }
278 }
279
280 void Timer::print(const Timer &Total, std::ostream &OS) {
281   if (Total.UserTime)
282     printVal(UserTime, Total.UserTime, OS);
283   if (Total.SystemTime)
284     printVal(SystemTime, Total.SystemTime, OS);
285   if (Total.getProcessTime())
286     printVal(getProcessTime(), Total.getProcessTime(), OS);
287   printVal(Elapsed, Total.Elapsed, OS);
288
289   OS << "  ";
290
291   if (Total.MemUsed) {
292     OS.width(9);
293     OS << MemUsed << "  ";
294   }
295   if (Total.PeakMem) {
296     if (PeakMem) {
297       OS.width(9);
298       OS << PeakMem << "  ";
299     } else
300       OS << "           ";
301   }
302   OS << Name << "\n";
303
304   Started = false;  // Once printed, don't print again
305 }
306
307 // GetLibSupportInfoOutputFile - Return a file stream to print our output on...
308 std::ostream *
309 llvm::GetLibSupportInfoOutputFile() {
310   std::string &LibSupportInfoOutputFilename = getLibSupportInfoOutputFilename();
311   if (LibSupportInfoOutputFilename.empty())
312     return cerr.stream();
313   if (LibSupportInfoOutputFilename == "-")
314     return cout.stream();
315
316   std::ostream *Result = new std::ofstream(LibSupportInfoOutputFilename.c_str(),
317                                            std::ios::app);
318   if (!Result->good()) {
319     cerr << "Error opening info-output-file '"
320          << LibSupportInfoOutputFilename << " for appending!\n";
321     delete Result;
322     return cerr.stream();
323   }
324   return Result;
325 }
326
327
328 void TimerGroup::removeTimer() {
329   if (--NumTimers == 0 && !TimersToPrint.empty()) { // Print timing report...
330     // Sort the timers in descending order by amount of time taken...
331     std::sort(TimersToPrint.begin(), TimersToPrint.end(),
332               std::greater<Timer>());
333
334     // Figure out how many spaces to indent TimerGroup name...
335     unsigned Padding = (80-Name.length())/2;
336     if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
337
338     std::ostream *OutStream = GetLibSupportInfoOutputFile();
339
340     ++NumTimers;
341     {  // Scope to contain Total timer... don't allow total timer to drop us to
342        // zero timers...
343       Timer Total("TOTAL");
344
345       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
346         Total.sum(TimersToPrint[i]);
347
348       // Print out timing header...
349       *OutStream << "===" << std::string(73, '-') << "===\n"
350                  << std::string(Padding, ' ') << Name << "\n"
351                  << "===" << std::string(73, '-')
352                  << "===\n";
353
354       // If this is not an collection of ungrouped times, print the total time.
355       // Ungrouped timers don't really make sense to add up.  We still print the
356       // TOTAL line to make the percentages make sense.
357       if (this != &*DefaultTimerGroup) {
358         *OutStream << "  Total Execution Time: ";
359
360         printAlignedFP(Total.getProcessTime(), 4, 5, *OutStream);
361         *OutStream << " seconds (";
362         printAlignedFP(Total.getWallTime(), 4, 5, *OutStream);
363         *OutStream << " wall clock)\n";
364       }
365       *OutStream << "\n";
366
367       if (Total.UserTime)
368         *OutStream << "   ---User Time---";
369       if (Total.SystemTime)
370         *OutStream << "   --System Time--";
371       if (Total.getProcessTime())
372         *OutStream << "   --User+System--";
373       *OutStream << "   ---Wall Time---";
374       if (Total.getMemUsed())
375         *OutStream << "  ---Mem---";
376       if (Total.getPeakMem())
377         *OutStream << "  -PeakMem-";
378       *OutStream << "  --- Name ---\n";
379
380       // Loop through all of the timing data, printing it out...
381       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
382         TimersToPrint[i].print(Total, *OutStream);
383
384       Total.print(Total, *OutStream);
385       *OutStream << std::endl;  // Flush output
386     }
387     --NumTimers;
388
389     TimersToPrint.clear();
390
391     if (OutStream != cerr.stream() && OutStream != cout.stream())
392       delete OutStream;   // Close the file...
393   }
394 }
395