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