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 #include <optional> 31 32 #if HAVE_UNISTD_H 33 #include <unistd.h> 34 #endif 35 36 #ifdef HAVE_PROC_PID_RUSAGE 37 #include <libproc.h> 38 #endif 39 40 using namespace llvm; 41 42 //===----------------------------------------------------------------------===// 43 // Forward declarations for Managed Timer Globals getters. 44 // 45 // Globals have been placed at the end of the file to restrict direct 46 // access. Use of getters also has the benefit of making it a bit more explicit 47 // that a global is being used. 48 //===----------------------------------------------------------------------===// 49 namespace { 50 class Name2PairMap; 51 } 52 53 static std::string &libSupportInfoOutputFilename(); 54 static bool trackSpace(); 55 static bool sortTimers(); 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 signposts().startInterval(this, getName()); 153 StartTime = TimeRecord::getCurrentTime(true); 154 } 155 156 void Timer::stopTimer() { 157 assert(Running && "Cannot stop a paused timer"); 158 Running = false; 159 Time += TimeRecord::getCurrentTime(false); 160 Time -= StartTime; 161 signposts().endInterval(this, getName()); 162 } 163 164 void Timer::clear() { 165 Running = Triggered = false; 166 Time = StartTime = TimeRecord(); 167 } 168 169 void Timer::yieldTo(Timer &O) { 170 stopTimer(); 171 O.startTimer(); 172 } 173 174 static void printVal(double Val, double Total, raw_ostream &OS) { 175 if (Total < 1e-7) // Avoid dividing by zero. 176 OS << " ----- "; 177 else 178 OS << format(" %7.4f (%5.1f%%)", Val, Val*100/Total); 179 } 180 181 void TimeRecord::print(const TimeRecord &Total, raw_ostream &OS) const { 182 if (Total.getUserTime()) 183 printVal(getUserTime(), Total.getUserTime(), OS); 184 if (Total.getSystemTime()) 185 printVal(getSystemTime(), Total.getSystemTime(), OS); 186 if (Total.getProcessTime()) 187 printVal(getProcessTime(), Total.getProcessTime(), OS); 188 printVal(getWallTime(), Total.getWallTime(), OS); 189 190 OS << " "; 191 192 if (Total.getMemUsed()) 193 OS << format("%9" PRId64 " ", (int64_t)getMemUsed()); 194 if (Total.getInstructionsExecuted()) 195 OS << format("%9" PRId64 " ", (int64_t)getInstructionsExecuted()); 196 } 197 198 199 //===----------------------------------------------------------------------===// 200 // NamedRegionTimer Implementation 201 //===----------------------------------------------------------------------===// 202 203 namespace { 204 205 typedef StringMap<Timer> Name2TimerMap; 206 207 class Name2PairMap { 208 StringMap<std::pair<TimerGroup*, Name2TimerMap> > Map; 209 public: 210 ~Name2PairMap() { 211 for (StringMap<std::pair<TimerGroup*, Name2TimerMap> >::iterator 212 I = Map.begin(), E = Map.end(); I != E; ++I) 213 delete I->second.first; 214 } 215 216 Timer &get(StringRef Name, StringRef Description, StringRef GroupName, 217 StringRef GroupDescription) { 218 sys::SmartScopedLock<true> L(timerLock()); 219 220 std::pair<TimerGroup*, Name2TimerMap> &GroupEntry = Map[GroupName]; 221 222 if (!GroupEntry.first) 223 GroupEntry.first = new TimerGroup(GroupName, GroupDescription); 224 225 Timer &T = GroupEntry.second[Name]; 226 if (!T.isInitialized()) 227 T.init(Name, Description, *GroupEntry.first); 228 return T; 229 } 230 }; 231 232 } 233 234 NamedRegionTimer::NamedRegionTimer(StringRef Name, StringRef Description, 235 StringRef GroupName, 236 StringRef GroupDescription, bool Enabled) 237 : TimeRegion(!Enabled 238 ? nullptr 239 : &namedGroupedTimers().get(Name, Description, GroupName, 240 GroupDescription)) {} 241 242 //===----------------------------------------------------------------------===// 243 // TimerGroup Implementation 244 //===----------------------------------------------------------------------===// 245 246 /// This is the global list of TimerGroups, maintained by the TimerGroup 247 /// ctor/dtor and is protected by the timerLock lock. 248 static TimerGroup *TimerGroupList = nullptr; 249 250 TimerGroup::TimerGroup(StringRef Name, StringRef Description, 251 sys::SmartMutex<true> &lock) 252 : Name(Name.begin(), Name.end()), 253 Description(Description.begin(), Description.end()) { 254 // Add the group to TimerGroupList. 255 sys::SmartScopedLock<true> L(lock); 256 if (TimerGroupList) 257 TimerGroupList->Prev = &Next; 258 Next = TimerGroupList; 259 Prev = &TimerGroupList; 260 TimerGroupList = this; 261 } 262 263 TimerGroup::TimerGroup(StringRef Name, StringRef Description) 264 : TimerGroup(Name, Description, timerLock()) {} 265 266 TimerGroup::TimerGroup(StringRef Name, StringRef Description, 267 const StringMap<TimeRecord> &Records) 268 : TimerGroup(Name, Description) { 269 TimersToPrint.reserve(Records.size()); 270 for (const auto &P : Records) 271 TimersToPrint.emplace_back(P.getValue(), std::string(P.getKey()), 272 std::string(P.getKey())); 273 assert(TimersToPrint.size() == Records.size() && "Size mismatch"); 274 } 275 276 TimerGroup::~TimerGroup() { 277 // If the timer group is destroyed before the timers it owns, accumulate and 278 // print the timing data. 279 while (FirstTimer) 280 removeTimer(*FirstTimer); 281 282 // Remove the group from the TimerGroupList. 283 sys::SmartScopedLock<true> L(timerLock()); 284 *Prev = Next; 285 if (Next) 286 Next->Prev = Prev; 287 } 288 289 290 void TimerGroup::removeTimer(Timer &T) { 291 sys::SmartScopedLock<true> L(timerLock()); 292 293 // If the timer was started, move its data to TimersToPrint. 294 if (T.hasTriggered()) 295 TimersToPrint.emplace_back(T.Time, T.Name, T.Description); 296 297 T.TG = nullptr; 298 299 // Unlink the timer from our list. 300 *T.Prev = T.Next; 301 if (T.Next) 302 T.Next->Prev = T.Prev; 303 304 // Print the report when all timers in this group are destroyed if some of 305 // them were started. 306 if (FirstTimer || TimersToPrint.empty()) 307 return; 308 309 std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile(); 310 PrintQueuedTimers(*OutStream); 311 } 312 313 void TimerGroup::addTimer(Timer &T) { 314 sys::SmartScopedLock<true> L(timerLock()); 315 316 // Add the timer to our list. 317 if (FirstTimer) 318 FirstTimer->Prev = &T.Next; 319 T.Next = FirstTimer; 320 T.Prev = &FirstTimer; 321 FirstTimer = &T; 322 } 323 324 void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { 325 // Perhaps sort the timers in descending order by amount of time taken. 326 if (sortTimers()) 327 llvm::sort(TimersToPrint); 328 329 TimeRecord Total; 330 for (const PrintRecord &Record : TimersToPrint) 331 Total += Record.Time; 332 333 // Print out timing header. 334 OS << "===" << std::string(73, '-') << "===\n"; 335 // Figure out how many spaces to indent TimerGroup name. 336 unsigned Padding = (80-Description.length())/2; 337 if (Padding > 80) Padding = 0; // Don't allow "negative" numbers 338 OS.indent(Padding) << Description << '\n'; 339 OS << "===" << std::string(73, '-') << "===\n"; 340 341 // If this is not an collection of ungrouped times, print the total time. 342 // Ungrouped timers don't really make sense to add up. We still print the 343 // TOTAL line to make the percentages make sense. 344 if (this != &defaultTimerGroup()) 345 OS << format(" Total Execution Time: %5.4f seconds (%5.4f wall clock)\n", 346 Total.getProcessTime(), Total.getWallTime()); 347 OS << '\n'; 348 349 if (Total.getUserTime()) 350 OS << " ---User Time---"; 351 if (Total.getSystemTime()) 352 OS << " --System Time--"; 353 if (Total.getProcessTime()) 354 OS << " --User+System--"; 355 OS << " ---Wall Time---"; 356 if (Total.getMemUsed()) 357 OS << " ---Mem---"; 358 if (Total.getInstructionsExecuted()) 359 OS << " ---Instr---"; 360 OS << " --- Name ---\n"; 361 362 // Loop through all of the timing data, printing it out. 363 for (const PrintRecord &Record : llvm::reverse(TimersToPrint)) { 364 Record.Time.print(Total, OS); 365 OS << Record.Description << '\n'; 366 } 367 368 Total.print(Total, OS); 369 OS << "Total\n\n"; 370 OS.flush(); 371 372 TimersToPrint.clear(); 373 } 374 375 void TimerGroup::prepareToPrintList(bool ResetTime) { 376 // See if any of our timers were started, if so add them to TimersToPrint. 377 for (Timer *T = FirstTimer; T; T = T->Next) { 378 if (!T->hasTriggered()) continue; 379 bool WasRunning = T->isRunning(); 380 if (WasRunning) 381 T->stopTimer(); 382 383 TimersToPrint.emplace_back(T->Time, T->Name, T->Description); 384 385 if (ResetTime) 386 T->clear(); 387 388 if (WasRunning) 389 T->startTimer(); 390 } 391 } 392 393 void TimerGroup::print(raw_ostream &OS, bool ResetAfterPrint) { 394 { 395 // After preparing the timers we can free the lock 396 sys::SmartScopedLock<true> L(timerLock()); 397 prepareToPrintList(ResetAfterPrint); 398 } 399 400 // If any timers were started, print the group. 401 if (!TimersToPrint.empty()) 402 PrintQueuedTimers(OS); 403 } 404 405 void TimerGroup::clear() { 406 sys::SmartScopedLock<true> L(timerLock()); 407 for (Timer *T = FirstTimer; T; T = T->Next) 408 T->clear(); 409 } 410 411 void TimerGroup::printAll(raw_ostream &OS) { 412 sys::SmartScopedLock<true> L(timerLock()); 413 414 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 415 TG->print(OS); 416 } 417 418 void TimerGroup::clearAll() { 419 sys::SmartScopedLock<true> L(timerLock()); 420 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 421 TG->clear(); 422 } 423 424 void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, 425 const char *suffix, double Value) { 426 assert(yaml::needsQuotes(Name) == yaml::QuotingType::None && 427 "TimerGroup name should not need quotes"); 428 assert(yaml::needsQuotes(R.Name) == yaml::QuotingType::None && 429 "Timer name should not need quotes"); 430 constexpr auto max_digits10 = std::numeric_limits<double>::max_digits10; 431 OS << "\t\"time." << Name << '.' << R.Name << suffix 432 << "\": " << format("%.*e", max_digits10 - 1, Value); 433 } 434 435 const char *TimerGroup::printJSONValues(raw_ostream &OS, const char *delim) { 436 sys::SmartScopedLock<true> L(timerLock()); 437 438 prepareToPrintList(false); 439 for (const PrintRecord &R : TimersToPrint) { 440 OS << delim; 441 delim = ",\n"; 442 443 const TimeRecord &T = R.Time; 444 printJSONValue(OS, R, ".wall", T.getWallTime()); 445 OS << delim; 446 printJSONValue(OS, R, ".user", T.getUserTime()); 447 OS << delim; 448 printJSONValue(OS, R, ".sys", T.getSystemTime()); 449 if (T.getMemUsed()) { 450 OS << delim; 451 printJSONValue(OS, R, ".mem", T.getMemUsed()); 452 } 453 if (T.getInstructionsExecuted()) { 454 OS << delim; 455 printJSONValue(OS, R, ".instr", T.getInstructionsExecuted()); 456 } 457 } 458 TimersToPrint.clear(); 459 return delim; 460 } 461 462 const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) { 463 sys::SmartScopedLock<true> L(timerLock()); 464 for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) 465 delim = TG->printJSONValues(OS, delim); 466 return delim; 467 } 468 469 //===----------------------------------------------------------------------===// 470 // Timer Globals 471 // 472 // Previously, these were independent ManagedStatics. This led to bugs because 473 // there are dependencies between the globals, but no reliable mechanism to 474 // control relative lifetimes. 475 // 476 // Placing the globals within one class instance lets us control the lifetimes 477 // of the various data members and ensure that no global uses another that has 478 // been deleted. 479 // 480 // Globals fall into two categories. First are simple data types and 481 // command-line options. These are cheap to construct and/or required early 482 // during launch. They are created when the ManagedTimerGlobals singleton is 483 // constructed. Second are types that are more expensive to construct or not 484 // needed until later during compilation. These are lazily constructed in order 485 // to reduce launch time. 486 //===----------------------------------------------------------------------===// 487 class llvm::TimerGlobals { 488 public: 489 std::string LibSupportInfoOutputFilename; 490 cl::opt<std::string, true> InfoOutputFilename{ 491 "info-output-file", cl::value_desc("filename"), 492 cl::desc("File to append -stats and -timer output to"), cl::Hidden, 493 cl::location(LibSupportInfoOutputFilename)}; 494 cl::opt<bool> TrackSpace{ 495 "track-memory", 496 cl::desc("Enable -time-passes memory tracking (this may be slow)"), 497 cl::Hidden}; 498 cl::opt<bool> SortTimers{ 499 "sort-timers", 500 cl::desc("In the report, sort the timers in each group in wall clock" 501 " time order"), 502 cl::init(true), cl::Hidden}; 503 504 sys::SmartMutex<true> TimerLock; 505 TimerGroup DefaultTimerGroup{"misc", "Miscellaneous Ungrouped Timers", 506 TimerLock}; 507 SignpostEmitter Signposts; 508 509 // Order of these members and initialization below is important. For example 510 // the defaultTimerGroup uses the timerLock. Most of these also depend on the 511 // options above. 512 std::once_flag InitDeferredFlag; 513 std::optional<Name2PairMap> NamedGroupedTimersPtr; 514 515 TimerGlobals &initDeferred() { 516 std::call_once(InitDeferredFlag, 517 [this]() { NamedGroupedTimersPtr.emplace(); }); 518 return *this; 519 } 520 }; 521 522 static ManagedStatic<TimerGlobals> ManagedTimerGlobals; 523 524 static std::string &libSupportInfoOutputFilename() { 525 return ManagedTimerGlobals->LibSupportInfoOutputFilename; 526 } 527 static bool trackSpace() { return ManagedTimerGlobals->TrackSpace; } 528 static bool sortTimers() { return ManagedTimerGlobals->SortTimers; } 529 static SignpostEmitter &signposts() { return ManagedTimerGlobals->Signposts; } 530 static sys::SmartMutex<true> &timerLock() { 531 return ManagedTimerGlobals->TimerLock; 532 } 533 static TimerGroup &defaultTimerGroup() { 534 return ManagedTimerGlobals->DefaultTimerGroup; 535 } 536 static Name2PairMap &namedGroupedTimers() { 537 return *ManagedTimerGlobals->initDeferred().NamedGroupedTimersPtr; 538 } 539 540 void llvm::initTimerOptions() { *ManagedTimerGlobals; } 541 void TimerGroup::constructForStatistics() { 542 ManagedTimerGlobals->initDeferred(); 543 } 544 545 void *TimerGroup::acquireTimerGlobals() { return ManagedTimerGlobals.claim(); } 546