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