-
Notifications
You must be signed in to change notification settings - Fork 12.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Performance tracing facility for clangd.
Summary: This lets you visualize clangd's activity on different threads over time, and understand critical paths of requests and object lifetimes. The data produced can be visualized in Chrome (at chrome://tracing), or in a standalone copy of catapult (http://github.com/catapult-project/catapult) This patch consists of: - a command line flag "-trace" that causes clangd to emit JSON trace data - an API (in Trace.h) allowing clangd code to easily add events to the stream - several initial uses of this API to capture JSON-RPC requests, builds, logs Example result: https://photos.app.goo.gl/12L9swaz5REGQ1rm1 Caveats: - JSON serialization is ad-hoc (isn't it everywhere?) so the API is limited to naming events rather than attaching arbitrary metadata. I'd like to fix this (I think we could use a JSON-object abstraction). - The recording is very naive: events are written immediately by locking a mutex. Contention on the mutex might disturb performance. - For now it just traces instants or spans on the current thread. There are other things that make sense to show (cross-thread flows, non-thread resources such as ASTs). But we have to start somewhere. Reviewers: ioeric, ilya-biryukov Subscribers: cfe-commits, mgorny Differential Revision: https://reviews.llvm.org/D39086 llvm-svn: 317193
- Loading branch information
1 parent
6a3ed9b
commit 8567cb3
Showing
11 changed files
with
381 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
//===--- 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" | ||
|
||
#include "llvm/ADT/DenseSet.h" | ||
#include "llvm/Support/Chrono.h" | ||
#include "llvm/Support/FormatProviders.h" | ||
#include "llvm/Support/FormatVariadic.h" | ||
#include "llvm/Support/Threading.h" | ||
#include "llvm/Support/YAMLParser.h" | ||
#include <mutex> | ||
|
||
namespace clang { | ||
namespace clangd { | ||
namespace trace { | ||
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 { | ||
public: | ||
Tracer(raw_ostream &Out) | ||
: Out(Out), Sep(""), Start(std::chrono::system_clock::now()) { | ||
// The displayTimeUnit must be ns to avoid low-precision overlap | ||
// calculations! | ||
Out << R"({"displayTimeUnit":"ns","traceEvents":[)" | ||
<< "\n"; | ||
rawEvent("M", R"("name": "process_name", "args":{"name":"clangd"})"); | ||
} | ||
|
||
~Tracer() { | ||
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. | ||
template <typename T> void event(StringRef Phase, const T &Contents) { | ||
uint64_t TID = get_threadid(); | ||
std::lock_guard<std::mutex> Lock(Mu); | ||
// If we haven't already, emit metadata describing this thread. | ||
if (ThreadsWithMD.insert(TID).second) { | ||
SmallString<32> Name; | ||
get_thread_name(Name); | ||
if (!Name.empty()) { | ||
rawEvent( | ||
"M", | ||
formatv( | ||
R"("tid": {0}, "name": "thread_name", "args":{"name":"{1}"})", | ||
TID, StringRef(&Name[0], Name.size()))); | ||
} | ||
} | ||
rawEvent(Phase, formatv(R"("ts":{0}, "tid":{1}, {2})", timestamp(), TID, | ||
Contents)); | ||
} | ||
|
||
private: | ||
// Record an event. ph and pid are set. | ||
// Contents must be a list of the other JSON key/values. | ||
template <typename T> | ||
void rawEvent(StringRef Phase, const T &Contents) /*REQUIRES(Mu)*/ { | ||
// PID 0 represents the clangd process. | ||
Out << Sep << R"({"pid":0, "ph":")" << Phase << "\", " << Contents << "}"; | ||
Sep = ",\n"; | ||
} | ||
|
||
double timestamp() { | ||
using namespace std::chrono; | ||
return duration<double, std::milli>(system_clock::now() - Start).count(); | ||
} | ||
|
||
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; | ||
}; | ||
|
||
static Tracer *T = nullptr; | ||
} // namespace | ||
|
||
std::unique_ptr<Session> Session::create(raw_ostream &OS) { | ||
assert(!T && "A session is already active"); | ||
T = new Tracer(OS); | ||
return std::unique_ptr<Session>(new Session()); | ||
} | ||
|
||
Session::~Session() { | ||
delete T; | ||
T = nullptr; | ||
} | ||
|
||
void log(const Twine &Message) { | ||
if (!T) | ||
return; | ||
T->event("i", formatv(R"("name":"{0}")", yaml::escape(Message.str()))); | ||
} | ||
|
||
Span::Span(const Twine &Text) { | ||
if (!T) | ||
return; | ||
T->event("B", formatv(R"("name":"{0}")", yaml::escape(Text.str()))); | ||
} | ||
|
||
Span::~Span() { | ||
if (!T) | ||
return; | ||
T->event("E", R"("_":0)" /* Dummy property to ensure valid JSON */); | ||
} | ||
|
||
} // namespace trace | ||
} // namespace clangd | ||
} // namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//===--- Trace.h - Performance tracing facilities ---------------*- C++ -*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Supports writing performance traces describing clangd's behavior. | ||
// Traces are written 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 | ||
// | ||
// All APIs are no-ops unless a Session is active (created by ClangdMain). | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRACE_H_ | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRACE_H_ | ||
|
||
#include "llvm/ADT/Twine.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
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 { | ||
public: | ||
// Starts a sessions capturing trace events and writing Trace Event JSON. | ||
static std::unique_ptr<Session> create(llvm::raw_ostream &OS); | ||
~Session(); | ||
|
||
private: | ||
Session() = default; | ||
}; | ||
|
||
// Records a single instant event, associated with the current thread. | ||
void log(const llvm::Twine &Name); | ||
|
||
// Records an event whose duration is the lifetime of the Span object. | ||
class Span { | ||
public: | ||
Span(const llvm::Twine &Name); | ||
~Span(); | ||
|
||
private: | ||
}; | ||
|
||
} // namespace trace | ||
} // namespace clangd | ||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRACE_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# RUN: clangd -run-synchronously -trace %t < %s && FileCheck %s < %t | ||
# It is absolutely vital that this file has CRLF line endings. | ||
# | ||
Content-Length: 125 | ||
|
||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} | ||
# | ||
Content-Length: 152 | ||
|
||
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"void main() {}"}}} | ||
# CHECK: "textDocument/didOpen" | ||
# CHECK: "Preamble: /foo.c" | ||
# CHECK: "Build: /foo.c" | ||
Content-Length: 44 | ||
|
||
{"jsonrpc":"2.0","id":5,"method":"shutdown"} |
Oops, something went wrong.