xref: /llvm-project/llvm/lib/Support/Timer.cpp (revision 52c338daece1ea5556865373c777bdad79a05fd8)
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 
15 #include "DebugOptions.h"
16 
17 #include "llvm/ADT/Statistic.h"
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/Config/config.h"
20 #include "llvm/Support/CommandLine.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/Format.h"
23 #include "llvm/Support/ManagedStatic.h"
24 #include "llvm/Support/Mutex.h"
25 #include "llvm/Support/Process.h"
26 #include "llvm/Support/Signposts.h"
27 #include "llvm/Support/YAMLTraits.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <limits>
30 
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 
35 #ifdef HAVE_PROC_PID_RUSAGE
36 #include <libproc.h>
37 #endif
38 
39 using namespace llvm;
40 
41 //===----------------------------------------------------------------------===//
42 // Forward declarations for Managed Timer Globals (mtg) getters.
43 //
44 // Globals have been placed at the end of the file to restrict direct
45 // access. Use of getters also has the benefit of making it a bit more explicit
46 // that a global is being used.
47 //===----------------------------------------------------------------------===//
48 namespace {
49 class Name2PairMap;
50 }
51 
52 namespace mtg {
53 static std::string &LibSupportInfoOutputFilename();
54 static const std::string &InfoOutputFilename();
55 static bool TrackSpace();
56 static bool SortTimers();
57 static SignpostEmitter &Signposts();
58 static sys::SmartMutex<true> &TimerLock();
59 static TimerGroup &DefaultTimerGroup();
60 static TimerGroup *claimDefaultTimerGroup();
61 static Name2PairMap &NamedGroupedTimers();
62 } // namespace mtg
63 
64 //===----------------------------------------------------------------------===//
65 //
66 //===----------------------------------------------------------------------===//
67 void llvm::initTimerOptions() {
68   mtg::TrackSpace();
69   mtg::InfoOutputFilename();
70   mtg::SortTimers();
71 }
72 
73 std::unique_ptr<raw_ostream> llvm::CreateInfoOutputFile() {
74   const std::string &OutputFilename = mtg::LibSupportInfoOutputFilename();
75   if (OutputFilename.empty())
76     return std::make_unique<raw_fd_ostream>(2, false); // stderr.
77   if (OutputFilename == "-")
78     return std::make_unique<raw_fd_ostream>(1, false); // stdout.
79 
80   // Append mode is used because the info output file is opened and closed
81   // each time -stats or -time-passes wants to print output to it. To
82   // compensate for this, the test-suite Makefiles have code to delete the
83   // info output file before running commands which write to it.
84   std::error_code EC;
85   auto Result = std::make_unique<raw_fd_ostream>(
86       OutputFilename, EC, sys::fs::OF_Append | sys::fs::OF_TextWithCRLF);
87   if (!EC)
88     return Result;
89 
90   errs() << "Error opening info-output-file '"
91     << OutputFilename << " for appending!\n";
92   return std::make_unique<raw_fd_ostream>(2, false); // stderr.
93 }
94 
95 //===----------------------------------------------------------------------===//
96 // Timer Implementation
97 //===----------------------------------------------------------------------===//
98 
99 void Timer::init(StringRef TimerName, StringRef TimerDescription) {
100   init(TimerName, TimerDescription, mtg::DefaultTimerGroup());
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 (!mtg::TrackSpace())
120     return 0;
121   return sys::Process::GetMallocUsage();
122 }
123 
124 static uint64_t getCurInstructionsExecuted() {
125 #if defined(HAVE_UNISTD_H) && defined(HAVE_PROC_PID_RUSAGE) &&                 \
126     defined(RUSAGE_INFO_V4)
127   struct rusage_info_v4 ru;
128   if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) {
129     return ru.ri_instructions;
130   }
131 #endif
132   return 0;
133 }
134 
135 TimeRecord TimeRecord::getCurrentTime(bool Start) {
136   using Seconds = std::chrono::duration<double, std::ratio<1>>;
137   TimeRecord Result;
138   sys::TimePoint<> now;
139   std::chrono::nanoseconds user, sys;
140 
141   if (Start) {
142     Result.MemUsed = getMemUsage();
143     Result.InstructionsExecuted = getCurInstructionsExecuted();
144     sys::Process::GetTimeUsage(now, user, sys);
145   } else {
146     sys::Process::GetTimeUsage(now, user, sys);
147     Result.InstructionsExecuted = getCurInstructionsExecuted();
148     Result.MemUsed = getMemUsage();
149   }
150 
151   Result.WallTime = Seconds(now.time_since_epoch()).count();
152   Result.UserTime = Seconds(user).count();
153   Result.SystemTime = Seconds(sys).count();
154   return Result;
155 }
156 
157 void Timer::startTimer() {
158   assert(!Running && "Cannot start a running timer");
159   Running = Triggered = true;
160   mtg::Signposts().startInterval(this, getName());
161   StartTime = TimeRecord::getCurrentTime(true);
162 }
163 
164 void Timer::stopTimer() {
165   assert(Running && "Cannot stop a paused timer");
166   Running = false;
167   Time += TimeRecord::getCurrentTime(false);
168   Time -= StartTime;
169   mtg::Signposts().endInterval(this, getName());
170 }
171 
172 void Timer::clear() {
173   Running = Triggered = false;
174   Time = StartTime = TimeRecord();
175 }
176 
177 static void printVal(double Val, double Total, raw_ostream &OS) {
178   if (Total < 1e-7)   // Avoid dividing by zero.
179     OS << "        -----     ";
180   else
181     OS << format("  %7.4f (%5.1f%%)", Val, Val*100/Total);
182 }
183 
184 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
185   if (Total.getUserTime())
186     printVal(getUserTime(), Total.getUserTime(), OS);
187   if (Total.getSystemTime())
188     printVal(getSystemTime(), Total.getSystemTime(), OS);
189   if (Total.getProcessTime())
190     printVal(getProcessTime(), Total.getProcessTime(), OS);
191   printVal(getWallTime(), Total.getWallTime(), OS);
192 
193   OS << "  ";
194 
195   if (Total.getMemUsed())
196     OS << format("%9" PRId64 "  ", (int64_t)getMemUsed());
197   if (Total.getInstructionsExecuted())
198     OS << format("%9" PRId64 "  ", (int64_t)getInstructionsExecuted());
199 }
200 
201 
202 //===----------------------------------------------------------------------===//
203 //   NamedRegionTimer Implementation
204 //===----------------------------------------------------------------------===//
205 
206 namespace {
207 
208 typedef StringMap<Timer> Name2TimerMap;
209 
210 class Name2PairMap {
211   StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
212 public:
213   ~Name2PairMap() {
214     for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
215          I = Map.begin(), E = Map.end(); I != E; ++I)
216       delete I->second.first;
217   }
218 
219   Timer &get(StringRef Name, StringRef Description, StringRef GroupName,
220              StringRef GroupDescription) {
221     sys::SmartScopedLock<true> L(mtg::TimerLock());
222 
223     std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
224 
225     if (!GroupEntry.first)
226       GroupEntry.first = new TimerGroup(GroupName, GroupDescription);
227 
228     Timer &T = GroupEntry.second[Name];
229     if (!T.isInitialized())
230       T.init(Name, Description, *GroupEntry.first);
231     return T;
232   }
233 };
234 
235 }
236 
237 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef Description,
238                                    StringRef GroupName,
239                                    StringRef GroupDescription, bool Enabled)
240     : TimeRegion(!Enabled ? nullptr
241                           : &mtg::NamedGroupedTimers().get(Name, Description,
242                                                            GroupName,
243                                                            GroupDescription)) {}
244 
245 //===----------------------------------------------------------------------===//
246 //   TimerGroup Implementation
247 //===----------------------------------------------------------------------===//
248 
249 /// This is the global list of TimerGroups, maintained by the TimerGroup
250 /// ctor/dtor and is protected by the TimerLock lock.
251 static TimerGroup *TimerGroupList = nullptr;
252 
253 TimerGroup::TimerGroup(StringRef Name, StringRef Description,
254                        sys::SmartMutex<true> &lock)
255     : Name(Name.begin(), Name.end()),
256       Description(Description.begin(), Description.end()) {
257   // Add the group to TimerGroupList.
258   sys::SmartScopedLock<true> L(lock);
259   if (TimerGroupList)
260     TimerGroupList->Prev = &Next;
261   Next = TimerGroupList;
262   Prev = &TimerGroupList;
263   TimerGroupList = this;
264 }
265 
266 TimerGroup::TimerGroup(StringRef Name, StringRef Description)
267     : TimerGroup(Name, Description, mtg::TimerLock()) {}
268 
269 TimerGroup::TimerGroup(StringRef Name, StringRef Description,
270                        const StringMap<TimeRecord> &Records)
271     : TimerGroup(Name, Description) {
272   TimersToPrint.reserve(Records.size());
273   for (const auto &P : Records)
274     TimersToPrint.emplace_back(P.getValue(), std::string(P.getKey()),
275                                std::string(P.getKey()));
276   assert(TimersToPrint.size() == Records.size() && "Size mismatch");
277 }
278 
279 TimerGroup::~TimerGroup() {
280   // If the timer group is destroyed before the timers it owns, accumulate and
281   // print the timing data.
282   while (FirstTimer)
283     removeTimer(*FirstTimer);
284 
285   // Remove the group from the TimerGroupList.
286   sys::SmartScopedLock<true> L(mtg::TimerLock());
287   *Prev = Next;
288   if (Next)
289     Next->Prev = Prev;
290 }
291 
292 
293 void TimerGroup::removeTimer(Timer &T) {
294   sys::SmartScopedLock<true> L(mtg::TimerLock());
295 
296   // If the timer was started, move its data to TimersToPrint.
297   if (T.hasTriggered())
298     TimersToPrint.emplace_back(T.Time, T.Name, T.Description);
299 
300   T.TG = nullptr;
301 
302   // Unlink the timer from our list.
303   *T.Prev = T.Next;
304   if (T.Next)
305     T.Next->Prev = T.Prev;
306 
307   // Print the report when all timers in this group are destroyed if some of
308   // them were started.
309   if (FirstTimer || TimersToPrint.empty())
310     return;
311 
312   std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
313   PrintQueuedTimers(*OutStream);
314 }
315 
316 void TimerGroup::addTimer(Timer &T) {
317   sys::SmartScopedLock<true> L(mtg::TimerLock());
318 
319   // Add the timer to our list.
320   if (FirstTimer)
321     FirstTimer->Prev = &T.Next;
322   T.Next = FirstTimer;
323   T.Prev = &FirstTimer;
324   FirstTimer = &T;
325 }
326 
327 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
328   // Perhaps sort the timers in descending order by amount of time taken.
329   if (mtg::SortTimers())
330     llvm::sort(TimersToPrint);
331 
332   TimeRecord Total;
333   for (const PrintRecord &Record : TimersToPrint)
334     Total += Record.Time;
335 
336   // Print out timing header.
337   OS << "===" << std::string(73, '-') << "===\n";
338   // Figure out how many spaces to indent TimerGroup name.
339   unsigned Padding = (80-Description.length())/2;
340   if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
341   OS.indent(Padding) << Description << '\n';
342   OS << "===" << std::string(73, '-') << "===\n";
343 
344   // If this is not an collection of ungrouped times, print the total time.
345   // Ungrouped timers don't really make sense to add up.  We still print the
346   // TOTAL line to make the percentages make sense.
347   if (this != &mtg::DefaultTimerGroup())
348     OS << format("  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n",
349                  Total.getProcessTime(), Total.getWallTime());
350   OS << '\n';
351 
352   if (Total.getUserTime())
353     OS << "   ---User Time---";
354   if (Total.getSystemTime())
355     OS << "   --System Time--";
356   if (Total.getProcessTime())
357     OS << "   --User+System--";
358   OS << "   ---Wall Time---";
359   if (Total.getMemUsed())
360     OS << "  ---Mem---";
361   if (Total.getInstructionsExecuted())
362     OS << "  ---Instr---";
363   OS << "  --- Name ---\n";
364 
365   // Loop through all of the timing data, printing it out.
366   for (const PrintRecord &Record : llvm::reverse(TimersToPrint)) {
367     Record.Time.print(Total, OS);
368     OS << Record.Description << '\n';
369   }
370 
371   Total.print(Total, OS);
372   OS << "Total\n\n";
373   OS.flush();
374 
375   TimersToPrint.clear();
376 }
377 
378 void TimerGroup::prepareToPrintList(bool ResetTime) {
379   // See if any of our timers were started, if so add them to TimersToPrint.
380   for (Timer *T = FirstTimer; T; T = T->Next) {
381     if (!T->hasTriggered()) continue;
382     bool WasRunning = T->isRunning();
383     if (WasRunning)
384       T->stopTimer();
385 
386     TimersToPrint.emplace_back(T->Time, T->Name, T->Description);
387 
388     if (ResetTime)
389       T->clear();
390 
391     if (WasRunning)
392       T->startTimer();
393   }
394 }
395 
396 void TimerGroup::print(raw_ostream &OS, bool ResetAfterPrint) {
397   {
398     // After preparing the timers we can free the lock
399     sys::SmartScopedLock<true> L(mtg::TimerLock());
400     prepareToPrintList(ResetAfterPrint);
401   }
402 
403   // If any timers were started, print the group.
404   if (!TimersToPrint.empty())
405     PrintQueuedTimers(OS);
406 }
407 
408 void TimerGroup::clear() {
409   sys::SmartScopedLock<true> L(mtg::TimerLock());
410   for (Timer *T = FirstTimer; T; T = T->Next)
411     T->clear();
412 }
413 
414 void TimerGroup::printAll(raw_ostream &OS) {
415   sys::SmartScopedLock<true> L(mtg::TimerLock());
416 
417   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
418     TG->print(OS);
419 }
420 
421 void TimerGroup::clearAll() {
422   sys::SmartScopedLock<true> L(mtg::TimerLock());
423   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
424     TG->clear();
425 }
426 
427 void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R,
428                                 const char *suffix, double Value) {
429   assert(yaml::needsQuotes(Name) == yaml::QuotingType::None &&
430          "TimerGroup name should not need quotes");
431   assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None &&
432          "Timer name should not need quotes");
433   constexpr auto max_digits10 = std::numeric_limits<double>::max_digits10;
434   OS << "\t\"time." << Name << '.' << R.Name << suffix
435      << "\": " << format("%.*e", max_digits10 - 1, Value);
436 }
437 
438 const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) {
439   sys::SmartScopedLock<true> L(mtg::TimerLock());
440 
441   prepareToPrintList(false);
442   for (const PrintRecord &R : TimersToPrint) {
443     OS << delim;
444     delim = ",\n";
445 
446     const TimeRecord &T = R.Time;
447     printJSONValue(OS, R, ".wall", T.getWallTime());
448     OS << delim;
449     printJSONValue(OS, R, ".user", T.getUserTime());
450     OS << delim;
451     printJSONValue(OS, R, ".sys", T.getSystemTime());
452     if (T.getMemUsed()) {
453       OS << delim;
454       printJSONValue(OS, R, ".mem", T.getMemUsed());
455     }
456     if (T.getInstructionsExecuted()) {
457       OS << delim;
458       printJSONValue(OS, R, ".instr", T.getInstructionsExecuted());
459     }
460   }
461   TimersToPrint.clear();
462   return delim;
463 }
464 
465 const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) {
466   sys::SmartScopedLock<true> L(mtg::TimerLock());
467   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
468     delim = TG->printJSONValues(OS, delim);
469   return delim;
470 }
471 
472 void TimerGroup::constructForStatistics() {
473   mtg::LibSupportInfoOutputFilename();
474   mtg::NamedGroupedTimers();
475 }
476 
477 std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() {
478   return std::unique_ptr<TimerGroup>(mtg::claimDefaultTimerGroup());
479 }
480 
481 //===----------------------------------------------------------------------===//
482 // Timer Globals
483 //
484 // Previously, these were independent ManagedStatics. This led to bugs because
485 // there are dependencies between the globals, but no reliable mechanism to
486 // control relative lifetimes.
487 //
488 // Placing the globals within one class instance lets us control the lifetimes
489 // of the various data members and ensure that no global uses another that has
490 // been deleted.
491 //
492 // Globals fall into two categories. First are simple data types and
493 // command-line options. These are cheap to construct and/or required early
494 // during launch. They are created when the ManagedTimerGlobals singleton is
495 // constructed. Second are types that are more expensive to construct or not
496 // needed until later during compilation. These are lazily constructed in order
497 // to reduce launch time.
498 //===----------------------------------------------------------------------===//
499 class llvm::TimerGlobals {
500 public:
501   std::string LibSupportInfoOutputFilename;
502   cl::opt<std::string, true> InfoOutputFilename;
503   cl::opt<bool> TrackSpace;
504   cl::opt<bool> SortTimers;
505 
506 private:
507   // Order of these members and initialization below is important. For example
508   // the DefaultTimerGroup uses the TimerLock. Most of these also depend on the
509   // options above.
510   std::unique_ptr<SignpostEmitter> SignpostsPtr;
511   std::unique_ptr<sys::SmartMutex<true>> TimerLockPtr;
512   std::unique_ptr<TimerGroup> DefaultTimerGroupPtr;
513   std::unique_ptr<Name2PairMap> NamedGroupedTimersPtr;
514   std::once_flag InitDeferredFlag;
515   TimerGlobals &initDeferred() {
516     std::call_once(InitDeferredFlag, [this]() {
517       SignpostsPtr = std::make_unique<SignpostEmitter>();
518       TimerLockPtr = std::make_unique<sys::SmartMutex<true>>();
519       DefaultTimerGroupPtr.reset(new TimerGroup(
520           "misc", "Miscellaneous Ungrouped Timers", *TimerLockPtr));
521       NamedGroupedTimersPtr = std::make_unique<Name2PairMap>();
522     });
523     return *this;
524   }
525 
526 public:
527   SignpostEmitter &Signposts() { return *initDeferred().SignpostsPtr; }
528   sys::SmartMutex<true> &TimerLock() { return *initDeferred().TimerLockPtr; }
529   TimerGroup &DefaultTimerGroup() {
530     return *initDeferred().DefaultTimerGroupPtr;
531   }
532   TimerGroup *claimDefaultTimerGroup() {
533     return initDeferred().DefaultTimerGroupPtr.release();
534   }
535   Name2PairMap &NamedGroupedTimers() {
536     return *initDeferred().NamedGroupedTimersPtr;
537   }
538 
539 public:
540   TimerGlobals()
541       : InfoOutputFilename(
542             "info-output-file", cl::value_desc("filename"),
543             cl::desc("File to append -stats and -timer output to"), cl::Hidden,
544             cl::location(LibSupportInfoOutputFilename)),
545         TrackSpace(
546             "track-memory",
547             cl::desc("Enable -time-passes memory tracking (this may be slow)"),
548             cl::Hidden),
549         SortTimers(
550             "sort-timers",
551             cl::desc(
552                 "In the report, sort the timers in each group in wall clock"
553                 " time order"),
554             cl::init(true), cl::Hidden) {}
555 };
556 
557 static ManagedStatic<TimerGlobals> ManagedTimerGlobals;
558 
559 static std::string &mtg::LibSupportInfoOutputFilename() {
560   return ManagedTimerGlobals->LibSupportInfoOutputFilename;
561 }
562 static const std::string &mtg::InfoOutputFilename() {
563   return ManagedTimerGlobals->InfoOutputFilename.getValue();
564 }
565 static bool mtg::TrackSpace() { return ManagedTimerGlobals->TrackSpace; };
566 static bool mtg::SortTimers() { return ManagedTimerGlobals->SortTimers; }
567 static SignpostEmitter &mtg::Signposts() {
568   return ManagedTimerGlobals->Signposts();
569 }
570 static sys::SmartMutex<true> &mtg::TimerLock() {
571   return ManagedTimerGlobals->TimerLock();
572 }
573 static TimerGroup &mtg::DefaultTimerGroup() {
574   return ManagedTimerGlobals->DefaultTimerGroup();
575 }
576 static TimerGroup *mtg::claimDefaultTimerGroup() {
577   return ManagedTimerGlobals->claimDefaultTimerGroup();
578 }
579 static Name2PairMap &mtg::NamedGroupedTimers() {
580   return ManagedTimerGlobals->NamedGroupedTimers();
581 }
582