1 //===-- Timer.cpp - Interval Timing Support -------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file was developed by the LLVM research group and is distributed under 6 // the University of Illinois Open Source License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Interval Timing implementation. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "Support/Timer.h" 15 #include "Support/CommandLine.h" 16 #include "Config/sys/resource.h" 17 #include "Config/sys/time.h" 18 #include "Config/unistd.h" 19 #include "Config/malloc.h" 20 #include <iostream> 21 #include <algorithm> 22 #include <functional> 23 #include <fstream> 24 #include <map> 25 26 namespace llvm { 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 std::string &getLibSupportInfoOutputFilename() { 37 static std::string *LibSupportInfoOutputFilename = new std::string(); 38 return *LibSupportInfoOutputFilename; 39 } 40 41 namespace { 42 #ifdef HAVE_MALLINFO 43 cl::opt<bool> 44 TrackSpace("track-memory", cl::desc("Enable -time-passes memory " 45 "tracking (this may be slow)"), 46 cl::Hidden); 47 #endif 48 49 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 static TimerGroup *DefaultTimerGroup = 0; 56 static TimerGroup *getDefaultTimerGroup() { 57 if (DefaultTimerGroup) return DefaultTimerGroup; 58 return DefaultTimerGroup = new TimerGroup("Miscellaneous Ungrouped Timers"); 59 } 60 61 Timer::Timer(const std::string &N) 62 : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), 63 Started(false), TG(getDefaultTimerGroup()) { 64 TG->addTimer(); 65 } 66 67 Timer::Timer(const std::string &N, TimerGroup &tg) 68 : Elapsed(0), UserTime(0), SystemTime(0), MemUsed(0), PeakMem(0), Name(N), 69 Started(false), TG(&tg) { 70 TG->addTimer(); 71 } 72 73 Timer::Timer(const Timer &T) { 74 TG = T.TG; 75 if (TG) TG->addTimer(); 76 operator=(T); 77 } 78 79 80 // Copy ctor, initialize with no TG member. 81 Timer::Timer(bool, const Timer &T) { 82 TG = T.TG; // Avoid assertion in operator= 83 operator=(T); // Copy contents 84 TG = 0; 85 } 86 87 88 Timer::~Timer() { 89 if (TG) { 90 if (Started) { 91 Started = false; 92 TG->addTimerToPrint(*this); 93 } 94 TG->removeTimer(); 95 } 96 } 97 98 static long getMemUsage() { 99 #ifdef HAVE_MALLINFO 100 if (TrackSpace) { 101 struct mallinfo MI = mallinfo(); 102 return MI.uordblks/*+MI.hblkhd*/; 103 } 104 #endif 105 return 0; 106 } 107 108 struct TimeRecord { 109 double Elapsed, UserTime, SystemTime; 110 long MemUsed; 111 }; 112 113 static TimeRecord getTimeRecord(bool Start) { 114 struct rusage RU; 115 struct timeval T; 116 long MemUsed = 0; 117 if (Start) { 118 MemUsed = getMemUsage(); 119 if (getrusage(RUSAGE_SELF, &RU)) 120 perror("getrusage call failed: -time-passes info incorrect!"); 121 } 122 gettimeofday(&T, 0); 123 124 if (!Start) { 125 MemUsed = getMemUsage(); 126 if (getrusage(RUSAGE_SELF, &RU)) 127 perror("getrusage call failed: -time-passes info incorrect!"); 128 } 129 130 TimeRecord Result; 131 Result.Elapsed = T.tv_sec + T.tv_usec/1000000.0; 132 Result.UserTime = RU.ru_utime.tv_sec + RU.ru_utime.tv_usec/1000000.0; 133 Result.SystemTime = RU.ru_stime.tv_sec + RU.ru_stime.tv_usec/1000000.0; 134 Result.MemUsed = MemUsed; 135 136 return Result; 137 } 138 139 static std::vector<Timer*> ActiveTimers; 140 141 void Timer::startTimer() { 142 Started = true; 143 TimeRecord TR = getTimeRecord(true); 144 Elapsed -= TR.Elapsed; 145 UserTime -= TR.UserTime; 146 SystemTime -= TR.SystemTime; 147 MemUsed -= TR.MemUsed; 148 PeakMemBase = TR.MemUsed; 149 ActiveTimers.push_back(this); 150 } 151 152 void Timer::stopTimer() { 153 TimeRecord TR = getTimeRecord(false); 154 Elapsed += TR.Elapsed; 155 UserTime += TR.UserTime; 156 SystemTime += TR.SystemTime; 157 MemUsed += TR.MemUsed; 158 159 if (ActiveTimers.back() == this) { 160 ActiveTimers.pop_back(); 161 } else { 162 std::vector<Timer*>::iterator I = 163 std::find(ActiveTimers.begin(), ActiveTimers.end(), this); 164 assert(I != ActiveTimers.end() && "stop but no startTimer?"); 165 ActiveTimers.erase(I); 166 } 167 } 168 169 void Timer::sum(const Timer &T) { 170 Elapsed += T.Elapsed; 171 UserTime += T.UserTime; 172 SystemTime += T.SystemTime; 173 MemUsed += T.MemUsed; 174 PeakMem += T.PeakMem; 175 } 176 177 /// addPeakMemoryMeasurement - This method should be called whenever memory 178 /// usage needs to be checked. It adds a peak memory measurement to the 179 /// currently active timers, which will be printed when the timer group prints 180 /// 181 void Timer::addPeakMemoryMeasurement() { 182 long MemUsed = getMemUsage(); 183 184 for (std::vector<Timer*>::iterator I = ActiveTimers.begin(), 185 E = ActiveTimers.end(); I != E; ++I) 186 (*I)->PeakMem = std::max((*I)->PeakMem, MemUsed-(*I)->PeakMemBase); 187 } 188 189 //===----------------------------------------------------------------------===// 190 // NamedRegionTimer Implementation 191 //===----------------------------------------------------------------------===// 192 193 static Timer &getNamedRegionTimer(const std::string &Name) { 194 static std::map<std::string, Timer> NamedTimers; 195 196 std::map<std::string, Timer>::iterator I = NamedTimers.lower_bound(Name); 197 if (I != NamedTimers.end() && I->first == Name) 198 return I->second; 199 200 return NamedTimers.insert(I, std::make_pair(Name, Timer(Name)))->second; 201 } 202 203 NamedRegionTimer::NamedRegionTimer(const std::string &Name) 204 : TimeRegion(getNamedRegionTimer(Name)) {} 205 206 207 //===----------------------------------------------------------------------===// 208 // TimerGroup Implementation 209 //===----------------------------------------------------------------------===// 210 211 // printAlignedFP - Simulate the printf "%A.Bf" format, where A is the 212 // TotalWidth size, and B is the AfterDec size. 213 // 214 static void printAlignedFP(double Val, unsigned AfterDec, unsigned TotalWidth, 215 std::ostream &OS) { 216 assert(TotalWidth >= AfterDec+1 && "Bad FP Format!"); 217 OS.width(TotalWidth-AfterDec-1); 218 char OldFill = OS.fill(); 219 OS.fill(' '); 220 OS << (int)Val; // Integer part; 221 OS << "."; 222 OS.width(AfterDec); 223 OS.fill('0'); 224 unsigned ResultFieldSize = 1; 225 while (AfterDec--) ResultFieldSize *= 10; 226 OS << (int)(Val*ResultFieldSize) % ResultFieldSize; 227 OS.fill(OldFill); 228 } 229 230 static void printVal(double Val, double Total, std::ostream &OS) { 231 if (Total < 1e-7) // Avoid dividing by zero... 232 OS << " ----- "; 233 else { 234 OS << " "; 235 printAlignedFP(Val, 4, 7, OS); 236 OS << " ("; 237 printAlignedFP(Val*100/Total, 1, 5, OS); 238 OS << "%)"; 239 } 240 } 241 242 void Timer::print(const Timer &Total, std::ostream &OS) { 243 if (Total.UserTime) 244 printVal(UserTime, Total.UserTime, OS); 245 if (Total.SystemTime) 246 printVal(SystemTime, Total.SystemTime, OS); 247 if (Total.getProcessTime()) 248 printVal(getProcessTime(), Total.getProcessTime(), OS); 249 printVal(Elapsed, Total.Elapsed, OS); 250 251 OS << " "; 252 253 if (Total.MemUsed) { 254 OS.width(9); 255 OS << MemUsed << " "; 256 } 257 if (Total.PeakMem) { 258 if (PeakMem) { 259 OS.width(9); 260 OS << PeakMem << " "; 261 } else 262 OS << " "; 263 } 264 OS << Name << "\n"; 265 266 Started = false; // Once printed, don't print again 267 } 268 269 // GetLibSupportInfoOutputFile - Return a file stream to print our output on... 270 std::ostream * 271 GetLibSupportInfoOutputFile() { 272 std::string &LibSupportInfoOutputFilename = getLibSupportInfoOutputFilename(); 273 if (LibSupportInfoOutputFilename.empty()) 274 return &std::cerr; 275 if (LibSupportInfoOutputFilename == "-") 276 return &std::cout; 277 278 std::ostream *Result = new std::ofstream(LibSupportInfoOutputFilename.c_str(), 279 std::ios::app); 280 if (!Result->good()) { 281 std::cerr << "Error opening info-output-file '" 282 << LibSupportInfoOutputFilename << " for appending!\n"; 283 delete Result; 284 return &std::cerr; 285 } 286 return Result; 287 } 288 289 290 void TimerGroup::removeTimer() { 291 if (--NumTimers == 0 && !TimersToPrint.empty()) { // Print timing report... 292 // Sort the timers in descending order by amount of time taken... 293 std::sort(TimersToPrint.begin(), TimersToPrint.end(), 294 std::greater<Timer>()); 295 296 // Figure out how many spaces to indent TimerGroup name... 297 unsigned Padding = (80-Name.length())/2; 298 if (Padding > 80) Padding = 0; // Don't allow "negative" numbers 299 300 std::ostream *OutStream = GetLibSupportInfoOutputFile(); 301 302 ++NumTimers; 303 { // Scope to contain Total timer... don't allow total timer to drop us to 304 // zero timers... 305 Timer Total("TOTAL"); 306 307 for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) 308 Total.sum(TimersToPrint[i]); 309 310 // Print out timing header... 311 *OutStream << "===" << std::string(73, '-') << "===\n" 312 << std::string(Padding, ' ') << Name << "\n" 313 << "===" << std::string(73, '-') 314 << "===\n Total Execution Time: "; 315 316 printAlignedFP(Total.getProcessTime(), 4, 5, *OutStream); 317 *OutStream << " seconds ("; 318 printAlignedFP(Total.getWallTime(), 4, 5, *OutStream); 319 *OutStream << " wall clock)\n\n"; 320 321 if (Total.UserTime) 322 *OutStream << " ---User Time---"; 323 if (Total.SystemTime) 324 *OutStream << " --System Time--"; 325 if (Total.getProcessTime()) 326 *OutStream << " --User+System--"; 327 *OutStream << " ---Wall Time---"; 328 if (Total.getMemUsed()) 329 *OutStream << " ---Mem---"; 330 if (Total.getPeakMem()) 331 *OutStream << " -PeakMem-"; 332 *OutStream << " --- Name ---\n"; 333 334 // Loop through all of the timing data, printing it out... 335 for (unsigned i = 0, e = TimersToPrint.size(); i != e; ++i) 336 TimersToPrint[i].print(Total, *OutStream); 337 338 Total.print(Total, *OutStream); 339 *OutStream << std::endl; // Flush output 340 } 341 --NumTimers; 342 343 TimersToPrint.clear(); 344 345 if (OutStream != &std::cerr && OutStream != &std::cout) 346 delete OutStream; // Close the file... 347 } 348 349 // Delete default timer group! 350 if (NumTimers == 0 && this == DefaultTimerGroup) { 351 delete DefaultTimerGroup; 352 DefaultTimerGroup = 0; 353 } 354 } 355 356 } // End llvm namespace 357