clang-tools 10.0.0

Trace.cpp
Go to the documentation of this file.
1 //===--- Trace.cpp - Performance tracing facilities -----------------------===//
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 #include "Trace.h"
10 #include "Context.h"
11 #include "llvm/ADT/DenseSet.h"
12 #include "llvm/ADT/ScopeExit.h"
13 #include "llvm/Support/Chrono.h"
14 #include "llvm/Support/FormatProviders.h"
15 #include "llvm/Support/FormatVariadic.h"
16 #include "llvm/Support/Threading.h"
17 #include <atomic>
18 #include <mutex>
19
20 namespace clang {
21 namespace clangd {
22 namespace trace {
23
24 namespace {
25 // The current implementation is naive: each thread writes to Out guarded by Mu.
26 // Perhaps we should replace this by something that disturbs performance less.
27 class JSONTracer : public EventTracer {
28 public:
29 JSONTracer(llvm::raw_ostream &OS, bool Pretty)
30 : Out(OS, Pretty ? 2 : 0), Start(std::chrono::system_clock::now()) {
31 // The displayTimeUnit must be ns to avoid low-precision overlap
32 // calculations!
33 Out.objectBegin();
34 Out.attribute("displayTimeUnit", "ns");
35 Out.attributeBegin("traceEvents");
36 Out.arrayBegin();
37 rawEvent("M", llvm::json::Object{
38 {"name", "process_name"},
39 {"args", llvm::json::Object{{"name", "clangd"}}},
40 });
41 }
42
43 ~JSONTracer() {
44 Out.arrayEnd();
45 Out.attributeEnd();
46 Out.objectEnd();
47 Out.flush();
48 }
49
50 // 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.
52 Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) override {
53 return Context::current().derive(
54 SpanKey, std::make_unique<JSONSpan>(this, Name, Args));
55 }
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();
63 }
64
65 void instant(llvm::StringRef Name, llvm::json::Object &&Args) override {
66 captureThreadMetadata();
67 jsonEvent("i",
68 llvm::json::Object{{"name", Name}, {"args", std::move(Args)}});
69 }
70
71 // Record an event on the current thread. ph, pid, tid, ts are set.
72 // Contents must be a list of the other JSON key/values.
73 void jsonEvent(llvm::StringRef Phase, llvm::json::Object &&Contents,
74 uint64_t TID = llvm::get_threadid(), double Timestamp = 0) {
75 Contents["ts"] = Timestamp ? Timestamp : timestamp();
76 Contents["tid"] = int64_t(TID);
77 std::lock_guard<std::mutex> Lock(Mu);
78 rawEvent(Phase, Contents);
79 }
80
81 private:
82 class JSONSpan {
83 public:
84 JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, llvm::json::Object *Args)
85 : StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
86 TID(llvm::get_threadid()), Tracer(Tracer), Args(Args) {
87 // ~JSONSpan() may run in a different thread, so we need to capture now.
88 Tracer->captureThreadMetadata();
89
90 // We don't record begin events here (and end events in the destructor)
91 // because B/E pairs have to appear in the right order, which is awkward.
92 // Instead we send the complete (X) event in the destructor.
93
94 // If our parent was on a different thread, add an arrow to this span.
95 auto *Parent = Context::current().get(SpanKey);
96 if (Parent && *Parent && (*Parent)->TID != TID) {
97 // If the parent span ended already, then show this as "following" it.
98 // Otherwise show us as "parallel".
99 double OriginTime = (*Parent)->EndTime;
100 if (!OriginTime)
101 OriginTime = (*Parent)->StartTime;
102
103 auto FlowID = nextID();
104 Tracer->jsonEvent(
105 "s",
106 llvm::json::Object{{"id", FlowID},
107 {"name", "Context crosses threads"},
108 {"cat", "dummy"}},
109 (*Parent)->TID, (*Parent)->StartTime);
110 Tracer->jsonEvent(
111 "f",
112 llvm::json::Object{{"id", FlowID},
113 {"bp", "e"},
114 {"name", "Context crosses threads"},
115 {"cat", "dummy"}},
116 TID);
117 }
118 }
119
120 ~JSONSpan() {
121 // Finally, record the event (ending at EndTime, not timestamp())!
122 Tracer->jsonEvent("X",
123 llvm::json::Object{{"name", std::move(Name)},
124 {"args", std::move(*Args)},
125 {"dur", EndTime - StartTime}},
126 TID, StartTime);
127 }
128
129 // May be called by any thread.
130 void markEnded() { EndTime = Tracer->timestamp(); }
131
132 private:
133 static int64_t nextID() {
134 static std::atomic<int64_t> Next = {0};
135 return Next++;
136 }
137
138 double StartTime;
139 std::atomic<double> EndTime; // Filled in by markEnded().
140 std::string Name;
141 uint64_t TID;
142 JSONTracer *Tracer;
143 llvm::json::Object *Args;
144 };
145 static Key<std::unique_ptr<JSONSpan>> SpanKey;
146
147 // Record an event. ph and pid are set.
148 // Contents must be a list of the other JSON key/values.
149 void rawEvent(llvm::StringRef Phase,
150 const llvm::json::Object &Event) /*REQUIRES(Mu)*/ {
151 // PID 0 represents the clangd process.
152 Out.object([&]{
153 Out.attribute("pid", 0);
154 Out.attribute("ph", Phase);
155 for (const auto& KV : Event)
156 Out.attribute(KV.first, KV.second);
157 });
158 }
159
160 // If we haven't already, emit metadata describing this thread.
161 void captureThreadMetadata() {
162 uint64_t TID = llvm::get_threadid();
163 std::lock_guard<std::mutex> Lock(Mu);
164 if (ThreadsWithMD.insert(TID).second) {
165 llvm::SmallString<32> Name;
166 llvm::get_thread_name(Name);
167 if (!Name.empty()) {
168 rawEvent("M", llvm::json::Object{
169 {"tid", int64_t(TID)},
170 {"name", "thread_name"},
171 {"args", llvm::json::Object{{"name", Name}}},
172 });
173 }
174 }
175 }
176
177 double timestamp() {
178 using namespace std::chrono;
179 return duration<double, std::micro>(system_clock::now() - Start).count();
180 }
181
182 std::mutex Mu;
183 llvm::json::OStream Out /*GUARDED_BY(Mu)*/;
184 llvm::DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
185 const llvm::sys::TimePoint<> Start;
186 };
187
188 Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
189
190 EventTracer *T = nullptr;
191 } // namespace
192
193 Session::Session(EventTracer &Tracer) {
194 assert(!T && "Resetting global tracer is not allowed.");
195 T = &Tracer;
196 }
197
198 Session::~Session() { T = nullptr; }
199
200 std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
201 bool Pretty) {
202 return std::make_unique<JSONTracer>(OS, Pretty);
203 }
204
205 void log(const llvm::Twine &Message) {
206 if (!T)
207 return;
208 T->instant("Log", llvm::json::Object{{"Message", Message.str()}});
209 }
210
211 // Returned context owns Args.
212 static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args) {
213 if (!T)
214 return Context::current().clone();
215 WithContextValue WithArgs{std::unique_ptr<llvm::json::Object>(Args)};
216 return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
217 : llvm::StringRef(Name.str()),
218 Args);
219 }
220
221 // 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.
224 Span::Span(llvm::Twine Name)
225 : Args(T ? new llvm::json::Object() : nullptr),
226 RestoreCtx(makeSpanContext(Name, Args)) {}
227
228 Span::~Span() {
229 if (T)
230 T->endSpan();
231 }
232
233 } // namespace trace
234 } // namespace clangd
235 } // namespace clang
Name
llvm::StringRef Name
Definition: CodeComplete.cpp:156
clang::clangd::trace::Session::Session
Session(EventTracer &Tracer)
Definition: Trace.cpp:193
Trace.h
Contents
llvm::StringRef Contents
Definition: CodeComplete.cpp:1021
llvm
Some operations such as code completion produce a set of candidates.
Definition: YAMLGenerator.cpp:28
Parent
const Node * Parent
Definition: ExtractFunction.cpp:148
clang::clangd::Event
An Event<T> allows events of type T to be broadcast to listeners.
Definition: Function.h:31
clang::tidy::modernize::Message
constexpr llvm::StringLiteral Message
Definition: UseTrailingReturnTypeCheck.cpp:101
clang::clangd::Key
Values in a Context are indexed by typed keys.
Definition: Context.h:40
Context.h
clang::clangd::SymbolKind::Object
clang::clangd::Context::get
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
Definition: Context.h:100
clang::clangd::Context::clone
Context clone() const
Clone this context object.
Definition: Context.cpp:20
clang::clangd::Context::current
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:27
clang::clangd::trace::Span::~Span
~Span()
Definition: Trace.cpp:228
clang::clangd::Context
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
clang::clangd::Context::getExisting
const Type & getExisting(const Key< Type > &Key) const
A helper to get a reference to a Key that must exist in the map.
Definition: Context.h:111
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::trace::log
void log(const llvm::Twine &Message)
Records a single instant event, associated with the current thread.
Definition: Trace.cpp:205
clang::clangd::Context::derive
Context derive(const Key< Type > &Key, typename std::decay< Type >::type Value) const &
Derives a child context It is safe to move or destroy a parent context after calling derive()...
Definition: Context.h:121
clang::clangd::trace::Span::Span
Span(llvm::Twine Name)
Definition: Trace.cpp:224
clang::clangd::trace::createJSONTracer
std::unique_ptr< EventTracer > createJSONTracer(llvm::raw_ostream &OS, bool Pretty)
Create an instance of EventTracer that produces an output in the Trace Event format supported by Chro...
Definition: Trace.cpp:200
clang::clangd::WithContextValue
WithContextValue extends Context::current() with a single value.
Definition: Context.h:204
clang::clangd::trace::makeSpanContext
static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args)
Definition: Trace.cpp:212
clang::clangd::trace::EventTracer
A consumer of trace events.
Definition: Trace.h:31
clang::clangd::trace::Session::~Session
~Session()
Definition: Trace.cpp:198