1d880de2dSAnton Afanasyev //===-- TimeProfiler.cpp - Hierarchical Time Profiler ---------------------===//
2d880de2dSAnton Afanasyev //
326536728SAnton Afanasyev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
426536728SAnton Afanasyev // See https://llvm.org/LICENSE.txt for license information.
526536728SAnton Afanasyev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d880de2dSAnton Afanasyev //
7d880de2dSAnton Afanasyev //===----------------------------------------------------------------------===//
8d880de2dSAnton Afanasyev //
926536728SAnton Afanasyev // This file implements hierarchical time profiler.
10d880de2dSAnton Afanasyev //
11d880de2dSAnton Afanasyev //===----------------------------------------------------------------------===//
12d880de2dSAnton Afanasyev
13d880de2dSAnton Afanasyev #include "llvm/Support/TimeProfiler.h"
14*5f290c09Sserge-sans-paille #include "llvm/ADT/STLFunctionalExtras.h"
1503c3e0d3SAnton Afanasyev #include "llvm/ADT/StringMap.h"
166547d514SAnton Afanasyev #include "llvm/Support/JSON.h"
1775e164f6Sserge-sans-paille #include "llvm/Support/ManagedStatic.h"
18aedeab7fSRussell Gallop #include "llvm/Support/Path.h"
19a5bf0281SSergej Jaskiewicz #include "llvm/Support/Process.h"
2077e6bb3cSRussell Gallop #include "llvm/Support/Threading.h"
2177e6bb3cSRussell Gallop #include <algorithm>
22d880de2dSAnton Afanasyev #include <cassert>
23d880de2dSAnton Afanasyev #include <chrono>
2477e6bb3cSRussell Gallop #include <mutex>
25d880de2dSAnton Afanasyev #include <string>
26d880de2dSAnton Afanasyev #include <vector>
27d880de2dSAnton Afanasyev
28d880de2dSAnton Afanasyev using namespace std::chrono;
293f6a2f1eSMarkus Böck using namespace llvm;
30d880de2dSAnton Afanasyev
317c135509SFangrui Song static std::mutex Mu;
323f6a2f1eSMarkus Böck // List of all instances
3376374573SMehdi Amini static ManagedStatic<std::vector<TimeTraceProfiler *>>
347c135509SFangrui Song ThreadTimeTraceProfilerInstances; // GUARDED_BY(Mu)
353f6a2f1eSMarkus Böck // Per Thread instance
367c135509SFangrui Song static LLVM_THREAD_LOCAL TimeTraceProfiler *TimeTraceProfilerInstance = nullptr;
3777e6bb3cSRussell Gallop
getTimeTraceProfilerInstance()387c135509SFangrui Song TimeTraceProfiler *llvm::getTimeTraceProfilerInstance() {
393f6a2f1eSMarkus Böck return TimeTraceProfilerInstance;
403f6a2f1eSMarkus Böck }
41d880de2dSAnton Afanasyev
42d880de2dSAnton Afanasyev typedef duration<steady_clock::rep, steady_clock::period> DurationType;
43c6fda60dSRussell Gallop typedef time_point<steady_clock> TimePointType;
4403c3e0d3SAnton Afanasyev typedef std::pair<size_t, DurationType> CountAndDurationType;
4503c3e0d3SAnton Afanasyev typedef std::pair<std::string, CountAndDurationType>
4603c3e0d3SAnton Afanasyev NameAndCountAndDurationType;
47d880de2dSAnton Afanasyev
487c135509SFangrui Song namespace {
49d880de2dSAnton Afanasyev struct Entry {
50df943a7aSRussell Gallop const TimePointType Start;
51c6fda60dSRussell Gallop TimePointType End;
52df943a7aSRussell Gallop const std::string Name;
53df943a7aSRussell Gallop const std::string Detail;
5426536728SAnton Afanasyev
Entry__anona21739de0111::Entry55c6fda60dSRussell Gallop Entry(TimePointType &&S, TimePointType &&E, std::string &&N, std::string &&Dt)
56c6fda60dSRussell Gallop : Start(std::move(S)), End(std::move(E)), Name(std::move(N)),
57df943a7aSRussell Gallop Detail(std::move(Dt)) {}
58c6fda60dSRussell Gallop
59c6fda60dSRussell Gallop // Calculate timings for FlameGraph. Cast time points to microsecond precision
60c6fda60dSRussell Gallop // rather than casting duration. This avoid truncation issues causing inner
61c6fda60dSRussell Gallop // scopes overruning outer scopes.
getFlameGraphStartUs__anona21739de0111::Entry62c6fda60dSRussell Gallop steady_clock::rep getFlameGraphStartUs(TimePointType StartTime) const {
63c6fda60dSRussell Gallop return (time_point_cast<microseconds>(Start) -
64c6fda60dSRussell Gallop time_point_cast<microseconds>(StartTime))
65c6fda60dSRussell Gallop .count();
66c6fda60dSRussell Gallop }
67c6fda60dSRussell Gallop
getFlameGraphDurUs__anona21739de0111::Entry68c6fda60dSRussell Gallop steady_clock::rep getFlameGraphDurUs() const {
69c6fda60dSRussell Gallop return (time_point_cast<microseconds>(End) -
70c6fda60dSRussell Gallop time_point_cast<microseconds>(Start))
71c6fda60dSRussell Gallop .count();
72c6fda60dSRussell Gallop }
73d880de2dSAnton Afanasyev };
747c135509SFangrui Song } // namespace
75d880de2dSAnton Afanasyev
767c135509SFangrui Song struct llvm::TimeTraceProfiler {
TimeTraceProfilerllvm::TimeTraceProfiler77aedeab7fSRussell Gallop TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "")
7828991031SSergej Jaskiewicz : BeginningOfTime(system_clock::now()), StartTime(steady_clock::now()),
7928991031SSergej Jaskiewicz ProcName(ProcName), Pid(sys::Process::getProcessId()),
8028991031SSergej Jaskiewicz Tid(llvm::get_threadid()), TimeTraceGranularity(TimeTraceGranularity) {
81a5bf0281SSergej Jaskiewicz llvm::get_thread_name(ThreadName);
82a5bf0281SSergej Jaskiewicz }
83d880de2dSAnton Afanasyev
beginllvm::TimeTraceProfiler84d880de2dSAnton Afanasyev void begin(std::string Name, llvm::function_ref<std::string()> Detail) {
85c6fda60dSRussell Gallop Stack.emplace_back(steady_clock::now(), TimePointType(), std::move(Name),
8626536728SAnton Afanasyev Detail());
87d880de2dSAnton Afanasyev }
88d880de2dSAnton Afanasyev
endllvm::TimeTraceProfiler89d880de2dSAnton Afanasyev void end() {
90d880de2dSAnton Afanasyev assert(!Stack.empty() && "Must call begin() first");
917c135509SFangrui Song Entry &E = Stack.back();
92c6fda60dSRussell Gallop E.End = steady_clock::now();
93c6fda60dSRussell Gallop
94c6fda60dSRussell Gallop // Check that end times monotonically increase.
95c6fda60dSRussell Gallop assert((Entries.empty() ||
96c6fda60dSRussell Gallop (E.getFlameGraphStartUs(StartTime) + E.getFlameGraphDurUs() >=
97c6fda60dSRussell Gallop Entries.back().getFlameGraphStartUs(StartTime) +
98c6fda60dSRussell Gallop Entries.back().getFlameGraphDurUs())) &&
99c6fda60dSRussell Gallop "TimeProfiler scope ended earlier than previous scope");
100c6fda60dSRussell Gallop
101c6fda60dSRussell Gallop // Calculate duration at full precision for overall counts.
102c6fda60dSRussell Gallop DurationType Duration = E.End - E.Start;
103d880de2dSAnton Afanasyev
1043f3a2573SAnton Afanasyev // Only include sections longer or equal to TimeTraceGranularity msec.
105c6fda60dSRussell Gallop if (duration_cast<microseconds>(Duration).count() >= TimeTraceGranularity)
106d880de2dSAnton Afanasyev Entries.emplace_back(E);
107d880de2dSAnton Afanasyev
108d880de2dSAnton Afanasyev // Track total time taken by each "name", but only the topmost levels of
109d880de2dSAnton Afanasyev // them; e.g. if there's a template instantiation that instantiates other
110d880de2dSAnton Afanasyev // templates from within, we only want to add the topmost one. "topmost"
111d880de2dSAnton Afanasyev // happens to be the ones that don't have any currently open entries above
112d880de2dSAnton Afanasyev // itself.
1134bd46501SKazu Hirata if (llvm::none_of(llvm::drop_begin(llvm::reverse(Stack)),
1144bd46501SKazu Hirata [&](const Entry &Val) { return Val.Name == E.Name; })) {
11503c3e0d3SAnton Afanasyev auto &CountAndTotal = CountAndTotalPerName[E.Name];
11603c3e0d3SAnton Afanasyev CountAndTotal.first++;
117c6fda60dSRussell Gallop CountAndTotal.second += Duration;
118d880de2dSAnton Afanasyev }
119d880de2dSAnton Afanasyev
120d880de2dSAnton Afanasyev Stack.pop_back();
121d880de2dSAnton Afanasyev }
122d880de2dSAnton Afanasyev
12377e6bb3cSRussell Gallop // Write events from this TimeTraceProfilerInstance and
12477e6bb3cSRussell Gallop // ThreadTimeTraceProfilerInstances.
writellvm::TimeTraceProfiler1257c135509SFangrui Song void write(raw_pwrite_stream &OS) {
12677e6bb3cSRussell Gallop // Acquire Mutex as reading ThreadTimeTraceProfilerInstances.
12777e6bb3cSRussell Gallop std::lock_guard<std::mutex> Lock(Mu);
128d880de2dSAnton Afanasyev assert(Stack.empty() &&
1297c135509SFangrui Song "All profiler sections should be ended when calling write");
13076374573SMehdi Amini assert(llvm::all_of(*ThreadTimeTraceProfilerInstances,
13177e6bb3cSRussell Gallop [](const auto &TTP) { return TTP->Stack.empty(); }) &&
1327c135509SFangrui Song "All profiler sections should be ended when calling write");
13377e6bb3cSRussell Gallop
134a7edcfb5SSam McCall json::OStream J(OS);
135a7edcfb5SSam McCall J.objectBegin();
136a7edcfb5SSam McCall J.attributeBegin("traceEvents");
137a7edcfb5SSam McCall J.arrayBegin();
138d880de2dSAnton Afanasyev
139d880de2dSAnton Afanasyev // Emit all events for the main flame graph.
14077e6bb3cSRussell Gallop auto writeEvent = [&](const auto &E, uint64_t Tid) {
141c6fda60dSRussell Gallop auto StartUs = E.getFlameGraphStartUs(StartTime);
142c6fda60dSRussell Gallop auto DurUs = E.getFlameGraphDurUs();
1436547d514SAnton Afanasyev
144a7edcfb5SSam McCall J.object([&] {
145a5bf0281SSergej Jaskiewicz J.attribute("pid", Pid);
14677e6bb3cSRussell Gallop J.attribute("tid", int64_t(Tid));
147a7edcfb5SSam McCall J.attribute("ph", "X");
148a7edcfb5SSam McCall J.attribute("ts", StartUs);
149a7edcfb5SSam McCall J.attribute("dur", DurUs);
150a7edcfb5SSam McCall J.attribute("name", E.Name);
151df494f75SRussell Gallop if (!E.Detail.empty()) {
152a7edcfb5SSam McCall J.attributeObject("args", [&] { J.attribute("detail", E.Detail); });
153df494f75SRussell Gallop }
1546547d514SAnton Afanasyev });
15577e6bb3cSRussell Gallop };
1567c135509SFangrui Song for (const Entry &E : Entries)
15777e6bb3cSRussell Gallop writeEvent(E, this->Tid);
15876374573SMehdi Amini for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances)
1597c135509SFangrui Song for (const Entry &E : TTP->Entries)
16077e6bb3cSRussell Gallop writeEvent(E, TTP->Tid);
161d880de2dSAnton Afanasyev
162d880de2dSAnton Afanasyev // Emit totals by section name as additional "thread" events, sorted from
163d880de2dSAnton Afanasyev // longest one.
16477e6bb3cSRussell Gallop // Find highest used thread id.
16577e6bb3cSRussell Gallop uint64_t MaxTid = this->Tid;
16676374573SMehdi Amini for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances)
16777e6bb3cSRussell Gallop MaxTid = std::max(MaxTid, TTP->Tid);
16877e6bb3cSRussell Gallop
16977e6bb3cSRussell Gallop // Combine all CountAndTotalPerName from threads into one.
17077e6bb3cSRussell Gallop StringMap<CountAndDurationType> AllCountAndTotalPerName;
17177e6bb3cSRussell Gallop auto combineStat = [&](const auto &Stat) {
172adcd0268SBenjamin Kramer StringRef Key = Stat.getKey();
17377e6bb3cSRussell Gallop auto Value = Stat.getValue();
17477e6bb3cSRussell Gallop auto &CountAndTotal = AllCountAndTotalPerName[Key];
17577e6bb3cSRussell Gallop CountAndTotal.first += Value.first;
17677e6bb3cSRussell Gallop CountAndTotal.second += Value.second;
17777e6bb3cSRussell Gallop };
1787c135509SFangrui Song for (const auto &Stat : CountAndTotalPerName)
17977e6bb3cSRussell Gallop combineStat(Stat);
18076374573SMehdi Amini for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances)
1817c135509SFangrui Song for (const auto &Stat : TTP->CountAndTotalPerName)
18277e6bb3cSRussell Gallop combineStat(Stat);
18377e6bb3cSRussell Gallop
18403c3e0d3SAnton Afanasyev std::vector<NameAndCountAndDurationType> SortedTotals;
18577e6bb3cSRussell Gallop SortedTotals.reserve(AllCountAndTotalPerName.size());
18677e6bb3cSRussell Gallop for (const auto &Total : AllCountAndTotalPerName)
1878b6320c7SBenjamin Kramer SortedTotals.emplace_back(std::string(Total.getKey()), Total.getValue());
18826536728SAnton Afanasyev
1897c135509SFangrui Song llvm::sort(SortedTotals, [](const NameAndCountAndDurationType &A,
19003c3e0d3SAnton Afanasyev const NameAndCountAndDurationType &B) {
19103c3e0d3SAnton Afanasyev return A.second.second > B.second.second;
192d880de2dSAnton Afanasyev });
19377e6bb3cSRussell Gallop
19477e6bb3cSRussell Gallop // Report totals on separate threads of tracing file.
19577e6bb3cSRussell Gallop uint64_t TotalTid = MaxTid + 1;
1967c135509SFangrui Song for (const NameAndCountAndDurationType &Total : SortedTotals) {
19777e6bb3cSRussell Gallop auto DurUs = duration_cast<microseconds>(Total.second.second).count();
19877e6bb3cSRussell Gallop auto Count = AllCountAndTotalPerName[Total.first].first;
1996547d514SAnton Afanasyev
200a7edcfb5SSam McCall J.object([&] {
201a5bf0281SSergej Jaskiewicz J.attribute("pid", Pid);
20277e6bb3cSRussell Gallop J.attribute("tid", int64_t(TotalTid));
203a7edcfb5SSam McCall J.attribute("ph", "X");
204a7edcfb5SSam McCall J.attribute("ts", 0);
205a7edcfb5SSam McCall J.attribute("dur", DurUs);
20677e6bb3cSRussell Gallop J.attribute("name", "Total " + Total.first);
207a7edcfb5SSam McCall J.attributeObject("args", [&] {
208a7edcfb5SSam McCall J.attribute("count", int64_t(Count));
209a7edcfb5SSam McCall J.attribute("avg ms", int64_t(DurUs / Count / 1000));
210a7edcfb5SSam McCall });
2116547d514SAnton Afanasyev });
2126547d514SAnton Afanasyev
21377e6bb3cSRussell Gallop ++TotalTid;
214d880de2dSAnton Afanasyev }
215d880de2dSAnton Afanasyev
216a5bf0281SSergej Jaskiewicz auto writeMetadataEvent = [&](const char *Name, uint64_t Tid,
217a5bf0281SSergej Jaskiewicz StringRef arg) {
218a7edcfb5SSam McCall J.object([&] {
219a7edcfb5SSam McCall J.attribute("cat", "");
220a5bf0281SSergej Jaskiewicz J.attribute("pid", Pid);
221a5bf0281SSergej Jaskiewicz J.attribute("tid", int64_t(Tid));
222a7edcfb5SSam McCall J.attribute("ts", 0);
223a7edcfb5SSam McCall J.attribute("ph", "M");
224a5bf0281SSergej Jaskiewicz J.attribute("name", Name);
225a5bf0281SSergej Jaskiewicz J.attributeObject("args", [&] { J.attribute("name", arg); });
2266547d514SAnton Afanasyev });
227a5bf0281SSergej Jaskiewicz };
228a5bf0281SSergej Jaskiewicz
229a5bf0281SSergej Jaskiewicz writeMetadataEvent("process_name", Tid, ProcName);
230a5bf0281SSergej Jaskiewicz writeMetadataEvent("thread_name", Tid, ThreadName);
23176374573SMehdi Amini for (const TimeTraceProfiler *TTP : *ThreadTimeTraceProfilerInstances)
232a5bf0281SSergej Jaskiewicz writeMetadataEvent("thread_name", TTP->Tid, TTP->ThreadName);
2336547d514SAnton Afanasyev
234a7edcfb5SSam McCall J.arrayEnd();
235a7edcfb5SSam McCall J.attributeEnd();
23628991031SSergej Jaskiewicz
23728991031SSergej Jaskiewicz // Emit the absolute time when this TimeProfiler started.
23828991031SSergej Jaskiewicz // This can be used to combine the profiling data from
23928991031SSergej Jaskiewicz // multiple processes and preserve actual time intervals.
24028991031SSergej Jaskiewicz J.attribute("beginningOfTime",
24128991031SSergej Jaskiewicz time_point_cast<microseconds>(BeginningOfTime)
24228991031SSergej Jaskiewicz .time_since_epoch()
24328991031SSergej Jaskiewicz .count());
24428991031SSergej Jaskiewicz
245a7edcfb5SSam McCall J.objectEnd();
246d880de2dSAnton Afanasyev }
247d880de2dSAnton Afanasyev
24826536728SAnton Afanasyev SmallVector<Entry, 16> Stack;
24926536728SAnton Afanasyev SmallVector<Entry, 128> Entries;
25003c3e0d3SAnton Afanasyev StringMap<CountAndDurationType> CountAndTotalPerName;
25128991031SSergej Jaskiewicz const time_point<system_clock> BeginningOfTime;
252df943a7aSRussell Gallop const TimePointType StartTime;
253aedeab7fSRussell Gallop const std::string ProcName;
254a5bf0281SSergej Jaskiewicz const sys::Process::Pid Pid;
255a5bf0281SSergej Jaskiewicz SmallString<0> ThreadName;
25677e6bb3cSRussell Gallop const uint64_t Tid;
2574fdcabf2SAnton Afanasyev
2584fdcabf2SAnton Afanasyev // Minimum time granularity (in microseconds)
259df943a7aSRussell Gallop const unsigned TimeTraceGranularity;
260d880de2dSAnton Afanasyev };
261d880de2dSAnton Afanasyev
timeTraceProfilerInitialize(unsigned TimeTraceGranularity,StringRef ProcName)2627c135509SFangrui Song void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity,
263aedeab7fSRussell Gallop StringRef ProcName) {
264d880de2dSAnton Afanasyev assert(TimeTraceProfilerInstance == nullptr &&
265d880de2dSAnton Afanasyev "Profiler should not be initialized");
2662e9bfa12SRussell Gallop TimeTraceProfilerInstance = new TimeTraceProfiler(
267aedeab7fSRussell Gallop TimeTraceGranularity, llvm::sys::path::filename(ProcName));
268d880de2dSAnton Afanasyev }
269d880de2dSAnton Afanasyev
27077e6bb3cSRussell Gallop // Removes all TimeTraceProfilerInstances.
27177e6bb3cSRussell Gallop // Called from main thread.
timeTraceProfilerCleanup()2727c135509SFangrui Song void llvm::timeTraceProfilerCleanup() {
2732e9bfa12SRussell Gallop delete TimeTraceProfilerInstance;
2740cf624caSShoaib Meenai TimeTraceProfilerInstance = nullptr;
27577e6bb3cSRussell Gallop std::lock_guard<std::mutex> Lock(Mu);
2763322354bSKazu Hirata for (auto *TTP : *ThreadTimeTraceProfilerInstances)
27777e6bb3cSRussell Gallop delete TTP;
27876374573SMehdi Amini ThreadTimeTraceProfilerInstances->clear();
27977e6bb3cSRussell Gallop }
28077e6bb3cSRussell Gallop
28177e6bb3cSRussell Gallop // Finish TimeTraceProfilerInstance on a worker thread.
28277e6bb3cSRussell Gallop // This doesn't remove the instance, just moves the pointer to global vector.
timeTraceProfilerFinishThread()2837c135509SFangrui Song void llvm::timeTraceProfilerFinishThread() {
28477e6bb3cSRussell Gallop std::lock_guard<std::mutex> Lock(Mu);
28576374573SMehdi Amini ThreadTimeTraceProfilerInstances->push_back(TimeTraceProfilerInstance);
2862e9bfa12SRussell Gallop TimeTraceProfilerInstance = nullptr;
287d880de2dSAnton Afanasyev }
288d880de2dSAnton Afanasyev
timeTraceProfilerWrite(raw_pwrite_stream & OS)2897c135509SFangrui Song void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) {
290d880de2dSAnton Afanasyev assert(TimeTraceProfilerInstance != nullptr &&
291d880de2dSAnton Afanasyev "Profiler object can't be null");
2927c135509SFangrui Song TimeTraceProfilerInstance->write(OS);
293d880de2dSAnton Afanasyev }
294d880de2dSAnton Afanasyev
timeTraceProfilerWrite(StringRef PreferredFileName,StringRef FallbackFileName)2957c135509SFangrui Song Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName,
2963669f0edSAndrew Monshizadeh StringRef FallbackFileName) {
2973669f0edSAndrew Monshizadeh assert(TimeTraceProfilerInstance != nullptr &&
2983669f0edSAndrew Monshizadeh "Profiler object can't be null");
2993669f0edSAndrew Monshizadeh
3003669f0edSAndrew Monshizadeh std::string Path = PreferredFileName.str();
3013669f0edSAndrew Monshizadeh if (Path.empty()) {
3023669f0edSAndrew Monshizadeh Path = FallbackFileName == "-" ? "out" : FallbackFileName.str();
3033669f0edSAndrew Monshizadeh Path += ".time-trace";
3043669f0edSAndrew Monshizadeh }
3053669f0edSAndrew Monshizadeh
3063669f0edSAndrew Monshizadeh std::error_code EC;
30782b3e28eSAbhina Sreeskantharajan raw_fd_ostream OS(Path, EC, sys::fs::OF_TextWithCRLF);
3083669f0edSAndrew Monshizadeh if (EC)
3093669f0edSAndrew Monshizadeh return createStringError(EC, "Could not open " + Path);
3103669f0edSAndrew Monshizadeh
3113669f0edSAndrew Monshizadeh timeTraceProfilerWrite(OS);
3123669f0edSAndrew Monshizadeh return Error::success();
3133669f0edSAndrew Monshizadeh }
3143669f0edSAndrew Monshizadeh
timeTraceProfilerBegin(StringRef Name,StringRef Detail)3157c135509SFangrui Song void llvm::timeTraceProfilerBegin(StringRef Name, StringRef Detail) {
316d880de2dSAnton Afanasyev if (TimeTraceProfilerInstance != nullptr)
317adcd0268SBenjamin Kramer TimeTraceProfilerInstance->begin(std::string(Name),
318adcd0268SBenjamin Kramer [&]() { return std::string(Detail); });
319d880de2dSAnton Afanasyev }
320d880de2dSAnton Afanasyev
timeTraceProfilerBegin(StringRef Name,llvm::function_ref<std::string ()> Detail)3217c135509SFangrui Song void llvm::timeTraceProfilerBegin(StringRef Name,
322d880de2dSAnton Afanasyev llvm::function_ref<std::string()> Detail) {
323d880de2dSAnton Afanasyev if (TimeTraceProfilerInstance != nullptr)
324adcd0268SBenjamin Kramer TimeTraceProfilerInstance->begin(std::string(Name), Detail);
325d880de2dSAnton Afanasyev }
326d880de2dSAnton Afanasyev
timeTraceProfilerEnd()3277c135509SFangrui Song void llvm::timeTraceProfilerEnd() {
328d880de2dSAnton Afanasyev if (TimeTraceProfilerInstance != nullptr)
329d880de2dSAnton Afanasyev TimeTraceProfilerInstance->end();
330d880de2dSAnton Afanasyev }
331