Skip to content

Commit 871f69f

Browse files
[TimeProfiler] Added instant events to llvm TimeProfiler. (#103039)
This adds support for adding instant events to the TimeProfiler. Later we plan to use it to record deferring of template instantiations.
1 parent bfe0666 commit 871f69f

File tree

3 files changed

+126
-31
lines changed

3 files changed

+126
-31
lines changed

llvm/include/llvm/Support/TimeProfiler.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ namespace llvm {
8383

8484
class raw_pwrite_stream;
8585

86+
// Type of the time trace event.
87+
enum class TimeTraceEventType {
88+
// Complete events have a duration (start and end time points) and are marked
89+
// by the "X" phase type.
90+
CompleteEvent,
91+
// Instant events don't have a duration, they happen at an instant in time.
92+
// They are marked with "i" phase type. The field End is ignored for them.
93+
InstantEvent,
94+
// Async events mark asynchronous operations and are specified by the "b"
95+
// (start) and "e" (end) phase types.
96+
AsyncEvent
97+
};
98+
8699
struct TimeTraceMetadata {
87100
std::string Detail;
88101
// Source file and line number information for the event.
@@ -152,6 +165,10 @@ timeTraceProfilerBegin(StringRef Name,
152165
TimeTraceProfilerEntry *timeTraceAsyncProfilerBegin(StringRef Name,
153166
StringRef Detail);
154167

168+
// Mark an instant event.
169+
void timeTraceAddInstantEvent(StringRef Name,
170+
llvm::function_ref<std::string()> Detail);
171+
155172
/// Manually end the last time section.
156173
void timeTraceProfilerEnd();
157174
void timeTraceProfilerEnd(TimeTraceProfilerEntry *E);

llvm/lib/Support/TimeProfiler.cpp

Lines changed: 85 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/Support/TimeProfiler.h"
1414
#include "llvm/ADT/STLExtras.h"
1515
#include "llvm/ADT/STLFunctionalExtras.h"
16+
#include "llvm/ADT/SmallVector.h"
1617
#include "llvm/ADT/StringMap.h"
1718
#include "llvm/Support/JSON.h"
1819
#include "llvm/Support/Path.h"
@@ -75,18 +76,18 @@ struct llvm::TimeTraceProfilerEntry {
7576
const std::string Name;
7677
TimeTraceMetadata Metadata;
7778

78-
const bool AsyncEvent = false;
79+
const TimeTraceEventType EventType = TimeTraceEventType::CompleteEvent;
7980
TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
80-
std::string &&Dt, bool Ae)
81+
std::string &&Dt, TimeTraceEventType Et)
8182
: Start(std::move(S)), End(std::move(E)), Name(std::move(N)), Metadata(),
82-
AsyncEvent(Ae) {
83+
EventType(Et) {
8384
Metadata.Detail = std::move(Dt);
8485
}
8586

8687
TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
87-
TimeTraceMetadata &&Mt, bool Ae)
88+
TimeTraceMetadata &&Mt, TimeTraceEventType Et)
8889
: Start(std::move(S)), End(std::move(E)), Name(std::move(N)),
89-
Metadata(std::move(Mt)), AsyncEvent(Ae) {}
90+
Metadata(std::move(Mt)), EventType(Et) {}
9091

9192
// Calculate timings for FlameGraph. Cast time points to microsecond precision
9293
// rather than casting duration. This avoids truncation issues causing inner
@@ -104,6 +105,25 @@ struct llvm::TimeTraceProfilerEntry {
104105
}
105106
};
106107

108+
// Represents a currently open (in-progress) time trace entry. InstantEvents
109+
// that happen during an open event are associated with the duration of this
110+
// parent event and they are dropped if parent duration is shorter than
111+
// the granularity.
112+
struct InProgressEntry {
113+
TimeTraceProfilerEntry Event;
114+
std::vector<TimeTraceProfilerEntry> InstantEvents;
115+
116+
InProgressEntry(TimePointType S, TimePointType E, std::string N,
117+
std::string Dt, TimeTraceEventType Et)
118+
: Event(std::move(S), std::move(E), std::move(N), std::move(Dt), Et),
119+
InstantEvents() {}
120+
121+
InProgressEntry(TimePointType S, TimePointType E, std::string N,
122+
TimeTraceMetadata Mt, TimeTraceEventType Et)
123+
: Event(std::move(S), std::move(E), std::move(N), std::move(Mt), Et),
124+
InstantEvents() {}
125+
};
126+
107127
struct llvm::TimeTraceProfiler {
108128
TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "",
109129
bool TimeTraceVerbose = false)
@@ -114,27 +134,40 @@ struct llvm::TimeTraceProfiler {
114134
llvm::get_thread_name(ThreadName);
115135
}
116136

117-
TimeTraceProfilerEntry *begin(std::string Name,
118-
llvm::function_ref<std::string()> Detail,
119-
bool AsyncEvent = false) {
120-
Stack.emplace_back(std::make_unique<TimeTraceProfilerEntry>(
137+
TimeTraceProfilerEntry *
138+
begin(std::string Name, llvm::function_ref<std::string()> Detail,
139+
TimeTraceEventType EventType = TimeTraceEventType::CompleteEvent) {
140+
assert(EventType != TimeTraceEventType::InstantEvent &&
141+
"Instant Events don't have begin and end.");
142+
Stack.emplace_back(std::make_unique<InProgressEntry>(
121143
ClockType::now(), TimePointType(), std::move(Name), Detail(),
122-
AsyncEvent));
123-
return Stack.back().get();
144+
EventType));
145+
return &Stack.back()->Event;
124146
}
125147

126148
TimeTraceProfilerEntry *
127149
begin(std::string Name, llvm::function_ref<TimeTraceMetadata()> Metadata,
128-
bool AsyncEvent = false) {
129-
Stack.emplace_back(std::make_unique<TimeTraceProfilerEntry>(
150+
TimeTraceEventType EventType = TimeTraceEventType::CompleteEvent) {
151+
assert(EventType != TimeTraceEventType::InstantEvent &&
152+
"Instant Events don't have begin and end.");
153+
Stack.emplace_back(std::make_unique<InProgressEntry>(
130154
ClockType::now(), TimePointType(), std::move(Name), Metadata(),
131-
AsyncEvent));
132-
return Stack.back().get();
155+
EventType));
156+
return &Stack.back()->Event;
157+
}
158+
159+
void insert(std::string Name, llvm::function_ref<std::string()> Detail) {
160+
if (Stack.empty())
161+
return;
162+
163+
Stack.back().get()->InstantEvents.emplace_back(TimeTraceProfilerEntry(
164+
ClockType::now(), TimePointType(), std::move(Name), Detail(),
165+
TimeTraceEventType::InstantEvent));
133166
}
134167

135168
void end() {
136169
assert(!Stack.empty() && "Must call begin() first");
137-
end(*Stack.back());
170+
end(Stack.back()->Event);
138171
}
139172

140173
void end(TimeTraceProfilerEntry &E) {
@@ -144,28 +177,35 @@ struct llvm::TimeTraceProfiler {
144177
// Calculate duration at full precision for overall counts.
145178
DurationType Duration = E.End - E.Start;
146179

180+
const auto *Iter =
181+
llvm::find_if(Stack, [&](const std::unique_ptr<InProgressEntry> &Val) {
182+
return &Val->Event == &E;
183+
});
184+
assert(Iter != Stack.end() && "Event not in the Stack");
185+
147186
// Only include sections longer or equal to TimeTraceGranularity msec.
148-
if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity)
187+
if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity) {
149188
Entries.emplace_back(E);
189+
for (auto &IE : Iter->get()->InstantEvents) {
190+
Entries.emplace_back(IE);
191+
}
192+
}
150193

151194
// Track total time taken by each "name", but only the topmost levels of
152195
// them; e.g. if there's a template instantiation that instantiates other
153196
// templates from within, we only want to add the topmost one. "topmost"
154197
// happens to be the ones that don't have any currently open entries above
155198
// itself.
156199
if (llvm::none_of(llvm::drop_begin(llvm::reverse(Stack)),
157-
[&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
158-
return Val->Name == E.Name;
200+
[&](const std::unique_ptr<InProgressEntry> &Val) {
201+
return Val->Event.Name == E.Name;
159202
})) {
160203
auto &CountAndTotal = CountAndTotalPerName[E.Name];
161204
CountAndTotal.first++;
162205
CountAndTotal.second += Duration;
163206
};
164207

165-
llvm::erase_if(Stack,
166-
[&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
167-
return Val.get() == &E;
168-
});
208+
Stack.erase(Iter);
169209
}
170210

171211
// Write events from this TimeTraceProfilerInstance and
@@ -194,13 +234,17 @@ struct llvm::TimeTraceProfiler {
194234
J.attribute("pid", Pid);
195235
J.attribute("tid", int64_t(Tid));
196236
J.attribute("ts", StartUs);
197-
if (E.AsyncEvent) {
237+
if (E.EventType == TimeTraceEventType::AsyncEvent) {
198238
J.attribute("cat", E.Name);
199239
J.attribute("ph", "b");
200240
J.attribute("id", 0);
201-
} else {
241+
} else if (E.EventType == TimeTraceEventType::CompleteEvent) {
202242
J.attribute("ph", "X");
203243
J.attribute("dur", DurUs);
244+
} else { // instant event
245+
assert(E.EventType == TimeTraceEventType::InstantEvent &&
246+
"InstantEvent expected");
247+
J.attribute("ph", "i");
204248
}
205249
J.attribute("name", E.Name);
206250
if (!E.Metadata.isEmpty()) {
@@ -215,7 +259,7 @@ struct llvm::TimeTraceProfiler {
215259
}
216260
});
217261

218-
if (E.AsyncEvent) {
262+
if (E.EventType == TimeTraceEventType::AsyncEvent) {
219263
J.object([&] {
220264
J.attribute("pid", Pid);
221265
J.attribute("tid", int64_t(Tid));
@@ -319,7 +363,7 @@ struct llvm::TimeTraceProfiler {
319363
J.objectEnd();
320364
}
321365

322-
SmallVector<std::unique_ptr<TimeTraceProfilerEntry>, 16> Stack;
366+
SmallVector<std::unique_ptr<InProgressEntry>, 16> Stack;
323367
SmallVector<TimeTraceProfilerEntry, 128> Entries;
324368
StringMap<CountAndDurationType> CountAndTotalPerName;
325369
// System clock time when the session was begun.
@@ -406,34 +450,44 @@ TimeTraceProfilerEntry *llvm::timeTraceProfilerBegin(StringRef Name,
406450
StringRef Detail) {
407451
if (TimeTraceProfilerInstance != nullptr)
408452
return TimeTraceProfilerInstance->begin(
409-
std::string(Name), [&]() { return std::string(Detail); }, false);
453+
std::string(Name), [&]() { return std::string(Detail); },
454+
TimeTraceEventType::CompleteEvent);
410455
return nullptr;
411456
}
412457

413458
TimeTraceProfilerEntry *
414459
llvm::timeTraceProfilerBegin(StringRef Name,
415460
llvm::function_ref<std::string()> Detail) {
416461
if (TimeTraceProfilerInstance != nullptr)
417-
return TimeTraceProfilerInstance->begin(std::string(Name), Detail, false);
462+
return TimeTraceProfilerInstance->begin(std::string(Name), Detail,
463+
TimeTraceEventType::CompleteEvent);
418464
return nullptr;
419465
}
420466

421467
TimeTraceProfilerEntry *
422468
llvm::timeTraceProfilerBegin(StringRef Name,
423469
llvm::function_ref<TimeTraceMetadata()> Metadata) {
424470
if (TimeTraceProfilerInstance != nullptr)
425-
return TimeTraceProfilerInstance->begin(std::string(Name), Metadata, false);
471+
return TimeTraceProfilerInstance->begin(std::string(Name), Metadata,
472+
TimeTraceEventType::CompleteEvent);
426473
return nullptr;
427474
}
428475

429476
TimeTraceProfilerEntry *llvm::timeTraceAsyncProfilerBegin(StringRef Name,
430477
StringRef Detail) {
431478
if (TimeTraceProfilerInstance != nullptr)
432479
return TimeTraceProfilerInstance->begin(
433-
std::string(Name), [&]() { return std::string(Detail); }, true);
480+
std::string(Name), [&]() { return std::string(Detail); },
481+
TimeTraceEventType::AsyncEvent);
434482
return nullptr;
435483
}
436484

485+
void llvm::timeTraceAddInstantEvent(StringRef Name,
486+
llvm::function_ref<std::string()> Detail) {
487+
if (TimeTraceProfilerInstance != nullptr)
488+
TimeTraceProfilerInstance->insert(std::string(Name), Detail);
489+
}
490+
437491
void llvm::timeTraceProfilerEnd() {
438492
if (TimeTraceProfilerInstance != nullptr)
439493
TimeTraceProfilerInstance->end();

llvm/unittests/Support/TimeProfilerTest.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,28 @@ TEST(TimeProfiler, Begin_End_Disabled) {
7272
timeTraceProfilerEnd();
7373
}
7474

75+
TEST(TimeProfiler, Instant_Add_Smoke) {
76+
setupProfiler();
77+
78+
timeTraceProfilerBegin("sync event", "sync detail");
79+
timeTraceAddInstantEvent("instant event", [&] { return "instant detail"; });
80+
timeTraceProfilerEnd();
81+
82+
std::string json = teardownProfiler();
83+
ASSERT_TRUE(json.find(R"("name":"sync event")") != std::string::npos);
84+
ASSERT_TRUE(json.find(R"("detail":"sync detail")") != std::string::npos);
85+
ASSERT_TRUE(json.find(R"("name":"instant event")") != std::string::npos);
86+
ASSERT_TRUE(json.find(R"("detail":"instant detail")") != std::string::npos);
87+
}
88+
89+
TEST(TimeProfiler, Instant_Not_Added_Smoke) {
90+
setupProfiler();
91+
92+
timeTraceAddInstantEvent("instant event", [&] { return "instant detail"; });
93+
94+
std::string json = teardownProfiler();
95+
ASSERT_TRUE(json.find(R"("name":"instant event")") == std::string::npos);
96+
ASSERT_TRUE(json.find(R"("detail":"instant detail")") == std::string::npos);
97+
}
98+
7599
} // namespace

0 commit comments

Comments
 (0)