clang-tools  8.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 
26 namespace {
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.
29 class JSONTracer : public EventTracer {
30 public:
31  JSONTracer(llvm::raw_ostream &Out, bool Pretty)
32  : Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
33  JSONFormat(Pretty ? "{0:2}" : "{0}") {
34  // The displayTimeUnit must be ns to avoid low-precision overlap
35  // calculations!
36  Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
37  << "\n";
38  rawEvent("M", llvm::json::Object{
39  {"name", "process_name"},
40  {"args", llvm::json::Object{{"name", "clangd"}}},
41  });
42  }
43 
44  ~JSONTracer() {
45  Out << "\n]}";
46  Out.flush();
47  }
48 
49  // 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.
51  Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) override {
52  return Context::current().derive(
53  SpanKey, llvm::make_unique<JSONSpan>(this, Name, Args));
54  }
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();
62  }
63 
64  void instant(llvm::StringRef Name, llvm::json::Object &&Args) override {
65  captureThreadMetadata();
66  jsonEvent("i",
67  llvm::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(llvm::StringRef Phase, llvm::json::Object &&Contents,
73  uint64_t TID = llvm::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, llvm::json::Object *Args)
84  : StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
85  TID(llvm::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(
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);
116  }
117  }
118 
119  ~JSONSpan() {
120  // Finally, record the event (ending at EndTime, not timestamp())!
121  Tracer->jsonEvent("X",
122  llvm::json::Object{{"name", std::move(Name)},
123  {"args", std::move(*Args)},
124  {"dur", EndTime - StartTime}},
125  TID, StartTime);
126  }
127 
128  // May be called by any thread.
129  void markEnded() { EndTime = Tracer->timestamp(); }
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  llvm::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(llvm::StringRef Phase,
149  llvm::json::Object &&Event) /*REQUIRES(Mu)*/ {
150  // PID 0 represents the clangd process.
151  Event["pid"] = 0;
152  Event["ph"] = Phase;
153  Out << Sep
154  << llvm::formatv(JSONFormat, llvm::json::Value(std::move(Event)));
155  Sep = ",\n";
156  }
157 
158  // If we haven't already, emit metadata describing this thread.
159  void captureThreadMetadata() {
160  uint64_t TID = llvm::get_threadid();
161  std::lock_guard<std::mutex> Lock(Mu);
162  if (ThreadsWithMD.insert(TID).second) {
163  llvm::SmallString<32> Name;
164  llvm::get_thread_name(Name);
165  if (!Name.empty()) {
166  rawEvent("M", llvm::json::Object{
167  {"tid", int64_t(TID)},
168  {"name", "thread_name"},
169  {"args", llvm::json::Object{{"name", Name}}},
170  });
171  }
172  }
173  }
174 
175  double timestamp() {
176  using namespace std::chrono;
177  return duration<double, std::micro>(system_clock::now() - Start).count();
178  }
179 
180  std::mutex Mu;
181  llvm::raw_ostream &Out /*GUARDED_BY(Mu)*/;
182  const char *Sep /*GUARDED_BY(Mu)*/;
183  llvm::DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
184  const llvm::sys::TimePoint<> Start;
185  const char *JSONFormat;
186 };
187 
188 Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
189 
190 EventTracer *T = nullptr;
191 } // namespace
192 
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 llvm::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 
229  if (T)
230  T->endSpan();
231 }
232 
233 } // namespace trace
234 } // namespace clangd
235 } // namespace clang
llvm::StringRef Name
Session(EventTracer &Tracer)
Definition: Trace.cpp:193
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:88
Values in a Context are indexed by typed keys.
Definition: Context.h:41
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
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++ -*-===//
void log(const llvm::Twine &Message)
Records a single instant event, associated with the current thread.
Definition: Trace.cpp:205
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:224
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
WithContextValue extends Context::current() with a single value.
Definition: Context.h:205
static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args)
Definition: Trace.cpp:212
A consumer of trace events.
Definition: Trace.h:33