Skip to content

Commit 90ebde0

Browse files
committed
Expose TimeTraceProfiler for Async Events
1 parent 7bad74e commit 90ebde0

File tree

3 files changed

+107
-39
lines changed

3 files changed

+107
-39
lines changed

llvm/include/llvm/Support/TimeProfiler.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class raw_pwrite_stream;
8686
struct TimeTraceProfiler;
8787
TimeTraceProfiler *getTimeTraceProfilerInstance();
8888

89+
struct TimeTraceProfilerEntry;
90+
8991
/// Initialize the time trace profiler.
9092
/// This sets up the global \p TimeTraceProfilerInstance
9193
/// variable to be the profiler instance.
@@ -120,19 +122,30 @@ Error timeTraceProfilerWrite(StringRef PreferredFileName,
120122
/// Profiler copies the string data, so the pointers can be given into
121123
/// temporaries. Time sections can be hierarchical; every Begin must have a
122124
/// matching End pair but they can nest.
123-
void timeTraceProfilerBegin(StringRef Name, StringRef Detail);
124-
void timeTraceProfilerBegin(StringRef Name,
125-
llvm::function_ref<std::string()> Detail);
125+
TimeTraceProfilerEntry *timeTraceProfilerBegin(StringRef Name,
126+
StringRef Detail);
127+
TimeTraceProfilerEntry *
128+
timeTraceProfilerBegin(StringRef Name,
129+
llvm::function_ref<std::string()> Detail);
130+
131+
/// Manually begin a time section, with the given \p Name and \p Detail.
132+
/// This starts Async Events having \p Name as a category which is shown
133+
/// separately from other traces. See
134+
/// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1
135+
/// for more details.
136+
TimeTraceProfilerEntry *timeTraceAsyncProfilerBegin(StringRef Name,
137+
StringRef Detail);
126138

127139
/// Manually end the last time section.
128140
void timeTraceProfilerEnd();
141+
void timeTraceProfilerEnd(TimeTraceProfilerEntry *E);
129142

130143
/// The TimeTraceScope is a helper class to call the begin and end functions
131144
/// of the time trace profiler. When the object is constructed, it begins
132145
/// the section; and when it is destroyed, it stops it. If the time profiler
133146
/// is not initialized, the overhead is a single branch.
134-
struct TimeTraceScope {
135-
147+
class TimeTraceScope {
148+
public:
136149
TimeTraceScope() = delete;
137150
TimeTraceScope(const TimeTraceScope &) = delete;
138151
TimeTraceScope &operator=(const TimeTraceScope &) = delete;
@@ -141,20 +154,23 @@ struct TimeTraceScope {
141154

142155
TimeTraceScope(StringRef Name) {
143156
if (getTimeTraceProfilerInstance() != nullptr)
144-
timeTraceProfilerBegin(Name, StringRef(""));
157+
Entry = timeTraceProfilerBegin(Name, StringRef(""));
145158
}
146159
TimeTraceScope(StringRef Name, StringRef Detail) {
147160
if (getTimeTraceProfilerInstance() != nullptr)
148-
timeTraceProfilerBegin(Name, Detail);
161+
Entry = timeTraceProfilerBegin(Name, Detail);
149162
}
150163
TimeTraceScope(StringRef Name, llvm::function_ref<std::string()> Detail) {
151164
if (getTimeTraceProfilerInstance() != nullptr)
152-
timeTraceProfilerBegin(Name, Detail);
165+
Entry = timeTraceProfilerBegin(Name, Detail);
153166
}
154167
~TimeTraceScope() {
155168
if (getTimeTraceProfilerInstance() != nullptr)
156-
timeTraceProfilerEnd();
169+
timeTraceProfilerEnd(Entry);
157170
}
171+
172+
private:
173+
TimeTraceProfilerEntry *Entry = nullptr;
158174
};
159175

160176
} // end namespace llvm

llvm/lib/Support/TimeProfiler.cpp

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/Support/TimeProfiler.h"
14+
#include "llvm/ADT/STLExtras.h"
1415
#include "llvm/ADT/STLFunctionalExtras.h"
1516
#include "llvm/ADT/StringMap.h"
1617
#include "llvm/Support/JSON.h"
@@ -20,6 +21,7 @@
2021
#include <algorithm>
2122
#include <cassert>
2223
#include <chrono>
24+
#include <memory>
2325
#include <mutex>
2426
#include <string>
2527
#include <vector>
@@ -64,17 +66,19 @@ using CountAndDurationType = std::pair<size_t, DurationType>;
6466
using NameAndCountAndDurationType =
6567
std::pair<std::string, CountAndDurationType>;
6668

69+
} // anonymous namespace
70+
6771
/// Represents an open or completed time section entry to be captured.
68-
struct TimeTraceProfilerEntry {
72+
struct llvm::TimeTraceProfilerEntry {
6973
const TimePointType Start;
7074
TimePointType End;
7175
const std::string Name;
7276
const std::string Detail;
73-
77+
const bool AsyncEvent = false;
7478
TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
75-
std::string &&Dt)
79+
std::string &&Dt, bool Ae)
7680
: Start(std::move(S)), End(std::move(E)), Name(std::move(N)),
77-
Detail(std::move(Dt)) {}
81+
Detail(std::move(Dt)), AsyncEvent(Ae) {}
7882

7983
// Calculate timings for FlameGraph. Cast time points to microsecond precision
8084
// rather than casting duration. This avoids truncation issues causing inner
@@ -92,8 +96,6 @@ struct TimeTraceProfilerEntry {
9296
}
9397
};
9498

