Bug Summary

File:clang-tools-extra/clangd/support/Trace.cpp
Warning:line 108, column 11
Value stored to 'OriginTime' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Trace.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-14/lib/clang/14.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/clang/tools/extra/clangd/support -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang-tools-extra/clangd/support -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang/include -I tools/clang/include -I include -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/llvm/include -I /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang-tools-extra/clangd -I tools/clang/tools/extra/clangd -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -O3 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -std=c++14 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-01-25-232935-20746-1 -x c++ /build/llvm-toolchain-snapshot-14~++20220125101009+ceec4383681c/clang-tools-extra/clangd/support/Trace.cpp
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 "support/Trace.h"
10#include "support/Context.h"
11#include "llvm/ADT/DenseSet.h"
12#include "llvm/ADT/Optional.h"
13#include "llvm/ADT/ScopeExit.h"
14#include "llvm/ADT/StringRef.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 <chrono>
21#include <memory>
22#include <mutex>
23
24namespace clang {
25namespace clangd {
26namespace trace {
27
28namespace {
29// The current implementation is naive: each thread writes to Out guarded by Mu.
30// Perhaps we should replace this by something that disturbs performance less.
31class JSONTracer : public EventTracer {
32public:
33 JSONTracer(llvm::raw_ostream &OS, bool Pretty)
34 : Out(OS, Pretty ? 2 : 0), Start(std::chrono::system_clock::now()) {
35 // The displayTimeUnit must be ns to avoid low-precision overlap
36 // calculations!
37 Out.objectBegin();
38 Out.attribute("displayTimeUnit", "ns");
39 Out.attributeBegin("traceEvents");
40 Out.arrayBegin();
41 rawEvent("M", llvm::json::Object{
42 {"name", "process_name"},
43 {"args", llvm::json::Object{{"name", "clangd"}}},
44 });
45 }
46
47 ~JSONTracer() {
48 Out.arrayEnd();
49 Out.attributeEnd();
50 Out.objectEnd();
51 Out.flush();
52 }
53
54 // We stash a Span object in the context. It will record the start/end,
55 // and this also allows us to look up the parent Span's information.
56 Context beginSpan(
57 llvm::StringRef Name,
58 llvm::function_ref<void(llvm::json::Object *)> AttachDetails) override {
59 auto JS = std::make_unique<JSONSpan>(this, Name);
60 AttachDetails(&JS->Args);
61 return Context::current().derive(SpanKey, std::move(JS));
62 }
63
64 // Trace viewer requires each thread to properly stack events.
65 // So we need to mark only duration that the span was active on the thread.
66 // (Hopefully any off-thread activity will be connected by a flow event).
67 // Record the end time here, but don't write the event: Args aren't ready yet.
68 void endSpan() override {
69 Context::current().getExisting(SpanKey)->markEnded();
70 }
71
72 void instant(llvm::StringRef Name, llvm::json::Object &&Args) override {
73 captureThreadMetadata();
74 jsonEvent("i",
75 llvm::json::Object{{"name", Name}, {"args", std::move(Args)}});
76 }
77
78 // Record an event on the current thread. ph, pid, tid, ts are set.
79 // Contents must be a list of the other JSON key/values.
80 void jsonEvent(llvm::StringRef Phase, llvm::json::Object &&Contents,
81 uint64_t TID = llvm::get_threadid(), double Timestamp = 0) {
82 Contents["ts"] = Timestamp ? Timestamp : timestamp();
83 Contents["tid"] = int64_t(TID);
84 std::lock_guard<std::mutex> Lock(Mu);
85 rawEvent(Phase, Contents);
86 }
87
88private:
89 class JSONSpan {
90 public:
91 JSONSpan(JSONTracer *Tracer, llvm::StringRef Name)
92 : StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
93 TID(llvm::get_threadid()), Tracer(Tracer) {
94 // ~JSONSpan() may run in a different thread, so we need to capture now.
95 Tracer->captureThreadMetadata();
96
97 // We don't record begin events here (and end events in the destructor)
98 // because B/E pairs have to appear in the right order, which is awkward.
99 // Instead we send the complete (X) event in the destructor.
100
101 // If our parent was on a different thread, add an arrow to this span.
102 auto *Parent = Context::current().get(SpanKey);
103 if (Parent && *Parent && (*Parent)->TID != TID) {
104 // If the parent span ended already, then show this as "following" it.
105 // Otherwise show us as "parallel".
106 double OriginTime = (*Parent)->EndTime;
107 if (!OriginTime)
108 OriginTime = (*Parent)->StartTime;
Value stored to 'OriginTime' is never read
109
110 auto FlowID = nextID();
111 Tracer->jsonEvent(
112 "s",
113 llvm::json::Object{{"id", FlowID},
114 {"name", "Context crosses threads"},
115 {"cat", "mock_cat"}},
116 (*Parent)->TID, (*Parent)->StartTime);
117 Tracer->jsonEvent(
118 "f",
119 llvm::json::Object{{"id", FlowID},
120 {"bp", "e"},
121 {"name", "Context crosses threads"},
122 {"cat", "mock_cat"}},
123 TID);
124 }
125 }
126
127 ~JSONSpan() {
128 // Finally, record the event (ending at EndTime, not timestamp())!
129 Tracer->jsonEvent("X",
130 llvm::json::Object{{"name", std::move(Name)},
131 {"args", std::move(Args)},
132 {"dur", EndTime - StartTime}},
133 TID, StartTime);
134 }
135
136 // May be called by any thread.
137 void markEnded() { EndTime = Tracer->timestamp(); }
138
139 llvm::json::Object Args;
140
141 private:
142 static int64_t nextID() {
143 static std::atomic<int64_t> Next = {0};
144 return Next++;
145 }
146
147 double StartTime;
148 std::atomic<double> EndTime; // Filled in by markEnded().
149 std::string Name;
150 uint64_t TID;
151 JSONTracer *Tracer;
152 };
153 static Key<std::unique_ptr<JSONSpan>> SpanKey;
154
155 // Record an event. ph and pid are set.
156 // Contents must be a list of the other JSON key/values.
157 void rawEvent(llvm::StringRef Phase,
158 const llvm::json::Object &Event) /*REQUIRES(Mu)*/ {
159 // PID 0 represents the clangd process.
160 Out.object([&] {
161 Out.attribute("pid", 0);
162 Out.attribute("ph", Phase);
163 for (const auto &KV : Event)
164 Out.attribute(KV.first, KV.second);
165 });
166 }
167
168 // If we haven't already, emit metadata describing this thread.
169 void captureThreadMetadata() {
170 uint64_t TID = llvm::get_threadid();
171 std::lock_guard<std::mutex> Lock(Mu);
172 if (ThreadsWithMD.insert(TID).second) {
173 llvm::SmallString<32> Name;
174 llvm::get_thread_name(Name);
175 if (!Name.empty()) {
176 rawEvent("M", llvm::json::Object{
177 {"tid", int64_t(TID)},
178 {"name", "thread_name"},
179 {"args", llvm::json::Object{{"name", Name}}},
180 });
181 }
182 }
183 }
184
185 double timestamp() {
186 using namespace std::chrono;
187 return duration<double, std::micro>(system_clock::now() - Start).count();
188 }
189
190 std::mutex Mu;
191 llvm::json::OStream Out /*GUARDED_BY(Mu)*/;
192 llvm::DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
193 const llvm::sys::TimePoint<> Start;
194};
195
196// We emit CSV as specified in RFC 4180: https://www.ietf.org/rfc/rfc4180.txt.
197// \r\n line endings are used, cells with \r\n," are quoted, quotes are doubled.
198class CSVMetricTracer : public EventTracer {
199public:
200 CSVMetricTracer(llvm::raw_ostream &Out) : Out(Out) {
201 Start = std::chrono::steady_clock::now();
202
203 Out.SetUnbuffered(); // We write each line atomically.
204 Out << "Kind,Metric,Label,Value,Timestamp\r\n";
205 }
206
207 void record(const Metric &Metric, double Value,
208 llvm::StringRef Label) override {
209 assert(!needsQuote(Metric.Name))(static_cast <bool> (!needsQuote(Metric.Name)) ? void (
0) : __assert_fail ("!needsQuote(Metric.Name)", "clang-tools-extra/clangd/support/Trace.cpp"
, 209, __extension__ __PRETTY_FUNCTION__))
;
210 std::string QuotedLabel;
211 if (needsQuote(Label))
212 Label = QuotedLabel = quote(Label);
213 uint64_t Micros = std::chrono::duration_cast<std::chrono::microseconds>(
214 std::chrono::steady_clock::now() - Start)
215 .count();
216 std::lock_guard<std::mutex> Lock(Mu);
217 Out << llvm::formatv("{0},{1},{2},{3:e},{4}.{5:6}\r\n",
218 typeName(Metric.Type), Metric.Name, Label, Value,
219 Micros / 1000000, Micros % 1000000);
220 }
221
222private:
223 llvm::StringRef typeName(Metric::MetricType T) {
224 switch (T) {
225 case Metric::Value:
226 return "v";
227 case Metric::Counter:
228 return "c";
229 case Metric::Distribution:
230 return "d";
231 }
232 llvm_unreachable("Unknown Metric::MetricType enum")::llvm::llvm_unreachable_internal("Unknown Metric::MetricType enum"
, "clang-tools-extra/clangd/support/Trace.cpp", 232)
;
233 }
234
235 static bool needsQuote(llvm::StringRef Text) {
236 // https://www.ietf.org/rfc/rfc4180.txt section 2.6
237 return Text.find_first_of(",\"\r\n") != llvm::StringRef::npos;
238 }
239
240 std::string quote(llvm::StringRef Text) {
241 std::string Result = "\"";
242 for (char C : Text) {
243 Result.push_back(C);
244 if (C == '"')
245 Result.push_back('"');
246 }
247 Result.push_back('"');
248 return Result;
249 }
250
251private:
252 std::mutex Mu;
253 llvm::raw_ostream &Out /*GUARDED_BY(Mu)*/;
254 std::chrono::steady_clock::time_point Start;
255};
256
257Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
258
259EventTracer *T = nullptr;
260} // namespace
261
262Session::Session(EventTracer &Tracer) {
263 assert(!T && "Resetting global tracer is not allowed.")(static_cast <bool> (!T && "Resetting global tracer is not allowed."
) ? void (0) : __assert_fail ("!T && \"Resetting global tracer is not allowed.\""
, "clang-tools-extra/clangd/support/Trace.cpp", 263, __extension__
__PRETTY_FUNCTION__))
;
264 T = &Tracer;
265}
266
267Session::~Session() { T = nullptr; }
268
269std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
270 bool Pretty) {
271 return std::make_unique<JSONTracer>(OS, Pretty);
272}
273
274std::unique_ptr<EventTracer> createCSVMetricTracer(llvm::raw_ostream &OS) {
275 return std::make_unique<CSVMetricTracer>(OS);
276}
277
278void log(const llvm::Twine &Message) {
279 if (!T)
280 return;
281 T->instant("Log", llvm::json::Object{{"Message", Message.str()}});
282}
283
284bool enabled() { return T != nullptr; }
285
286// The JSON object is event args (owned by context), if the tracer wants them.
287static std::pair<Context, llvm::json::Object *>
288makeSpanContext(llvm::Twine Name, const Metric &LatencyMetric) {
289 if (!T)
290 return std::make_pair(Context::current().clone(), nullptr);
291 llvm::Optional<WithContextValue> WithLatency;
292 using Clock = std::chrono::high_resolution_clock;
293 WithLatency.emplace(llvm::make_scope_exit(
294 [StartTime = Clock::now(), Name = Name.str(), &LatencyMetric] {
295 LatencyMetric.record(
296 std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() -
297 StartTime)
298 .count(),
299 Name);
300 }));
301 llvm::json::Object *Args = nullptr;
302 Context Ctx = T->beginSpan(
303 Name.isSingleStringRef() ? Name.getSingleStringRef()
304 : llvm::StringRef(Name.str()),
305 [&](llvm::json::Object *A) {
306 assert(A && A->empty() && "Invalid AttachDetails() placeholder!")(static_cast <bool> (A && A->empty() &&
"Invalid AttachDetails() placeholder!") ? void (0) : __assert_fail
("A && A->empty() && \"Invalid AttachDetails() placeholder!\""
, "clang-tools-extra/clangd/support/Trace.cpp", 306, __extension__
__PRETTY_FUNCTION__))
;
307 Args = A;
308 });
309 return std::make_pair(std::move(Ctx), Args);
310}
311
312// Fallback metric that measures latencies for spans without an explicit latency
313// metric. Labels are span names.
314constexpr Metric SpanLatency("span_latency", Metric::Distribution, "span_name");
315
316// Span keeps a non-owning pointer to the args, which is how users access them.
317// The args are owned by the context though. They stick around until the
318// beginSpan() context is destroyed, when the tracing engine will consume them.
319Span::Span(llvm::Twine Name) : Span(Name, SpanLatency) {}
320Span::Span(llvm::Twine Name, const Metric &LatencyMetric)
321 : Span(makeSpanContext(Name, LatencyMetric)) {}
322Span::Span(std::pair<Context, llvm::json::Object *> Pair)
323 : Args(Pair.second), RestoreCtx(std::move(Pair.first)) {}
324
325Span::~Span() {
326 if (T)
327 T->endSpan();
328}
329
330void Metric::record(double Value, llvm::StringRef Label) const {
331 if (!T)
332 return;
333 assert((LabelName.empty() == Label.empty()) &&(static_cast <bool> ((LabelName.empty() == Label.empty(
)) && "recording a measurement with inconsistent labeling"
) ? void (0) : __assert_fail ("(LabelName.empty() == Label.empty()) && \"recording a measurement with inconsistent labeling\""
, "clang-tools-extra/clangd/support/Trace.cpp", 334, __extension__
__PRETTY_FUNCTION__))
334 "recording a measurement with inconsistent labeling")(static_cast <bool> ((LabelName.empty() == Label.empty(
)) && "recording a measurement with inconsistent labeling"
) ? void (0) : __assert_fail ("(LabelName.empty() == Label.empty()) && \"recording a measurement with inconsistent labeling\""
, "clang-tools-extra/clangd/support/Trace.cpp", 334, __extension__
__PRETTY_FUNCTION__))
;
335 T->record(*this, Value, Label);
336}
337
338Context EventTracer::beginSpan(
339 llvm::StringRef Name,
340 llvm::function_ref<void(llvm::json::Object *)> AttachDetails) {
341 return Context::current().clone();
342}
343} // namespace trace
344} // namespace clangd
345} // namespace clang