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