Index: clangd/JSONRPCDispatcher.cpp =================================================================== --- clangd/JSONRPCDispatcher.cpp +++ clangd/JSONRPCDispatcher.cpp @@ -60,14 +60,14 @@ } void clangd::reply(const Context &Ctx, json::Expr &&Result) { - auto ID = Ctx->get(IDKey); + auto ID = Ctx.get(IDKey); if (!ID) { log(Ctx, "Attempted to reply to a notification!"); return; } - SPAN_ATTACH(*Ctx->getExisting(TracerKey), "Reply", Result); - Ctx->getExisting(OutKey)->writeMessage(json::obj{ + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Reply", Result); + Ctx.getExisting(OutKey)->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, {"result", std::move(Result)}, @@ -77,12 +77,12 @@ void clangd::replyError(const Context &Ctx, ErrorCode code, const llvm::StringRef &Message) { log(Ctx, "Error " + Twine(static_cast(code)) + ": " + Message); - SPAN_ATTACH(*Ctx->getExisting(TracerKey), "Error", + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Error", (json::obj{{"code", static_cast(code)}, {"message", Message.str()}})); - if (auto ID = Ctx->get(IDKey)) { - Ctx->getExisting(OutKey)->writeMessage(json::obj{ + if (auto ID = Ctx.get(IDKey)) { + Ctx.getExisting(OutKey)->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", *ID}, {"error", @@ -94,9 +94,9 @@ void clangd::call(const Context &Ctx, StringRef Method, json::Expr &&Params) { // FIXME: Generate/Increment IDs for every request so that we can get proper // replies once we need to. - SPAN_ATTACH(*Ctx->getExisting(TracerKey), "Call", + SPAN_ATTACH(*Ctx.getExisting(TracerKey), "Call", (json::obj{{"method", Method.str()}, {"params", Params}})); - Ctx->getExisting(OutKey)->writeMessage(json::obj{ + Ctx.getExisting(OutKey)->writeMessage(json::obj{ {"jsonrpc", "2.0"}, {"id", 1}, {"method", Method}, Index: clangd/Trace.h =================================================================== --- clangd/Trace.h +++ clangd/Trace.h @@ -28,12 +28,21 @@ namespace trace { /// A consumer of trace events. The events are produced by Spans and trace::log. +/// Calls to begin_event and end_event with the same name are always properly +/// nested by using a RAII object (Span). +/// Implmentations of this interface must be thread-safe. class EventTracer { public: virtual ~EventTracer() = default; - /// Consume a trace event. - virtual void event(const ContextData &Ctx, llvm::StringRef Phase, - json::obj &&Contents) = 0; + + /// Called when event with \p Name starts. + virtual void begin_event(const Context &Ctx, llvm::StringRef Name) = 0; + /// Called when event with \p Name ends. + virtual void end_event(const Context &Ctx, llvm::StringRef Name, + json::obj &&Args) = 0; + /// Called for instant events. + virtual void instant_event(const Context &Ctx, llvm::StringRef Name, + json::obj &&Args) = 0; }; /// Sets up a global EventTracer that consumes events produced by Span and @@ -57,7 +66,7 @@ bool Pretty = false); /// Records a single instant event, associated with the current thread. -void log(Context &Ctx, const llvm::Twine &Name); +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. @@ -67,7 +76,7 @@ /// SomeJSONExpr is evaluated and copied only if actually needed. class Span { public: - Span(Context &Ctx, std::string Name); + Span(const Context &Ctx, llvm::StringRef Name); ~Span(); /// Returns mutable span metadata if this span is interested. @@ -75,7 +84,8 @@ json::obj *args() { return Args.get(); } private: - const ContextData &Ctx; + llvm::Optional Ctx; + std::string Name; std::unique_ptr Args; }; Index: clangd/Trace.cpp =================================================================== --- clangd/Trace.cpp +++ clangd/Trace.cpp @@ -44,10 +44,23 @@ Out.flush(); } + void begin_event(const Context &Ctx, llvm::StringRef Name) override { + jsonEvent("B", json::obj{{"name", Name}}); + } + + void end_event(const Context &Ctx, llvm::StringRef Name, + json::obj &&Args) override { + jsonEvent("E", json::obj{{"args", std::move(Args)}}); + } + + void instant_event(const Context &Ctx, llvm::StringRef Name, + json::obj &&Args) override { + jsonEvent("i", json::obj{{"name", Name}, {"args", std::move(Args)}}); + } + // 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(const ContextData &Ctx, StringRef Phase, - json::obj &&Contents) override { + void jsonEvent(StringRef Phase, json::obj &&Contents) { uint64_t TID = get_threadid(); std::lock_guard Lock(Mu); // If we haven't already, emit metadata describing this thread. @@ -106,30 +119,27 @@ return llvm::make_unique(OS, Pretty); } -void log(Context &Ctx, const Twine &Message) { +void log(const Context &Ctx, const Twine &Message) { if (!T) return; - T->event(*Ctx, "i", - json::obj{ - {"name", "Log"}, - {"args", json::obj{{"Message", Message.str()}}}, - }); + T->instant_event(Ctx, "Log", json::obj{{"Message", Message.str()}}); } -Span::Span(Context &Ctx, std::string Name) : Ctx(*Ctx) { +Span::Span(const Context &Ctx, llvm::StringRef Name) : Name(Name) { if (!T) return; - T->event(this->Ctx, "B", json::obj{{"name", std::move(Name)}}); + // Clone the context, so that the original Context can be moved. + this->Ctx.emplace(Ctx.clone()); + + T->begin_event(*this->Ctx, this->Name); Args = llvm::make_unique(); } Span::~Span() { if (!T) return; - if (!Args) - Args = llvm::make_unique(); - T->event(Ctx, "E", - Args ? json::obj{{"args", std::move(*Args)}} : json::obj{}); + assert(Args && "Args can't be null at this point"); + T->end_event(*this->Ctx, Name, std::move(*Args)); } } // namespace trace