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