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