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