2017-11-02 17:21:51 +08:00
|
|
|
//===--- Trace.cpp - Performance tracing facilities -----------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Trace.h"
|
2018-01-26 17:00:30 +08:00
|
|
|
#include "Context.h"
|
|
|
|
#include "Function.h"
|
2017-11-02 17:21:51 +08:00
|
|
|
#include "llvm/ADT/DenseSet.h"
|
2018-01-26 17:00:30 +08:00
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
2017-11-02 17:21:51 +08:00
|
|
|
#include "llvm/Support/Chrono.h"
|
|
|
|
#include "llvm/Support/FormatProviders.h"
|
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
#include "llvm/Support/Threading.h"
|
2018-02-15 16:40:54 +08:00
|
|
|
#include <atomic>
|
2017-11-02 17:21:51 +08:00
|
|
|
#include <mutex>
|
|
|
|
|
2018-10-20 23:30:37 +08:00
|
|
|
using namespace llvm;
|
2017-11-02 17:21:51 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace trace {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// The current implementation is naive: each thread writes to Out guarded by Mu.
|
|
|
|
// Perhaps we should replace this by something that disturbs performance less.
|
2017-12-14 23:04:59 +08:00
|
|
|
class JSONTracer : public EventTracer {
|
2017-11-02 17:21:51 +08:00
|
|
|
public:
|
2017-12-14 23:04:59 +08:00
|
|
|
JSONTracer(raw_ostream &Out, bool Pretty)
|
2017-11-24 01:12:04 +08:00
|
|
|
: Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
|
|
|
|
JSONFormat(Pretty ? "{0:2}" : "{0}") {
|
2017-11-02 17:21:51 +08:00
|
|
|
// The displayTimeUnit must be ns to avoid low-precision overlap
|
|
|
|
// calculations!
|
|
|
|
Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
|
|
|
|
<< "\n";
|
2018-07-09 22:25:59 +08:00
|
|
|
rawEvent("M", json::Object{
|
2017-11-24 01:12:04 +08:00
|
|
|
{"name", "process_name"},
|
2018-07-09 22:25:59 +08:00
|
|
|
{"args", json::Object{{"name", "clangd"}}},
|
2017-11-24 01:12:04 +08:00
|
|
|
});
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
2017-12-14 23:04:59 +08:00
|
|
|
~JSONTracer() {
|
2017-11-02 17:21:51 +08:00
|
|
|
Out << "\n]}";
|
|
|
|
Out.flush();
|
|
|
|
}
|
|
|
|
|
2018-02-15 16:40:54 +08:00
|
|
|
// We stash a Span object in the context. It will record the start/end,
|
|
|
|
// and this also allows us to look up the parent Span's information.
|
2018-10-20 23:30:37 +08:00
|
|
|
Context beginSpan(StringRef Name, json::Object *Args) override {
|
2018-02-15 22:16:17 +08:00
|
|
|
return Context::current().derive(
|
|
|
|
SpanKey, llvm::make_unique<JSONSpan>(this, Name, Args));
|
2018-02-15 16:40:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Trace viewer requires each thread to properly stack events.
|
|
|
|
// So we need to mark only duration that the span was active on the thread.
|
|
|
|
// (Hopefully any off-thread activity will be connected by a flow event).
|
|
|
|
// Record the end time here, but don't write the event: Args aren't ready yet.
|
|
|
|
void endSpan() override {
|
|
|
|
Context::current().getExisting(SpanKey)->markEnded();
|
2017-12-14 23:33:38 +08:00
|
|
|
}
|
|
|
|
|
2018-10-20 23:30:37 +08:00
|
|
|
void instant(StringRef Name, json::Object &&Args) override {
|
2018-02-15 16:40:54 +08:00
|
|
|
captureThreadMetadata();
|
2018-07-09 22:25:59 +08:00
|
|
|
jsonEvent("i", json::Object{{"name", Name}, {"args", std::move(Args)}});
|
2017-12-14 23:33:38 +08:00
|
|
|
}
|
|
|
|
|
2017-11-02 17:21:51 +08:00
|
|
|
// Record an event on the current thread. ph, pid, tid, ts are set.
|
|
|
|
// Contents must be a list of the other JSON key/values.
|
2018-07-09 22:25:59 +08:00
|
|
|
void jsonEvent(StringRef Phase, json::Object &&Contents,
|
|
|
|
uint64_t TID = get_threadid(), double Timestamp = 0) {
|
2018-02-15 16:40:54 +08:00
|
|
|
Contents["ts"] = Timestamp ? Timestamp : timestamp();
|
2018-07-09 22:25:59 +08:00
|
|
|
Contents["tid"] = int64_t(TID);
|
2018-02-15 16:40:54 +08:00
|
|
|
std::lock_guard<std::mutex> Lock(Mu);
|
2017-11-24 01:12:04 +08:00
|
|
|
rawEvent(Phase, std::move(Contents));
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-02-15 16:40:54 +08:00
|
|
|
class JSONSpan {
|
|
|
|
public:
|
2018-10-20 23:30:37 +08:00
|
|
|
JSONSpan(JSONTracer *Tracer, StringRef Name, json::Object *Args)
|
2018-02-15 16:40:54 +08:00
|
|
|
: StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
|
|
|
|
TID(get_threadid()), Tracer(Tracer), Args(Args) {
|
|
|
|
// ~JSONSpan() may run in a different thread, so we need to capture now.
|
|
|
|
Tracer->captureThreadMetadata();
|
|
|
|
|
|
|
|
// We don't record begin events here (and end events in the destructor)
|
|
|
|
// because B/E pairs have to appear in the right order, which is awkward.
|
|
|
|
// Instead we send the complete (X) event in the destructor.
|
|
|
|
|
|
|
|
// If our parent was on a different thread, add an arrow to this span.
|
|
|
|
auto *Parent = Context::current().get(SpanKey);
|
|
|
|
if (Parent && *Parent && (*Parent)->TID != TID) {
|
|
|
|
// If the parent span ended already, then show this as "following" it.
|
|
|
|
// Otherwise show us as "parallel".
|
|
|
|
double OriginTime = (*Parent)->EndTime;
|
|
|
|
if (!OriginTime)
|
|
|
|
OriginTime = (*Parent)->StartTime;
|
|
|
|
|
|
|
|
auto FlowID = nextID();
|
|
|
|
Tracer->jsonEvent("s",
|
2018-07-09 22:25:59 +08:00
|
|
|
json::Object{{"id", FlowID},
|
|
|
|
{"name", "Context crosses threads"},
|
|
|
|
{"cat", "dummy"}},
|
2018-02-15 16:40:54 +08:00
|
|
|
(*Parent)->TID, (*Parent)->StartTime);
|
|
|
|
Tracer->jsonEvent("f",
|
2018-07-09 22:25:59 +08:00
|
|
|
json::Object{{"id", FlowID},
|
|
|
|
{"bp", "e"},
|
|
|
|
{"name", "Context crosses threads"},
|
|
|
|
{"cat", "dummy"}},
|
2018-02-15 16:40:54 +08:00
|
|
|
TID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~JSONSpan() {
|
|
|
|
// Finally, record the event (ending at EndTime, not timestamp())!
|
|
|
|
Tracer->jsonEvent("X",
|
2018-07-09 22:25:59 +08:00
|
|
|
json::Object{{"name", std::move(Name)},
|
|
|
|
{"args", std::move(*Args)},
|
|
|
|
{"dur", EndTime - StartTime}},
|
2018-02-15 16:40:54 +08:00
|
|
|
TID, StartTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
// May be called by any thread.
|
|
|
|
void markEnded() {
|
|
|
|
EndTime = Tracer->timestamp();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-07-09 22:25:59 +08:00
|
|
|
static int64_t nextID() {
|
|
|
|
static std::atomic<int64_t> Next = {0};
|
2018-02-15 16:40:54 +08:00
|
|
|
return Next++;
|
|
|
|
}
|
|
|
|
|
|
|
|
double StartTime;
|
|
|
|
std::atomic<double> EndTime; // Filled in by markEnded().
|
|
|
|
std::string Name;
|
|
|
|
uint64_t TID;
|
|
|
|
JSONTracer *Tracer;
|
2018-07-09 22:25:59 +08:00
|
|
|
json::Object *Args;
|
2018-02-15 16:40:54 +08:00
|
|
|
};
|
|
|
|
static Key<std::unique_ptr<JSONSpan>> SpanKey;
|
|
|
|
|
2017-11-02 17:21:51 +08:00
|
|
|
// Record an event. ph and pid are set.
|
|
|
|
// Contents must be a list of the other JSON key/values.
|
2018-07-09 22:25:59 +08:00
|
|
|
void rawEvent(StringRef Phase, json::Object &&Event) /*REQUIRES(Mu)*/ {
|
2017-11-02 17:21:51 +08:00
|
|
|
// PID 0 represents the clangd process.
|
2017-11-24 01:12:04 +08:00
|
|
|
Event["pid"] = 0;
|
|
|
|
Event["ph"] = Phase;
|
2018-07-09 22:25:59 +08:00
|
|
|
Out << Sep << formatv(JSONFormat, json::Value(std::move(Event)));
|
2017-11-02 17:21:51 +08:00
|
|
|
Sep = ",\n";
|
|
|
|
}
|
|
|
|
|
2018-02-15 16:40:54 +08:00
|
|
|
// If we haven't already, emit metadata describing this thread.
|
|
|
|
void captureThreadMetadata() {
|
|
|
|
uint64_t TID = get_threadid();
|
|
|
|
std::lock_guard<std::mutex> Lock(Mu);
|
|
|
|
if (ThreadsWithMD.insert(TID).second) {
|
|
|
|
SmallString<32> Name;
|
|
|
|
get_thread_name(Name);
|
|
|
|
if (!Name.empty()) {
|
2018-07-09 22:25:59 +08:00
|
|
|
rawEvent("M", json::Object{
|
|
|
|
{"tid", int64_t(TID)},
|
2018-02-15 16:40:54 +08:00
|
|
|
{"name", "thread_name"},
|
2018-07-09 22:25:59 +08:00
|
|
|
{"args", json::Object{{"name", Name}}},
|
2018-02-15 16:40:54 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-02 17:21:51 +08:00
|
|
|
double timestamp() {
|
|
|
|
using namespace std::chrono;
|
2017-11-16 01:53:46 +08:00
|
|
|
return duration<double, std::micro>(system_clock::now() - Start).count();
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
std::mutex Mu;
|
|
|
|
raw_ostream &Out /*GUARDED_BY(Mu)*/;
|
|
|
|
const char *Sep /*GUARDED_BY(Mu)*/;
|
|
|
|
DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
|
|
|
|
const sys::TimePoint<> Start;
|
2017-11-24 01:12:04 +08:00
|
|
|
const char *JSONFormat;
|
2017-11-02 17:21:51 +08:00
|
|
|
};
|
|
|
|
|
2018-02-15 16:40:54 +08:00
|
|
|
Key<std::unique_ptr<JSONTracer::JSONSpan>> JSONTracer::SpanKey;
|
|
|
|
|
2017-12-14 23:04:59 +08:00
|
|
|
EventTracer *T = nullptr;
|
2017-11-02 17:21:51 +08:00
|
|
|
} // namespace
|
|
|
|
|
2017-12-14 23:04:59 +08:00
|
|
|
Session::Session(EventTracer &Tracer) {
|
|
|
|
assert(!T && "Resetting global tracer is not allowed.");
|
|
|
|
T = &Tracer;
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
2017-12-14 23:04:59 +08:00
|
|
|
Session::~Session() { T = nullptr; }
|
|
|
|
|
2018-10-20 23:30:37 +08:00
|
|
|
std::unique_ptr<EventTracer> createJSONTracer(raw_ostream &OS, bool Pretty) {
|
2017-12-14 23:04:59 +08:00
|
|
|
return llvm::make_unique<JSONTracer>(OS, Pretty);
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
void log(const Twine &Message) {
|
2017-11-02 17:21:51 +08:00
|
|
|
if (!T)
|
|
|
|
return;
|
2018-07-09 22:25:59 +08:00
|
|
|
T->instant("Log", json::Object{{"Message", Message.str()}});
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returned context owns Args.
|
2018-10-20 23:30:37 +08:00
|
|
|
static Context makeSpanContext(Twine Name, json::Object *Args) {
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
if (!T)
|
|
|
|
return Context::current().clone();
|
2018-07-09 22:25:59 +08:00
|
|
|
WithContextValue WithArgs{std::unique_ptr<json::Object>(Args)};
|
2018-02-19 17:56:28 +08:00
|
|
|
return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
|
2018-10-20 23:30:37 +08:00
|
|
|
: StringRef(Name.str()),
|
2018-02-19 17:56:28 +08:00
|
|
|
Args);
|
2017-11-02 17:21:51 +08:00
|
|
|
}
|
|
|
|
|
2018-01-26 17:00:30 +08:00
|
|
|
// Span keeps a non-owning pointer to the args, which is how users access them.
|
|
|
|
// The args are owned by the context though. They stick around until the
|
|
|
|
// beginSpan() context is destroyed, when the tracing engine will consume them.
|
2018-10-20 23:30:37 +08:00
|
|
|
Span::Span(Twine Name)
|
2018-07-09 22:25:59 +08:00
|
|
|
: Args(T ? new json::Object() : nullptr),
|
[clangd] Pass Context implicitly using TLS.
Summary:
Instead of passing Context explicitly around, we now have a thread-local
Context object `Context::current()` which is an implicit argument to
every function.
Most manipulation of this should use the WithContextValue helper, which
augments the current Context to add a single KV pair, and restores the
old context on destruction.
Advantages are:
- less boilerplate in functions that just propagate contexts
- reading most code doesn't require understanding context at all, and
using context as values in fewer places still
- fewer options to pass the "wrong" context when it changes within a
scope (e.g. when using Span)
- contexts pass through interfaces we can't modify, such as VFS
- propagating contexts across threads was slightly tricky (e.g.
copy vs move, no move-init in lambdas), and is now encapsulated in
the threadpool
Disadvantages are all the usual TLS stuff - hidden magic, and
potential for higher memory usage on threads that don't use the
context. (In practice, it's just one pointer)
Reviewers: ilya-biryukov
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits
Differential Revision: https://reviews.llvm.org/D42517
llvm-svn: 323872
2018-01-31 21:40:48 +08:00
|
|
|
RestoreCtx(makeSpanContext(Name, Args)) {}
|
2017-11-02 17:21:51 +08:00
|
|
|
|
2018-02-15 16:40:54 +08:00
|
|
|
Span::~Span() {
|
|
|
|
if (T)
|
|
|
|
T->endSpan();
|
|
|
|
}
|
|
|
|
|
2017-11-02 17:21:51 +08:00
|
|
|
} // namespace trace
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|