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