forked from OSchip/llvm-project
[clangd] Implemented tracing using Context
Reviewers: sammccall, ioeric, hokein Reviewed By: sammccall Subscribers: klimek, luckygeck, cfe-commits Differential Revision: https://reviews.llvm.org/D40488 llvm-svn: 320706
This commit is contained in:
parent
f1208e7aef
commit
ee27d2ebae
|
@ -770,7 +770,7 @@ CppFile::deferRebuild(StringRef NewContents,
|
|||
// (if there are no other references to it).
|
||||
OldPreamble.reset();
|
||||
|
||||
trace::Span Tracer("Preamble");
|
||||
trace::Span Tracer(Ctx, "Preamble");
|
||||
SPAN_ATTACH(Tracer, "File", That->FileName);
|
||||
std::vector<DiagWithFixIts> PreambleDiags;
|
||||
StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags);
|
||||
|
@ -816,7 +816,7 @@ CppFile::deferRebuild(StringRef NewContents,
|
|||
// Compute updated AST.
|
||||
llvm::Optional<ParsedAST> NewAST;
|
||||
{
|
||||
trace::Span Tracer("Build");
|
||||
trace::Span Tracer(Ctx, "Build");
|
||||
SPAN_ATTACH(Tracer, "File", That->FileName);
|
||||
NewAST = ParsedAST::Build(Ctx, std::move(CI), std::move(NewPreamble),
|
||||
std::move(ContentsBuffer), PCHs, VFS);
|
||||
|
|
|
@ -45,7 +45,7 @@ void JSONOutput::writeMessage(const json::Expr &Message) {
|
|||
}
|
||||
|
||||
void JSONOutput::log(const Context &Ctx, const Twine &Message) {
|
||||
trace::log(Message);
|
||||
trace::log(Ctx, Message);
|
||||
std::lock_guard<std::mutex> Guard(StreamMutex);
|
||||
Logs << Message << '\n';
|
||||
Logs.flush();
|
||||
|
@ -137,16 +137,19 @@ bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const {
|
|||
auto I = Handlers.find(*Method);
|
||||
auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
|
||||
|
||||
auto Tracer = llvm::make_unique<trace::Span>(*Method);
|
||||
// Create a Context that contains request information.
|
||||
auto Ctx = Context::empty().derive(RequestOut, &Out);
|
||||
if (ID)
|
||||
Ctx = std::move(Ctx).derive(RequestID, *ID);
|
||||
|
||||
// Create a tracing Span covering the whole request lifetime.
|
||||
auto Tracer = llvm::make_unique<trace::Span>(Ctx, *Method);
|
||||
if (ID)
|
||||
SPAN_ATTACH(*Tracer, "ID", *ID);
|
||||
SPAN_ATTACH(*Tracer, "Params", Params);
|
||||
|
||||
auto Ctx = Context::empty()
|
||||
.derive(RequestOut, &Out)
|
||||
.derive(RequestSpan, std::move(Tracer));
|
||||
if (ID)
|
||||
Ctx = std::move(Ctx).derive(RequestID, *ID);
|
||||
// Update Ctx to include Tracer.
|
||||
Ctx = std::move(Ctx).derive(RequestSpan, std::move(Tracer));
|
||||
|
||||
Handler(std::move(Ctx), std::move(Params));
|
||||
return true;
|
||||
|
|
|
@ -24,9 +24,9 @@ using namespace llvm;
|
|||
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.
|
||||
class Tracer {
|
||||
class JSONTracer : public EventTracer {
|
||||
public:
|
||||
Tracer(raw_ostream &Out, bool Pretty)
|
||||
JSONTracer(raw_ostream &Out, bool Pretty)
|
||||
: Out(Out), Sep(""), Start(std::chrono::system_clock::now()),
|
||||
JSONFormat(Pretty ? "{0:2}" : "{0}") {
|
||||
// The displayTimeUnit must be ns to avoid low-precision overlap
|
||||
|
@ -39,14 +39,15 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
~Tracer() {
|
||||
~JSONTracer() {
|
||||
Out << "\n]}";
|
||||
Out.flush();
|
||||
}
|
||||
|
||||
// Record an event on the current thread. ph, pid, tid, ts are set.
|
||||
// Contents must be a list of the other JSON key/values.
|
||||
void event(StringRef Phase, json::obj &&Contents) {
|
||||
void event(const Context &Ctx, StringRef Phase,
|
||||
json::obj &&Contents) override {
|
||||
uint64_t TID = get_threadid();
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
// If we haven't already, emit metadata describing this thread.
|
||||
|
@ -90,33 +91,38 @@ private:
|
|||
const char *JSONFormat;
|
||||
};
|
||||
|
||||
static Tracer *T = nullptr;
|
||||
EventTracer *T = nullptr;
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Session> Session::create(raw_ostream &OS, bool Pretty) {
|
||||
assert(!T && "A session is already active");
|
||||
T = new Tracer(OS, Pretty);
|
||||
return std::unique_ptr<Session>(new Session());
|
||||
Session::Session(EventTracer &Tracer) {
|
||||
assert(!T && "Resetting global tracer is not allowed.");
|
||||
T = &Tracer;
|
||||
}
|
||||
|
||||
Session::~Session() {
|
||||
delete T;
|
||||
T = nullptr;
|
||||
Session::~Session() { T = nullptr; }
|
||||
|
||||
std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
|
||||
bool Pretty) {
|
||||
return llvm::make_unique<JSONTracer>(OS, Pretty);
|
||||
}
|
||||
|
||||
void log(const Twine &Message) {
|
||||
void log(const Context &Ctx, const Twine &Message) {
|
||||
if (!T)
|
||||
return;
|
||||
T->event("i", json::obj{
|
||||
{"name", "Log"},
|
||||
{"args", json::obj{{"Message", Message.str()}}},
|
||||
});
|
||||
T->event(Ctx, "i",
|
||||
json::obj{
|
||||
{"name", "Log"},
|
||||
{"args", json::obj{{"Message", Message.str()}}},
|
||||
});
|
||||
}
|
||||
|
||||
Span::Span(std::string Name) {
|
||||
Span::Span(const Context &Ctx, std::string Name) {
|
||||
if (!T)
|
||||
return;
|
||||
T->event("B", json::obj{{"name", std::move(Name)}});
|
||||
// Clone the context, so that the original Context can be moved.
|
||||
this->Ctx.emplace(Ctx.clone());
|
||||
|
||||
T->event(*this->Ctx, "B", json::obj{{"name", std::move(Name)}});
|
||||
Args = llvm::make_unique<json::obj>();
|
||||
}
|
||||
|
||||
|
@ -125,7 +131,8 @@ Span::~Span() {
|
|||
return;
|
||||
if (!Args)
|
||||
Args = llvm::make_unique<json::obj>();
|
||||
T->event("E", Args ? json::obj{{"args", std::move(*Args)}} : json::obj{});
|
||||
T->event(*Ctx, "E",
|
||||
Args ? json::obj{{"args", std::move(*Args)}} : json::obj{});
|
||||
}
|
||||
|
||||
} // namespace trace
|
||||
|
|
|
@ -8,11 +8,8 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Supports writing performance traces describing clangd's behavior.
|
||||
// Traces are written in the Trace Event format supported by chrome's trace
|
||||
// viewer (chrome://tracing).
|
||||
// Traces are consumed by implementations of the EventTracer interface.
|
||||
//
|
||||
// The format is documented here:
|
||||
// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
|
||||
//
|
||||
// All APIs are no-ops unless a Session is active (created by ClangdMain).
|
||||
//
|
||||
|
@ -21,6 +18,7 @@
|
|||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRACE_H_
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRACE_H_
|
||||
|
||||
#include "Context.h"
|
||||
#include "JSONExpr.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
@ -29,39 +27,55 @@ namespace clang {
|
|||
namespace clangd {
|
||||
namespace trace {
|
||||
|
||||
// A session directs the output of trace events. Only one Session can exist.
|
||||
// It should be created before clangd threads are spawned, and destroyed after
|
||||
// they exit.
|
||||
// TODO: we may want to add pluggable support for other tracing backends.
|
||||
class Session {
|
||||
/// A consumer of trace events. The events are produced by Spans and trace::log.
|
||||
class EventTracer {
|
||||
public:
|
||||
// Starts a sessions capturing trace events and writing Trace Event JSON.
|
||||
static std::unique_ptr<Session> create(llvm::raw_ostream &OS,
|
||||
bool Pretty = false);
|
||||
~Session();
|
||||
|
||||
private:
|
||||
Session() = default;
|
||||
virtual ~EventTracer() = default;
|
||||
/// Consume a trace event.
|
||||
virtual void event(const Context &Ctx, llvm::StringRef Phase,
|
||||
json::obj &&Contents) = 0;
|
||||
};
|
||||
|
||||
// Records a single instant event, associated with the current thread.
|
||||
void log(const llvm::Twine &Name);
|
||||
/// Sets up a global EventTracer that consumes events produced by Span and
|
||||
/// trace::log. Only one TracingSession can be active at a time and it should be
|
||||
/// set up before calling any clangd-specific functions.
|
||||
class Session {
|
||||
public:
|
||||
Session(EventTracer &Tracer);
|
||||
~Session();
|
||||
};
|
||||
|
||||
// Records an event whose duration is the lifetime of the Span object.
|
||||
//
|
||||
// Arbitrary JSON metadata can be attached while this span is active:
|
||||
// SPAN_ATTACH(MySpan, "Payload", SomeJSONExpr);
|
||||
// SomeJSONExpr is evaluated and copied only if actually needed.
|
||||
/// Create an instance of EventTracer that produces an output in the Trace Event
|
||||
/// format supported by Chrome's trace viewer (chrome://tracing).
|
||||
///
|
||||
/// The format is documented here:
|
||||
/// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
|
||||
///
|
||||
/// The implementation supports concurrent calls and can be used as a global
|
||||
/// tracer (i.e., can be put into a global Context).
|
||||
std::unique_ptr<EventTracer> createJSONTracer(llvm::raw_ostream &OS,
|
||||
bool Pretty = false);
|
||||
|
||||
/// Records a single instant event, associated with the current thread.
|
||||
void log(const Context &Ctx, const llvm::Twine &Name);
|
||||
|
||||
/// Records an event whose duration is the lifetime of the Span object.
|
||||
/// This is the main public interface for producing tracing events.
|
||||
///
|
||||
/// Arbitrary JSON metadata can be attached while this span is active:
|
||||
/// SPAN_ATTACH(MySpan, "Payload", SomeJSONExpr);
|
||||
/// SomeJSONExpr is evaluated and copied only if actually needed.
|
||||
class Span {
|
||||
public:
|
||||
Span(std::string Name);
|
||||
Span(const Context &Ctx, std::string Name);
|
||||
~Span();
|
||||
|
||||
// Returns mutable span metadata if this span is interested.
|
||||
// Prefer to use SPAN_ATTACH rather than accessing this directly.
|
||||
/// Returns mutable span metadata if this span is interested.
|
||||
/// Prefer to use SPAN_ATTACH rather than accessing this directly.
|
||||
json::obj *args() { return Args.get(); }
|
||||
|
||||
private:
|
||||
llvm::Optional<Context> Ctx;
|
||||
std::unique_ptr<json::obj> Args;
|
||||
};
|
||||
|
||||
|
|
|
@ -115,8 +115,10 @@ int main(int argc, char *argv[]) {
|
|||
<< EC.message();
|
||||
}
|
||||
}
|
||||
|
||||
// Setup tracing facilities.
|
||||
llvm::Optional<llvm::raw_fd_ostream> TraceStream;
|
||||
std::unique_ptr<trace::Session> TraceSession;
|
||||
std::unique_ptr<trace::EventTracer> Tracer;
|
||||
if (!TraceFile.empty()) {
|
||||
std::error_code EC;
|
||||
TraceStream.emplace(TraceFile, /*ref*/ EC, llvm::sys::fs::F_RW);
|
||||
|
@ -124,10 +126,14 @@ int main(int argc, char *argv[]) {
|
|||
TraceFile.reset();
|
||||
llvm::errs() << "Error while opening trace file: " << EC.message();
|
||||
} else {
|
||||
TraceSession = trace::Session::create(*TraceStream, PrettyPrint);
|
||||
Tracer = trace::createJSONTracer(*TraceStream, PrettyPrint);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Optional<trace::Session> TracingSession;
|
||||
if (Tracer)
|
||||
TracingSession.emplace(*Tracer);
|
||||
|
||||
llvm::raw_ostream &Outs = llvm::outs();
|
||||
llvm::raw_ostream &Logs = llvm::errs();
|
||||
JSONOutput Out(Outs, Logs,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Context.h"
|
||||
#include "Trace.h"
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
@ -74,10 +75,11 @@ TEST(TraceTest, SmokeTest) {
|
|||
std::string JSON;
|
||||
{
|
||||
raw_string_ostream OS(JSON);
|
||||
auto Session = trace::Session::create(OS);
|
||||
auto JSONTracer = trace::createJSONTracer(OS);
|
||||
trace::Session Session(*JSONTracer);
|
||||
{
|
||||
trace::Span S("A");
|
||||
trace::log("B");
|
||||
trace::Span S(Context::empty(), "A");
|
||||
trace::log(Context::empty(), "B");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue