Home | History | Annotate | Download | only in Support
      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 // 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/raw_ostream.h"
     24 using namespace llvm;
     25 
     26 // getLibSupportInfoOutputFilename - This ugly hack is brought to you courtesy
     27 // of constructor/destructor ordering being unspecified by C++.  Basically the
     28 // problem is that a Statistic object gets destroyed, which ends up calling
     29 // 'GetLibSupportInfoOutputFile()' (below), which calls this function.
     30 // LibSupportInfoOutputFilename used to be a global variable, but sometimes it
     31 // would get destroyed before the Statistic, causing havoc to ensue.  We "fix"
     32 // this by creating the string the first time it is needed and never destroying
     33 // it.
     34 static ManagedStatic<std::string> LibSupportInfoOutputFilename;
     35 static std::string &getLibSupportInfoOutputFilename() {
     36   return *LibSupportInfoOutputFilename;
     37 }
     38 
     39 static ManagedStatic<sys::SmartMutex<true> > TimerLock;
     40 
     41 namespace {
     42   static cl::opt<bool>
     43   TrackSpace("track-memory", cl::desc("Enable -time-passes memory "
     44                                       "tracking (this may be slow)"),
     45              cl::Hidden);
     46 
     47   static cl::opt<std::string, true>
     48   InfoOutputFilename("info-output-file", cl::value_desc("filename"),
     49                      cl::desc("File to append -stats and -timer output to"),
     50                    cl::Hidden, cl::location(getLibSupportInfoOutputFilename()));
     51 }
     52 
     53 // Return a file stream to print our output on.
     54 std::unique_ptr<raw_fd_ostream> llvm::CreateInfoOutputFile() {
     55   const std::string &OutputFilename = getLibSupportInfoOutputFilename();
     56   if (OutputFilename.empty())
     57     return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
     58   if (OutputFilename == "-")
     59     return llvm::make_unique<raw_fd_ostream>(1, false); // stdout.
     60 
     61   // Append mode is used because the info output file is opened and closed
     62   // each time -stats or -time-passes wants to print output to it. To
     63   // compensate for this, the test-suite Makefiles have code to delete the
     64   // info output file before running commands which write to it.
     65   std::error_code EC;
     66   auto Result = llvm::make_unique<raw_fd_ostream>(
     67       OutputFilename, EC, sys::fs::F_Append | sys::fs::F_Text);
     68   if (!EC)
     69     return Result;
     70 
     71   errs() << "Error opening info-output-file '"
     72     << OutputFilename << " for appending!\n";
     73   return llvm::make_unique<raw_fd_ostream>(2, false); // stderr.
     74 }
     75 
     76 
     77 static TimerGroup *DefaultTimerGroup = nullptr;
     78 static TimerGroup *getDefaultTimerGroup() {
     79   TimerGroup *tmp = DefaultTimerGroup;
     80   sys::MemoryFence();
     81   if (tmp) return tmp;
     82 
     83   sys::SmartScopedLock<true> Lock(*TimerLock);
     84   tmp = DefaultTimerGroup;
     85   if (!tmp) {
     86     tmp = new TimerGroup("Miscellaneous Ungrouped Timers");
     87     sys::MemoryFence();
     88     DefaultTimerGroup = tmp;
     89   }
     90 
     91   return tmp;
     92 }
     93 
     94 //===----------------------------------------------------------------------===//
     95 // Timer Implementation
     96 //===----------------------------------------------------------------------===//
     97 
     98 void Timer::init(StringRef N) {
     99   init(N, *getDefaultTimerGroup());
    100 }
    101 
    102 void Timer::init(StringRef N, TimerGroup &tg) {
    103   assert(!TG && "Timer already initialized");
    104   Name.assign(N.begin(), N.end());
    105   Started = false;
    106   TG = &tg;
    107   TG->addTimer(*this);
    108 }
    109 
    110 Timer::~Timer() {
    111   if (!TG) return;  // Never initialized, or already cleared.
    112   TG->removeTimer(*this);
    113 }
    114 
    115 static inline size_t getMemUsage() {
    116   if (!TrackSpace) return 0;
    117   return sys::Process::GetMallocUsage();
    118 }
    119 
    120 TimeRecord TimeRecord::getCurrentTime(bool Start) {
    121   TimeRecord Result;
    122   sys::TimeValue now(0,0), user(0,0), sys(0,0);
    123 
    124   if (Start) {
    125     Result.MemUsed = getMemUsage();
    126     sys::Process::GetTimeUsage(now, user, sys);
    127   } else {
    128     sys::Process::GetTimeUsage(now, user, sys);
    129     Result.MemUsed = getMemUsage();
    130   }
    131 
    132   Result.WallTime   =  now.seconds() +  now.microseconds() / 1000000.0;
    133   Result.UserTime   = user.seconds() + user.microseconds() / 1000000.0;
    134   Result.SystemTime =  sys.seconds() +  sys.microseconds() / 1000000.0;
    135   return Result;
    136 }
    137 
    138 static ManagedStatic<std::vector<Timer*> > ActiveTimers;
    139 
    140 void Timer::startTimer() {
    141   Started = true;
    142   ActiveTimers->push_back(this);
    143   Time -= TimeRecord::getCurrentTime(true);
    144 }
    145 
    146 void Timer::stopTimer() {
    147   Time += TimeRecord::getCurrentTime(false);
    148 
    149   if (ActiveTimers->back() == this) {
    150     ActiveTimers->pop_back();
    151   } else {
    152     std::vector<Timer*>::iterator I =
    153       std::find(ActiveTimers->begin(), ActiveTimers->end(), this);
    154     assert(I != ActiveTimers->end() && "stop but no startTimer?");
    155     ActiveTimers->erase(I);
    156   }
    157 }
    158 
    159 static void printVal(double Val, double Total, raw_ostream &OS) {
    160   if (Total < 1e-7)   // Avoid dividing by zero.
    161     OS << "        -----     ";
    162   else
    163     OS << format("  %7.4f (%5.1f%%)", Val, Val*100/Total);
    164 }
    165 
    166 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const {
    167   if (Total.getUserTime())
    168     printVal(getUserTime(), Total.getUserTime(), OS);
    169   if (Total.getSystemTime())
    170     printVal(getSystemTime(), Total.getSystemTime(), OS);
    171   if (Total.getProcessTime())
    172     printVal(getProcessTime(), Total.getProcessTime(), OS);
    173   printVal(getWallTime(), Total.getWallTime(), OS);
    174 
    175   OS << "  ";
    176 
    177   if (Total.getMemUsed())
    178     OS << format("%9" PRId64 "  ", (int64_t)getMemUsed());
    179 }
    180 
    181 
    182 //===----------------------------------------------------------------------===//
    183 //   NamedRegionTimer Implementation
    184 //===----------------------------------------------------------------------===//
    185 
    186 namespace {
    187 
    188 typedef StringMap<Timer> Name2TimerMap;
    189 
    190 class Name2PairMap {
    191   StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map;
    192 public:
    193   ~Name2PairMap() {
    194     for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator
    195          I = Map.begin(), E = Map.end(); I != E; ++I)
    196       delete I->second.first;
    197   }
    198 
    199   Timer &get(StringRef Name, StringRef GroupName) {
    200     sys::SmartScopedLock<true> L(*TimerLock);
    201 
    202     std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName];
    203 
    204     if (!GroupEntry.first)
    205       GroupEntry.first = new TimerGroup(GroupName);
    206 
    207     Timer &T = GroupEntry.second[Name];
    208     if (!T.isInitialized())
    209       T.init(Name, *GroupEntry.first);
    210     return T;
    211   }
    212 };
    213 
    214 }
    215 
    216 static ManagedStatic<Name2TimerMap> NamedTimers;
    217 static ManagedStatic<Name2PairMap> NamedGroupedTimers;
    218 
    219 static Timer &getNamedRegionTimer(StringRef Name) {
    220   sys::SmartScopedLock<true> L(*TimerLock);
    221 
    222   Timer &T = (*NamedTimers)[Name];
    223   if (!T.isInitialized())
    224     T.init(Name);
    225   return T;
    226 }
    227 
    228 NamedRegionTimer::NamedRegionTimer(StringRef Name,
    229                                    bool Enabled)
    230   : TimeRegion(!Enabled ? nullptr : &getNamedRegionTimer(Name)) {}
    231 
    232 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName,
    233                                    bool Enabled)
    234   : TimeRegion(!Enabled ? nullptr : &NamedGroupedTimers->get(Name, GroupName)){}
    235 
    236 //===----------------------------------------------------------------------===//
    237 //   TimerGroup Implementation
    238 //===----------------------------------------------------------------------===//
    239 
    240 /// TimerGroupList - This is the global list of TimerGroups, maintained by the
    241 /// TimerGroup ctor/dtor and is protected by the TimerLock lock.
    242 static TimerGroup *TimerGroupList = nullptr;
    243 
    244 TimerGroup::TimerGroup(StringRef name)
    245   : Name(name.begin(), name.end()), FirstTimer(nullptr) {
    246 
    247   // Add the group to TimerGroupList.
    248   sys::SmartScopedLock<true> L(*TimerLock);
    249   if (TimerGroupList)
    250     TimerGroupList->Prev = &Next;
    251   Next = TimerGroupList;
    252   Prev = &TimerGroupList;
    253   TimerGroupList = this;
    254 }
    255 
    256 TimerGroup::~TimerGroup() {
    257   // If the timer group is destroyed before the timers it owns, accumulate and
    258   // print the timing data.
    259   while (FirstTimer)
    260     removeTimer(*FirstTimer);
    261 
    262   // Remove the group from the TimerGroupList.
    263   sys::SmartScopedLock<true> L(*TimerLock);
    264   *Prev = Next;
    265   if (Next)
    266     Next->Prev = Prev;
    267 }
    268 
    269 
    270 void TimerGroup::removeTimer(Timer &T) {
    271   sys::SmartScopedLock<true> L(*TimerLock);
    272 
    273   // If the timer was started, move its data to TimersToPrint.
    274   if (T.Started)
    275     TimersToPrint.emplace_back(T.Time, T.Name);
    276 
    277   T.TG = nullptr;
    278 
    279   // Unlink the timer from our list.
    280   *T.Prev = T.Next;
    281   if (T.Next)
    282     T.Next->Prev = T.Prev;
    283 
    284   // Print the report when all timers in this group are destroyed if some of
    285   // them were started.
    286   if (FirstTimer || TimersToPrint.empty())
    287     return;
    288 
    289   std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
    290   PrintQueuedTimers(*OutStream);
    291 }
    292 
    293 void TimerGroup::addTimer(Timer &T) {
    294   sys::SmartScopedLock<true> L(*TimerLock);
    295 
    296   // Add the timer to our list.
    297   if (FirstTimer)
    298     FirstTimer->Prev = &T.Next;
    299   T.Next = FirstTimer;
    300   T.Prev = &FirstTimer;
    301   FirstTimer = &T;
    302 }
    303 
    304 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) {
    305   // Sort the timers in descending order by amount of time taken.
    306   std::sort(TimersToPrint.begin(), TimersToPrint.end());
    307 
    308   TimeRecord Total;
    309   for (auto &RecordNamePair : TimersToPrint)
    310     Total += RecordNamePair.first;
    311 
    312   // Print out timing header.
    313   OS << "===" << std::string(73, '-') << "===\n";
    314   // Figure out how many spaces to indent TimerGroup name.
    315   unsigned Padding = (80-Name.length())/2;
    316   if (Padding > 80) Padding = 0;         // Don't allow "negative" numbers
    317   OS.indent(Padding) << Name << '\n';
    318   OS << "===" << std::string(73, '-') << "===\n";
    319 
    320   // If this is not an collection of ungrouped times, print the total time.
    321   // Ungrouped timers don't really make sense to add up.  We still print the
    322   // TOTAL line to make the percentages make sense.
    323   if (this != DefaultTimerGroup)
    324     OS << format("  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n",
    325                  Total.getProcessTime(), Total.getWallTime());
    326   OS << '\n';
    327 
    328   if (Total.getUserTime())
    329     OS << "   ---User Time---";
    330   if (Total.getSystemTime())
    331     OS << "   --System Time--";
    332   if (Total.getProcessTime())
    333     OS << "   --User+System--";
    334   OS << "   ---Wall Time---";
    335   if (Total.getMemUsed())
    336     OS << "  ---Mem---";
    337   OS << "  --- Name ---\n";
    338 
    339   // Loop through all of the timing data, printing it out.
    340   for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) {
    341     const std::pair<TimeRecord, std::string> &Entry = TimersToPrint[e-i-1];
    342     Entry.first.print(Total, OS);
    343     OS << Entry.second << '\n';
    344   }
    345 
    346   Total.print(Total, OS);
    347   OS << "Total\n\n";
    348   OS.flush();
    349 
    350   TimersToPrint.clear();
    351 }
    352 
    353 /// print - Print any started timers in this group and zero them.
    354 void TimerGroup::print(raw_ostream &OS) {
    355   sys::SmartScopedLock<true> L(*TimerLock);
    356 
    357   // See if any of our timers were started, if so add them to TimersToPrint and
    358   // reset them.
    359   for (Timer *T = FirstTimer; T; T = T->Next) {
    360     if (!T->Started) continue;
    361     TimersToPrint.emplace_back(T->Time, T->Name);
    362 
    363     // Clear out the time.
    364     T->Started = 0;
    365     T->Time = TimeRecord();
    366   }
    367 
    368   // If any timers were started, print the group.
    369   if (!TimersToPrint.empty())
    370     PrintQueuedTimers(OS);
    371 }
    372 
    373 /// printAll - This static method prints all timers and clears them all out.
    374 void TimerGroup::printAll(raw_ostream &OS) {
    375   sys::SmartScopedLock<true> L(*TimerLock);
    376 
    377   for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next)
    378     TG->print(OS);
    379 }
    380