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