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