Squelch warning
[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
19 namespace {
20   cl::opt<bool>
21   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
22                                       "tracking (this may be slow)"),
23              cl::Hidden);
24 }
25
26 // getNumBytesToNotCount - This function is supposed to return the number of
27 // bytes that are to be considered not allocated, even though malloc thinks they
28 // are allocated.
29 //
30 static unsigned getNumBytesToNotCount();
31
32 static TimerGroup *DefaultTimerGroup = 0;
33 static TimerGroup *getDefaultTimerGroup() {
34   if (DefaultTimerGroup) return DefaultTimerGroup;
35   return DefaultTimerGroup = new TimerGroup("Miscellaneous Ungrouped Timers");
36 }
37
38 Timer::Timer(const std::string &N)
39   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
40     Started(false), TG(getDefaultTimerGroup()) {
41   TG->addTimer();
42 }
43
44 Timer::Timer(const std::string &N, TimerGroup &tg)
45   : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N),
46     Started(false), TG(&tg) {
47   TG->addTimer();
48 }
49
50 Timer::Timer(const Timer &T) {
51   TG = T.TG;
52   if (TG) TG->addTimer();
53   operator=(T);
54 }
55
56
57 // Copy ctor, initialize with no TG member.
58 Timer::Timer(bool, const Timer &T) {
59   TG = T.TG;     // Avoid assertion in operator=
60   operator=(T);  // Copy contents
61   TG = 0;
62 }
63
64
65 Timer::~Timer() {
66   if (TG) {
67     if (Started) {
68       Started = false;
69       TG->addTimerToPrint(*this);
70     }
71     TG->removeTimer();
72   }
73 }
74
75 static long getMemUsage() {
76   if (TrackSpace) {
77     struct mallinfo MI = mallinfo();
78     return MI.uordblks/*+MI.hblkhd-getNumBytesToNotCount()*/;
79   } else {
80     return 0;
81   }
82 }
83
84 struct TimeRecord {
85   double Elapsed, UserTime, SystemTime;
86   long MemUsed;
87 };
88
89 static TimeRecord getTimeRecord(bool Start) {
90   struct rusage RU;
91   struct timeval T;
92   long MemUsed = 0;
93   if (Start) {
94     MemUsed = getMemUsage();
95     if (getrusage(RUSAGE_SELF, &RU))
96       perror("getrusage call failed: -time-passes info incorrect!");
97   }
98   gettimeofday(&T, 0);
99
100   if (!Start) {
101     MemUsed = getMemUsage();
102     if (getrusage(RUSAGE_SELF, &RU))
103       perror("getrusage call failed: -time-passes info incorrect!");
104   }
105
106   TimeRecord Result;
107   Result.Elapsed    =           T.tv_sec +           T.tv_usec/1000000.0;
108   Result.UserTime   = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0;
109   Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0;
110   Result.MemUsed = MemUsed;
111
112   return Result;
113 }
114
115 static std::vector<Timer*> ActiveTimers;
116
117 void Timer::startTimer() {
118   Started = true;
119   TimeRecord TR = getTimeRecord(true);
120   Elapsed    -= TR.Elapsed;
121   UserTime   -= TR.UserTime;
122   SystemTime -= TR.SystemTime;
123   MemUsed    -= TR.MemUsed;
124   PeakMemBase = TR.MemUsed;
125   ActiveTimers.push_back(this);
126 }
127
128 void Timer::stopTimer() {
129   TimeRecord TR = getTimeRecord(false);
130   Elapsed    += TR.Elapsed;
131   UserTime   += TR.UserTime;
132   SystemTime += TR.SystemTime;
133   MemUsed    += TR.MemUsed;
134
135   if (ActiveTimers.back() == this) {
136     ActiveTimers.pop_back();
137   } else {
138     std::vector<Timer*>::iterator I =
139       std::find(ActiveTimers.begin(), ActiveTimers.end(), this);
140     assert(I != ActiveTimers.end() && "stop but no startTimer?");
141     ActiveTimers.erase(I);
142   }
143 }
144
145 void Timer::sum(const Timer &T) {
146   Elapsed    += T.Elapsed;
147   UserTime   += T.UserTime;
148   SystemTime += T.SystemTime;
149   MemUsed    += T.MemUsed;
150   PeakMem    += T.PeakMem;
151 }
152
153 /// addPeakMemoryMeasurement - This method should be called whenever memory
154 /// usage needs to be checked.  It adds a peak memory measurement to the
155 /// currently active timers, which will be printed when the timer group prints
156 ///
157 void Timer::addPeakMemoryMeasurement() {
158   long MemUsed = getMemUsage();
159
160   for (std::vector<Timer*>::iterator I = ActiveTimers.begin(),
161          E = ActiveTimers.end(); I != E; ++I)
162     (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase);
163 }
164
165
166 //===----------------------------------------------------------------------===//
167 //   TimerGroup Implementation
168 //===----------------------------------------------------------------------===//
169
170 static void printVal(double Val, double Total) {
171   if (Total < 1e-7)   // Avoid dividing by zero...
172     fprintf(stderr, "        -----     ");
173   else
174     fprintf(stderr, "  %7.4f (%5.1f%%)", Val, Val*100/Total);
175 }
176
177 void Timer::print(const Timer &Total) {
178   if (Total.UserTime)
179     printVal(UserTime, Total.UserTime);
180   if (Total.SystemTime)
181     printVal(SystemTime, Total.SystemTime);
182   if (Total.getProcessTime())
183     printVal(getProcessTime(), Total.getProcessTime());
184   printVal(Elapsed, Total.Elapsed);
185   
186   fprintf(stderr, "  ");
187
188   if (Total.MemUsed)
189     fprintf(stderr, " %8ld  ", MemUsed);
190   if (Total.PeakMem) {
191     if (PeakMem)
192       fprintf(stderr, " %8ld  ", PeakMem);
193     else
194       fprintf(stderr, "           ");
195   }
196   std::cerr << Name << "\n";
197
198   Started = false;  // Once printed, don't print again
199 }
200
201
202 void TimerGroup::removeTimer() {
203   if (--NumTimers == 0 && !TimersToPrint.empty()) { // Print timing report...
204     // Sort the timers in descending order by amount of time taken...
205     std::sort(TimersToPrint.begin(), TimersToPrint.end(),
206               std::greater<Timer>());
207
208     // Figure out how many spaces to indent TimerGroup name...
209     unsigned Padding = (80-Name.length())/2;
210     if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
211
212     ++NumTimers;
213     {  // Scope to contain Total timer... don't allow total timer to drop us to
214        // zero timers...
215       Timer Total("TOTAL");
216   
217       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
218         Total.sum(TimersToPrint[i]);
219       
220       // Print out timing header...
221       std::cerr << "===" << std::string(73, '-') << "===\n"
222                 << std::string(Padding, ' ') << Name << "\n"
223                 << "===" << std::string(73, '-')
224                 << "===\n  Total Execution Time: " << Total.getProcessTime()
225                 << " seconds (" << Total.getWallTime()
226                 << " wall clock)\n\n";
227
228       if (Total.UserTime)
229         std::cerr << "   ---User Time---";
230       if (Total.SystemTime)
231         std::cerr << "   --System Time--";
232       if (Total.getProcessTime())
233         std::cerr << "   --User+System--";
234       std::cerr << "   ---Wall Time---";
235       if (Total.getMemUsed())
236         std::cerr << "  ---Mem---";
237       if (Total.getPeakMem())
238         std::cerr << "  -PeakMem-";
239       std::cerr << "  --- Name ---\n";
240       
241       // Loop through all of the timing data, printing it out...
242       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
243         TimersToPrint[i].print(Total);
244     
245       Total.print(Total);
246       std::cerr << std::endl;  // Flush output
247     }
248     --NumTimers;
249
250     TimersToPrint.clear();
251   }
252
253   // Delete default timer group!
254   if (NumTimers == 0 && this == DefaultTimerGroup) {
255     delete DefaultTimerGroup;
256     DefaultTimerGroup = 0;
257   }
258 }
259
260
261
262 #if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && (__GNUC_PATCHLEVEL__ == 0)
263 // If we have GCC 3.2.0, we can calculate pool allocation bookkeeping info
264 #define HAVE_POOL
265 extern "C" {
266   // Taken from GCC 3.2's stl_alloc.h file:
267   enum {_ALIGN = 8, _MAX_BYTES = 128, NFREE = _MAX_BYTES / _ALIGN};
268   struct FreeList { FreeList *Next; };
269
270   FreeList *_ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE[NFREE];
271   char *_ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE;
272   char *_ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE;
273   size_t _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE;
274   
275   // Make the symbols possible to use...
276   FreeList* (&TheFreeList)[NFREE] = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE;
277   char * &StartFree = _ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE;
278   char * &EndFree   = _ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE;
279   size_t &HeapSize  = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE;
280 }
281 #endif
282
283 // getNumBytesToNotCount - This function is supposed to return the number of
284 // bytes that are to be considered not allocated, even though malloc thinks they
285 // are allocated.
286 //
287 static unsigned getNumBytesToNotCount() {
288 #ifdef HAVE_POOL
289   // If we have GCC 3.2.0, we can subtract off pool allocation bookkeeping info
290
291   // Size of the free slab section... 
292   unsigned FreePoolMem = (unsigned)(EndFree-StartFree);
293
294   // Walk all of the free lists, adding memory to the free counter whenever we
295   // have a free bucket.
296   for (unsigned i = 0; i != NFREE; ++i) {
297     unsigned NumEntries = 0;
298     for (FreeList *FL = TheFreeList[i]; FL; ++NumEntries, FL = FL->Next)
299       /*empty*/ ;
300     
301 #if 0
302     if (NumEntries)
303       std::cerr << "  For Size[" << (i+1)*_ALIGN << "]: " << NumEntries
304                 << " Free entries\n";
305 #endif
306     FreePoolMem += NumEntries*(i+1)*_ALIGN;
307   }
308   return FreePoolMem;
309   
310 #else
311 #warning "Don't know how to avoid pool allocation accounting overhead for this"
312 #warning " compiler: Space usage numbers (with -time-passes) may be off!"
313   return 0;
314 #endif
315 }