blob: 0575d03bc850fe529231935681470218c1d53d95 [file] [log] [blame]
Anton Afanasyevd880de22019-03-30 08:42:48 +00001//===-- TimeProfiler.cpp - Hierarchical Time Profiler ---------------------===//
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 Hierarchical time profiler implementation.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/TimeProfiler.h"
15#include "llvm/ADT/StringExtras.h"
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +000016#include "llvm/ADT/StringMap.h"
Anton Afanasyevd880de22019-03-30 08:42:48 +000017#include "llvm/Support/FileSystem.h"
18#include <cassert>
19#include <chrono>
20#include <string>
21#include <unordered_map>
22#include <vector>
23
24using namespace std::chrono;
25
26namespace llvm {
27
28TimeTraceProfiler *TimeTraceProfilerInstance = nullptr;
29
30static std::string escapeString(StringRef Src) {
31 std::string OS;
32 for (const unsigned char &C : Src) {
33 switch (C) {
34 case '"':
35 case '/':
36 case '\\':
37 case '\b':
38 case '\f':
39 case '\n':
40 case '\r':
41 case '\t':
42 OS += '\\';
43 OS += C;
44 break;
45 default:
46 if (isPrint(C)) {
47 OS += C;
48 }
49 }
50 }
51 return OS;
52}
53
54typedef duration<steady_clock::rep, steady_clock::period> DurationType;
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +000055typedef std::pair<size_t, DurationType> CountAndDurationType;
56typedef std::pair<std::string, CountAndDurationType>
57 NameAndCountAndDurationType;
Anton Afanasyevd880de22019-03-30 08:42:48 +000058
59struct Entry {
60 time_point<steady_clock> Start;
61 DurationType Duration;
62 std::string Name;
63 std::string Detail;
64};
65
66struct TimeTraceProfiler {
67 TimeTraceProfiler() {
68 Stack.reserve(8);
69 Entries.reserve(128);
70 StartTime = steady_clock::now();
71 }
72
73 void begin(std::string Name, llvm::function_ref<std::string()> Detail) {
74 Entry E = {steady_clock::now(), {}, Name, Detail()};
75 Stack.push_back(std::move(E));
76 }
77
78 void end() {
79 assert(!Stack.empty() && "Must call begin() first");
80 auto &E = Stack.back();
81 E.Duration = steady_clock::now() - E.Start;
82
83 // Only include sections longer than 500us.
84 if (duration_cast<microseconds>(E.Duration).count() > 500)
85 Entries.emplace_back(E);
86
87 // Track total time taken by each "name", but only the topmost levels of
88 // them; e.g. if there's a template instantiation that instantiates other
89 // templates from within, we only want to add the topmost one. "topmost"
90 // happens to be the ones that don't have any currently open entries above
91 // itself.
92 if (std::find_if(++Stack.rbegin(), Stack.rend(), [&](const Entry &Val) {
93 return Val.Name == E.Name;
94 }) == Stack.rend()) {
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +000095 auto &CountAndTotal = CountAndTotalPerName[E.Name];
96 CountAndTotal.first++;
97 CountAndTotal.second += E.Duration;
Anton Afanasyevd880de22019-03-30 08:42:48 +000098 }
99
100 Stack.pop_back();
101 }
102
103 void Write(std::unique_ptr<raw_pwrite_stream> &OS) {
104 assert(Stack.empty() &&
105 "All profiler sections should be ended when calling Write");
106
107 *OS << "{ \"traceEvents\": [\n";
108
109 // Emit all events for the main flame graph.
110 for (const auto &E : Entries) {
111 auto StartUs = duration_cast<microseconds>(E.Start - StartTime).count();
112 auto DurUs = duration_cast<microseconds>(E.Duration).count();
113 *OS << "{ \"pid\":1, \"tid\":0, \"ph\":\"X\", \"ts\":" << StartUs
114 << ", \"dur\":" << DurUs << ", \"name\":\"" << escapeString(E.Name)
115 << "\", \"args\":{ \"detail\":\"" << escapeString(E.Detail)
116 << "\"} },\n";
117 }
118
119 // Emit totals by section name as additional "thread" events, sorted from
120 // longest one.
121 int Tid = 1;
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +0000122 std::vector<NameAndCountAndDurationType> SortedTotals;
123 SortedTotals.reserve(CountAndTotalPerName.size());
124 for (const auto &E : CountAndTotalPerName) {
125 SortedTotals.emplace_back(E.getKey(), E.getValue());
Anton Afanasyevd880de22019-03-30 08:42:48 +0000126 }
127 std::sort(SortedTotals.begin(), SortedTotals.end(),
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +0000128 [](const NameAndCountAndDurationType &A,
129 const NameAndCountAndDurationType &B) {
130 return A.second.second > B.second.second;
Anton Afanasyevd880de22019-03-30 08:42:48 +0000131 });
132 for (const auto &E : SortedTotals) {
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +0000133 auto DurUs = duration_cast<microseconds>(E.second.second).count();
134 auto Count = CountAndTotalPerName[E.first].first;
Anton Afanasyevd880de22019-03-30 08:42:48 +0000135 *OS << "{ \"pid\":1, \"tid\":" << Tid << ", \"ph\":\"X\", \"ts\":" << 0
136 << ", \"dur\":" << DurUs << ", \"name\":\"Total "
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +0000137 << escapeString(E.first) << "\", \"args\":{ \"count\":" << Count
138 << ", \"avg ms\":" << (DurUs / Count / 1000) << "} },\n";
Anton Afanasyevd880de22019-03-30 08:42:48 +0000139 ++Tid;
140 }
141
142 // Emit metadata event with process name.
143 *OS << "{ \"cat\":\"\", \"pid\":1, \"tid\":0, \"ts\":0, \"ph\":\"M\", "
144 "\"name\":\"process_name\", \"args\":{ \"name\":\"clang\" } }\n";
145 *OS << "] }\n";
146 }
147
148 std::vector<Entry> Stack;
149 std::vector<Entry> Entries;
Anton Afanasyev03c3e0d2019-04-09 12:18:44 +0000150 StringMap<CountAndDurationType> CountAndTotalPerName;
Anton Afanasyevd880de22019-03-30 08:42:48 +0000151 time_point<steady_clock> StartTime;
152};
153
154void timeTraceProfilerInitialize() {
155 assert(TimeTraceProfilerInstance == nullptr &&
156 "Profiler should not be initialized");
157 TimeTraceProfilerInstance = new TimeTraceProfiler();
158}
159
160void timeTraceProfilerCleanup() {
161 delete TimeTraceProfilerInstance;
162 TimeTraceProfilerInstance = nullptr;
163}
164
165void timeTraceProfilerWrite(std::unique_ptr<raw_pwrite_stream> &OS) {
166 assert(TimeTraceProfilerInstance != nullptr &&
167 "Profiler object can't be null");
168 TimeTraceProfilerInstance->Write(OS);
169}
170
171void timeTraceProfilerBegin(StringRef Name, StringRef Detail) {
172 if (TimeTraceProfilerInstance != nullptr)
173 TimeTraceProfilerInstance->begin(Name, [&]() { return Detail; });
174}
175
176void timeTraceProfilerBegin(StringRef Name,
177 llvm::function_ref<std::string()> Detail) {
178 if (TimeTraceProfilerInstance != nullptr)
179 TimeTraceProfilerInstance->begin(Name, Detail);
180}
181
182void timeTraceProfilerEnd() {
183 if (TimeTraceProfilerInstance != nullptr)
184 TimeTraceProfilerInstance->end();
185}
186
187} // namespace llvm