1 //===-- Timer.cpp - Interval Timing Support -------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 /// \file Interval Timing implementation. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Support/Timer.h" 14 15 #include "DebugOptions.h" 16 17 #include "llvm/ADT/Statistic.h" 18 #include "llvm/ADT/StringMap.h" 19 #include "llvm/Config/config.h" 20 #include "llvm/Support/CommandLine.h" 21 #include "llvm/Support/FileSystem.h" 22 #include "llvm/Support/Format.h" 23 #include "llvm/Support/ManagedStatic.h" 24 #include "llvm/Support/Mutex.h" 25 #include "llvm/Support/Process.h" 26 #include "llvm/Support/Signposts.h" 27 #include "llvm/Support/YAMLTraits.h" 28 #include "llvm/Support/raw_ostream.h" 29 #include <limits> 30 31 #if HAVE_UNISTD_H 32 #include <unistd.h> 33 #endif 34 35 #ifdef HAVE_PROC_PID_RUSAGE 36 #include <libproc.h> 37 #endif 38 39 using namespace llvm; 40 41 //===----------------------------------------------------------------------===// 42 // Forward declarations for Managed Timer Globals (mtg) getters. 43 // 44 // Globals have been placed at the end of the file to restrict direct 45 // access. Use of getters also has the benefit of making it a bit more explicit 46 // that a global is being used. 47 //===----------------------------------------------------------------------===// 48 namespace { 49 class Name2PairMap; 50 } 51 52 namespace mtg { 53 static std::string &LibSupportInfoOutputFilename(); 54 static const std::string &InfoOutputFilename(); 55 static bool TrackSpace(); 56 static bool SortTimers(); 57 static SignpostEmitter &Signposts(); 58 static sys::SmartMutex<true> &TimerLock(); 59 static TimerGroup &DefaultTimerGroup(); 60 static TimerGroup *claimDefaultTimerGroup(); 61 static Name2PairMap &NamedGroupedTimers(); 62 } // namespace mtg 63 64 //===----------------------------------------------------------------------===// 65 // 66 //===----------------------------------------------------------------------===// 67 void llvm::initTimerOptions() { 68 mtg::TrackSpace(); 69 mtg::InfoOutputFilename(); 70 mtg::SortTimers(); 71 } 72 73 std::unique_ptr<raw_ostream> llvm::CreateInfoOutputFile() { 74 const std::string &OutputFilename = mtg::LibSupportInfoOutputFilename(); 75 if (OutputFilename.empty()) 76 return std::make_unique<raw_fd_ostream>(2, false); // stderr. 77 if (OutputFilename == "-") 78 return std::make_unique<raw_fd_ostream>(1, false); // stdout. 79 80 // Append mode is used because the info output file is opened and closed 81 // each time -stats or -time-passes wants to print output to it. To 82 // compensate for this, the test-suite Makefiles have code to delete the 83 // info output file before running commands which write to it. 84 std::error_code EC; 85 auto Result = std::make_unique<raw_fd_ostream>( 86 OutputFilename, EC, sys::fs::OF_Append | sys::fs::OF_TextWithCRLF); 87 if (!EC) 88 return Result; 89 90 errs() << "Error opening info-output-file '" 91 << OutputFilename << " for appending!\n"; 92 return std::make_unique<raw_fd_ostream>(2, false); // stderr. 93 } 94 95 //===----------------------------------------------------------------------===// 96 // Timer Implementation 97 //===----------------------------------------------------------------------===// 98 99 void Timer::init(StringRef TimerName, StringRef TimerDescription) { 100 init(TimerName, TimerDescription, mtg::DefaultTimerGroup()); 101 } 102 103 void Timer::init(StringRef TimerName, StringRef TimerDescription, 104 TimerGroup &tg) { 105 assert(!TG && "Timer already initialized"); 106 Name.assign(TimerName.begin(), TimerName.end()); 107 Description.assign(TimerDescription.begin(), TimerDescription.end()); 108 Running = Triggered = false; 109 TG = &tg; 110 TG->addTimer(*this); 111 } 112 113 Timer::~Timer() { 114 if (!TG) return; // Never initialized, or already cleared. 115 TG->removeTimer(*this); 116 } 117 118 static inline size_t getMemUsage() { 119 if (!mtg::TrackSpace()) 120 return 0; 121 return sys::Process::GetMallocUsage(); 122 } 123 124 static uint64_t getCurInstructionsExecuted() { 125 #if defined(HAVE_UNISTD_H) && defined(HAVE_PROC_PID_RUSAGE) && \ 126 defined(RUSAGE_INFO_V4) 127 struct rusage_info_v4 ru; 128 if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) { 129 return ru.ri_instructions; 130 } 131 #endif 132 return 0; 133 } 134 135 TimeRecord TimeRecord::getCurrentTime(bool Start) { 136 using Seconds = std::chrono::duration<double, std::ratio<1>>; 137 TimeRecord Result; 138 sys::TimePoint<> now; 139 std::chrono::nanoseconds user, sys; 140 141 if (Start) { 142 Result.MemUsed = getMemUsage(); 143 Result.InstructionsExecuted = getCurInstructionsExecuted(); 144 sys::Process::GetTimeUsage(now, user, sys); 145 } else { 146 sys::Process::GetTimeUsage(now, user, sys); 147 Result.InstructionsExecuted = getCurInstructionsExecuted(); 148 Result.MemUsed = getMemUsage(); 149 } 150 151 Result.WallTime = Seconds(now.time_since_epoch()).count(); 152 Result.UserTime = Seconds(user).count(); 153 Result.SystemTime = Seconds(sys).count(); 154 return Result; 155 } 156 157 void Timer::startTimer() { 158 assert(!Running && "Cannot start a running timer"); 159 Running = Triggered = true; 160 mtg::Signposts().startInterval(this, getName()); 161 StartTime = TimeRecord::getCurrentTime(true); 162 } 163 164 void Timer::stopTimer() { 165 assert(Running && "Cannot stop a paused timer"); 166 Running = false; 167 Time += TimeRecord::getCurrentTime(false); 168 Time -= StartTime; 169 mtg::Signposts().endInterval(this, getName()); 170 } 171 172 void Timer::clear() { 173 Running = Triggered = false; 174 Time = StartTime = TimeRecord(); 175 } 176 177 static void printVal(double Val, double Total, raw_ostream &OS) { 178 if (Total < 1e-7) // Avoid dividing by zero. 179 OS << " ----- "; 180 else 181 OS << format(" %7.4f (%5.1f%%)", Val, Val*100/Total); 182 } 183 184 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const { 185 if (Total.getUserTime()) 186 printVal(getUserTime(), Total.getUserTime(), OS); 187 if (Total.getSystemTime()) 188 printVal(getSystemTime(), Total.getSystemTime(), OS); 189 if (Total.getProcessTime()) 190 printVal(getProcessTime(), Total.getProcessTime(), OS); 191 printVal(getWallTime(), Total.getWallTime(), OS); 192 193 OS << " "; 194 195 if (Total.getMemUsed()) 196 OS << format("%9" PRId64 " ", (int64_t)getMemUsed()); 197 if (Total.getInstructionsExecuted()) 198 OS << format("%9" PRId64 " ", (int64_t)getInstructionsExecuted()); 199 } 200 201 202 //===----------------------------------------------------------------------===// 203 // NamedRegionTimer Implementation 204 //===----------------------------------------------------------------------===// 205 206 namespace { 207 208 typedef StringMap<Timer> Name2TimerMap; 209 210 class Name2PairMap { 211 StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map; 212 public: 213 ~Name2PairMap() { 214 for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator 215 I = Map.begin(), E = Map.end(); I != E; ++I) 216 delete I->second.first; 217 } 218 219 Timer &get(StringRef Name, StringRef Description, StringRef GroupName, 220 StringRef GroupDescription) { 221 sys::SmartScopedLock<true> L(mtg::TimerLock()); 222 223 std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName]; 224 225 if (!GroupEntry.first) 226 GroupEntry.first = new TimerGroup(GroupName, GroupDescription); 227 228 Timer &T = GroupEntry.second[Name]; 229 if (!T.isInitialized()) 230 T.init(Name, Description, *GroupEntry.first); 231 return T; 232 } 233 }; 234 235 } 236 237 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef Description, 238 StringRef GroupName, 239 StringRef GroupDescription, bool Enabled) 240 : TimeRegion(!Enabled ? nullptr 241 : &mtg::NamedGroupedTimers().get(Name, Description, 242 GroupName, 243 GroupDescription)) {} 244 245 //===----------------------------------------------------------------------===// 246 // TimerGroup Implementation 247 //===----------------------------------------------------------------------===// 248 249 /// This is the global list of TimerGroups, maintained by the TimerGroup 250 /// ctor/dtor and is protected by the TimerLock lock. 251 static TimerGroup *TimerGroupList = nullptr; 252 253 TimerGroup::TimerGroup(StringRef Name, StringRef Description, 254 sys::SmartMutex<true> &lock) 255 : Name(Name.begin(), Name.end()), 256 Description(Description.begin(), Description.end()) { 257 // Add the group to TimerGroupList. 258 sys::SmartScopedLock<true> L(lock); 259 if (TimerGroupList) 260 TimerGroupList->Prev = &Next; 261 Next = TimerGroupList; 262 Prev = &TimerGroupList; 263 TimerGroupList = this; 264 } 265 266 TimerGroup::TimerGroup(StringRef Name, StringRef Description) 267 : TimerGroup(Name, Description, mtg::TimerLock()) {} 268 269 TimerGroup::TimerGroup(StringRef Name, StringRef Description, 270 const StringMap<TimeRecord> &Records) 271 : TimerGroup(Name, Description) { 272 TimersToPrint.reserve(Records.size()); 273 for (const auto &P : Records) 274 TimersToPrint.emplace_back(P.getValue(), std::string(P.getKey()), 275 std::string(P.getKey())); 276 assert(TimersToPrint.size() == Records.size() && "Size mismatch"); 277 } 278 279 TimerGroup::~TimerGroup() { 280 // If the timer group is destroyed before the timers it owns, accumulate and 281 // print the timing data. 282 while (FirstTimer) 283 removeTimer(*FirstTimer); 284 285 // Remove the group from the TimerGroupList. 286 sys::SmartScopedLock<true> L(mtg::TimerLock()); 287 *Prev = Next; 288 if (Next) 289 Next->Prev = Prev; 290 } 291 292 293 void TimerGroup::removeTimer(Timer &T) { 294 sys::SmartScopedLock<true> L(mtg::TimerLock()); 295 296 // If the timer was started, move its data to TimersToPrint. 297 if (T.hasTriggered()) 298 TimersToPrint.emplace_back(T.Time, T.Name, T.Description); 299 300 T.TG = nullptr; 301 302 // Unlink the timer from our list. 303 *T.Prev = T.Next; 304 if (T.Next) 305 T.Next->Prev = T.Prev; 306 307 // Print the report when all timers in this group are destroyed if some of 308 // them were started. 309 if (FirstTimer || TimersToPrint.empty()) 310 return; 311 312 std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile(); 313 PrintQueuedTimers(*OutStream); 314 } 315 316 void TimerGroup::addTimer(Timer &T) { 317 sys::SmartScopedLock<true> L(mtg::TimerLock()); 318 319 // Add the timer to our list. 320 if (FirstTimer) 321 FirstTimer->Prev = &T.Next; 322 T.Next = FirstTimer; 323 T.Prev = &FirstTimer; 324 FirstTimer = &T; 325 } 326 327 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { 328 // Perhaps sort the timers in descending order by amount of time taken. 329 if (mtg::SortTimers()) 330 llvm::sort(TimersToPrint); 331 332 TimeRecord Total; 333 for (const PrintRecord &Record : TimersToPrint) 334 Total += Record.Time; 335 336 // Print out timing header. 337 OS << "===" << std::string(73, '-') << "===\n"; 338 // Figure out how many spaces to indent TimerGroup name. 339 unsigned Padding = (80-Description.length())/2; 340 if (Padding > 80) Padding = 0; // Don't allow "negative" numbers 341 OS.indent(Padding) << Description << '\n'; 342 OS << "===" << std::string(73, '-') << "===\n"; 343 344 // If this is not an collection of ungrouped times, print the total time. 345 // Ungrouped timers don't really make sense to add up. We still print the 346 // TOTAL line to make the percentages make sense. 347 if (this != &mtg::DefaultTimerGroup()) 348 OS << format(" Total Execution Time: %5.4f seconds (%5.4f wall clock)\n", 349 Total.getProcessTime(), Total.getWallTime()); 350 OS << '\n'; 351 352 if (Total.getUserTime()) 353 OS << " ---User Time---"; 354 if (Total.getSystemTime()) 355 OS << " --System Time--"; 356 if (Total.getProcessTime()) 357 OS << " --User+System--"; 358 OS << " ---Wall Time---"; 359 if (Total.getMemUsed()) 360 OS << " ---Mem---"; 361 if (Total.getInstructionsExecuted()) 362 OS << " ---Instr---"; 363 OS << " --- Name ---\n"; 364 365 // Loop through all of the timing data, printing it out. 366 for (const PrintRecord &Record : llvm::reverse(TimersToPrint)) { 367 Record.Time.print(Total, OS); 368 OS << Record.Description << '\n'; 369 } 370 371 Total.print(Total, OS); 372 OS << "Total\n\n"; 373 OS.flush(); 374 375 TimersToPrint.clear(); 376 } 377 378 void TimerGroup::prepareToPrintList(bool ResetTime) { 379 // See if any of our timers were started, if so add them to TimersToPrint. 380 for (Timer *T = FirstTimer; T; T = T->Next) { 381 if (!T->hasTriggered()) continue; 382 bool WasRunning = T->isRunning(); 383 if (WasRunning) 384 T->stopTimer(); 385 386 TimersToPrint.emplace_back(T->Time, T->Name, T->Description); 387 388 if (ResetTime) 389 T->clear(); 390 391 if (WasRunning) 392 T->startTimer(); 393 } 394 } 395 396 void TimerGroup::print(raw_ostream &OS, bool ResetAfterPrint) { 397 { 398 // After preparing the timers we can free the lock 399 sys::SmartScopedLock<true> L(mtg::TimerLock()); 400 prepareToPrintList(ResetAfterPrint); 401 } 402 403 // If any timers were started, print the group. 404 if (!TimersToPrint.empty()) 405 PrintQueuedTimers(OS); 406 } 407 408 void TimerGroup::clear() { 409 sys::SmartScopedLock<true> L(mtg::TimerLock()); 410 for (Timer *T = FirstTimer; T; T = T->Next) 411 T->clear(); 412 } 413 414 void TimerGroup::printAll(raw_ostream &OS) { 415 sys::SmartScopedLock<true> L(mtg::TimerLock()); 416 417 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 418 TG->print(OS); 419 } 420 421 void TimerGroup::clearAll() { 422 sys::SmartScopedLock<true> L(mtg::TimerLock()); 423 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 424 TG->clear(); 425 } 426 427 void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, 428 const char *suffix, double Value) { 429 assert(yaml::needsQuotes(Name) == yaml::QuotingType::None && 430 "TimerGroup name should not need quotes"); 431 assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None && 432 "Timer name should not need quotes"); 433 constexpr auto max_digits10 = std::numeric_limits<double>::max_digits10; 434 OS << "\t\"time." << Name << '.' << R.Name << suffix 435 << "\": " << format("%.*e", max_digits10 - 1, Value); 436 } 437 438 const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) { 439 sys::SmartScopedLock<true> L(mtg::TimerLock()); 440 441 prepareToPrintList(false); 442 for (const PrintRecord &R : TimersToPrint) { 443 OS << delim; 444 delim = ",\n"; 445 446 const TimeRecord &T = R.Time; 447 printJSONValue(OS, R, ".wall", T.getWallTime()); 448 OS << delim; 449 printJSONValue(OS, R, ".user", T.getUserTime()); 450 OS << delim; 451 printJSONValue(OS, R, ".sys", T.getSystemTime()); 452 if (T.getMemUsed()) { 453 OS << delim; 454 printJSONValue(OS, R, ".mem", T.getMemUsed()); 455 } 456 if (T.getInstructionsExecuted()) { 457 OS << delim; 458 printJSONValue(OS, R, ".instr", T.getInstructionsExecuted()); 459 } 460 } 461 TimersToPrint.clear(); 462 return delim; 463 } 464 465 const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) { 466 sys::SmartScopedLock<true> L(mtg::TimerLock()); 467 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 468 delim = TG->printJSONValues(OS, delim); 469 return delim; 470 } 471 472 void TimerGroup::constructForStatistics() { 473 mtg::LibSupportInfoOutputFilename(); 474 mtg::NamedGroupedTimers(); 475 } 476 477 std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() { 478 return std::unique_ptr<TimerGroup>(mtg::claimDefaultTimerGroup()); 479 } 480 481 //===----------------------------------------------------------------------===// 482 // Timer Globals 483 // 484 // Previously, these were independent ManagedStatics. This led to bugs because 485 // there are dependencies between the globals, but no reliable mechanism to 486 // control relative lifetimes. 487 // 488 // Placing the globals within one class instance lets us control the lifetimes 489 // of the various data members and ensure that no global uses another that has 490 // been deleted. 491 // 492 // Globals fall into two categories. First are simple data types and 493 // command-line options. These are cheap to construct and/or required early 494 // during launch. They are created when the ManagedTimerGlobals singleton is 495 // constructed. Second are types that are more expensive to construct or not 496 // needed until later during compilation. These are lazily constructed in order 497 // to reduce launch time. 498 //===----------------------------------------------------------------------===// 499 class llvm::TimerGlobals { 500 public: 501 std::string LibSupportInfoOutputFilename; 502 cl::opt<std::string, true> InfoOutputFilename; 503 cl::opt<bool> TrackSpace; 504 cl::opt<bool> SortTimers; 505 506 private: 507 // Order of these members and initialization below is important. For example 508 // the DefaultTimerGroup uses the TimerLock. Most of these also depend on the 509 // options above. 510 std::unique_ptr<SignpostEmitter> SignpostsPtr; 511 std::unique_ptr<sys::SmartMutex<true>> TimerLockPtr; 512 std::unique_ptr<TimerGroup> DefaultTimerGroupPtr; 513 std::unique_ptr<Name2PairMap> NamedGroupedTimersPtr; 514 std::once_flag InitDeferredFlag; 515 TimerGlobals &initDeferred() { 516 std::call_once(InitDeferredFlag, [this]() { 517 SignpostsPtr = std::make_unique<SignpostEmitter>(); 518 TimerLockPtr = std::make_unique<sys::SmartMutex<true>>(); 519 DefaultTimerGroupPtr.reset(new TimerGroup( 520 "misc", "Miscellaneous Ungrouped Timers", *TimerLockPtr)); 521 NamedGroupedTimersPtr = std::make_unique<Name2PairMap>(); 522 }); 523 return *this; 524 } 525 526 public: 527 SignpostEmitter &Signposts() { return *initDeferred().SignpostsPtr; } 528 sys::SmartMutex<true> &TimerLock() { return *initDeferred().TimerLockPtr; } 529 TimerGroup &DefaultTimerGroup() { 530 return *initDeferred().DefaultTimerGroupPtr; 531 } 532 TimerGroup *claimDefaultTimerGroup() { 533 return initDeferred().DefaultTimerGroupPtr.release(); 534 } 535 Name2PairMap &NamedGroupedTimers() { 536 return *initDeferred().NamedGroupedTimersPtr; 537 } 538 539 public: 540 TimerGlobals() 541 : InfoOutputFilename( 542 "info-output-file", cl::value_desc("filename"), 543 cl::desc("File to append -stats and -timer output to"), cl::Hidden, 544 cl::location(LibSupportInfoOutputFilename)), 545 TrackSpace( 546 "track-memory", 547 cl::desc("Enable -time-passes memory tracking (this may be slow)"), 548 cl::Hidden), 549 SortTimers( 550 "sort-timers", 551 cl::desc( 552 "In the report, sort the timers in each group in wall clock" 553 " time order"), 554 cl::init(true), cl::Hidden) {} 555 }; 556 557 static ManagedStatic<TimerGlobals> ManagedTimerGlobals; 558 559 static std::string &mtg::LibSupportInfoOutputFilename() { 560 return ManagedTimerGlobals->LibSupportInfoOutputFilename; 561 } 562 static const std::string &mtg::InfoOutputFilename() { 563 return ManagedTimerGlobals->InfoOutputFilename.getValue(); 564 } 565 static bool mtg::TrackSpace() { return ManagedTimerGlobals->TrackSpace; }; 566 static bool mtg::SortTimers() { return ManagedTimerGlobals->SortTimers; } 567 static SignpostEmitter &mtg::Signposts() { 568 return ManagedTimerGlobals->Signposts(); 569 } 570 static sys::SmartMutex<true> &mtg::TimerLock() { 571 return ManagedTimerGlobals->TimerLock(); 572 } 573 static TimerGroup &mtg::DefaultTimerGroup() { 574 return ManagedTimerGlobals->DefaultTimerGroup(); 575 } 576 static TimerGroup *mtg::claimDefaultTimerGroup() { 577 return ManagedTimerGlobals->claimDefaultTimerGroup(); 578 } 579 static Name2PairMap &mtg::NamedGroupedTimers() { 580 return ManagedTimerGlobals->NamedGroupedTimers(); 581 } 582