Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}