LLVM API Documentation
00001 //===-- Timer.cpp - Interval Timing Support -------------------------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // Interval Timing implementation. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "llvm/Support/Timer.h" 00015 #include "llvm/ADT/StringMap.h" 00016 #include "llvm/Support/CommandLine.h" 00017 #include "llvm/Support/Debug.h" 00018 #include "llvm/Support/FileSystem.h" 00019 #include "llvm/Support/Format.h" 00020 #include "llvm/Support/ManagedStatic.h" 00021 #include "llvm/Support/Mutex.h" 00022 #include "llvm/Support/MutexGuard.h" 00023 #include "llvm/Support/Process.h" 00024 #include "llvm/Support/raw_ostream.h" 00025 using namespace llvm; 00026 00027 // CreateInfoOutputFile - Return a file stream to print our output on. 00028 namespace llvm { extern raw_ostream *CreateInfoOutputFile(); } 00029 00030 // getLibSupportInfoOutputFilename - This ugly hack is brought to you courtesy 00031 // of constructor/destructor ordering being unspecified by C++. Basically the 00032 // problem is that a Statistic object gets destroyed, which ends up calling 00033 // 'GetLibSupportInfoOutputFile()' (below), which calls this function. 00034 // LibSupportInfoOutputFilename used to be a global variable, but sometimes it 00035 // would get destroyed before the Statistic, causing havoc to ensue. We "fix" 00036 // this by creating the string the first time it is needed and never destroying 00037 // it. 00038 static ManagedStatic<std::string> LibSupportInfoOutputFilename; 00039 static std::string &getLibSupportInfoOutputFilename() { 00040 return *LibSupportInfoOutputFilename; 00041 } 00042 00043 static ManagedStatic<sys::SmartMutex<true> > TimerLock; 00044 00045 namespace { 00046 static cl::opt<bool> 00047 TrackSpace("track-memory", cl::desc("Enable -time-passes memory " 00048 "tracking (this may be slow)"), 00049 cl::Hidden); 00050 00051 static cl::opt<std::string, true> 00052 InfoOutputFilename("info-output-file", cl::value_desc("filename"), 00053 cl::desc("File to append -stats and -timer output to"), 00054 cl::Hidden, cl::location(getLibSupportInfoOutputFilename())); 00055 } 00056 00057 // CreateInfoOutputFile - Return a file stream to print our output on. 00058 raw_ostream *llvm::CreateInfoOutputFile() { 00059 const std::string &OutputFilename = getLibSupportInfoOutputFilename(); 00060 if (OutputFilename.empty()) 00061 return new raw_fd_ostream(2, false); // stderr. 00062 if (OutputFilename == "-") 00063 return new raw_fd_ostream(1, false); // stdout. 00064 00065 // Append mode is used because the info output file is opened and closed 00066 // each time -stats or -time-passes wants to print output to it. To 00067 // compensate for this, the test-suite Makefiles have code to delete the 00068 // info output file before running commands which write to it. 00069 std::error_code EC; 00070 raw_ostream *Result = new raw_fd_ostream(OutputFilename, EC, 00071 sys::fs::F_Append | sys::fs::F_Text); 00072 if (!EC) 00073 return Result; 00074 00075 errs() << "Error opening info-output-file '" 00076 << OutputFilename << " for appending!\n"; 00077 delete Result; 00078 return new raw_fd_ostream(2, false); // stderr. 00079 } 00080 00081 00082 static TimerGroup *DefaultTimerGroup = nullptr; 00083 static TimerGroup *getDefaultTimerGroup() { 00084 TimerGroup *tmp = DefaultTimerGroup; 00085 sys::MemoryFence(); 00086 if (tmp) return tmp; 00087 00088 sys::SmartScopedLock<true> Lock(*TimerLock); 00089 tmp = DefaultTimerGroup; 00090 if (!tmp) { 00091 tmp = new TimerGroup("Miscellaneous Ungrouped Timers"); 00092 sys::MemoryFence(); 00093 DefaultTimerGroup = tmp; 00094 } 00095 00096 return tmp; 00097 } 00098 00099 //===----------------------------------------------------------------------===// 00100 // Timer Implementation 00101 //===----------------------------------------------------------------------===// 00102 00103 void Timer::init(StringRef N) { 00104 assert(!TG && "Timer already initialized"); 00105 Name.assign(N.begin(), N.end()); 00106 Started = false; 00107 TG = getDefaultTimerGroup(); 00108 TG->addTimer(*this); 00109 } 00110 00111 void Timer::init(StringRef N, TimerGroup &tg) { 00112 assert(!TG && "Timer already initialized"); 00113 Name.assign(N.begin(), N.end()); 00114 Started = false; 00115 TG = &tg; 00116 TG->addTimer(*this); 00117 } 00118 00119 Timer::~Timer() { 00120 if (!TG) return; // Never initialized, or already cleared. 00121 TG->removeTimer(*this); 00122 } 00123 00124 static inline size_t getMemUsage() { 00125 if (!TrackSpace) return 0; 00126 return sys::Process::GetMallocUsage(); 00127 } 00128 00129 TimeRecord TimeRecord::getCurrentTime(bool Start) { 00130 TimeRecord Result; 00131 sys::TimeValue now(0,0), user(0,0), sys(0,0); 00132 00133 if (Start) { 00134 Result.MemUsed = getMemUsage(); 00135 sys::Process::GetTimeUsage(now, user, sys); 00136 } else { 00137 sys::Process::GetTimeUsage(now, user, sys); 00138 Result.MemUsed = getMemUsage(); 00139 } 00140 00141 Result.WallTime = now.seconds() + now.microseconds() / 1000000.0; 00142 Result.UserTime = user.seconds() + user.microseconds() / 1000000.0; 00143 Result.SystemTime = sys.seconds() + sys.microseconds() / 1000000.0; 00144 return Result; 00145 } 00146 00147 static ManagedStatic<std::vector<Timer*> > ActiveTimers; 00148 00149 void Timer::startTimer() { 00150 Started = true; 00151 ActiveTimers->push_back(this); 00152 Time -= TimeRecord::getCurrentTime(true); 00153 } 00154 00155 void Timer::stopTimer() { 00156 Time += TimeRecord::getCurrentTime(false); 00157 00158 if (ActiveTimers->back() == this) { 00159 ActiveTimers->pop_back(); 00160 } else { 00161 std::vector<Timer*>::iterator I = 00162 std::find(ActiveTimers->begin(), ActiveTimers->end(), this); 00163 assert(I != ActiveTimers->end() && "stop but no startTimer?"); 00164 ActiveTimers->erase(I); 00165 } 00166 } 00167 00168 static void printVal(double Val, double Total, raw_ostream &OS) { 00169 if (Total < 1e-7) // Avoid dividing by zero. 00170 OS << " ----- "; 00171 else 00172 OS << format(" %7.4f (%5.1f%%)", Val, Val*100/Total); 00173 } 00174 00175 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const { 00176 if (Total.getUserTime()) 00177 printVal(getUserTime(), Total.getUserTime(), OS); 00178 if (Total.getSystemTime()) 00179 printVal(getSystemTime(), Total.getSystemTime(), OS); 00180 if (Total.getProcessTime()) 00181 printVal(getProcessTime(), Total.getProcessTime(), OS); 00182 printVal(getWallTime(), Total.getWallTime(), OS); 00183 00184 OS << " "; 00185 00186 if (Total.getMemUsed()) 00187 OS << format("%9" PRId64 " ", (int64_t)getMemUsed()); 00188 } 00189 00190 00191 //===----------------------------------------------------------------------===// 00192 // NamedRegionTimer Implementation 00193 //===----------------------------------------------------------------------===// 00194 00195 namespace { 00196 00197 typedef StringMap<Timer> Name2TimerMap; 00198 00199 class Name2PairMap { 00200 StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map; 00201 public: 00202 ~Name2PairMap() { 00203 for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator 00204 I = Map.begin(), E = Map.end(); I != E; ++I) 00205 delete I->second.first; 00206 } 00207 00208 Timer &get(StringRef Name, StringRef GroupName) { 00209 sys::SmartScopedLock<true> L(*TimerLock); 00210 00211 std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName]; 00212 00213 if (!GroupEntry.first) 00214 GroupEntry.first = new TimerGroup(GroupName); 00215 00216 Timer &T = GroupEntry.second[Name]; 00217 if (!T.isInitialized()) 00218 T.init(Name, *GroupEntry.first); 00219 return T; 00220 } 00221 }; 00222 00223 } 00224 00225 static ManagedStatic<Name2TimerMap> NamedTimers; 00226 static ManagedStatic<Name2PairMap> NamedGroupedTimers; 00227 00228 static Timer &getNamedRegionTimer(StringRef Name) { 00229 sys::SmartScopedLock<true> L(*TimerLock); 00230 00231 Timer &T = (*NamedTimers)[Name]; 00232 if (!T.isInitialized()) 00233 T.init(Name); 00234 return T; 00235 } 00236 00237 NamedRegionTimer::NamedRegionTimer(StringRef Name, 00238 bool Enabled) 00239 : TimeRegion(!Enabled ? nullptr : &getNamedRegionTimer(Name)) {} 00240 00241 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef GroupName, 00242 bool Enabled) 00243 : TimeRegion(!Enabled ? nullptr : &NamedGroupedTimers->get(Name, GroupName)){} 00244 00245 //===----------------------------------------------------------------------===// 00246 // TimerGroup Implementation 00247 //===----------------------------------------------------------------------===// 00248 00249 /// TimerGroupList - This is the global list of TimerGroups, maintained by the 00250 /// TimerGroup ctor/dtor and is protected by the TimerLock lock. 00251 static TimerGroup *TimerGroupList = nullptr; 00252 00253 TimerGroup::TimerGroup(StringRef name) 00254 : Name(name.begin(), name.end()), FirstTimer(nullptr) { 00255 00256 // Add the group to TimerGroupList. 00257 sys::SmartScopedLock<true> L(*TimerLock); 00258 if (TimerGroupList) 00259 TimerGroupList->Prev = &Next; 00260 Next = TimerGroupList; 00261 Prev = &TimerGroupList; 00262 TimerGroupList = this; 00263 } 00264 00265 TimerGroup::~TimerGroup() { 00266 // If the timer group is destroyed before the timers it owns, accumulate and 00267 // print the timing data. 00268 while (FirstTimer) 00269 removeTimer(*FirstTimer); 00270 00271 // Remove the group from the TimerGroupList. 00272 sys::SmartScopedLock<true> L(*TimerLock); 00273 *Prev = Next; 00274 if (Next) 00275 Next->Prev = Prev; 00276 } 00277 00278 00279 void TimerGroup::removeTimer(Timer &T) { 00280 sys::SmartScopedLock<true> L(*TimerLock); 00281 00282 // If the timer was started, move its data to TimersToPrint. 00283 if (T.Started) 00284 TimersToPrint.push_back(std::make_pair(T.Time, T.Name)); 00285 00286 T.TG = nullptr; 00287 00288 // Unlink the timer from our list. 00289 *T.Prev = T.Next; 00290 if (T.Next) 00291 T.Next->Prev = T.Prev; 00292 00293 // Print the report when all timers in this group are destroyed if some of 00294 // them were started. 00295 if (FirstTimer || TimersToPrint.empty()) 00296 return; 00297 00298 raw_ostream *OutStream = CreateInfoOutputFile(); 00299 PrintQueuedTimers(*OutStream); 00300 delete OutStream; // Close the file. 00301 } 00302 00303 void TimerGroup::addTimer(Timer &T) { 00304 sys::SmartScopedLock<true> L(*TimerLock); 00305 00306 // Add the timer to our list. 00307 if (FirstTimer) 00308 FirstTimer->Prev = &T.Next; 00309 T.Next = FirstTimer; 00310 T.Prev = &FirstTimer; 00311 FirstTimer = &T; 00312 } 00313 00314 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { 00315 // Sort the timers in descending order by amount of time taken. 00316 std::sort(TimersToPrint.begin(), TimersToPrint.end()); 00317 00318 TimeRecord Total; 00319 for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) 00320 Total += TimersToPrint[i].first; 00321 00322 // Print out timing header. 00323 OS << "===" << std::string(73, '-') << "===\n"; 00324 // Figure out how many spaces to indent TimerGroup name. 00325 unsigned Padding = (80-Name.length())/2; 00326 if (Padding > 80) Padding = 0; // Don't allow "negative" numbers 00327 OS.indent(Padding) << Name << '\n'; 00328 OS << "===" << std::string(73, '-') << "===\n"; 00329 00330 // If this is not an collection of ungrouped times, print the total time. 00331 // Ungrouped timers don't really make sense to add up. We still print the 00332 // TOTAL line to make the percentages make sense. 00333 if (this != DefaultTimerGroup) 00334 OS << format(" Total Execution Time: %5.4f seconds (%5.4f wall clock)\n", 00335 Total.getProcessTime(), Total.getWallTime()); 00336 OS << '\n'; 00337 00338 if (Total.getUserTime()) 00339 OS << " ---User Time---"; 00340 if (Total.getSystemTime()) 00341 OS << " --System Time--"; 00342 if (Total.getProcessTime()) 00343 OS << " --User+System--"; 00344 OS << " ---Wall Time---"; 00345 if (Total.getMemUsed()) 00346 OS << " ---Mem---"; 00347 OS << " --- Name ---\n"; 00348 00349 // Loop through all of the timing data, printing it out. 00350 for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) { 00351 const std::pair<TimeRecord, std::string> &Entry = TimersToPrint[e-i-1]; 00352 Entry.first.print(Total, OS); 00353 OS << Entry.second << '\n'; 00354 } 00355 00356 Total.print(Total, OS); 00357 OS << "Total\n\n"; 00358 OS.flush(); 00359 00360 TimersToPrint.clear(); 00361 } 00362 00363 /// print - Print any started timers in this group and zero them. 00364 void TimerGroup::print(raw_ostream &OS) { 00365 sys::SmartScopedLock<true> L(*TimerLock); 00366 00367 // See if any of our timers were started, if so add them to TimersToPrint and 00368 // reset them. 00369 for (Timer *T = FirstTimer; T; T = T->Next) { 00370 if (!T->Started) continue; 00371 TimersToPrint.push_back(std::make_pair(T->Time, T->Name)); 00372 00373 // Clear out the time. 00374 T->Started = 0; 00375 T->Time = TimeRecord(); 00376 } 00377 00378 // If any timers were started, print the group. 00379 if (!TimersToPrint.empty()) 00380 PrintQueuedTimers(OS); 00381 } 00382 00383 /// printAll - This static method prints all timers and clears them all out. 00384 void TimerGroup::printAll(raw_ostream &OS) { 00385 sys::SmartScopedLock<true> L(*TimerLock); 00386 00387 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 00388 TG->print(OS); 00389 }