1 //===-- Timer.cpp - Interval Timing Support -------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 /// \file Interval Timing implementation.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/Support/Timer.h"
15 #include "llvm/ADT/Statistic.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/Support/CommandLine.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/Format.h"
20 #include "llvm/Support/ManagedStatic.h"
21 #include "llvm/Support/Mutex.h"
22 #include "llvm/Support/Process.h"
23 #include "llvm/Support/raw_ostream.h"
24 using namespace llvm;
25 
26 // This ugly hack is brought to you courtesy of constructor/destructor ordering
27 // being unspecified by C++.  Basically the problem is that a Statistic object
28 // gets destroyed, which ends up calling 'GetLibSupportInfoOutputFile()'
29 // (below), which calls this function.  LibSupportInfoOutputFilename used to be
30 // a global variable, but sometimes it would get destroyed before the Statistic,
31 // causing havoc to ensue.  We "fix" this by creating the string the first time
32 // it is needed and never destroying it.
33 static ManagedStatic<std::string> LibSupportInfoOutputFilename;
34 static std::string &getLibSupportInfoOutputFilename() {
35   return *LibSupportInfoOutputFilename;
36 }
37 
38 static ManagedStatic<sys::SmartMutex<true> > TimerLock;
39 
40 namespace {
41   static cl::opt<bool>
42   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
43                                       "tracking (this may be slow)"),
44              cl::Hidden);
45 
46   static cl::opt<std::string, true>
47   InfoOutputFilename("info-output-file", cl::value_desc("filename"),
48                      cl::desc("File to append -stats and -timer output to"),
49                    cl::Hidden, cl::location(getLibSupportInfoOutputFilename()));
50 }
51 
52 std::unique_ptr<raw_fd_ostream> llvm::CreateInfoOutputFile() {
53   const std::string &OutputFilename = getLibSupportInfoOutputFilename();
54   if (OutputFilename.empty())
55     return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
56   if (OutputFilename == "-")
57     return llvm::make_unique<raw_fd_ostream>(1, false); // stdout.
58 
59   // Append mode is used because the info output file is opened and closed
60   // each time -stats or -time-passes wants to print output to it. To
61   // compensate for this, the test-suite Makefiles have code to delete the
62   // info output file before running commands which write to it.
63   std::error_code EC;
64   auto Result = llvm::make_unique<raw_fd_ostream>(
65       OutputFilename, EC, sys::fs::F_Append | sys::fs::F_Text);
66   if (!EC)
67     return Result;
68 
69   errs() << "Error opening info-output-file '"
70     << OutputFilename << " for appending!\n";
71   return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
72 }
73 
74 
75 static TimerGroup *DefaultTimerGroup = nullptr;
76 static TimerGroup *getDefaultTimerGroup() {
77   TimerGroup *tmp = DefaultTimerGroup;
78   sys::MemoryFence();
79   if (tmp) return tmp;
80 
81   sys::SmartScopedLock<true> Lock(*TimerLock);
82   tmp = DefaultTimerGroup;
83   if (!tmp) {
84     tmp = new TimerGroup("Miscellaneous Ungrouped Timers");
85     sys::MemoryFence();
86     DefaultTimerGroup = tmp;
87   }
88 
89   return tmp;
90 }
91 
92 //===----------------------------------------------------------------------===//
93 // Timer Implementation
94 //===----------------------------------------------------------------------===//
95 
96 void Timer::init(StringRef N) {
97   init(N, *getDefaultTimerGroup());
98 }
99 
100 void Timer::init(StringRef N, TimerGroup &tg) {
101   assert(!TG && "Timer already initialized");
102   Name.assign(N.begin(), N.end());
103   Running = Triggered = false;
104   TG = &tg;
105   TG->addTimer(*this);
106 }
107 
108 Timer::~Timer() {
109   if (!TG) return;  // Never initialized, or already cleared.
110   TG->removeTimer(*this);
111 }
112 
113 static inline size_t getMemUsage() {
114   if (!TrackSpace) return 0;
115   return sys::Process::GetMallocUsage();
116 }
117 
118 TimeRecord TimeRecord::getCurrentTime(bool Start) {
119   TimeRecord Result;
120   sys::TimeValue now(0,0), user(0,0), sys(0,0);
121 
122   if (Start) {
123     Result.MemUsed = getMemUsage();
124     sys::Process::GetTimeUsage(now, user, sys);
125   } else {
126     sys::Process::GetTimeUsage(now, user, sys);
127     Result.MemUsed = getMemUsage();
128   }
129 
130   Result.WallTime   =  now.seconds() +  now.microseconds() / 1000000.0;
131   Result.UserTime   = user.seconds() + user.microseconds() / 1000000.0;
132   Result.SystemTime =  sys.seconds() +  sys.microseconds() / 1000000.0;
133   return Result;
134 }
135 
136 void Timer::startTimer() {
137   assert(!Running && "Cannot start a running timer");
138   Running = Triggered = true;
139   StartTime = TimeRecord::getCurrentTime(true);
140 }
141 
142 void Timer::stopTimer() {
143   assert(Running && "Cannot stop a paused timer");
144   Running = false;
145   Time += TimeRecord::getCurrentTime(false);
146   Time -= StartTime;
147 }
148 
149 void Timer::clear() {
150   Running = Triggered = false;
151   Time = StartTime = TimeRecord();
152 }
153 
154 static void printVal(double Val, double Total, raw_ostream &OS) {
155   if (Total < 1e-7)   // Avoid dividing by zero.
156     OS << "        -----     ";
157   else
158     OS << format("  %7.4f (%5.1f%%)", Val, Val*100/Total);
159 }
160 
161 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
162   if (Total.getUserTime())
163     printVal(getUserTime(), Total.getUserTime(), OS);
164   if (Total.getSystemTime())
165     printVal(getSystemTime(), Total.getSystemTime(), OS);
166   if (Total.getProcessTime())
167     printVal(getProcessTime(), Total.getProcessTime(), OS);
168   printVal(getWallTime(), Total.getWallTime(), OS);
169 
170   OS << "  ";
171 
172   if (Total.getMemUsed())
173     OS << format("%9" PRId64 "  ", (int64_t)getMemUsed());
174 }
175 
176 
177 //===----------------------------------------------------------------------===//
178 //   NamedRegionTimer Implementation
179 //===----------------------------------------------------------------------===//
180 
181 namespace {
182 
183 typedef StringMap<Timer> Name2TimerMap;
184 
185 class Name2PairMap {
186   StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
187 public:
188   ~Name2PairMap() {
189     for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
190          I = Map.begin(), E = Map.end(); I != E; ++I)
191       delete I->second.first;
192   }
193 
194   Timer &get(StringRef Name, StringRef GroupName) {
195     sys::SmartScopedLock<true> L(*TimerLock);
196 
197     std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
198 
199     if (!GroupEntry.first)
200       GroupEntry.first = new TimerGroup(GroupName);
201 
202     Timer &T = GroupEntry.second[Name];
203     if (!T.isInitialized())
204       T.init(Name, *GroupEntry.first);
205     return T;
206   }
207 };
208 
209 }
210 
211 static ManagedStatic<Name2TimerMap> NamedTimers;
212 static ManagedStatic<Name2PairMap> NamedGroupedTimers;
213 
214 static Timer &getNamedRegionTimer(StringRef Name) {
215   sys::SmartScopedLock<true> L(*TimerLock);
216 
217   Timer &T = (*NamedTimers)[Name];
218   if (!T.isInitialized())
219     T.init(Name);
220   return T;
221 }
222 
223 NamedRegionTimer::NamedRegionTimer(StringRef Name,
224                                    bool Enabled)
225   : TimeRegion(!Enabled ? nullptr : &getNamedRegionTimer(Name)) {}
226 
227 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName,
228                                    bool Enabled)
229   : TimeRegion(!Enabled ? nullptr : &NamedGroupedTimers->get(Name, GroupName)){}
230 
231 //===----------------------------------------------------------------------===//
232 //   TimerGroup Implementation
233 //===----------------------------------------------------------------------===//
234 
235 /// This is the global list of TimerGroups, maintained by the TimerGroup
236 /// ctor/dtor and is protected by the TimerLock lock.
237 static TimerGroup *TimerGroupList = nullptr;
238 
239 TimerGroup::TimerGroup(StringRef name)
240   : Name(name.begin(), name.end()) {
241 
242   // Add the group to TimerGroupList.
243   sys::SmartScopedLock<true> L(*TimerLock);
244   if (TimerGroupList)
245     TimerGroupList->Prev = &Next;
246   Next = TimerGroupList;
247   Prev = &TimerGroupList;
248   TimerGroupList = this;
249 }
250 
251 TimerGroup::~TimerGroup() {
252   // If the timer group is destroyed before the timers it owns, accumulate and
253   // print the timing data.
254   while (FirstTimer)
255     removeTimer(*FirstTimer);
256 
257   // Remove the group from the TimerGroupList.
258   sys::SmartScopedLock<true> L(*TimerLock);
259   *Prev = Next;
260   if (Next)
261     Next->Prev = Prev;
262 }
263 
264 
265 void TimerGroup::removeTimer(Timer &T) {
266   sys::SmartScopedLock<true> L(*TimerLock);
267 
268   // If the timer was started, move its data to TimersToPrint.
269   if (T.hasTriggered())
270     TimersToPrint.emplace_back(T.Time, T.Name);
271 
272   T.TG = nullptr;
273 
274   // Unlink the timer from our list.
275   *T.Prev = T.Next;
276   if (T.Next)
277     T.Next->Prev = T.Prev;
278 
279   // Print the report when all timers in this group are destroyed if some of
280   // them were started.
281   if (FirstTimer || TimersToPrint.empty())
282     return;
283 
284   std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
285   PrintQueuedTimers(*OutStream);
286 }
287 
288 void TimerGroup::addTimer(Timer &T) {
289   sys::SmartScopedLock<true> L(*TimerLock);
290 
291   // Add the timer to our list.
292   if (FirstTimer)
293     FirstTimer->Prev = &T.Next;
294   T.Next = FirstTimer;
295   T.Prev = &FirstTimer;
296   FirstTimer = &T;
297 }
298 
299 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
300   // Sort the timers in descending order by amount of time taken.
301   std::sort(TimersToPrint.begin(), TimersToPrint.end());
302 
303   TimeRecord Total;
304   for (auto &RecordNamePair : TimersToPrint)
305     Total += RecordNamePair.first;
306 
307   // Print out timing header.
308   OS << "===" << std::string(73, '-') << "===\n";
309   // Figure out how many spaces to indent TimerGroup name.
310   unsigned Padding = (80-Name.length())/2;
311   if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
312   OS.indent(Padding) << Name << '\n';
313   OS << "===" << std::string(73, '-') << "===\n";
314 
315   // If this is not an collection of ungrouped times, print the total time.
316   // Ungrouped timers don't really make sense to add up.  We still print the
317   // TOTAL line to make the percentages make sense.
318   if (this != DefaultTimerGroup)
319     OS << format("  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n",
320                  Total.getProcessTime(), Total.getWallTime());
321   OS << '\n';
322 
323   if (Total.getUserTime())
324     OS << "   ---User Time---";
325   if (Total.getSystemTime())
326     OS << "   --System Time--";
327   if (Total.getProcessTime())
328     OS << "   --User+System--";
329   OS << "   ---Wall Time---";
330   if (Total.getMemUsed())
331     OS << "  ---Mem---";
332   OS << "  --- Name ---\n";
333 
334   // Loop through all of the timing data, printing it out.
335   for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) {
336     const std::pair<TimeRecord, std::string> &Entry = TimersToPrint[e-i-1];
337     Entry.first.print(Total, OS);
338     OS << Entry.second << '\n';
339   }
340 
341   Total.print(Total, OS);
342   OS << "Total\n\n";
343   OS.flush();
344 
345   TimersToPrint.clear();
346 }
347 
348 void TimerGroup::print(raw_ostream &OS) {
349   sys::SmartScopedLock<true> L(*TimerLock);
350 
351   // See if any of our timers were started, if so add them to TimersToPrint and
352   // reset them.
353   for (Timer *T = FirstTimer; T; T = T->Next) {
354     if (!T->hasTriggered()) continue;
355     TimersToPrint.emplace_back(T->Time, T->Name);
356 
357     // Clear out the time.
358     T->clear();
359   }
360 
361   // If any timers were started, print the group.
362   if (!TimersToPrint.empty())
363     PrintQueuedTimers(OS);
364 }
365 
366 void TimerGroup::printAll(raw_ostream &OS) {
367   sys::SmartScopedLock<true> L(*TimerLock);
368 
369   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
370     TG->print(OS);
371 }
372