Don't output times in "scientific" notation
[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: " << std::fixed
225                 << Total.getProcessTime()
226                 << " seconds (" << Total.getWallTime() << std::scientific
227                 << " wall clock)\n\n";
228
229       if (Total.UserTime)
230         std::cerr << "   ---User Time---";
231       if (Total.SystemTime)
232         std::cerr << "   --System Time--";
233       if (Total.getProcessTime())
234         std::cerr << "   --User+System--";
235       std::cerr << "   ---Wall Time---";
236       if (Total.getMemUsed())
237         std::cerr << "  ---Mem---";
238       if (Total.getPeakMem())
239         std::cerr << "  -PeakMem-";
240       std::cerr << "  --- Name ---\n";
241       
242       // Loop through all of the timing data, printing it out...
243       for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i)
244         TimersToPrint[i].print(Total);
245     
246       Total.print(Total);
247       std::cerr << std::endl;  // Flush output
248     }
249     --NumTimers;
250
251     TimersToPrint.clear();
252   }
253
254   // Delete default timer group!
255   if (NumTimers == 0 && this == DefaultTimerGroup) {
256     delete DefaultTimerGroup;
257     DefaultTimerGroup = 0;
258   }
259 }
260
261
262
263 #if (__GNUC__ == 3) && (__GNUC_MINOR__ == 2) && (__GNUC_PATCHLEVEL__ == 0)
264 // If we have GCC 3.2.0, we can calculate pool allocation bookkeeping info
265 #define HAVE_POOL
266 extern "C" {
267   // Taken from GCC 3.2's stl_alloc.h file:
268   enum {_ALIGN = 8, _MAX_BYTES = 128, NFREE = _MAX_BYTES / _ALIGN};
269   struct FreeList { FreeList *Next; };
270
271   FreeList *_ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE[NFREE];
272   char *_ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE;
273   char *_ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE;
274   size_t _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE;
275   
276   // Make the symbols possible to use...
277   FreeList* (&TheFreeList)[NFREE] = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_free_listE;
278   char * &StartFree = _ZNSt24__default_alloc_templateILb1ELi0EE13_S_start_freeE;
279   char * &EndFree   = _ZNSt24__default_alloc_templateILb1ELi0EE11_S_end_freeE;
280   size_t &HeapSize  = _ZNSt24__default_alloc_templateILb1ELi0EE12_S_heap_sizeE;
281 }
282 #endif
283
284 // getNumBytesToNotCount - This function is supposed to return the number of
285 // bytes that are to be considered not allocated, even though malloc thinks they
286 // are allocated.
287 //
288 static unsigned getNumBytesToNotCount() {
289 #ifdef HAVE_POOL
290   // If we have GCC 3.2.0, we can subtract off pool allocation bookkeeping info
291
292   // Size of the free slab section... 
293   unsigned FreePoolMem = (unsigned)(EndFree-StartFree);
294
295   // Walk all of the free lists, adding memory to the free counter whenever we
296   // have a free bucket.
297   for (unsigned i = 0; i != NFREE; ++i) {
298     unsigned NumEntries = 0;
299     for (FreeList *FL = TheFreeList[i]; FL; ++NumEntries, FL = FL->Next)
300       /*empty*/ ;
301     
302 #if 0
303     if (NumEntries)
304       std::cerr << "  For Size[" << (i+1)*_ALIGN << "]: " << NumEntries
305                 << " Free entries\n";
306 #endif
307     FreePoolMem += NumEntries*(i+1)*_ALIGN;
308   }
309   return FreePoolMem;
310   
311 #else
312 #warning "Don't know how to avoid pool allocation accounting overhead for this"
313 #warning " compiler: Space usage numbers (with -time-passes) may be off!"
314   return 0;
315 #endif
316 }