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" 14*fe6060f1SDimitry Andric 15*fe6060f1SDimitry Andric #include "DebugOptions.h" 16*fe6060f1SDimitry Andric 170b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 180b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h" 19*fe6060f1SDimitry 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 31*fe6060f1SDimitry Andric #if HAVE_UNISTD_H 32*fe6060f1SDimitry Andric #include <unistd.h> 33*fe6060f1SDimitry Andric #endif 34*fe6060f1SDimitry Andric 35*fe6060f1SDimitry Andric #ifdef HAVE_PROC_PID_RUSAGE 36*fe6060f1SDimitry Andric #include <libproc.h> 37*fe6060f1SDimitry Andric #endif 38*fe6060f1SDimitry 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; 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 { 59*fe6060f1SDimitry Andric struct CreateTrackSpace { 60*fe6060f1SDimitry Andric static void *call() { 61*fe6060f1SDimitry Andric return new cl::opt<bool>("track-memory", 62*fe6060f1SDimitry Andric cl::desc("Enable -time-passes memory " 630b57cec5SDimitry Andric "tracking (this may be slow)"), 640b57cec5SDimitry Andric cl::Hidden); 65*fe6060f1SDimitry Andric } 66*fe6060f1SDimitry Andric }; 67*fe6060f1SDimitry Andric static ManagedStatic<cl::opt<bool>, CreateTrackSpace> TrackSpace; 68*fe6060f1SDimitry Andric struct CreateInfoOutputFilename { 69*fe6060f1SDimitry Andric static void *call() { 70*fe6060f1SDimitry Andric return new cl::opt<std::string, true>( 71*fe6060f1SDimitry Andric "info-output-file", cl::value_desc("filename"), 72*fe6060f1SDimitry Andric cl::desc("File to append -stats and -timer output to"), cl::Hidden, 73*fe6060f1SDimitry Andric cl::location(getLibSupportInfoOutputFilename())); 74*fe6060f1SDimitry Andric } 75*fe6060f1SDimitry Andric }; 76*fe6060f1SDimitry Andric static ManagedStatic<cl::opt<std::string, true>, CreateInfoOutputFilename> 77*fe6060f1SDimitry Andric InfoOutputFilename; 78*fe6060f1SDimitry Andric struct CreateSortTimers { 79*fe6060f1SDimitry Andric static void *call() { 80*fe6060f1SDimitry Andric return new cl::opt<bool>( 81*fe6060f1SDimitry Andric "sort-timers", 82*fe6060f1SDimitry 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 } 86*fe6060f1SDimitry Andric }; 87*fe6060f1SDimitry Andric ManagedStatic<cl::opt<bool>, CreateSortTimers> SortTimers; 88*fe6060f1SDimitry Andric } // namespace 89*fe6060f1SDimitry Andric 90*fe6060f1SDimitry Andric void llvm::initTimerOptions() { 91*fe6060f1SDimitry Andric *TrackSpace; 92*fe6060f1SDimitry Andric *InfoOutputFilename; 93*fe6060f1SDimitry Andric *SortTimers; 94*fe6060f1SDimitry Andric } 950b57cec5SDimitry Andric 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>( 109*fe6060f1SDimitry 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 { 1200b57cec5SDimitry 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; 1260b57cec5SDimitry Andric static TimerGroup *getDefaultTimerGroup() { return &*DefaultTimerGroup; } 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1290b57cec5SDimitry Andric // Timer Implementation 1300b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1310b57cec5SDimitry Andric 132480093f4SDimitry Andric void Timer::init(StringRef TimerName, StringRef TimerDescription) { 133480093f4SDimitry Andric init(TimerName, TimerDescription, *getDefaultTimerGroup()); 1340b57cec5SDimitry Andric } 1350b57cec5SDimitry Andric 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 1460b57cec5SDimitry Andric Timer::~Timer() { 1470b57cec5SDimitry Andric if (!TG) return; // Never initialized, or already cleared. 1480b57cec5SDimitry Andric TG->removeTimer(*this); 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric static inline size_t getMemUsage() { 152*fe6060f1SDimitry Andric if (!*TrackSpace) 153*fe6060f1SDimitry Andric return 0; 1540b57cec5SDimitry Andric return sys::Process::GetMallocUsage(); 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric 157*fe6060f1SDimitry Andric static uint64_t getCurInstructionsExecuted() { 158*fe6060f1SDimitry Andric #if defined(HAVE_UNISTD_H) && defined(HAVE_PROC_PID_RUSAGE) && \ 159*fe6060f1SDimitry Andric defined(RUSAGE_INFO_V4) 160*fe6060f1SDimitry Andric struct rusage_info_v4 ru; 161*fe6060f1SDimitry Andric if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) { 162*fe6060f1SDimitry Andric return ru.ri_instructions; 163*fe6060f1SDimitry Andric } 164*fe6060f1SDimitry Andric #endif 165*fe6060f1SDimitry Andric return 0; 166*fe6060f1SDimitry Andric } 167*fe6060f1SDimitry Andric 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(); 176*fe6060f1SDimitry Andric Result.InstructionsExecuted = getCurInstructionsExecuted(); 1770b57cec5SDimitry Andric sys::Process::GetTimeUsage(now, user, sys); 1780b57cec5SDimitry Andric } else { 1790b57cec5SDimitry Andric sys::Process::GetTimeUsage(now, user, sys); 180*fe6060f1SDimitry 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 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 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; 202*fe6060f1SDimitry Andric Signposts->endInterval(this); 2030b57cec5SDimitry Andric } 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric void Timer::clear() { 2060b57cec5SDimitry Andric Running = Triggered = false; 2070b57cec5SDimitry Andric Time = StartTime = TimeRecord(); 2080b57cec5SDimitry Andric } 2090b57cec5SDimitry Andric 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 2170b57cec5SDimitry 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()); 230*fe6060f1SDimitry Andric if (Total.getInstructionsExecuted()) 231*fe6060f1SDimitry 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: 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 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 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 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 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 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 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 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 3570b57cec5SDimitry Andric void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { 358e8d8bef9SDimitry Andric // Perhaps sort the timers in descending order by amount of time taken. 359*fe6060f1SDimitry 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---"; 391*fe6060f1SDimitry Andric if (Total.getInstructionsExecuted()) 392*fe6060f1SDimitry Andric OS << " ---Instr---"; 3930b57cec5SDimitry Andric OS << " --- Name ---\n"; 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric // Loop through all of the timing data, printing it out. 3960b57cec5SDimitry Andric for (const PrintRecord &Record : make_range(TimersToPrint.rbegin(), 3970b57cec5SDimitry Andric TimersToPrint.rend())) { 3980b57cec5SDimitry Andric Record.Time.print(Total, OS); 3990b57cec5SDimitry Andric OS << Record.Description << '\n'; 4000b57cec5SDimitry Andric } 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric Total.print(Total, OS); 4030b57cec5SDimitry Andric OS << "Total\n\n"; 4040b57cec5SDimitry Andric OS.flush(); 4050b57cec5SDimitry Andric 4060b57cec5SDimitry Andric TimersToPrint.clear(); 4070b57cec5SDimitry Andric } 4080b57cec5SDimitry Andric 4090b57cec5SDimitry Andric void TimerGroup::prepareToPrintList(bool ResetTime) { 4100b57cec5SDimitry Andric // See if any of our timers were started, if so add them to TimersToPrint. 4110b57cec5SDimitry Andric for (Timer *T = FirstTimer; T; T = T->Next) { 4120b57cec5SDimitry Andric if (!T->hasTriggered()) continue; 4130b57cec5SDimitry Andric bool WasRunning = T->isRunning(); 4140b57cec5SDimitry Andric if (WasRunning) 4150b57cec5SDimitry Andric T->stopTimer(); 4160b57cec5SDimitry Andric 4170b57cec5SDimitry Andric TimersToPrint.emplace_back(T->Time, T->Name, T->Description); 4180b57cec5SDimitry Andric 4190b57cec5SDimitry Andric if (ResetTime) 4200b57cec5SDimitry Andric T->clear(); 4210b57cec5SDimitry Andric 4220b57cec5SDimitry Andric if (WasRunning) 4230b57cec5SDimitry Andric T->startTimer(); 4240b57cec5SDimitry Andric } 4250b57cec5SDimitry Andric } 4260b57cec5SDimitry Andric 4270b57cec5SDimitry Andric void TimerGroup::print(raw_ostream &OS, bool ResetAfterPrint) { 4280b57cec5SDimitry Andric { 4290b57cec5SDimitry Andric // After preparing the timers we can free the lock 4300b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4310b57cec5SDimitry Andric prepareToPrintList(ResetAfterPrint); 4320b57cec5SDimitry Andric } 4330b57cec5SDimitry Andric 4340b57cec5SDimitry Andric // If any timers were started, print the group. 4350b57cec5SDimitry Andric if (!TimersToPrint.empty()) 4360b57cec5SDimitry Andric PrintQueuedTimers(OS); 4370b57cec5SDimitry Andric } 4380b57cec5SDimitry Andric 4390b57cec5SDimitry Andric void TimerGroup::clear() { 4400b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4410b57cec5SDimitry Andric for (Timer *T = FirstTimer; T; T = T->Next) 4420b57cec5SDimitry Andric T->clear(); 4430b57cec5SDimitry Andric } 4440b57cec5SDimitry Andric 4450b57cec5SDimitry Andric void TimerGroup::printAll(raw_ostream &OS) { 4460b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4470b57cec5SDimitry Andric 4480b57cec5SDimitry Andric for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 4490b57cec5SDimitry Andric TG->print(OS); 4500b57cec5SDimitry Andric } 4510b57cec5SDimitry Andric 4520b57cec5SDimitry Andric void TimerGroup::clearAll() { 4530b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4540b57cec5SDimitry Andric for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 4550b57cec5SDimitry Andric TG->clear(); 4560b57cec5SDimitry Andric } 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, 4590b57cec5SDimitry Andric const char *suffix, double Value) { 4600b57cec5SDimitry Andric assert(yaml::needsQuotes(Name) == yaml::QuotingType::None && 4610b57cec5SDimitry Andric "TimerGroup name should not need quotes"); 4620b57cec5SDimitry Andric assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None && 4630b57cec5SDimitry Andric "Timer name should not need quotes"); 4640b57cec5SDimitry Andric constexpr auto max_digits10 = std::numeric_limits<double>::max_digits10; 4650b57cec5SDimitry Andric OS << "\t\"time." << Name << '.' << R.Name << suffix 4660b57cec5SDimitry Andric << "\": " << format("%.*e", max_digits10 - 1, Value); 4670b57cec5SDimitry Andric } 4680b57cec5SDimitry Andric 4690b57cec5SDimitry Andric const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) { 4700b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric prepareToPrintList(false); 4730b57cec5SDimitry Andric for (const PrintRecord &R : TimersToPrint) { 4740b57cec5SDimitry Andric OS << delim; 4750b57cec5SDimitry Andric delim = ",\n"; 4760b57cec5SDimitry Andric 4770b57cec5SDimitry Andric const TimeRecord &T = R.Time; 4780b57cec5SDimitry Andric printJSONValue(OS, R, ".wall", T.getWallTime()); 4790b57cec5SDimitry Andric OS << delim; 4800b57cec5SDimitry Andric printJSONValue(OS, R, ".user", T.getUserTime()); 4810b57cec5SDimitry Andric OS << delim; 4820b57cec5SDimitry Andric printJSONValue(OS, R, ".sys", T.getSystemTime()); 4830b57cec5SDimitry Andric if (T.getMemUsed()) { 4840b57cec5SDimitry Andric OS << delim; 4850b57cec5SDimitry Andric printJSONValue(OS, R, ".mem", T.getMemUsed()); 4860b57cec5SDimitry Andric } 487*fe6060f1SDimitry Andric if (T.getInstructionsExecuted()) { 488*fe6060f1SDimitry Andric OS << delim; 489*fe6060f1SDimitry Andric printJSONValue(OS, R, ".instr", T.getInstructionsExecuted()); 490*fe6060f1SDimitry Andric } 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric TimersToPrint.clear(); 4930b57cec5SDimitry Andric return delim; 4940b57cec5SDimitry Andric } 4950b57cec5SDimitry Andric 4960b57cec5SDimitry Andric const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) { 4970b57cec5SDimitry Andric sys::SmartScopedLock<true> L(*TimerLock); 4980b57cec5SDimitry Andric for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 4990b57cec5SDimitry Andric delim = TG->printJSONValues(OS, delim); 5000b57cec5SDimitry Andric return delim; 5010b57cec5SDimitry Andric } 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric void TimerGroup::ConstructTimerLists() { 5040b57cec5SDimitry Andric (void)*NamedGroupedTimers; 5050b57cec5SDimitry Andric } 506cd675bb6SDimitry Andric 507cd675bb6SDimitry Andric std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() { 508cd675bb6SDimitry Andric return std::unique_ptr<TimerGroup>(DefaultTimerGroup.claim()); 509cd675bb6SDimitry Andric } 510