10b57cec5SDimitry Andric //===-- Timer.cpp - Interval Timing Support -------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric /// \file Interval Timing implementation.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/Support/Timer.h"
14fe6060f1SDimitry Andric 
15fe6060f1SDimitry Andric #include "DebugOptions.h"
16fe6060f1SDimitry Andric 
170b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
180b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
19fe6060f1SDimitry Andric #include "llvm/Config/config.h"
200b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
210b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
220b57cec5SDimitry Andric #include "llvm/Support/Format.h"
230b57cec5SDimitry Andric #include "llvm/Support/ManagedStatic.h"
240b57cec5SDimitry Andric #include "llvm/Support/Mutex.h"
250b57cec5SDimitry Andric #include "llvm/Support/Process.h"
260b57cec5SDimitry Andric #include "llvm/Support/Signposts.h"
270b57cec5SDimitry Andric #include "llvm/Support/YAMLTraits.h"
280b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
290b57cec5SDimitry Andric #include <limits>
300b57cec5SDimitry Andric 
31fe6060f1SDimitry Andric #if HAVE_UNISTD_H
32fe6060f1SDimitry Andric #include <unistd.h>
33fe6060f1SDimitry Andric #endif
34fe6060f1SDimitry Andric 
35fe6060f1SDimitry Andric #ifdef HAVE_PROC_PID_RUSAGE
36fe6060f1SDimitry Andric #include <libproc.h>
37fe6060f1SDimitry Andric #endif
38fe6060f1SDimitry Andric 
390b57cec5SDimitry Andric using namespace llvm;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric // This ugly hack is brought to you courtesy of constructor/destructor ordering
420b57cec5SDimitry Andric // being unspecified by C++.  Basically the problem is that a Statistic object
430b57cec5SDimitry Andric // gets destroyed, which ends up calling 'GetLibSupportInfoOutputFile()'
440b57cec5SDimitry Andric // (below), which calls this function.  LibSupportInfoOutputFilename used to be
450b57cec5SDimitry Andric // a global variable, but sometimes it would get destroyed before the Statistic,
460b57cec5SDimitry Andric // causing havoc to ensue.  We "fix" this by creating the string the first time
470b57cec5SDimitry Andric // it is needed and never destroying it.
480b57cec5SDimitry Andric static ManagedStatic<std::string> LibSupportInfoOutputFilename;
getLibSupportInfoOutputFilename()490b57cec5SDimitry Andric static std::string &getLibSupportInfoOutputFilename() {
500b57cec5SDimitry Andric   return *LibSupportInfoOutputFilename;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric static ManagedStatic<sys::SmartMutex<true> > TimerLock;
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric /// Allows llvm::Timer to emit signposts when supported.
560b57cec5SDimitry Andric static ManagedStatic<SignpostEmitter> Signposts;
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric namespace {
59fe6060f1SDimitry Andric struct CreateTrackSpace {
call__anon28378b850111::CreateTrackSpace60fe6060f1SDimitry Andric   static void *call() {
61fe6060f1SDimitry Andric     return new cl::opt<bool>("track-memory",
62fe6060f1SDimitry Andric                              cl::desc("Enable -time-passes memory "
630b57cec5SDimitry Andric                                       "tracking (this may be slow)"),
640b57cec5SDimitry Andric                              cl::Hidden);
65fe6060f1SDimitry Andric   }
66fe6060f1SDimitry Andric };
67fe6060f1SDimitry Andric static ManagedStatic<cl::opt<bool>, CreateTrackSpace> TrackSpace;
68fe6060f1SDimitry Andric struct CreateInfoOutputFilename {
call__anon28378b850111::CreateInfoOutputFilename69fe6060f1SDimitry Andric   static void *call() {
70fe6060f1SDimitry Andric     return new cl::opt<std::string, true>(
71fe6060f1SDimitry Andric         "info-output-file", cl::value_desc("filename"),
72fe6060f1SDimitry Andric         cl::desc("File to append -stats and -timer output to"), cl::Hidden,
73fe6060f1SDimitry Andric         cl::location(getLibSupportInfoOutputFilename()));
74fe6060f1SDimitry Andric   }
75fe6060f1SDimitry Andric };
76fe6060f1SDimitry Andric static ManagedStatic<cl::opt<std::string, true>, CreateInfoOutputFilename>
77fe6060f1SDimitry Andric     InfoOutputFilename;
78fe6060f1SDimitry Andric struct CreateSortTimers {
call__anon28378b850111::CreateSortTimers79fe6060f1SDimitry Andric   static void *call() {
80fe6060f1SDimitry Andric     return new cl::opt<bool>(
81fe6060f1SDimitry Andric         "sort-timers",
82fe6060f1SDimitry Andric         cl::desc("In the report, sort the timers in each group "
83e8d8bef9SDimitry Andric                  "in wall clock time order"),
84e8d8bef9SDimitry Andric         cl::init(true), cl::Hidden);
850b57cec5SDimitry Andric   }
86fe6060f1SDimitry Andric };
87fe6060f1SDimitry Andric ManagedStatic<cl::opt<bool>, CreateSortTimers> SortTimers;
88fe6060f1SDimitry Andric } // namespace
89fe6060f1SDimitry Andric 
initTimerOptions()90fe6060f1SDimitry Andric void llvm::initTimerOptions() {
91fe6060f1SDimitry Andric   *TrackSpace;
92fe6060f1SDimitry Andric   *InfoOutputFilename;
93fe6060f1SDimitry Andric   *SortTimers;
94fe6060f1SDimitry Andric }
950b57cec5SDimitry Andric 
CreateInfoOutputFile()960b57cec5SDimitry Andric std::unique_ptr<raw_fd_ostream> llvm::CreateInfoOutputFile() {
970b57cec5SDimitry Andric   const std::string &OutputFilename = getLibSupportInfoOutputFilename();
980b57cec5SDimitry Andric   if (OutputFilename.empty())
998bcb0991SDimitry Andric     return std::make_unique<raw_fd_ostream>(2, false); // stderr.
1000b57cec5SDimitry Andric   if (OutputFilename == "-")
1018bcb0991SDimitry Andric     return std::make_unique<raw_fd_ostream>(1, false); // stdout.
1020b57cec5SDimitry Andric 
1030b57cec5SDimitry Andric   // Append mode is used because the info output file is opened and closed
1040b57cec5SDimitry Andric   // each time -stats or -time-passes wants to print output to it. To
1050b57cec5SDimitry Andric   // compensate for this, the test-suite Makefiles have code to delete the
1060b57cec5SDimitry Andric   // info output file before running commands which write to it.
1070b57cec5SDimitry Andric   std::error_code EC;
1088bcb0991SDimitry Andric   auto Result = std::make_unique<raw_fd_ostream>(
109fe6060f1SDimitry Andric       OutputFilename, EC, sys::fs::OF_Append | sys::fs::OF_TextWithCRLF);
1100b57cec5SDimitry Andric   if (!EC)
1110b57cec5SDimitry Andric     return Result;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   errs() << "Error opening info-output-file '"
1140b57cec5SDimitry Andric     << OutputFilename << " for appending!\n";
1158bcb0991SDimitry Andric   return std::make_unique<raw_fd_ostream>(2, false); // stderr.
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric namespace {
1190b57cec5SDimitry Andric struct CreateDefaultTimerGroup {
call__anon28378b850211::CreateDefaultTimerGroup1200b57cec5SDimitry Andric   static void *call() {
1210b57cec5SDimitry Andric     return new TimerGroup("misc", "Miscellaneous Ungrouped Timers");
1220b57cec5SDimitry Andric   }
1230b57cec5SDimitry Andric };
1240b57cec5SDimitry Andric } // namespace
1250b57cec5SDimitry Andric static ManagedStatic<TimerGroup, CreateDefaultTimerGroup> DefaultTimerGroup;
getDefaultTimerGroup()1260b57cec5SDimitry Andric static TimerGroup *getDefaultTimerGroup() { return &*DefaultTimerGroup; }
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1290b57cec5SDimitry Andric // Timer Implementation
1300b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1310b57cec5SDimitry Andric 
init(StringRef TimerName,StringRef TimerDescription)132480093f4SDimitry Andric void Timer::init(StringRef TimerName, StringRef TimerDescription) {
133480093f4SDimitry Andric   init(TimerName, TimerDescription, *getDefaultTimerGroup());
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric 
init(StringRef TimerName,StringRef TimerDescription,TimerGroup & tg)136480093f4SDimitry Andric void Timer::init(StringRef TimerName, StringRef TimerDescription,
137480093f4SDimitry Andric                  TimerGroup &tg) {
1380b57cec5SDimitry Andric   assert(!TG && "Timer already initialized");
139480093f4SDimitry Andric   Name.assign(TimerName.begin(), TimerName.end());
140480093f4SDimitry Andric   Description.assign(TimerDescription.begin(), TimerDescription.end());
1410b57cec5SDimitry Andric   Running = Triggered = false;
1420b57cec5SDimitry Andric   TG = &tg;
1430b57cec5SDimitry Andric   TG->addTimer(*this);
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric 
~Timer()1460b57cec5SDimitry Andric Timer::~Timer() {
1470b57cec5SDimitry Andric   if (!TG) return;  // Never initialized, or already cleared.
1480b57cec5SDimitry Andric   TG->removeTimer(*this);
1490b57cec5SDimitry Andric }
1500b57cec5SDimitry Andric 
getMemUsage()1510b57cec5SDimitry Andric static inline size_t getMemUsage() {
152fe6060f1SDimitry Andric   if (!*TrackSpace)
153fe6060f1SDimitry Andric     return 0;
1540b57cec5SDimitry Andric   return sys::Process::GetMallocUsage();
1550b57cec5SDimitry Andric }
1560b57cec5SDimitry Andric 
getCurInstructionsExecuted()157fe6060f1SDimitry Andric static uint64_t getCurInstructionsExecuted() {
158fe6060f1SDimitry Andric #if defined(HAVE_UNISTD_H) && defined(HAVE_PROC_PID_RUSAGE) &&                 \
159fe6060f1SDimitry Andric     defined(RUSAGE_INFO_V4)
160fe6060f1SDimitry Andric   struct rusage_info_v4 ru;
161fe6060f1SDimitry Andric   if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) {
162fe6060f1SDimitry Andric     return ru.ri_instructions;
163fe6060f1SDimitry Andric   }
164fe6060f1SDimitry Andric #endif
165fe6060f1SDimitry Andric   return 0;
166fe6060f1SDimitry Andric }
167fe6060f1SDimitry Andric 
getCurrentTime(bool Start)1680b57cec5SDimitry Andric TimeRecord TimeRecord::getCurrentTime(bool Start) {
1690b57cec5SDimitry Andric   using Seconds = std::chrono::duration<double, std::ratio<1>>;
1700b57cec5SDimitry Andric   TimeRecord Result;
1710b57cec5SDimitry Andric   sys::TimePoint<> now;
1720b57cec5SDimitry Andric   std::chrono::nanoseconds user, sys;
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   if (Start) {
1750b57cec5SDimitry Andric     Result.MemUsed = getMemUsage();
176fe6060f1SDimitry Andric     Result.InstructionsExecuted = getCurInstructionsExecuted();
1770b57cec5SDimitry Andric     sys::Process::GetTimeUsage(now, user, sys);
1780b57cec5SDimitry Andric   } else {
1790b57cec5SDimitry Andric     sys::Process::GetTimeUsage(now, user, sys);
180fe6060f1SDimitry Andric     Result.InstructionsExecuted = getCurInstructionsExecuted();
1810b57cec5SDimitry Andric     Result.MemUsed = getMemUsage();
1820b57cec5SDimitry Andric   }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric   Result.WallTime = Seconds(now.time_since_epoch()).count();
1850b57cec5SDimitry Andric   Result.UserTime = Seconds(user).count();
1860b57cec5SDimitry Andric   Result.SystemTime = Seconds(sys).count();
1870b57cec5SDimitry Andric   return Result;
1880b57cec5SDimitry Andric }
1890b57cec5SDimitry Andric 
startTimer()1900b57cec5SDimitry Andric void Timer::startTimer() {
1910b57cec5SDimitry Andric   assert(!Running && "Cannot start a running timer");
1920b57cec5SDimitry Andric   Running = Triggered = true;
193e8d8bef9SDimitry Andric   Signposts->startInterval(this, getName());
1940b57cec5SDimitry Andric   StartTime = TimeRecord::getCurrentTime(true);
1950b57cec5SDimitry Andric }
1960b57cec5SDimitry Andric 
stopTimer()1970b57cec5SDimitry Andric void Timer::stopTimer() {
1980b57cec5SDimitry Andric   assert(Running && "Cannot stop a paused timer");
1990b57cec5SDimitry Andric   Running = false;
2000b57cec5SDimitry Andric   Time += TimeRecord::getCurrentTime(false);
2010b57cec5SDimitry Andric   Time -= StartTime;
202349cc55cSDimitry Andric   Signposts->endInterval(this, getName());
2030b57cec5SDimitry Andric }
2040b57cec5SDimitry Andric 
clear()2050b57cec5SDimitry Andric void Timer::clear() {
2060b57cec5SDimitry Andric   Running = Triggered = false;
2070b57cec5SDimitry Andric   Time = StartTime = TimeRecord();
2080b57cec5SDimitry Andric }
2090b57cec5SDimitry Andric 
printVal(double Val,double Total,raw_ostream & OS)2100b57cec5SDimitry Andric static void printVal(double Val, double Total, raw_ostream &OS) {
2110b57cec5SDimitry Andric   if (Total < 1e-7)   // Avoid dividing by zero.
2120b57cec5SDimitry Andric     OS << "        -----     ";
2130b57cec5SDimitry Andric   else
2140b57cec5SDimitry Andric     OS << format("  %7.4f (%5.1f%%)", Val, Val*100/Total);
2150b57cec5SDimitry Andric }
2160b57cec5SDimitry Andric 
print(const TimeRecord & Total,raw_ostream & OS) const2170b57cec5SDimitry Andric void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
2180b57cec5SDimitry Andric   if (Total.getUserTime())
2190b57cec5SDimitry Andric     printVal(getUserTime(), Total.getUserTime(), OS);
2200b57cec5SDimitry Andric   if (Total.getSystemTime())
2210b57cec5SDimitry Andric     printVal(getSystemTime(), Total.getSystemTime(), OS);
2220b57cec5SDimitry Andric   if (Total.getProcessTime())
2230b57cec5SDimitry Andric     printVal(getProcessTime(), Total.getProcessTime(), OS);
2240b57cec5SDimitry Andric   printVal(getWallTime(), Total.getWallTime(), OS);
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric   OS << "  ";
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric   if (Total.getMemUsed())
2290b57cec5SDimitry Andric     OS << format("%9" PRId64 "  ", (int64_t)getMemUsed());
230fe6060f1SDimitry Andric   if (Total.getInstructionsExecuted())
231fe6060f1SDimitry Andric     OS << format("%9" PRId64 "  ", (int64_t)getInstructionsExecuted());
2320b57cec5SDimitry Andric }
2330b57cec5SDimitry Andric 
2340b57cec5SDimitry Andric 
2350b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2360b57cec5SDimitry Andric //   NamedRegionTimer Implementation
2370b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2380b57cec5SDimitry Andric 
2390b57cec5SDimitry Andric namespace {
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric typedef StringMap<Timer> Name2TimerMap;
2420b57cec5SDimitry Andric 
2430b57cec5SDimitry Andric class Name2PairMap {
2440b57cec5SDimitry Andric   StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
2450b57cec5SDimitry Andric public:
~Name2PairMap()2460b57cec5SDimitry Andric   ~Name2PairMap() {
2470b57cec5SDimitry Andric     for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
2480b57cec5SDimitry Andric          I = Map.begin(), E = Map.end(); I != E; ++I)
2490b57cec5SDimitry Andric       delete I->second.first;
2500b57cec5SDimitry Andric   }
2510b57cec5SDimitry Andric 
get(StringRef Name,StringRef Description,StringRef GroupName,StringRef GroupDescription)2520b57cec5SDimitry Andric   Timer &get(StringRef Name, StringRef Description, StringRef GroupName,
2530b57cec5SDimitry Andric              StringRef GroupDescription) {
2540b57cec5SDimitry Andric     sys::SmartScopedLock<true> L(*TimerLock);
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric     std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
2570b57cec5SDimitry Andric 
2580b57cec5SDimitry Andric     if (!GroupEntry.first)
2590b57cec5SDimitry Andric       GroupEntry.first = new TimerGroup(GroupName, GroupDescription);
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric     Timer &T = GroupEntry.second[Name];
2620b57cec5SDimitry Andric     if (!T.isInitialized())
2630b57cec5SDimitry Andric       T.init(Name, Description, *GroupEntry.first);
2640b57cec5SDimitry Andric     return T;
2650b57cec5SDimitry Andric   }
2660b57cec5SDimitry Andric };
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric }
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric static ManagedStatic<Name2PairMap> NamedGroupedTimers;
2710b57cec5SDimitry Andric 
NamedRegionTimer(StringRef Name,StringRef Description,StringRef GroupName,StringRef GroupDescription,bool Enabled)2720b57cec5SDimitry Andric NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef Description,
2730b57cec5SDimitry Andric                                    StringRef GroupName,
2740b57cec5SDimitry Andric                                    StringRef GroupDescription, bool Enabled)
2750b57cec5SDimitry Andric   : TimeRegion(!Enabled ? nullptr
2760b57cec5SDimitry Andric                  : &NamedGroupedTimers->get(Name, Description, GroupName,
2770b57cec5SDimitry Andric                                             GroupDescription)) {}
2780b57cec5SDimitry Andric 
2790b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2800b57cec5SDimitry Andric //   TimerGroup Implementation
2810b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric /// This is the global list of TimerGroups, maintained by the TimerGroup
2840b57cec5SDimitry Andric /// ctor/dtor and is protected by the TimerLock lock.
2850b57cec5SDimitry Andric static TimerGroup *TimerGroupList = nullptr;
2860b57cec5SDimitry Andric 
TimerGroup(StringRef Name,StringRef Description)2870b57cec5SDimitry Andric TimerGroup::TimerGroup(StringRef Name, StringRef Description)
2880b57cec5SDimitry Andric   : Name(Name.begin(), Name.end()),
2890b57cec5SDimitry Andric     Description(Description.begin(), Description.end()) {
2900b57cec5SDimitry Andric   // Add the group to TimerGroupList.
2910b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
2920b57cec5SDimitry Andric   if (TimerGroupList)
2930b57cec5SDimitry Andric     TimerGroupList->Prev = &Next;
2940b57cec5SDimitry Andric   Next = TimerGroupList;
2950b57cec5SDimitry Andric   Prev = &TimerGroupList;
2960b57cec5SDimitry Andric   TimerGroupList = this;
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric 
TimerGroup(StringRef Name,StringRef Description,const StringMap<TimeRecord> & Records)2990b57cec5SDimitry Andric TimerGroup::TimerGroup(StringRef Name, StringRef Description,
3000b57cec5SDimitry Andric                        const StringMap<TimeRecord> &Records)
3010b57cec5SDimitry Andric     : TimerGroup(Name, Description) {
3020b57cec5SDimitry Andric   TimersToPrint.reserve(Records.size());
3030b57cec5SDimitry Andric   for (const auto &P : Records)
3045ffd83dbSDimitry Andric     TimersToPrint.emplace_back(P.getValue(), std::string(P.getKey()),
3055ffd83dbSDimitry Andric                                std::string(P.getKey()));
3060b57cec5SDimitry Andric   assert(TimersToPrint.size() == Records.size() && "Size mismatch");
3070b57cec5SDimitry Andric }
3080b57cec5SDimitry Andric 
~TimerGroup()3090b57cec5SDimitry Andric TimerGroup::~TimerGroup() {
3100b57cec5SDimitry Andric   // If the timer group is destroyed before the timers it owns, accumulate and
3110b57cec5SDimitry Andric   // print the timing data.
3120b57cec5SDimitry Andric   while (FirstTimer)
3130b57cec5SDimitry Andric     removeTimer(*FirstTimer);
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric   // Remove the group from the TimerGroupList.
3160b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
3170b57cec5SDimitry Andric   *Prev = Next;
3180b57cec5SDimitry Andric   if (Next)
3190b57cec5SDimitry Andric     Next->Prev = Prev;
3200b57cec5SDimitry Andric }
3210b57cec5SDimitry Andric 
3220b57cec5SDimitry Andric 
removeTimer(Timer & T)3230b57cec5SDimitry Andric void TimerGroup::removeTimer(Timer &T) {
3240b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric   // If the timer was started, move its data to TimersToPrint.
3270b57cec5SDimitry Andric   if (T.hasTriggered())
3280b57cec5SDimitry Andric     TimersToPrint.emplace_back(T.Time, T.Name, T.Description);
3290b57cec5SDimitry Andric 
3300b57cec5SDimitry Andric   T.TG = nullptr;
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric   // Unlink the timer from our list.
3330b57cec5SDimitry Andric   *T.Prev = T.Next;
3340b57cec5SDimitry Andric   if (T.Next)
3350b57cec5SDimitry Andric     T.Next->Prev = T.Prev;
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric   // Print the report when all timers in this group are destroyed if some of
3380b57cec5SDimitry Andric   // them were started.
3390b57cec5SDimitry Andric   if (FirstTimer || TimersToPrint.empty())
3400b57cec5SDimitry Andric     return;
3410b57cec5SDimitry Andric 
3420b57cec5SDimitry Andric   std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
3430b57cec5SDimitry Andric   PrintQueuedTimers(*OutStream);
3440b57cec5SDimitry Andric }
3450b57cec5SDimitry Andric 
addTimer(Timer & T)3460b57cec5SDimitry Andric void TimerGroup::addTimer(Timer &T) {
3470b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
3480b57cec5SDimitry Andric 
3490b57cec5SDimitry Andric   // Add the timer to our list.
3500b57cec5SDimitry Andric   if (FirstTimer)
3510b57cec5SDimitry Andric     FirstTimer->Prev = &T.Next;
3520b57cec5SDimitry Andric   T.Next = FirstTimer;
3530b57cec5SDimitry Andric   T.Prev = &FirstTimer;
3540b57cec5SDimitry Andric   FirstTimer = &T;
3550b57cec5SDimitry Andric }
3560b57cec5SDimitry Andric 
PrintQueuedTimers(raw_ostream & OS)3570b57cec5SDimitry Andric void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
358e8d8bef9SDimitry Andric   // Perhaps sort the timers in descending order by amount of time taken.
359fe6060f1SDimitry Andric   if (*SortTimers)
3600b57cec5SDimitry Andric     llvm::sort(TimersToPrint);
3610b57cec5SDimitry Andric 
3620b57cec5SDimitry Andric   TimeRecord Total;
3630b57cec5SDimitry Andric   for (const PrintRecord &Record : TimersToPrint)
3640b57cec5SDimitry Andric     Total += Record.Time;
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   // Print out timing header.
3670b57cec5SDimitry Andric   OS << "===" << std::string(73, '-') << "===\n";
3680b57cec5SDimitry Andric   // Figure out how many spaces to indent TimerGroup name.
3690b57cec5SDimitry Andric   unsigned Padding = (80-Description.length())/2;
3700b57cec5SDimitry Andric   if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
3710b57cec5SDimitry Andric   OS.indent(Padding) << Description << '\n';
3720b57cec5SDimitry Andric   OS << "===" << std::string(73, '-') << "===\n";
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric   // If this is not an collection of ungrouped times, print the total time.
3750b57cec5SDimitry Andric   // Ungrouped timers don't really make sense to add up.  We still print the
3760b57cec5SDimitry Andric   // TOTAL line to make the percentages make sense.
3770b57cec5SDimitry Andric   if (this != getDefaultTimerGroup())
3780b57cec5SDimitry Andric     OS << format("  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n",
3790b57cec5SDimitry Andric                  Total.getProcessTime(), Total.getWallTime());
3800b57cec5SDimitry Andric   OS << '\n';
3810b57cec5SDimitry Andric 
3820b57cec5SDimitry Andric   if (Total.getUserTime())
3830b57cec5SDimitry Andric     OS << "   ---User Time---";
3840b57cec5SDimitry Andric   if (Total.getSystemTime())
3850b57cec5SDimitry Andric     OS << "   --System Time--";
3860b57cec5SDimitry Andric   if (Total.getProcessTime())
3870b57cec5SDimitry Andric     OS << "   --User+System--";
3880b57cec5SDimitry Andric   OS << "   ---Wall Time---";
3890b57cec5SDimitry Andric   if (Total.getMemUsed())
3900b57cec5SDimitry Andric     OS << "  ---Mem---";
391fe6060f1SDimitry Andric   if (Total.getInstructionsExecuted())
392fe6060f1SDimitry Andric     OS << "  ---Instr---";
3930b57cec5SDimitry Andric   OS << "  --- Name ---\n";
3940b57cec5SDimitry Andric 
3950b57cec5SDimitry Andric   // Loop through all of the timing data, printing it out.
396349cc55cSDimitry Andric   for (const PrintRecord &Record : llvm::reverse(TimersToPrint)) {
3970b57cec5SDimitry Andric     Record.Time.print(Total, OS);
3980b57cec5SDimitry Andric     OS << Record.Description << '\n';
3990b57cec5SDimitry Andric   }
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric   Total.print(Total, OS);
4020b57cec5SDimitry Andric   OS << "Total\n\n";
4030b57cec5SDimitry Andric   OS.flush();
4040b57cec5SDimitry Andric 
4050b57cec5SDimitry Andric   TimersToPrint.clear();
4060b57cec5SDimitry Andric }
4070b57cec5SDimitry Andric 
prepareToPrintList(bool ResetTime)4080b57cec5SDimitry Andric void TimerGroup::prepareToPrintList(bool ResetTime) {
4090b57cec5SDimitry Andric   // See if any of our timers were started, if so add them to TimersToPrint.
4100b57cec5SDimitry Andric   for (Timer *T = FirstTimer; T; T = T->Next) {
4110b57cec5SDimitry Andric     if (!T->hasTriggered()) continue;
4120b57cec5SDimitry Andric     bool WasRunning = T->isRunning();
4130b57cec5SDimitry Andric     if (WasRunning)
4140b57cec5SDimitry Andric       T->stopTimer();
4150b57cec5SDimitry Andric 
4160b57cec5SDimitry Andric     TimersToPrint.emplace_back(T->Time, T->Name, T->Description);
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric     if (ResetTime)
4190b57cec5SDimitry Andric       T->clear();
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric     if (WasRunning)
4220b57cec5SDimitry Andric       T->startTimer();
4230b57cec5SDimitry Andric   }
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric 
print(raw_ostream & OS,bool ResetAfterPrint)4260b57cec5SDimitry Andric void TimerGroup::print(raw_ostream &OS, bool ResetAfterPrint) {
4270b57cec5SDimitry Andric   {
4280b57cec5SDimitry Andric     // After preparing the timers we can free the lock
4290b57cec5SDimitry Andric     sys::SmartScopedLock<true> L(*TimerLock);
4300b57cec5SDimitry Andric     prepareToPrintList(ResetAfterPrint);
4310b57cec5SDimitry Andric   }
4320b57cec5SDimitry Andric 
4330b57cec5SDimitry Andric   // If any timers were started, print the group.
4340b57cec5SDimitry Andric   if (!TimersToPrint.empty())
4350b57cec5SDimitry Andric     PrintQueuedTimers(OS);
4360b57cec5SDimitry Andric }
4370b57cec5SDimitry Andric 
clear()4380b57cec5SDimitry Andric void TimerGroup::clear() {
4390b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
4400b57cec5SDimitry Andric   for (Timer *T = FirstTimer; T; T = T->Next)
4410b57cec5SDimitry Andric     T->clear();
4420b57cec5SDimitry Andric }
4430b57cec5SDimitry Andric 
printAll(raw_ostream & OS)4440b57cec5SDimitry Andric void TimerGroup::printAll(raw_ostream &OS) {
4450b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
4460b57cec5SDimitry Andric 
4470b57cec5SDimitry Andric   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
4480b57cec5SDimitry Andric     TG->print(OS);
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric 
clearAll()4510b57cec5SDimitry Andric void TimerGroup::clearAll() {
4520b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
4530b57cec5SDimitry Andric   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
4540b57cec5SDimitry Andric     TG->clear();
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric 
printJSONValue(raw_ostream & OS,const PrintRecord & R,const char * suffix,double Value)4570b57cec5SDimitry Andric void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R,
4580b57cec5SDimitry Andric                                 const char *suffix, double Value) {
4590b57cec5SDimitry Andric   assert(yaml::needsQuotes(Name) == yaml::QuotingType::None &&
4600b57cec5SDimitry Andric          "TimerGroup name should not need quotes");
4610b57cec5SDimitry Andric   assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None &&
4620b57cec5SDimitry Andric          "Timer name should not need quotes");
4630b57cec5SDimitry Andric   constexpr auto max_digits10 = std::numeric_limits<double>::max_digits10;
4640b57cec5SDimitry Andric   OS << "\t\"time." << Name << '.' << R.Name << suffix
4650b57cec5SDimitry Andric      << "\": " << format("%.*e", max_digits10 - 1, Value);
4660b57cec5SDimitry Andric }
4670b57cec5SDimitry Andric 
printJSONValues(raw_ostream & OS,const char * delim)4680b57cec5SDimitry Andric const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) {
4690b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
4700b57cec5SDimitry Andric 
4710b57cec5SDimitry Andric   prepareToPrintList(false);
4720b57cec5SDimitry Andric   for (const PrintRecord &R : TimersToPrint) {
4730b57cec5SDimitry Andric     OS << delim;
4740b57cec5SDimitry Andric     delim = ",\n";
4750b57cec5SDimitry Andric 
4760b57cec5SDimitry Andric     const TimeRecord &T = R.Time;
4770b57cec5SDimitry Andric     printJSONValue(OS, R, ".wall", T.getWallTime());
4780b57cec5SDimitry Andric     OS << delim;
4790b57cec5SDimitry Andric     printJSONValue(OS, R, ".user", T.getUserTime());
4800b57cec5SDimitry Andric     OS << delim;
4810b57cec5SDimitry Andric     printJSONValue(OS, R, ".sys", T.getSystemTime());
4820b57cec5SDimitry Andric     if (T.getMemUsed()) {
4830b57cec5SDimitry Andric       OS << delim;
4840b57cec5SDimitry Andric       printJSONValue(OS, R, ".mem", T.getMemUsed());
4850b57cec5SDimitry Andric     }
486fe6060f1SDimitry Andric     if (T.getInstructionsExecuted()) {
487fe6060f1SDimitry Andric       OS << delim;
488fe6060f1SDimitry Andric       printJSONValue(OS, R, ".instr", T.getInstructionsExecuted());
489fe6060f1SDimitry Andric     }
4900b57cec5SDimitry Andric   }
4910b57cec5SDimitry Andric   TimersToPrint.clear();
4920b57cec5SDimitry Andric   return delim;
4930b57cec5SDimitry Andric }
4940b57cec5SDimitry Andric 
printAllJSONValues(raw_ostream & OS,const char * delim)4950b57cec5SDimitry Andric const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) {
4960b57cec5SDimitry Andric   sys::SmartScopedLock<true> L(*TimerLock);
4970b57cec5SDimitry Andric   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
4980b57cec5SDimitry Andric     delim = TG->printJSONValues(OS, delim);
4990b57cec5SDimitry Andric   return delim;
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric 
constructForStatistics()502bdd1243dSDimitry Andric void TimerGroup::constructForStatistics() {
503bdd1243dSDimitry Andric   (void)getLibSupportInfoOutputFilename();
5040b57cec5SDimitry Andric   (void)*NamedGroupedTimers;
5050b57cec5SDimitry Andric }
506cd675bb6SDimitry Andric 
aquireDefaultGroup()507cd675bb6SDimitry Andric std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() {
508cd675bb6SDimitry Andric   return std::unique_ptr<TimerGroup>(DefaultTimerGroup.claim());
509cd675bb6SDimitry Andric }
510