Fix compilation problem with some versions of G++
[oota-llvm.git] / lib / Support / Timer.cpp
1 //===-- Timer.cpp - Interval Timing Support -------------------------------===//
2 //
3 // Interval Timing implementation.
4 //
5 //===----------------------------------------------------------------------===//
6
7 #include "Support/Timer.h"
8 #include "Support/CommandLine.h"
9 #include <sys/resource.h>
10 #include <sys/time.h>
11 #include <sys/unistd.h>
12 #include <unistd.h>
13 #include <malloc.h>
14 #include <stdio.h>
15 #include <iostream>
16 #include <algorithm>
17 #include <functional>
18 #include <fstream>
19
20 std::string LibSupportInfoOutputFilename;
21
22 namespace {
23   cl::opt<bool>
24   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
25                                       "tracking (this may be slow)"),
26              cl::Hidden);
27
28   cl::opt<std::string, true>
29   InfoOutputFilename("info-output-file",
30                      cl::desc("File to append -stats and -timer output to"),
31                      cl::Hidden, cl::location(LibSupportInfoOutputFilename));
32 }
33
34 static TimerGroup *DefaultTimerGroup = 0;
35 static TimerGroup *getDefaultTimerGroup() {
36   if (DefaultTimerGroup) return DefaultTimerGroup;
37   return DefaultTimerGroup = new TimerGroup("Miscellaneous Ungrouped Timers");
38 }
39
40 Timer::Timer(const std::string &N)
41   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
42     Started(false), TG(getDefaultTimerGroup()) {
43   TG->addTimer();
44 }
45
46 Timer::Timer(const std::string &N, TimerGroup &tg)
47   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
48     Started(false), TG(&tg) {
49   TG->addTimer();
50 }
51
52 Timer::Timer(const Timer &T) {
53   TG = T.TG;
54   if (TG) TG->addTimer();
55   operator=(T);
56 }
57
58
59 // Copy ctor, initialize with no TG member.
60 Timer::Timer(bool, const Timer &T) {
61   TG = T.TG;     // Avoid assertion in operator=
62   operator=(T);  // Copy contents
63   TG = 0;
64 }
65
66
67 Timer::~Timer() {
68   if (TG) {
69     if (Started) {
70       Started = false;
71       TG->addTimerToPrint(*this);
72     }
73     TG->removeTimer();
74   }
75 }
76
77 static long getMemUsage() {
78   if (TrackSpace) {
79     struct mallinfo MI = mallinfo();
80     return MI.uordblks/*+MI.hblkhd*/;
81   } else {
82     return 0;
83   }
84 }
85
86 struct TimeRecord {
87   double Elapsed, UserTime, SystemTime;
88   long MemUsed;
89 };
90
91 static TimeRecord getTimeRecord(bool Start) {
92   struct rusage RU;
93   struct timeval T;
94   long MemUsed = 0;
95   if (Start) {
96     MemUsed = getMemUsage();
97     if (getrusage(RUSAGE_SELF, &RU))
98       perror("getrusage call failed: -time-passes info incorrect!");
99   }
100   gettimeofday(&T, 0);
101
102   if (!Start) {
103     MemUsed = getMemUsage();
104     if (getrusage(RUSAGE_SELF, &RU))
105       perror("getrusage call failed: -time-passes info incorrect!");
106   }
107
108   TimeRecord Result;
109   Result.Elapsed    =           T.tv_sec +           T.tv_usec/1000000.0;
110   Result.UserTime   = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0;
111   Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0;
112   Result.MemUsed = MemUsed;
113
114   return Result;
115 }
116
117 static std::vector<Timer*> ActiveTimers;
118
119 void Timer::startTimer() {
120   Started = true;
121   TimeRecord TR = getTimeRecord(true);
122   Elapsed    -= TR.Elapsed;
123   UserTime   -= TR.UserTime;
124   SystemTime -= TR.SystemTime;
125   MemUsed    -= TR.MemUsed;
126   PeakMemBase = TR.MemUsed;
127   ActiveTimers.push_back(this);
128 }
129
130 void Timer::stopTimer() {
131   TimeRecord TR = getTimeRecord(false);
132   Elapsed    += TR.Elapsed;
133   UserTime   += TR.UserTime;
134   SystemTime += TR.SystemTime;
135   MemUsed    += TR.MemUsed;
136
137   if (ActiveTimers.back() == this) {
138     ActiveTimers.pop_back();
139   } else {
140     std::vector<Timer*>::iterator I =
141       std::find(ActiveTimers.begin(), ActiveTimers.end(), this);
142     assert(I != ActiveTimers.end() && "stop but no startTimer?");
143     ActiveTimers.erase(I);
144   }
145 }
146
147 void Timer::sum(const Timer &T) {
148   Elapsed    += T.Elapsed;
149   UserTime   += T.UserTime;
150   SystemTime += T.SystemTime;
151   MemUsed    += T.MemUsed;
152   PeakMem    += T.PeakMem;
153 }
154
155 /// addPeakMemoryMeasurement - This method should be called whenever memory
156 /// usage needs to be checked.  It adds a peak memory measurement to the
157 /// currently active timers, which will be printed when the timer group prints
158 ///
159 void Timer::addPeakMemoryMeasurement() {
160   long MemUsed = getMemUsage();
161
162   for (std::vector<Timer*>::iterator I = ActiveTimers.begin(),
163          E = ActiveTimers.end(); I != E; ++I)
164     (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase);
165 }
166
167
168 //===----------------------------------------------------------------------===//
169 //   TimerGroup Implementation
170 //===----------------------------------------------------------------------===//
171
172 // printAlignedFP - Simulate the printf "%A.Bf" format, where A is the
173 // TotalWidth size, and B is the AfterDec size.
174 //
175 static void printAlignedFP(double Val, unsigned AfterDec, unsigned TotalWidth,
176                            std::ostream &OS) {
177   assert(TotalWidth >= AfterDec+1 && "Bad FP Format!");
178   OS.width(TotalWidth-AfterDec-1);
179   char OldFill = OS.fill();
180   OS.fill(' ');
181   OS << (int)Val;  // Integer part;
182   OS << ".";
183   OS.width(AfterDec);
184   OS.fill('0');
185   unsigned ResultFieldSize = 1;
186   while (AfterDec--) ResultFieldSize *= 10;
187   OS << (int)(Val*ResultFieldSize) % ResultFieldSize;
188   OS.fill(OldFill);
189 }
190
191 static void printVal(double Val, double Total, std::ostream &OS) {
192   if (Total < 1e-7)   // Avoid dividing by zero...
193     OS << "        -----     ";
194   else {
195     OS << "  ";
196     printAlignedFP(Val, 4, 7, OS);
197     OS << " (";
198     printAlignedFP(Val*100/Total, 1, 5, OS);
199     OS << "%)";
200   }
201 }
202
203 void Timer::print(const Timer &Total, std::ostream &OS) {
204   if (Total.UserTime)
205     printVal(UserTime, Total.UserTime, OS);
206   if (Total.SystemTime)
207     printVal(SystemTime, Total.SystemTime, OS);
208   if (Total.getProcessTime())
209     printVal(getProcessTime(), Total.getProcessTime(), OS);
210   printVal(Elapsed, Total.Elapsed, OS);
211   
212   OS << "  ";
213
214   if (Total.MemUsed) {
215     OS.width(9);
216     OS << MemUsed << "  ";
217   }
218   if (Total.PeakMem) {
219     if (PeakMem) {
220       OS.width(9);
221       OS << PeakMem << "  ";
222     } else
223       OS << "           ";
224   }
225   OS << Name << "\n";
226
227   Started = false;  // Once printed, don't print again
228 }
229
230 // GetLibSupportInfoOutputFile - Return a file stream to print our output on...
231 std::ostream *GetLibSupportInfoOutputFile() {
232   if (LibSupportInfoOutputFilename.empty())
233     return &std::cerr;
234   if (LibSupportInfoOutputFilename == "-")
235     return &std::cout;
236
237   std::ostream *Result = new std::ofstream(LibSupportInfoOutputFilename.c_str(),
238                                            std::ios::app);
239   if (!Result->good()) {
240     std::cerr << "Error opening info-output-file '"
241               << LibSupportInfoOutputFilename << " for appending!\n";
242     delete Result;
243     return &std::cerr;
244   }
245   return Result;
246 }
247
248
249 void TimerGroup::removeTimer() {
250   if (--NumTimers == 0 && !TimersToPrint.empty()) { // Print timing report...
251     // Sort the timers in descending order by amount of time taken...
252     std::sort(TimersToPrint.begin(), TimersToPrint.end(),
253               std::greater<Timer>());
254
255     // Figure out how many spaces to indent TimerGroup name...
256     unsigned Padding = (80-Name.length())/2;
257     if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
258
259     std::ostream *OutStream = GetLibSupportInfoOutputFile();
260
261     ++NumTimers;
262     {  // Scope to contain Total timer... don't allow total timer to drop us to
263        // zero timers...
264       Timer Total("TOTAL");
265   
266       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
267         Total.sum(TimersToPrint[i]);
268       
269       // Print out timing header...
270       *OutStream << "===" << std::string(73, '-') << "===\n"
271                  << std::string(Padding, ' ') << Name << "\n"
272                  << "===" << std::string(73, '-')
273                  << "===\n  Total Execution Time: ";
274
275       printAlignedFP(Total.getProcessTime(), 4, 5, *OutStream);
276       *OutStream << " seconds (";
277       printAlignedFP(Total.getWallTime(), 4, 5, *OutStream);
278       *OutStream << " wall clock)\n\n";
279
280       if (Total.UserTime)
281         *OutStream << "   ---User Time---";
282       if (Total.SystemTime)
283         *OutStream << "   --System Time--";
284       if (Total.getProcessTime())
285         *OutStream << "   --User+System--";
286       *OutStream << "   ---Wall Time---";
287       if (Total.getMemUsed())
288         *OutStream << "  ---Mem---";
289       if (Total.getPeakMem())
290         *OutStream << "  -PeakMem-";
291       *OutStream << "  --- Name ---\n";
292       
293       // Loop through all of the timing data, printing it out...
294       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
295         TimersToPrint[i].print(Total, *OutStream);
296     
297       Total.print(Total, *OutStream);
298       *OutStream << std::endl;  // Flush output
299     }
300     --NumTimers;
301
302     TimersToPrint.clear();
303
304     if (OutStream != &std::cerr && OutStream != &std::cout)
305       delete OutStream;   // Close the file...
306   }
307
308   // Delete default timer group!
309   if (NumTimers == 0 && this == DefaultTimerGroup) {
310     delete DefaultTimerGroup;
311     DefaultTimerGroup = 0;
312   }
313 }