File: | tools/clang/tools/extra/clangd/Trace.cpp |
Warning: | line 100, column 11 Value stored to 'OriginTime' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | using namespace llvm; |
23 | namespace clang { |
24 | namespace clangd { |
25 | namespace trace { |
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(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(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, 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; |
Value stored to 'OriginTime' is never read | |
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 | |
191 | Session::Session(EventTracer &Tracer) { |
192 | assert(!T && "Resetting global tracer is not allowed.")((!T && "Resetting global tracer is not allowed.") ? static_cast <void> (0) : __assert_fail ("!T && \"Resetting global tracer is not allowed.\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clangd/Trace.cpp" , 192, __PRETTY_FUNCTION__)); |
193 | T = &Tracer; |
194 | } |
195 | |
196 | Session::~Session() { T = nullptr; } |
197 | |
198 | std::unique_ptr<EventTracer> createJSONTracer(raw_ostream &OS, bool Pretty) { |
199 | return llvm::make_unique<JSONTracer>(OS, Pretty); |
200 | } |
201 | |
202 | void log(const Twine &Message) { |
203 | if (!T) |
204 | return; |
205 | T->instant("Log", json::Object{{"Message", Message.str()}}); |
206 | } |
207 | |
208 | // Returned context owns Args. |
209 | static Context makeSpanContext(Twine Name, json::Object *Args) { |
210 | if (!T) |
211 | return Context::current().clone(); |
212 | WithContextValue WithArgs{std::unique_ptr<json::Object>(Args)}; |
213 | return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef() |
214 | : StringRef(Name.str()), |
215 | Args); |
216 | } |
217 | |
218 | // Span keeps a non-owning pointer to the args, which is how users access them. |
219 | // The args are owned by the context though. They stick around until the |
220 | // beginSpan() context is destroyed, when the tracing engine will consume them. |
221 | Span::Span(Twine Name) |
222 | : Args(T ? new json::Object() : nullptr), |
223 | RestoreCtx(makeSpanContext(Name, Args)) {} |
224 | |
225 | Span::~Span() { |
226 | if (T) |
227 | T->endSpan(); |
228 | } |
229 | |
230 | } // namespace trace |
231 | } // namespace clangd |
232 | } // namespace clang |