blob: 1607327ae738ee1fd39e40881cbde88122b793a3 [file] [log] [blame]
Sam McCall8567cb32017-11-02 09:21:51 +00001//===--- Trace.cpp - Performance tracing facilities -----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "Trace.h"
11
12#include "llvm/ADT/DenseSet.h"
13#include "llvm/Support/Chrono.h"
14#include "llvm/Support/FormatProviders.h"
15#include "llvm/Support/FormatVariadic.h"
16#include "llvm/Support/Threading.h"
17#include "llvm/Support/YAMLParser.h"
18#include <mutex>
19
20namespace clang {
21namespace clangd {
22namespace trace {
23using namespace llvm;
24
25namespace {
26// The current implementation is naive: each thread writes to Out guarded by Mu.
27// Perhaps we should replace this by something that disturbs performance less.
28class Tracer {
29public:
30 Tracer(raw_ostream &Out)
31 : Out(Out), Sep(""), Start(std::chrono::system_clock::now()) {
32 // The displayTimeUnit must be ns to avoid low-precision overlap
33 // calculations!
34 Out << R"({"displayTimeUnit":"ns","traceEvents":[)"
35 << "\n";
36 rawEvent("M", R"("name": "process_name", "args":{"name":"clangd"})");
37 }
38
39 ~Tracer() {
40 Out << "\n]}";
41 Out.flush();
42 }
43
44 // Record an event on the current thread. ph, pid, tid, ts are set.
45 // Contents must be a list of the other JSON key/values.
46 template <typename T> void event(StringRef Phase, const T &Contents) {
47 uint64_t TID = get_threadid();
48 std::lock_guard<std::mutex> Lock(Mu);
49 // If we haven't already, emit metadata describing this thread.
50 if (ThreadsWithMD.insert(TID).second) {
51 SmallString<32> Name;
52 get_thread_name(Name);
53 if (!Name.empty()) {
54 rawEvent(
55 "M",
56 formatv(
57 R"("tid": {0}, "name": "thread_name", "args":{"name":"{1}"})",
58 TID, StringRef(&Name[0], Name.size())));
59 }
60 }
61 rawEvent(Phase, formatv(R"("ts":{0}, "tid":{1}, {2})", timestamp(), TID,
62 Contents));
63 }
64
65private:
66 // Record an event. ph and pid are set.
67 // Contents must be a list of the other JSON key/values.
68 template <typename T>
69 void rawEvent(StringRef Phase, const T &Contents) /*REQUIRES(Mu)*/ {
70 // PID 0 represents the clangd process.
71 Out << Sep << R"({"pid":0, "ph":")" << Phase << "\", " << Contents << "}";
72 Sep = ",\n";
73 }
74
75 double timestamp() {
76 using namespace std::chrono;
Sam McCall3d9e0242017-11-15 17:53:46 +000077 return duration<double, std::micro>(system_clock::now() - Start).count();
Sam McCall8567cb32017-11-02 09:21:51 +000078 }
79
80 std::mutex Mu;
81 raw_ostream &Out /*GUARDED_BY(Mu)*/;
82 const char *Sep /*GUARDED_BY(Mu)*/;
83 DenseSet<uint64_t> ThreadsWithMD /*GUARDED_BY(Mu)*/;
84 const sys::TimePoint<> Start;
85};
86
87static Tracer *T = nullptr;
88} // namespace
89
90std::unique_ptr<Session> Session::create(raw_ostream &OS) {
91 assert(!T && "A session is already active");
92 T = new Tracer(OS);
93 return std::unique_ptr<Session>(new Session());
94}
95
96Session::~Session() {
97 delete T;
98 T = nullptr;
99}
100
101void log(const Twine &Message) {
102 if (!T)
103 return;
104 T->event("i", formatv(R"("name":"{0}")", yaml::escape(Message.str())));
105}
106
107Span::Span(const Twine &Text) {
108 if (!T)
109 return;
110 T->event("B", formatv(R"("name":"{0}")", yaml::escape(Text.str())));
111}
112
113Span::~Span() {
114 if (!T)
115 return;
116 T->event("E", R"("_":0)" /* Dummy property to ensure valid JSON */);
117}
118
119} // namespace trace
120} // namespace clangd
121} // namespace clang