LCOV - code coverage report
Current view: top level - lib/Support - Timer.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 186 211 88.2 %
Date: 2018-10-20 13:21:21 Functions: 27 29 93.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13