95-
} // anonymous namespace
96-
9799
struct llvm::TimeTraceProfiler {
98100
TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "")
99101
: BeginningOfTime(system_clock::now()), StartTime(ClockType::now()),
@@ -102,23 +104,24 @@ struct llvm::TimeTraceProfiler {
102104
llvm::get_thread_name(ThreadName);
103105
}
104106

105-
void begin(std::string Name, llvm::function_ref<std::string()> Detail) {
106-
Stack.emplace_back(ClockType::now(), TimePointType(), std::move(Name),
107-
Detail());
107+
TimeTraceProfilerEntry *begin(std::string Name,
108+
llvm::function_ref<std::string()> Detail,
109+
bool AsyncEvent = false) {
110+
Stack.emplace_back(std::make_unique<TimeTraceProfilerEntry>(
111+
ClockType::now(), TimePointType(), std::move(Name), Detail(),
112+
AsyncEvent));
113+
return Stack.back().get();
108114
}
109115

110116
void end() {
117+
TimeTraceProfilerEntry *E = Stack.back().get();
118+
end(*E);
119+
}
120+
121+
void end(TimeTraceProfilerEntry &E) {
111122
assert(!Stack.empty() && "Must call begin() first");
112-
TimeTraceProfilerEntry &E = Stack.back();
113123
E.End = ClockType::now();
114124

115-
// Check that end times monotonically increase.
116-
assert((Entries.empty() ||
117-
(E.getFlameGraphStartUs(StartTime) + E.getFlameGraphDurUs() >=
118-
Entries.back().getFlameGraphStartUs(StartTime) +
119-
Entries.back().getFlameGraphDurUs())) &&
120-
"TimeProfiler scope ended earlier than previous scope");
121-
122125
// Calculate duration at full precision for overall counts.
123126
DurationType Duration = E.End - E.Start;
124127

@@ -132,15 +135,18 @@ struct llvm::TimeTraceProfiler {
132135
// happens to be the ones that don't have any currently open entries above
133136
// itself.
134137
if (llvm::none_of(llvm::drop_begin(llvm::reverse(Stack)),
135-
[&](const TimeTraceProfilerEntry &Val) {
136-
return Val.Name == E.Name;
138+
[&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
139+
return Val->Name == E.Name;
137140
})) {
138141
auto &CountAndTotal = CountAndTotalPerName[E.Name];
139142
CountAndTotal.first++;
140143
CountAndTotal.second += Duration;
141-
}
144+
};
142145

143-
Stack.pop_back();
146+
llvm::erase_if(Stack,
147+
[&](const std::unique_ptr<TimeTraceProfilerEntry> &Val) {
148+
return Val.get() == &E;
149+
});
144150
}
145151

146152
// Write events from this TimeTraceProfilerInstance and
@@ -168,14 +174,32 @@ struct llvm::TimeTraceProfiler {
168174
J.object([&] {
169175
J.attribute("pid", Pid);
170176
J.attribute("tid", int64_t(Tid));
171-
J.attribute("ph", "X");
172177
J.attribute("ts", StartUs);
173-
J.attribute("dur", DurUs);
178+
if (E.AsyncEvent) {
179+
J.attribute("cat", E.Name);
180+
J.attribute("ph", "b");
181+
J.attribute("id", 0);
182+
} else {
183+
J.attribute("ph", "X");
184+
J.attribute("dur", DurUs);
185+
}
174186
J.attribute("name", E.Name);
175187
if (!E.Detail.empty()) {
176188
J.attributeObject("args", [&] { J.attribute("detail", E.Detail); });
177189
}
178190
});
191+
192+
if (E.AsyncEvent) {
193+
J.object([&] {
194+
J.attribute("pid", Pid);
195+
J.attribute("tid", int64_t(Tid));
196+
J.attribute("ts", StartUs + DurUs);
197+
J.attribute("cat", E.Name);
198+
J.attribute("ph", "e");
199+
J.attribute("id", 0);
200+
J.attribute("name", E.Name);
201+
});
202+
}
179203
};
180204
for (const TimeTraceProfilerEntry &E : Entries)
181205
writeEvent(E, this->Tid);
@@ -269,7 +293,7 @@ struct llvm::TimeTraceProfiler {
269293
J.objectEnd();
270294
}
271295

272-
SmallVector<TimeTraceProfilerEntry, 16> Stack;
296+
SmallVector<std::unique_ptr<TimeTraceProfilerEntry>, 16> Stack;
273297
SmallVector<TimeTraceProfilerEntry, 128> Entries;
274298
StringMap<CountAndDurationType> CountAndTotalPerName;
275299
// System clock time when the session was begun.
@@ -341,19 +365,36 @@ Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
341365
return Error::success();
342366
}
343367

344-
void llvm::timeTraceProfilerBegin(StringRef Name, StringRef Detail) {
368+
TimeTraceProfilerEntry *llvm::timeTraceProfilerBegin(StringRef Name,
369+
StringRef Detail) {
370+
if (TimeTraceProfilerInstance != nullptr)
371+
return TimeTraceProfilerInstance->begin(
372+
std::string(Name), [&]() { return std::string(Detail); }, false);
373+
return nullptr;
374+
}
375+
376+
TimeTraceProfilerEntry *
377+
llvm::timeTraceProfilerBegin(StringRef Name,
378+
llvm::function_ref<std::string()> Detail) {
345379
if (TimeTraceProfilerInstance != nullptr)
346-
TimeTraceProfilerInstance->begin(std::string(Name),
347-
[&]() { return std::string(Detail); });
380+
return TimeTraceProfilerInstance->begin(std::string(Name), Detail, false);
381+
return nullptr;
348382
}
349383

350-
void llvm::timeTraceProfilerBegin(StringRef Name,
351-
llvm::function_ref<std::string()> Detail) {
384+
TimeTraceProfilerEntry *llvm::timeTraceAsyncProfilerBegin(StringRef Name,
385+
StringRef Detail) {
352386
if (TimeTraceProfilerInstance != nullptr)
353-
TimeTraceProfilerInstance->begin(std::string(Name), Detail);
387+
return TimeTraceProfilerInstance->begin(
388+
std::string(Name), [&]() { return std::string(Detail); }, true);
389+
return nullptr;
354390
}
355391

356392
void llvm::timeTraceProfilerEnd() {
357393
if (TimeTraceProfilerInstance != nullptr)
358394
TimeTraceProfilerInstance->end();
359395
}
396+
397+
void llvm::timeTraceProfilerEnd(TimeTraceProfilerEntry *E) {
398+
if (TimeTraceProfilerInstance != nullptr)
399+
TimeTraceProfilerInstance->end(*E);
400+
}

llvm/unittests/Support/TimeProfilerTest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ TEST(TimeProfiler, Begin_End_Smoke) {
5454
ASSERT_TRUE(json.find(R"("detail":"detail")") != std::string::npos);
5555
}
5656

57+
TEST(TimeProfiler, Async_Begin_End_Smoke) {
58+
setupProfiler();
59+
60+
auto *Profiler = timeTraceAsyncProfilerBegin("event", "detail");
61+
timeTraceProfilerEnd(Profiler);
62+
63+
std::string json = teardownProfiler();
64+
ASSERT_TRUE(json.find(R"("name":"event")") != std::string::npos);
65+
ASSERT_TRUE(json.find(R"("detail":"detail")") != std::string::npos);
66+
}
67+
5768
TEST(TimeProfiler, Begin_End_Disabled) {
5869
// Nothing should be observable here. The test is really just making sure
5970
// we've not got a stray nullptr deref.

0 commit comments

Comments
 (0)