Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -1286,7 +1286,7 @@ // (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 PreambleDiags; StoreDiagsConsumer PreambleDiagnosticsConsumer(/*ref*/ PreambleDiags); @@ -1332,7 +1332,7 @@ // Compute updated AST. llvm::Optional NewAST; { - trace::Span Tracer("Build"); + trace::Span Tracer(Ctx, "Build"); SPAN_ATTACH(Tracer, "File", That->FileName); NewAST = ParsedAST::Build(std::move(CI), std::move(NewPreamble), std::move(ContentsBuffer), PCHs, VFS, Ctx); Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -60,7 +60,7 @@ RequestContext(Context Ctx, JSONOutput &Out, StringRef Method, llvm::Optional ID) : Ctx(std::move(Ctx)), Out(Out), ID(std::move(ID)), - Tracer(llvm::make_unique(Method)) { + Tracer(llvm::make_unique(this->Ctx, Method)) { if (this->ID) SPAN_ATTACH(tracer(), "ID", *this->ID); } @@ -76,6 +76,7 @@ Context &ctx() { return Ctx; } private: + // Ctx must be before Tracer! Context Ctx; JSONOutput &Out; llvm::Optional ID; Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -40,8 +40,7 @@ } void JSONOutput::logImpl(Context &Ctx, const Twine &Message) { - // FIXME(ibiryukov): get rid of trace::log here. - trace::log(Message); + trace::log(Ctx, Message); std::lock_guard Guard(StreamMutex); Logs << Message; Logs.flush(); Index: clangd/ProtocolHandlers.cpp =================================================================== --- clangd/ProtocolHandlers.cpp +++ clangd/ProtocolHandlers.cpp @@ -31,7 +31,7 @@ Dispatcher.registerHandler( Method, [=](RequestContext C, llvm::yaml::MappingNode *RawParams) { if (auto P = [&] { - trace::Span Tracer("Parse"); + trace::Span Tracer(C.ctx(), "Parse"); return std::decay::type::parse(RawParams, C.ctx()); }()) { (Callbacks->*Handler)(std::move(C), *P); Index: clangd/Trace.h =================================================================== --- clangd/Trace.h +++ clangd/Trace.h @@ -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,45 @@ 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 { +class EventTracer { public: - // Starts a sessions capturing trace events and writing Trace Event JSON. - static std::unique_ptr create(llvm::raw_ostream &OS, - bool Pretty = false); - ~Session(); + static PtrKey CtxKey; -private: - Session() = default; + virtual ~EventTracer() = default; + virtual void event(llvm::StringRef Phase, json::obj &&Contents) = 0; }; -// Records a single instant event, associated with the current thread. -void log(const llvm::Twine &Name); +/// 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 createJSONTracer(llvm::raw_ostream &OS, + bool Pretty = false); -// 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. +/// Records a single instant event, associated with the current thread. +void log(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(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: + Context &Ctx; std::unique_ptr Args; }; Index: clangd/Trace.cpp =================================================================== --- clangd/Trace.cpp +++ clangd/Trace.cpp @@ -25,9 +25,9 @@ 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 @@ -40,14 +40,14 @@ }); } - ~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(StringRef Phase, json::obj &&Contents) override { uint64_t TID = get_threadid(); std::lock_guard Lock(Mu); // If we haven't already, emit metadata describing this thread. @@ -90,22 +90,17 @@ const sys::TimePoint<> Start; const char *JSONFormat; }; - -static Tracer *T = nullptr; } // namespace -std::unique_ptr Session::create(raw_ostream &OS, bool Pretty) { - assert(!T && "A session is already active"); - T = new Tracer(OS, Pretty); - return std::unique_ptr(new Session()); -} +PtrKey EventTracer::CtxKey; -Session::~Session() { - delete T; - T = nullptr; +std::unique_ptr createJSONTracer(llvm::raw_ostream &OS, + bool Pretty) { + return llvm::make_unique(OS, Pretty); } -void log(const Twine &Message) { +void log(Context &Ctx, const Twine &Message) { + EventTracer *T = Ctx.get(EventTracer::CtxKey); if (!T) return; T->event("i", json::obj{ @@ -114,7 +109,8 @@ }); } -Span::Span(std::string Name) { +Span::Span(Context &Ctx, std::string Name) : Ctx(Ctx) { + EventTracer* T = Ctx.get(EventTracer::CtxKey); if (!T) return; T->event("B", json::obj{{"name", std::move(Name)}}); @@ -122,6 +118,7 @@ } Span::~Span() { + auto T = Ctx.get(EventTracer::CtxKey); if (!T) return; if (!Args) Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -116,7 +116,7 @@ } } llvm::Optional TraceStream; - std::unique_ptr TraceSession; + std::unique_ptr TraceSession; if (!TraceFile.empty()) { std::error_code EC; TraceStream.emplace(TraceFile, /*ref*/ EC, llvm::sys::fs::F_RW); @@ -124,7 +124,7 @@ TraceFile.reset(); llvm::errs() << "Error while opening trace file: " << EC.message(); } else { - TraceSession = trace::Session::create(*TraceStream, PrettyPrint); + TraceSession = trace::createJSONTracer(*TraceStream, PrettyPrint); } } @@ -134,7 +134,11 @@ InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint); - GlobalSession Session(buildCtx().add(Logger::CtxKey, &Out)); + TypedValueMap ContextMap; + ContextMap.emplace(Logger::CtxKey, &Out); + if (TraceSession) + ContextMap.emplace(trace::EventTracer::CtxKey, TraceSession.get()); + GlobalSession Session(Context(/*Parent=*/nullptr, std::move(ContextMap))); // If --compile-commands-dir arg was invoked, check value and override default // path. Index: unittests/clangd/TraceTests.cpp =================================================================== --- unittests/clangd/TraceTests.cpp +++ unittests/clangd/TraceTests.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "Context.h" #include "Trace.h" #include "llvm/ADT/DenseMap.h" @@ -74,10 +75,12 @@ std::string JSON; { raw_string_ostream OS(JSON); - auto Session = trace::Session::create(OS); + auto JSONTracer = trace::createJSONTracer(OS); + Context TraceCtx = + buildCtx().add(trace::EventTracer::CtxKey, JSONTracer.get()); { - trace::Span S("A"); - trace::log("B"); + trace::Span S(TraceCtx, "A"); + trace::log(TraceCtx, "B"); } }