summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/Trace.cpp
blob: 5af8181737c2ab2b368dfa01ea3f85149a000acd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
OpenPOWER on IntegriCloud