clang-tools  7.0.0
Trace.cpp
Go to the documentation of this file.
1 //===--- 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"
11 #include "Context.h"
12 #include "Function.h"
13 #include "llvm/ADT/DenseSet.h"
14 #include "llvm/ADT/ScopeExit.h"
15 #include "llvm/Support/Chrono.h"
16 #include "llvm/Support/FormatProviders.h"
17 #include "llvm/Support/FormatVariadic.h"
18 #include "llvm/Support/Threading.h"
19 #include <atomic>
20 #include <mutex>
21 
22 namespace clang {
23 namespace clangd {
24 namespace trace {
25 using namespace llvm;
26 
27 namespace {
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.
30 class JSONTracer : public EventTracer {
31 public:
32  JSONTracer(raw_ostream &Out, bool Pretty)
33  : Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
34  JSONFormat(Pretty ? "{0:2}" : "{0}") {
35  // The displayTimeUnit must be ns to avoid low-precision overlap
36  // calculations!
37  Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
38  << "\n";
39  rawEvent("M", json::Object{
40  {"name", "process_name"},
41  {"args", json::Object{{"name", "clangd"}}},
42  });
43  }
44 
45  ~JSONTracer() {
46  Out << "\n]}";
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, json::Object *Args) override {
53  return Context::current().derive(
54  SpanKey, llvm::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, json::Object &&Args) override {
66  captureThreadMetadata();
67  jsonEvent("i", json::Object{{"name", Name}, {"args", std::move(Args)}});
68  }
69 
70  // 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.
72  void jsonEvent(StringRef Phase, json::Object &&Contents,
73  uint64_t TID = get_threadid(), double Timestamp = 0) {
74  Contents["ts"] = Timestamp ? Timestamp : timestamp();
75  Contents["tid"] = int64_t(TID);
76  std::lock_guard<std::mutex> Lock(Mu);
77  rawEvent(Phase, std::move(Contents));
78  }
79 
80 private:
81  class JSONSpan {
82  public:
83  JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, json::Object *Args)
84  : 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",
104  json::Object{{"id", FlowID},
105  {"name", "Context crosses threads"},
106  {"cat", "dummy"}},
107  (*Parent)->TID, (*Parent)->StartTime);
108  Tracer->jsonEvent("f",
109  json::Object{{"id", FlowID},
110  {"bp", "e"},
111  {"name", "Context crosses threads"},
112  {"cat", "dummy"}},
113  TID);
114  }
115  }
116 
117  ~JSONSpan() {
118  // Finally, record the event (ending at EndTime, not timestamp())!
119  Tracer->jsonEvent("X",
120  json::Object{{"name", std::move(Name)},
121  {"args", std::move(*Args)},
122  {"dur", EndTime - StartTime}},
123  TID, StartTime);
124  }
125 
126  // May be called by any thread.
127  void markEnded() {
128  EndTime = Tracer->timestamp();
129  }
130 
131  private:
132  static int64_t nextID() {
133  static std::atomic<int64_t> Next = {0};
134  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;
142  json::Object *Args;
143  };
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.
148  void rawEvent(StringRef Phase, json::Object &&Event) /*REQUIRES(Mu)*/ {
149  // PID 0 represents the clangd process.
150  Event["pid"] = 0;
151  Event["ph"] = Phase;
152  Out << Sep << formatv(JSONFormat, json::Value(std::move(Event)));
153  Sep = ",\n";
154  }
155 
156  // If we haven't already, emit metadata describing this thread.
157  void captureThreadMetadata() {
158  uint64_t TID = get_threadid();
159  std::lock_guard<std::mutex> Lock(Mu);
160  if (ThreadsWithMD.insert(TID).second) {
161  SmallString<32> Name;
162  get_thread_name(Name);
163  if (!Name.empty()) {
164  rawEvent("M", json::Object{
165  {"tid", int64_t(TID)},
166  {"name", "thread_name"},
167  {"args", json::Object{{"name", Name}}},
168  });
169  }
170  }
171  }
172 
173  double timestamp() {
174  using namespace std::chrono;
175  return duration<double, std::micro>(system_clock::now() - Start).count();
176  }
177 
178  std::mutex Mu;
179  raw_ostream &Out /*GUARDED_BY(Mu)*/;
180  const char *Sep /*GUARDED_BY(Mu)*/;
181  DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
182  const sys::TimePoint<> Start;
183  const char *JSONFormat;
184 };
185 
186 Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
187 
188 EventTracer *T = nullptr;
189 } // namespace
190 
192  assert(!T && "Resetting global tracer is not allowed.");
193  T = &Tracer;
194 }
195 
196 Session::~Session() { T = nullptr; }
197 
198 std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
199  bool Pretty) {
200  return llvm::make_unique<JSONTracer>(OS, Pretty);
201 }
202 
203 void log(const Twine &Message) {
204  if (!T)
205  return;
206  T->instant("Log", json::Object{{"Message", Message.str()}});
207 }
208 
209 // Returned context owns Args.
210 static Context makeSpanContext(llvm::Twine Name, json::Object *Args) {
211  if (!T)
212  return Context::current().clone();
213  WithContextValue WithArgs{std::unique_ptr<json::Object>(Args)};
214  return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
215  : llvm::StringRef(Name.str()),
216  Args);
217 }
218 
219 // Span keeps a non-owning pointer to the args, which is how users access them.
220 // The args are owned by the context though. They stick around until the
221 // beginSpan() context is destroyed, when the tracing engine will consume them.
222 Span::Span(llvm::Twine Name)
223  : Args(T ? new json::Object() : nullptr),
224  RestoreCtx(makeSpanContext(Name, Args)) {}
225 
227  if (T)
228  T->endSpan();
229 }
230 
231 } // namespace trace
232 } // namespace clangd
233 } // namespace clang
llvm::StringRef Name
Session(EventTracer &Tracer)
Definition: Trace.cpp:191
Some operations such as code completion produce a set of candidates.
Values in a Context are indexed by typed keys.
Definition: Context.h:41
StringRef Contents
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
Definition: Context.h:101
Context clone() const
Clone this context object.
Definition: Context.cpp:21
void log(const Twine &Message)
Definition: Trace.cpp:203
static const Context & current()
Returns the context for the current thread, creating it if needed.
Definition: Context.cpp:28
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:70
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:112
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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:122
Span(llvm::Twine Name)
Definition: Trace.cpp:222
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:198
static Context makeSpanContext(llvm::Twine Name, json::Object *Args)
Definition: Trace.cpp:210
WithContextValue extends Context::current() with a single value.
Definition: Context.h:205
A consumer of trace events.
Definition: Trace.h:33