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