blob: 4d73dabcf046794870cdb61040855d3730307369 [file] [log] [blame]
Sam McCall8567cb32017-11-02 09:21:51 +00001//===--- Trace.cpp - Performance tracing facilities -----------------------===//
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#include "Trace.h"
Sam McCall1b475a12018-01-26 09:00:30 +000011#include "Context.h"
12#include "Function.h"
Sam McCall8567cb32017-11-02 09:21:51 +000013#include "llvm/ADT/DenseSet.h"
Sam McCall1b475a12018-01-26 09:00:30 +000014#include "llvm/ADT/ScopeExit.h"
Sam McCall8567cb32017-11-02 09:21:51 +000015#include "llvm/Support/Chrono.h"
16#include "llvm/Support/FormatProviders.h"
17#include "llvm/Support/FormatVariadic.h"
18#include "llvm/Support/Threading.h"
Sam McCall7929d002018-02-15 08:40:54 +000019#include <atomic>
Sam McCall8567cb32017-11-02 09:21:51 +000020#include <mutex>
21
Sam McCallc008af62018-10-20 15:30:37 +000022using namespace llvm;
Sam McCall8567cb32017-11-02 09:21:51 +000023namespace clang {
24namespace clangd {
25namespace trace {
Sam McCall8567cb32017-11-02 09:21:51 +000026
27namespace {
28// The current implementation is naive: each thread writes to Out guarded by Mu.
29// Perhaps we should replace this by something that disturbs performance less.
Ilya Biryukovee27d2e2017-12-14 15:04:59 +000030class JSONTracer : public EventTracer {
Sam McCall8567cb32017-11-02 09:21:51 +000031public:
Ilya Biryukovee27d2e2017-12-14 15:04:59 +000032 JSONTracer(raw_ostream &Out, bool Pretty)
Sam McCall9cfd9c92017-11-23 17:12:04 +000033 : Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
34 JSONFormat(Pretty ? "{0:2}" : "{0}") {
Sam McCall8567cb32017-11-02 09:21:51 +000035 // The displayTimeUnit must be ns to avoid low-precision overlap
36 // calculations!
37 Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
38 << "\n";
Sam McCalld20d7982018-07-09 14:25:59 +000039 rawEvent("M", json::Object{
Sam McCall9cfd9c92017-11-23 17:12:04 +000040 {"name", "process_name"},
Sam McCalld20d7982018-07-09 14:25:59 +000041 {"args", json::Object{{"name", "clangd"}}},
Sam McCall9cfd9c92017-11-23 17:12:04 +000042 });
Sam McCall8567cb32017-11-02 09:21:51 +000043 }
44
Ilya Biryukovee27d2e2017-12-14 15:04:59 +000045 ~JSONTracer() {
Sam McCall8567cb32017-11-02 09:21:51 +000046 Out << "\n]}";
47 Out.flush();
48 }
49
Sam McCall7929d002018-02-15 08:40:54 +000050 // We stash a Span object in the context. It will record the start/end,
51 // and this also allows us to look up the parent Span's information.
Sam McCallc008af62018-10-20 15:30:37 +000052 Context beginSpan(StringRef Name, json::Object *Args) override {
Sam McCall892ea682018-02-15 14:16:17 +000053 return Context::current().derive(
54 SpanKey, llvm::make_unique<JSONSpan>(this, Name, Args));
Sam McCall7929d002018-02-15 08:40:54 +000055 }
56
57 // Trace viewer requires each thread to properly stack events.
58 // So we need to mark only duration that the span was active on the thread.
59 // (Hopefully any off-thread activity will be connected by a flow event).
60 // Record the end time here, but don't write the event: Args aren't ready yet.
61 void endSpan() override {
62 Context::current().getExisting(SpanKey)->markEnded();
Ilya Biryukov12cdb4f2017-12-14 15:33:38 +000063 }
64
Sam McCallc008af62018-10-20 15:30:37 +000065 void instant(StringRef Name, json::Object &&Args) override {
Sam McCall7929d002018-02-15 08:40:54 +000066 captureThreadMetadata();
Sam McCalld20d7982018-07-09 14:25:59 +000067 jsonEvent("i", json::Object{{"name", Name}, {"args", std::move(Args)}});
Ilya Biryukov12cdb4f2017-12-14 15:33:38 +000068 }
69
Sam McCall8567cb32017-11-02 09:21:51 +000070 // Record an event on the current thread. ph, pid, tid, ts are set.
71 // Contents must be a list of the other JSON key/values.
Sam McCalld20d7982018-07-09 14:25:59 +000072 void jsonEvent(StringRef Phase, json::Object &&Contents,
73 uint64_t TID = get_threadid(), double Timestamp = 0) {
Sam McCall7929d002018-02-15 08:40:54 +000074 Contents["ts"] = Timestamp ? Timestamp : timestamp();
Sam McCalld20d7982018-07-09 14:25:59 +000075 Contents["tid"] = int64_t(TID);
Sam McCall7929d002018-02-15 08:40:54 +000076 std::lock_guard<std::mutex> Lock(Mu);
77 rawEvent(Phase, std::move(Contents));
78 }
79
80private:
81 class JSONSpan {
82 public:
Sam McCallc008af62018-10-20 15:30:37 +000083 JSONSpan(JSONTracer *Tracer, StringRef Name, json::Object *Args)
Sam McCall7929d002018-02-15 08:40:54 +000084 : StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
85 TID(get_threadid()), Tracer(Tracer), Args(Args) {
86 // ~JSONSpan() may run in a different thread, so we need to capture now.
87 Tracer->captureThreadMetadata();
88
89 // We don't record begin events here (and end events in the destructor)
90 // because B/E pairs have to appear in the right order, which is awkward.
91 // Instead we send the complete (X) event in the destructor.
92
93 // If our parent was on a different thread, add an arrow to this span.
94 auto *Parent = Context::current().get(SpanKey);
95 if (Parent && *Parent && (*Parent)->TID != TID) {
96 // If the parent span ended already, then show this as "following" it.
97 // Otherwise show us as "parallel".
98 double OriginTime = (*Parent)->EndTime;
99 if (!OriginTime)
100 OriginTime = (*Parent)->StartTime;
101
102 auto FlowID = nextID();
103 Tracer->jsonEvent("s",
Sam McCalld20d7982018-07-09 14:25:59 +0000104 json::Object{{"id", FlowID},
105 {"name", "Context crosses threads"},
106 {"cat", "dummy"}},
Sam McCall7929d002018-02-15 08:40:54 +0000107 (*Parent)->TID, (*Parent)->StartTime);
108 Tracer->jsonEvent("f",
Sam McCalld20d7982018-07-09 14:25:59 +0000109 json::Object{{"id", FlowID},
110 {"bp", "e"},
111 {"name", "Context crosses threads"},
112 {"cat", "dummy"}},
Sam McCall7929d002018-02-15 08:40:54 +0000113 TID);
114 }
115 }
116
117 ~JSONSpan() {
118 // Finally, record the event (ending at EndTime, not timestamp())!
119 Tracer->jsonEvent("X",
Sam McCalld20d7982018-07-09 14:25:59 +0000120 json::Object{{"name", std::move(Name)},
121 {"args", std::move(*Args)},
122 {"dur", EndTime - StartTime}},
Sam McCall7929d002018-02-15 08:40:54 +0000123 TID, StartTime);
124 }
125
126 // May be called by any thread.
Ilya Biryukov22fa4652019-01-03 13:28:05 +0000127 void markEnded() { EndTime = Tracer->timestamp(); }
Sam McCall7929d002018-02-15 08:40:54 +0000128
129 private:
Sam McCalld20d7982018-07-09 14:25:59 +0000130 static int64_t nextID() {
131 static std::atomic<int64_t> Next = {0};
Sam McCall7929d002018-02-15 08:40:54 +0000132 return Next++;
133 }
134
135 double StartTime;
136 std::atomic<double> EndTime; // Filled in by markEnded().
137 std::string Name;
138 uint64_t TID;
139 JSONTracer *Tracer;
Sam McCalld20d7982018-07-09 14:25:59 +0000140 json::Object *Args;
Sam McCall7929d002018-02-15 08:40:54 +0000141 };
142 static Key<std::unique_ptr<JSONSpan>> SpanKey;
143
144 // Record an event. ph and pid are set.
145 // Contents must be a list of the other JSON key/values.
Sam McCalld20d7982018-07-09 14:25:59 +0000146 void rawEvent(StringRef Phase, json::Object &&Event) /*REQUIRES(Mu)*/ {
Sam McCall7929d002018-02-15 08:40:54 +0000147 // PID 0 represents the clangd process.
148 Event["pid"] = 0;
149 Event["ph"] = Phase;
Sam McCalld20d7982018-07-09 14:25:59 +0000150 Out << Sep << formatv(JSONFormat, json::Value(std::move(Event)));
Sam McCall7929d002018-02-15 08:40:54 +0000151 Sep = ",\n";
152 }
153
154 // If we haven't already, emit metadata describing this thread.
155 void captureThreadMetadata() {
Sam McCall8567cb32017-11-02 09:21:51 +0000156 uint64_t TID = get_threadid();
157 std::lock_guard<std::mutex> Lock(Mu);
Sam McCall8567cb32017-11-02 09:21:51 +0000158 if (ThreadsWithMD.insert(TID).second) {
159 SmallString<32> Name;
160 get_thread_name(Name);
161 if (!Name.empty()) {
Sam McCalld20d7982018-07-09 14:25:59 +0000162 rawEvent("M", json::Object{
163 {"tid", int64_t(TID)},
Sam McCall9cfd9c92017-11-23 17:12:04 +0000164 {"name", "thread_name"},
Sam McCalld20d7982018-07-09 14:25:59 +0000165 {"args", json::Object{{"name", Name}}},
Sam McCall9cfd9c92017-11-23 17:12:04 +0000166 });
Sam McCall8567cb32017-11-02 09:21:51 +0000167 }
168 }
Sam McCall8567cb32017-11-02 09:21:51 +0000169 }
170
171 double timestamp() {
172 using namespace std::chrono;
Sam McCall3d9e0242017-11-15 17:53:46 +0000173 return duration<double, std::micro>(system_clock::now() - Start).count();
Sam McCall8567cb32017-11-02 09:21:51 +0000174 }
175
176 std::mutex Mu;
177 raw_ostream &Out /*GUARDED_BY(Mu)*/;
178 const char *Sep /*GUARDED_BY(Mu)*/;
179 DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
180 const sys::TimePoint<> Start;
Sam McCall9cfd9c92017-11-23 17:12:04 +0000181 const char *JSONFormat;
Sam McCall8567cb32017-11-02 09:21:51 +0000182};
183
Sam McCall7929d002018-02-15 08:40:54 +0000184Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
185
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000186EventTracer *T = nullptr;
Sam McCall8567cb32017-11-02 09:21:51 +0000187} // namespace
188
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000189Session::Session(EventTracer &Tracer) {
190 assert(!T && "Resetting global tracer is not allowed.");
191 T = &Tracer;
Sam McCall8567cb32017-11-02 09:21:51 +0000192}
193
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000194Session::~Session() { T = nullptr; }
195
Sam McCallc008af62018-10-20 15:30:37 +0000196std::unique_ptr<EventTracer> createJSONTracer(raw_ostream &OS, bool Pretty) {
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000197 return llvm::make_unique<JSONTracer>(OS, Pretty);
Sam McCall8567cb32017-11-02 09:21:51 +0000198}
199
Sam McCalld1a7a372018-01-31 13:40:48 +0000200void log(const Twine &Message) {
Sam McCall8567cb32017-11-02 09:21:51 +0000201 if (!T)
202 return;
Sam McCalld20d7982018-07-09 14:25:59 +0000203 T->instant("Log", json::Object{{"Message", Message.str()}});
Sam McCalld1a7a372018-01-31 13:40:48 +0000204}
205
206// Returned context owns Args.
Sam McCallc008af62018-10-20 15:30:37 +0000207static Context makeSpanContext(Twine Name, json::Object *Args) {
Sam McCalld1a7a372018-01-31 13:40:48 +0000208 if (!T)
209 return Context::current().clone();
Sam McCalld20d7982018-07-09 14:25:59 +0000210 WithContextValue WithArgs{std::unique_ptr<json::Object>(Args)};
Sam McCallc901c5d2018-02-19 09:56:28 +0000211 return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
Sam McCallc008af62018-10-20 15:30:37 +0000212 : StringRef(Name.str()),
Sam McCallc901c5d2018-02-19 09:56:28 +0000213 Args);
Sam McCall8567cb32017-11-02 09:21:51 +0000214}
215
Sam McCall1b475a12018-01-26 09:00:30 +0000216// Span keeps a non-owning pointer to the args, which is how users access them.
217// The args are owned by the context though. They stick around until the
218// beginSpan() context is destroyed, when the tracing engine will consume them.
Sam McCallc008af62018-10-20 15:30:37 +0000219Span::Span(Twine Name)
Sam McCalld20d7982018-07-09 14:25:59 +0000220 : Args(T ? new json::Object() : nullptr),
Sam McCalld1a7a372018-01-31 13:40:48 +0000221 RestoreCtx(makeSpanContext(Name, Args)) {}
Sam McCall8567cb32017-11-02 09:21:51 +0000222
Sam McCall7929d002018-02-15 08:40:54 +0000223Span::~Span() {
224 if (T)
225 T->endSpan();
226}
227
Sam McCall8567cb32017-11-02 09:21:51 +0000228} // namespace trace
229} // namespace clangd
230} // namespace clang