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