blob: e44d3a138b3a12359514c4bf5d611a90d7f573d1 [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
22namespace clang {
23namespace clangd {
24namespace trace {
Sam McCall8567cb32017-11-02 09:21:51 +000025
26namespace {
27// The current implementation is naive: each thread writes to Out guarded by Mu.
28// Perhaps we should replace this by something that disturbs performance less.
Ilya Biryukovee27d2e2017-12-14 15:04:59 +000029class JSONTracer : public EventTracer {
Sam McCall8567cb32017-11-02 09:21:51 +000030public:
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000031 JSONTracer(llvm::raw_ostream &Out, bool Pretty)
Sam McCall9cfd9c92017-11-23 17:12:04 +000032 : Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
33 JSONFormat(Pretty ? "{0:2}" : "{0}") {
Sam McCall8567cb32017-11-02 09:21:51 +000034 // The displayTimeUnit must be ns to avoid low-precision overlap
35 // calculations!
36 Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
37 << "\n";
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000038 rawEvent("M", llvm::json::Object{
Sam McCall9cfd9c92017-11-23 17:12:04 +000039 {"name", "process_name"},
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000040 {"args", llvm::json::Object{{"name", "clangd"}}},
Sam McCall9cfd9c92017-11-23 17:12:04 +000041 });
Sam McCall8567cb32017-11-02 09:21:51 +000042 }
43
Ilya Biryukovee27d2e2017-12-14 15:04:59 +000044 ~JSONTracer() {
Sam McCall8567cb32017-11-02 09:21:51 +000045 Out << "\n]}";
46 Out.flush();
47 }
48
Sam McCall7929d002018-02-15 08:40:54 +000049 // We stash a Span object in the context. It will record the start/end,
50 // and this also allows us to look up the parent Span's information.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000051 Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) override {
Sam McCall892ea682018-02-15 14:16:17 +000052 return Context::current().derive(
53 SpanKey, llvm::make_unique<JSONSpan>(this, Name, Args));
Sam McCall7929d002018-02-15 08:40:54 +000054 }
55
56 // Trace viewer requires each thread to properly stack events.
57 // So we need to mark only duration that the span was active on the thread.
58 // (Hopefully any off-thread activity will be connected by a flow event).
59 // Record the end time here, but don't write the event: Args aren't ready yet.
60 void endSpan() override {
61 Context::current().getExisting(SpanKey)->markEnded();
Ilya Biryukov12cdb4f2017-12-14 15:33:38 +000062 }
63
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000064 void instant(llvm::StringRef Name, llvm::json::Object &&Args) override {
Sam McCall7929d002018-02-15 08:40:54 +000065 captureThreadMetadata();
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000066 jsonEvent("i",
67 llvm::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.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000072 void jsonEvent(llvm::StringRef Phase, llvm::json::Object &&Contents,
73 uint64_t TID = llvm::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:
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000083 JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, llvm::json::Object *Args)
Sam McCall7929d002018-02-15 08:40:54 +000084 : StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
Ilya Biryukovf2001aa2019-01-07 15:45:19 +000085 TID(llvm::get_threadid()), Tracer(Tracer), Args(Args) {
Sam McCall7929d002018-02-15 08:40:54 +000086 // ~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();
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000103 Tracer->jsonEvent(
104 "s",
105 llvm::json::Object{{"id", FlowID},
106 {"name", "Context crosses threads"},
107 {"cat", "dummy"}},
108 (*Parent)->TID, (*Parent)->StartTime);
109 Tracer->jsonEvent(
110 "f",
111 llvm::json::Object{{"id", FlowID},
112 {"bp", "e"},
113 {"name", "Context crosses threads"},
114 {"cat", "dummy"}},
115 TID);
Sam McCall7929d002018-02-15 08:40:54 +0000116 }
117 }
118
119 ~JSONSpan() {
120 // Finally, record the event (ending at EndTime, not timestamp())!
121 Tracer->jsonEvent("X",
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000122 llvm::json::Object{{"name", std::move(Name)},
123 {"args", std::move(*Args)},
124 {"dur", EndTime - StartTime}},
Sam McCall7929d002018-02-15 08:40:54 +0000125 TID, StartTime);
126 }
127
128 // May be called by any thread.
Ilya Biryukov22fa4652019-01-03 13:28:05 +0000129 void markEnded() { EndTime = Tracer->timestamp(); }
Sam McCall7929d002018-02-15 08:40:54 +0000130
131 private:
Sam McCalld20d7982018-07-09 14:25:59 +0000132 static int64_t nextID() {
133 static std::atomic<int64_t> Next = {0};
Sam McCall7929d002018-02-15 08:40:54 +0000134 return Next++;
135 }
136
137 double StartTime;
138 std::atomic<double> EndTime; // Filled in by markEnded().
139 std::string Name;
140 uint64_t TID;
141 JSONTracer *Tracer;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000142 llvm::json::Object *Args;
Sam McCall7929d002018-02-15 08:40:54 +0000143 };
144 static Key<std::unique_ptr<JSONSpan>> SpanKey;
145
146 // Record an event. ph and pid are set.
147 // Contents must be a list of the other JSON key/values.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000148 void rawEvent(llvm::StringRef Phase,
149 llvm::json::Object &&Event) /*REQUIRES(Mu)*/ {
Sam McCall7929d002018-02-15 08:40:54 +0000150 // PID 0 represents the clangd process.
151 Event["pid"] = 0;
152 Event["ph"] = Phase;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000153 Out << Sep
154 << llvm::formatv(JSONFormat, llvm::json::Value(std::move(Event)));
Sam McCall7929d002018-02-15 08:40:54 +0000155 Sep = ",\n";
156 }
157
158 // If we haven't already, emit metadata describing this thread.
159 void captureThreadMetadata() {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000160 uint64_t TID = llvm::get_threadid();
Sam McCall8567cb32017-11-02 09:21:51 +0000161 std::lock_guard<std::mutex> Lock(Mu);
Sam McCall8567cb32017-11-02 09:21:51 +0000162 if (ThreadsWithMD.insert(TID).second) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000163 llvm::SmallString<32> Name;
164 llvm::get_thread_name(Name);
Sam McCall8567cb32017-11-02 09:21:51 +0000165 if (!Name.empty()) {
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000166 rawEvent("M", llvm::json::Object{
Sam McCalld20d7982018-07-09 14:25:59 +0000167 {"tid", int64_t(TID)},
Sam McCall9cfd9c92017-11-23 17:12:04 +0000168 {"name", "thread_name"},
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000169 {"args", llvm::json::Object{{"name", Name}}},
Sam McCall9cfd9c92017-11-23 17:12:04 +0000170 });
Sam McCall8567cb32017-11-02 09:21:51 +0000171 }
172 }
Sam McCall8567cb32017-11-02 09:21:51 +0000173 }
174
175 double timestamp() {
176 using namespace std::chrono;
Sam McCall3d9e0242017-11-15 17:53:46 +0000177 return duration<double, std::micro>(system_clock::now() - Start).count();
Sam McCall8567cb32017-11-02 09:21:51 +0000178 }
179
180 std::mutex Mu;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000181 llvm::raw_ostream &Out /*GUARDED_BY(Mu)*/;
Sam McCall8567cb32017-11-02 09:21:51 +0000182 const char *Sep /*GUARDED_BY(Mu)*/;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000183 llvm::DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
184 const llvm::sys::TimePoint<> Start;
Sam McCall9cfd9c92017-11-23 17:12:04 +0000185 const char *JSONFormat;
Sam McCall8567cb32017-11-02 09:21:51 +0000186};
187
Sam McCall7929d002018-02-15 08:40:54 +0000188Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
189
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000190EventTracer *T = nullptr;
Sam McCall8567cb32017-11-02 09:21:51 +0000191} // namespace
192
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000193Session::Session(EventTracer &Tracer) {
194 assert(!T && "Resetting global tracer is not allowed.");
195 T = &Tracer;
Sam McCall8567cb32017-11-02 09:21:51 +0000196}
197
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000198Session::~Session() { T = nullptr; }
199
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000200std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
201 bool Pretty) {
Ilya Biryukovee27d2e2017-12-14 15:04:59 +0000202 return llvm::make_unique<JSONTracer>(OS, Pretty);
Sam McCall8567cb32017-11-02 09:21:51 +0000203}
204
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000205void log(const llvm::Twine &Message) {
Sam McCall8567cb32017-11-02 09:21:51 +0000206 if (!T)
207 return;
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000208 T->instant("Log", llvm::json::Object{{"Message", Message.str()}});
Sam McCalld1a7a372018-01-31 13:40:48 +0000209}
210
211// Returned context owns Args.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000212static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args) {
Sam McCalld1a7a372018-01-31 13:40:48 +0000213 if (!T)
214 return Context::current().clone();
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000215 WithContextValue WithArgs{std::unique_ptr<llvm::json::Object>(Args)};
Sam McCallc901c5d2018-02-19 09:56:28 +0000216 return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000217 : llvm::StringRef(Name.str()),
Sam McCallc901c5d2018-02-19 09:56:28 +0000218 Args);
Sam McCall8567cb32017-11-02 09:21:51 +0000219}
220
Sam McCall1b475a12018-01-26 09:00:30 +0000221// Span keeps a non-owning pointer to the args, which is how users access them.
222// The args are owned by the context though. They stick around until the
223// beginSpan() context is destroyed, when the tracing engine will consume them.
Ilya Biryukovf2001aa2019-01-07 15:45:19 +0000224Span::Span(llvm::Twine Name)
225 : Args(T ? new llvm::json::Object() : nullptr),
Sam McCalld1a7a372018-01-31 13:40:48 +0000226 RestoreCtx(makeSpanContext(Name, Args)) {}
Sam McCall8567cb32017-11-02 09:21:51 +0000227
Sam McCall7929d002018-02-15 08:40:54 +0000228Span::~Span() {
229 if (T)
230 T->endSpan();
231}
232
Sam McCall8567cb32017-11-02 09:21:51 +0000233} // namespace trace
234} // namespace clangd
235} // namespace clang