LLVM 23.0.0git
TimeProfiler.cpp
Go to the documentation of this file.
1//===-- TimeProfiler.cpp - Hierarchical Time Profiler ---------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements hierarchical time profiler.
10//
11//===----------------------------------------------------------------------===//
12
14#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/StringMap.h"
18#include "llvm/Support/JSON.h"
19#include "llvm/Support/Path.h"
22#include <algorithm>
23#include <cassert>
24#include <chrono>
25#include <memory>
26#include <mutex>
27#include <string>
28#include <vector>
29
30using namespace llvm;
31
32namespace {
33
34using std::chrono::duration;
35using std::chrono::duration_cast;
36using std::chrono::microseconds;
37using std::chrono::steady_clock;
39using std::chrono::time_point;
40using std::chrono::time_point_cast;
41
42struct TimeTraceProfilerInstances {
43 std::mutex Lock;
44 std::vector<TimeTraceProfiler *> List;
45};
46
47TimeTraceProfilerInstances &getTimeTraceProfilerInstances() {
48 static TimeTraceProfilerInstances Instances;
49 return Instances;
50}
51
52static const char TimeTraceFileExtension[] = ".time-trace.json";
53
54} // anonymous namespace
55
56// Per Thread instance
58
62
63namespace {
64
65using ClockType = steady_clock;
66using TimePointType = time_point<ClockType>;
67using DurationType = duration<ClockType::rep, ClockType::period>;
68using CountAndDurationType = std::pair<size_t, DurationType>;
69using NameAndCountAndDurationType =
70 std::pair<std::string, CountAndDurationType>;
71
72} // anonymous namespace
73
74/// Represents an open or completed time section entry to be captured.
76 const TimePointType Start;
77 TimePointType End;
78 const std::string Name;
80
82 TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
83 std::string &&Dt, TimeTraceEventType Et)
84 : Start(std::move(S)), End(std::move(E)), Name(std::move(N)), Metadata(),
85 EventType(Et) {
86 Metadata.Detail = std::move(Dt);
87 }
88
89 TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N,
91 : Start(std::move(S)), End(std::move(E)), Name(std::move(N)),
92 Metadata(std::move(Mt)), EventType(Et) {}
93
94 // Calculate timings for FlameGraph. Cast time points to microsecond precision
95 // rather than casting duration. This avoids truncation issues causing inner
96 // scopes overruning outer scopes.
97 ClockType::rep getFlameGraphStartUs(TimePointType StartTime) const {
98 return (time_point_cast<microseconds>(Start) -
99 time_point_cast<microseconds>(StartTime))
100 .count();
101 }
102
103 ClockType::rep getFlameGraphDurUs() const {
104 return (time_point_cast<microseconds>(End) -
105 time_point_cast<microseconds>(Start))
106 .count();
107 }
108};
109
110// Represents a currently open (in-progress) time trace entry. InstantEvents
111// that happen during an open event are associated with the duration of this
112// parent event and they are dropped if parent duration is shorter than
113// the granularity.
116 std::vector<TimeTraceProfilerEntry> InstantEvents;
117
118 InProgressEntry(TimePointType S, TimePointType E, std::string N,
119 std::string Dt, TimeTraceEventType Et)
120 : Event(std::move(S), std::move(E), std::move(N), std::move(Dt), Et),
121 InstantEvents() {}
122
123 InProgressEntry(TimePointType S, TimePointType E, std::string N,
125 : Event(std::move(S), std::move(E), std::move(N), std::move(Mt), Et),
126 InstantEvents() {}
127};
128
138
140 begin(std::string Name, llvm::function_ref<std::string()> Detail,
143 "Instant Events don't have begin and end.");
144 Stack.emplace_back(std::make_unique<InProgressEntry>(
145 ClockType::now(), TimePointType(), std::move(Name), Detail(),
146 EventType));
147 return &Stack.back()->Event;
148 }
149
154 "Instant Events don't have begin and end.");
155 Stack.emplace_back(std::make_unique<InProgressEntry>(
156 ClockType::now(), TimePointType(), std::move(Name), Metadata(),
157 EventType));
158 return &Stack.back()->Event;
159 }
160
161 void insert(std::string Name, llvm::function_ref<std::string()> Detail) {
162 if (Stack.empty())
163 return;
164
165 Stack.back()->InstantEvents.emplace_back(TimeTraceProfilerEntry(
166 ClockType::now(), TimePointType(), std::move(Name), Detail(),
168 }
169
170 void end() {
171 assert(!Stack.empty() && "Must call begin() first");
172 end(Stack.back()->Event);
173 }
174
176 assert(!Stack.empty() && "Must call begin() first");
177 E.End = ClockType::now();
178
179 // Calculate duration at full precision for overall counts.
180 DurationType Duration = E.End - E.Start;
181
182 const auto *Iter =
183 llvm::find_if(Stack, [&](const std::unique_ptr<InProgressEntry> &Val) {
184 return &Val->Event == &E;
185 });
186 assert(Iter != Stack.end() && "Event not in the Stack");
187
188 // Only include sections longer or equal to TimeTraceGranularity msec.
189 if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity) {
190 Entries.emplace_back(E);
191 for (auto &IE : Iter->get()->InstantEvents) {
192 Entries.emplace_back(IE);
193 }
194 }
195
196 // Track total time taken by each "name", but only the topmost levels of
197 // them; e.g. if there's a template instantiation that instantiates other
198 // templates from within, we only want to add the topmost one. "topmost"
199 // happens to be the ones that don't have any currently open entries above
200 // itself.
202 [&](const std::unique_ptr<InProgressEntry> &Val) {
203 return Val->Event.Name == E.Name;
204 })) {
205 auto &CountAndTotal = CountAndTotalPerName[E.Name];
206 CountAndTotal.first++;
207 CountAndTotal.second += Duration;
208 };
209
210 Stack.erase(Iter);
211 }
212
213 // Write events from this TimeTraceProfilerInstance and
214 // ThreadTimeTraceProfilerInstances.
216 // Acquire Mutex as reading ThreadTimeTraceProfilerInstances.
217 auto &Instances = getTimeTraceProfilerInstances();
218 std::lock_guard<std::mutex> Lock(Instances.Lock);
219 assert(Stack.empty() &&
220 "All profiler sections should be ended when calling write");
221 assert(llvm::all_of(Instances.List,
222 [](const auto &TTP) { return TTP->Stack.empty(); }) &&
223 "All profiler sections should be ended when calling write");
224
225 json::OStream J(OS);
226 J.objectBegin();
227 J.attributeBegin("traceEvents");
228 J.arrayBegin();
229
230 // Emit all events for the main flame graph.
231 auto writeEvent = [&](const auto &E, uint64_t Tid) {
232 auto StartUs = E.getFlameGraphStartUs(StartTime);
233 auto DurUs = E.getFlameGraphDurUs();
234
235 J.object([&] {
236 J.attribute("pid", Pid);
237 J.attribute("tid", int64_t(Tid));
238 J.attribute("ts", StartUs);
239 if (E.EventType == TimeTraceEventType::AsyncEvent) {
240 J.attribute("cat", E.Name);
241 J.attribute("ph", "b");
242 J.attribute("id", 0);
243 } else if (E.EventType == TimeTraceEventType::CompleteEvent) {
244 J.attribute("ph", "X");
245 J.attribute("dur", DurUs);
246 } else { // instant event
248 "InstantEvent expected");
249 J.attribute("ph", "i");
250 }
251 J.attribute("name", E.Name);
252 if (!E.Metadata.isEmpty()) {
253 J.attributeObject("args", [&] {
254 if (!E.Metadata.Detail.empty())
255 J.attribute("detail", E.Metadata.Detail);
256 if (!E.Metadata.File.empty())
257 J.attribute("file", E.Metadata.File);
258 if (E.Metadata.Line > 0)
259 J.attribute("line", E.Metadata.Line);
260 });
261 }
262 });
263
264 if (E.EventType == TimeTraceEventType::AsyncEvent) {
265 J.object([&] {
266 J.attribute("pid", Pid);
267 J.attribute("tid", int64_t(Tid));
268 J.attribute("ts", StartUs + DurUs);
269 J.attribute("cat", E.Name);
270 J.attribute("ph", "e");
271 J.attribute("id", 0);
272 J.attribute("name", E.Name);
273 });
274 }
275 };
276 for (const TimeTraceProfilerEntry &E : Entries)
277 writeEvent(E, this->Tid);
278 for (const TimeTraceProfiler *TTP : Instances.List)
279 for (const TimeTraceProfilerEntry &E : TTP->Entries)
280 writeEvent(E, TTP->Tid);
281
282 // Emit totals by section name as additional "thread" events, sorted from
283 // longest one.
284 // Find highest used thread id.
285 uint64_t MaxTid = this->Tid;
286 for (const TimeTraceProfiler *TTP : Instances.List)
287 MaxTid = std::max(MaxTid, TTP->Tid);
288
289 // Combine all CountAndTotalPerName from threads into one.
290 StringMap<CountAndDurationType> AllCountAndTotalPerName;
291 auto combineStat = [&](const auto &Stat) {
292 StringRef Key = Stat.getKey();
293 auto Value = Stat.getValue();
294 auto &CountAndTotal = AllCountAndTotalPerName[Key];
295 CountAndTotal.first += Value.first;
296 CountAndTotal.second += Value.second;
297 };
298 for (const auto &Stat : CountAndTotalPerName)
299 combineStat(Stat);
300 for (const TimeTraceProfiler *TTP : Instances.List)
301 for (const auto &Stat : TTP->CountAndTotalPerName)
302 combineStat(Stat);
303
304 std::vector<NameAndCountAndDurationType> SortedTotals;
305 SortedTotals.reserve(AllCountAndTotalPerName.size());
306 for (const auto &Total : AllCountAndTotalPerName)
307 SortedTotals.emplace_back(std::string(Total.getKey()), Total.getValue());
308
309 llvm::sort(SortedTotals, [](const NameAndCountAndDurationType &A,
310 const NameAndCountAndDurationType &B) {
311 return A.second.second > B.second.second;
312 });
313
314 // Report totals on separate threads of tracing file.
315 uint64_t TotalTid = MaxTid + 1;
316 for (const NameAndCountAndDurationType &Total : SortedTotals) {
317 auto DurUs = duration_cast<microseconds>(Total.second.second).count();
318 auto Count = AllCountAndTotalPerName[Total.first].first;
319
320 J.object([&] {
321 J.attribute("pid", Pid);
322 J.attribute("tid", int64_t(TotalTid));
323 J.attribute("ph", "X");
324 J.attribute("ts", 0);
325 J.attribute("dur", DurUs);
326 J.attribute("name", "Total " + Total.first);
327 J.attributeObject("args", [&] {
328 J.attribute("count", int64_t(Count));
329 J.attribute("avg ms", int64_t(DurUs / Count / 1000));
330 });
331 });
332
333 ++TotalTid;
334 }
335
336 auto writeMetadataEvent = [&](const char *Name, uint64_t Tid,
337 StringRef arg) {
338 J.object([&] {
339 J.attribute("cat", "");
340 J.attribute("pid", Pid);
341 J.attribute("tid", int64_t(Tid));
342 J.attribute("ts", 0);
343 J.attribute("ph", "M");
344 J.attribute("name", Name);
345 J.attributeObject("args", [&] { J.attribute("name", arg); });
346 });
347 };
348
349 writeMetadataEvent("process_name", Tid, ProcName);
350 writeMetadataEvent("thread_name", Tid, ThreadName);
351 for (const TimeTraceProfiler *TTP : Instances.List)
352 writeMetadataEvent("thread_name", TTP->Tid, TTP->ThreadName);
353
354 J.arrayEnd();
355 J.attributeEnd();
356
357 // Emit the absolute time when this TimeProfiler started.
358 // This can be used to combine the profiling data from
359 // multiple processes and preserve actual time intervals.
360 J.attribute("beginningOfTime",
361 time_point_cast<microseconds>(BeginningOfTime)
362 .time_since_epoch()
363 .count());
364
365 J.objectEnd();
366 }
367
371 // System clock time when the session was begun.
372 const time_point<system_clock> BeginningOfTime;
373 // Profiling clock time when the session was begun.
374 const TimePointType StartTime;
375 const std::string ProcName;
379
380 // Minimum time granularity (in microseconds)
381 const unsigned TimeTraceGranularity;
382
383 // Make time trace capture verbose event details (e.g. source filenames). This
384 // can increase the size of the output by 2-3 times.
386};
387
392
393void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
394 StringRef ProcName,
395 bool TimeTraceVerbose) {
397 "Profiler should not be initialized");
399 TimeTraceGranularity, llvm::sys::path::filename(ProcName),
400 TimeTraceVerbose);
401}
402
403// Removes all TimeTraceProfilerInstances.
404// Called from main thread.
408
409 auto &Instances = getTimeTraceProfilerInstances();
410 std::lock_guard<std::mutex> Lock(Instances.Lock);
411 for (auto *TTP : Instances.List)
412 delete TTP;
413 Instances.List.clear();
414}
415
416// Finish TimeTraceProfilerInstance on a worker thread.
417// This doesn't remove the instance, just moves the pointer to global vector.
419 auto &Instances = getTimeTraceProfilerInstances();
420 std::lock_guard<std::mutex> Lock(Instances.Lock);
421 Instances.List.push_back(TimeTraceProfilerInstance);
423}
424
427 "Profiler object can't be null");
428 TimeTraceProfilerInstance->write(OS);
429}
430
432 StringRef FallbackFileName) {
434 "Profiler object can't be null");
435
436 std::string Path = PreferredFileName.str();
437 if (Path.empty()) {
438 Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
439 Path += TimeTraceFileExtension;
440 }
441
442 std::error_code EC;
444 if (EC)
445 return createStringError(EC, "Could not open " + Path);
446
448 return Error::success();
449}
450
452 StringRef Detail) {
453 if (TimeTraceProfilerInstance != nullptr)
454 return TimeTraceProfilerInstance->begin(
455 std::string(Name), [&]() { return std::string(Detail); },
457 return nullptr;
458}
459
462 llvm::function_ref<std::string()> Detail) {
463 if (TimeTraceProfilerInstance != nullptr)
464 return TimeTraceProfilerInstance->begin(std::string(Name), Detail,
466 return nullptr;
467}
468
477
479 StringRef Detail) {
480 if (TimeTraceProfilerInstance != nullptr)
481 return TimeTraceProfilerInstance->begin(
482 std::string(Name), [&]() { return std::string(Detail); },
484 return nullptr;
485}
486
488 llvm::function_ref<std::string()> Detail) {
489 if (TimeTraceProfilerInstance != nullptr)
490 TimeTraceProfilerInstance->insert(std::string(Name), Detail);
491}
492
497
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file defines the StringMap class.
static sys::TimePoint< std::chrono::seconds > now(bool Deterministic)
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_THREAD_LOCAL
\macro LLVM_THREAD_LOCAL A thread-local storage specifier which can be used with globals,...
Definition Compiler.h:704
dxil translate DXIL Translate Metadata
This file supports working with JSON data.
Provides a library for accessing information about this process and other processes on the operating ...
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
static LLVM_THREAD_LOCAL TimeTraceProfiler * TimeTraceProfilerInstance
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Root of the metadata hierarchy.
Definition Metadata.h:64
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
unsigned size() const
Definition StringMap.h:103
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:128
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
std::string str() const
Get the contents as an std::string.
Definition StringRef.h:222
LLVM Value Representation.
Definition Value.h:75
An efficient, type-erasing, non-owning reference to a callable.
json::OStream allows writing well-formed JSON without materializing all structures as json::Value ahe...
Definition JSON.h:982
void object(Block Contents)
Emit an object whose elements are emitted in the provided Block.
Definition JSON.h:1012
void attributeObject(llvm::StringRef Key, Block Contents)
Emit an attribute whose value is an object with attributes from the Block.
Definition JSON.h:1045
LLVM_ABI void attributeBegin(llvm::StringRef Key)
Definition JSON.cpp:883
void attribute(llvm::StringRef Key, const Value &Contents)
Emit an attribute whose value is self-contained (number, vector<int> etc).
Definition JSON.h:1037
LLVM_ABI void arrayBegin()
Definition JSON.cpp:845
LLVM_ABI void objectBegin()
Definition JSON.cpp:864
LLVM_ABI void arrayEnd()
Definition JSON.cpp:853
LLVM_ABI void attributeEnd()
Definition JSON.cpp:903
LLVM_ABI void objectEnd()
Definition JSON.cpp:872
A raw_ostream that writes to a file descriptor.
An abstract base class for streams implementations that also support a pwrite operation.
A collection of legacy interfaces for querying information about the current executing process.
Definition Process.h:44
@ OF_TextWithCRLF
The file should be opened in text mode and use a carriage linefeed '\r '.
Definition FileSystem.h:804
LLVM_ABI StringRef filename(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get filename.
Definition Path.cpp:594
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1738
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
LLVM_ABI TimeTraceProfiler * getTimeTraceProfilerInstance()
LLVM_ABI void timeTraceProfilerInitialize(unsigned TimeTraceGranularity, StringRef ProcName, bool TimeTraceVerbose=false)
Initialize the time trace profiler.
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI void timeTraceProfilerFinishThread()
Finish a time trace profiler running on a worker thread.
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1635
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1752
FunctionAddr VTableAddr Count
Definition InstrProf.h:139
LLVM_ABI void timeTraceProfilerEnd()
Manually end the last time section.
LLVM_ABI void timeTraceAddInstantEvent(StringRef Name, llvm::function_ref< std::string()> Detail)
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
LLVM_ABI void get_thread_name(SmallVectorImpl< char > &Name)
Get the name of the current thread.
Definition Threading.cpp:39
LLVM_ABI TimeTraceProfilerEntry * timeTraceAsyncProfilerBegin(StringRef Name, StringRef Detail)
Manually begin a time section, with the given Name and Detail.
LLVM_ABI bool isTimeTraceVerbose()
LLVM_ABI uint64_t get_threadid()
Return the current thread id, as used in various OS system calls.
Definition Threading.cpp:33
TimeTraceEventType
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
Definition STLExtras.h:2011
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1916
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1771
LLVM_ABI void timeTraceProfilerCleanup()
Cleanup the time trace profiler, if it was initialized.
LLVM_ABI void timeTraceProfilerWrite(raw_pwrite_stream &OS)
Write profiling data to output stream.
LLVM_ABI TimeTraceProfilerEntry * timeTraceProfilerBegin(StringRef Name, StringRef Detail)
Manually begin a time section, with the given Name and Detail.
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:860
#define N
std::vector< TimeTraceProfilerEntry > InstantEvents
InProgressEntry(TimePointType S, TimePointType E, std::string N, TimeTraceMetadata Mt, TimeTraceEventType Et)
TimeTraceProfilerEntry Event
InProgressEntry(TimePointType S, TimePointType E, std::string N, std::string Dt, TimeTraceEventType Et)
Represents an open or completed time section entry to be captured.
const TimePointType Start
TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N, std::string &&Dt, TimeTraceEventType Et)
ClockType::rep getFlameGraphDurUs() const
TimeTraceProfilerEntry(TimePointType &&S, TimePointType &&E, std::string &&N, TimeTraceMetadata &&Mt, TimeTraceEventType Et)
const TimeTraceEventType EventType
ClockType::rep getFlameGraphStartUs(TimePointType StartTime) const
const sys::Process::Pid Pid
void write(raw_pwrite_stream &OS)
StringMap< CountAndDurationType > CountAndTotalPerName
const unsigned TimeTraceGranularity
void insert(std::string Name, llvm::function_ref< std::string()> Detail)
TimeTraceProfilerEntry * begin(std::string Name, llvm::function_ref< TimeTraceMetadata()> Metadata, TimeTraceEventType EventType=TimeTraceEventType::CompleteEvent)
const time_point< system_clock > BeginningOfTime
SmallVector< std::unique_ptr< InProgressEntry >, 16 > Stack
TimeTraceProfiler(unsigned TimeTraceGranularity=0, StringRef ProcName="", bool TimeTraceVerbose=false)
SmallString< 0 > ThreadName
const std::string ProcName
TimeTraceProfilerEntry * begin(std::string Name, llvm::function_ref< std::string()> Detail, TimeTraceEventType EventType=TimeTraceEventType::CompleteEvent)
SmallVector< TimeTraceProfilerEntry, 128 > Entries
const TimePointType StartTime
void end(TimeTraceProfilerEntry &E)