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