1ad97ccf6SSam McCall //===-- TraceTests.cpp - Tracing unit tests ---------------------*- C++ -*-===//
2ad97ccf6SSam McCall //
3ad97ccf6SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ad97ccf6SSam McCall // See https://llvm.org/LICENSE.txt for license information.
5ad97ccf6SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ad97ccf6SSam McCall //
7ad97ccf6SSam McCall //===----------------------------------------------------------------------===//
8ad97ccf6SSam McCall
97016043dSKadir Cetinkaya #include "TestTracer.h"
10ad97ccf6SSam McCall #include "support/Trace.h"
11ad97ccf6SSam McCall #include "llvm/ADT/SmallString.h"
12e64f99c5SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
13ad97ccf6SSam McCall #include "llvm/Support/SourceMgr.h"
14ad97ccf6SSam McCall #include "llvm/Support/Threading.h"
15ad97ccf6SSam McCall #include "llvm/Support/YAMLParser.h"
16ad97ccf6SSam McCall #include "gmock/gmock.h"
17ad97ccf6SSam McCall #include "gtest/gtest.h"
18ad97ccf6SSam McCall
19ad97ccf6SSam McCall namespace clang {
20ad97ccf6SSam McCall namespace clangd {
21ad97ccf6SSam McCall namespace {
22ad97ccf6SSam McCall
239b88a190SSam McCall using testing::_;
249b88a190SSam McCall using testing::ElementsAre;
25e64f99c5SKadir Cetinkaya using testing::SizeIs;
269b88a190SSam McCall using testing::StartsWith;
27e64f99c5SKadir Cetinkaya
28*8edfc2f8SChristian Kühnel MATCHER_P(stringNode, Val, "") {
29ad97ccf6SSam McCall if (arg->getType() != llvm::yaml::Node::NK_Scalar) {
30ad97ccf6SSam McCall *result_listener << "is a " << arg->getVerbatimTag();
31ad97ccf6SSam McCall return false;
32ad97ccf6SSam McCall }
33ad97ccf6SSam McCall llvm::SmallString<32> S;
34ad97ccf6SSam McCall return Val == static_cast<llvm::yaml::ScalarNode *>(arg)->getValue(S);
35ad97ccf6SSam McCall }
36ad97ccf6SSam McCall
37ad97ccf6SSam McCall // Checks that N is a Mapping (JS object) with the expected scalar properties.
38ad97ccf6SSam McCall // The object must have all the Expected properties, but may have others.
verifyObject(llvm::yaml::Node & N,std::map<std::string,std::string> Expected)39*8edfc2f8SChristian Kühnel bool verifyObject(llvm::yaml::Node &N,
40ad97ccf6SSam McCall std::map<std::string, std::string> Expected) {
41ad97ccf6SSam McCall auto *M = llvm::dyn_cast<llvm::yaml::MappingNode>(&N);
42ad97ccf6SSam McCall if (!M) {
43ad97ccf6SSam McCall ADD_FAILURE() << "Not an object";
44ad97ccf6SSam McCall return false;
45ad97ccf6SSam McCall }
46ad97ccf6SSam McCall bool Match = true;
47ad97ccf6SSam McCall llvm::SmallString<32> Tmp;
48ad97ccf6SSam McCall for (auto &Prop : *M) {
49ad97ccf6SSam McCall auto *K = llvm::dyn_cast_or_null<llvm::yaml::ScalarNode>(Prop.getKey());
50ad97ccf6SSam McCall if (!K)
51ad97ccf6SSam McCall continue;
52ad97ccf6SSam McCall std::string KS = K->getValue(Tmp).str();
53ad97ccf6SSam McCall auto I = Expected.find(KS);
54ad97ccf6SSam McCall if (I == Expected.end())
55ad97ccf6SSam McCall continue; // Ignore properties with no assertion.
56ad97ccf6SSam McCall
57ad97ccf6SSam McCall auto *V = llvm::dyn_cast_or_null<llvm::yaml::ScalarNode>(Prop.getValue());
58ad97ccf6SSam McCall if (!V) {
59ad97ccf6SSam McCall ADD_FAILURE() << KS << " is not a string";
60ad97ccf6SSam McCall Match = false;
6168b48339SKadir Cetinkaya continue;
62ad97ccf6SSam McCall }
63ad97ccf6SSam McCall std::string VS = V->getValue(Tmp).str();
64ad97ccf6SSam McCall if (VS != I->second) {
65ad97ccf6SSam McCall ADD_FAILURE() << KS << " expected " << I->second << " but actual " << VS;
66ad97ccf6SSam McCall Match = false;
67ad97ccf6SSam McCall }
68ad97ccf6SSam McCall Expected.erase(I);
69ad97ccf6SSam McCall }
70ad97ccf6SSam McCall for (const auto &P : Expected) {
71ad97ccf6SSam McCall ADD_FAILURE() << P.first << " missing, expected " << P.second;
72ad97ccf6SSam McCall Match = false;
73ad97ccf6SSam McCall }
74ad97ccf6SSam McCall return Match;
75ad97ccf6SSam McCall }
76ad97ccf6SSam McCall
TEST(TraceTest,SmokeTest)77ad97ccf6SSam McCall TEST(TraceTest, SmokeTest) {
78ad97ccf6SSam McCall // Capture some events.
79ad97ccf6SSam McCall std::string JSON;
80ad97ccf6SSam McCall {
81ad97ccf6SSam McCall llvm::raw_string_ostream OS(JSON);
82ad97ccf6SSam McCall auto JSONTracer = trace::createJSONTracer(OS);
83ad97ccf6SSam McCall trace::Session Session(*JSONTracer);
84ad97ccf6SSam McCall {
85ad97ccf6SSam McCall trace::Span Tracer("A");
86ad97ccf6SSam McCall trace::log("B");
87ad97ccf6SSam McCall }
88ad97ccf6SSam McCall }
89ad97ccf6SSam McCall
90ad97ccf6SSam McCall // Get the root JSON object using the YAML parser.
91ad97ccf6SSam McCall llvm::SourceMgr SM;
92ad97ccf6SSam McCall llvm::yaml::Stream Stream(JSON, SM);
93ad97ccf6SSam McCall auto Doc = Stream.begin();
94ad97ccf6SSam McCall ASSERT_NE(Doc, Stream.end());
95ad97ccf6SSam McCall auto *Root = llvm::dyn_cast_or_null<llvm::yaml::MappingNode>(Doc->getRoot());
96ad97ccf6SSam McCall ASSERT_NE(Root, nullptr) << "Root should be an object";
97ad97ccf6SSam McCall
98ad97ccf6SSam McCall // Check whether we expect thread name events on this platform.
99ad97ccf6SSam McCall llvm::SmallString<32> ThreadName;
100ad97ccf6SSam McCall get_thread_name(ThreadName);
101ad97ccf6SSam McCall bool ThreadsHaveNames = !ThreadName.empty();
102ad97ccf6SSam McCall
103ad97ccf6SSam McCall // We expect in order:
104ad97ccf6SSam McCall // displayTimeUnit: "ns"
105ad97ccf6SSam McCall // traceEvents: [process name, thread name, start span, log, end span]
106ad97ccf6SSam McCall // (The order doesn't matter, but the YAML parser is awkward to use otherwise)
107ad97ccf6SSam McCall auto Prop = Root->begin();
108ad97ccf6SSam McCall ASSERT_NE(Prop, Root->end()) << "Expected displayTimeUnit property";
109*8edfc2f8SChristian Kühnel ASSERT_THAT(Prop->getKey(), stringNode("displayTimeUnit"));
110*8edfc2f8SChristian Kühnel EXPECT_THAT(Prop->getValue(), stringNode("ns"));
111ad97ccf6SSam McCall ASSERT_NE(++Prop, Root->end()) << "Expected traceEvents property";
112*8edfc2f8SChristian Kühnel EXPECT_THAT(Prop->getKey(), stringNode("traceEvents"));
113ad97ccf6SSam McCall auto *Events =
114ad97ccf6SSam McCall llvm::dyn_cast_or_null<llvm::yaml::SequenceNode>(Prop->getValue());
115ad97ccf6SSam McCall ASSERT_NE(Events, nullptr) << "traceEvents should be an array";
116ad97ccf6SSam McCall auto Event = Events->begin();
117ad97ccf6SSam McCall ASSERT_NE(Event, Events->end()) << "Expected process name";
118*8edfc2f8SChristian Kühnel EXPECT_TRUE(verifyObject(*Event, {{"ph", "M"}, {"name", "process_name"}}));
119ad97ccf6SSam McCall if (ThreadsHaveNames) {
120ad97ccf6SSam McCall ASSERT_NE(++Event, Events->end()) << "Expected thread name";
121*8edfc2f8SChristian Kühnel EXPECT_TRUE(verifyObject(*Event, {{"ph", "M"}, {"name", "thread_name"}}));
122ad97ccf6SSam McCall }
123ad97ccf6SSam McCall ASSERT_NE(++Event, Events->end()) << "Expected log message";
124*8edfc2f8SChristian Kühnel EXPECT_TRUE(verifyObject(*Event, {{"ph", "i"}, {"name", "Log"}}));
125ad97ccf6SSam McCall ASSERT_NE(++Event, Events->end()) << "Expected span end";
126*8edfc2f8SChristian Kühnel EXPECT_TRUE(verifyObject(*Event, {{"ph", "X"}, {"name", "A"}}));
127ad97ccf6SSam McCall ASSERT_EQ(++Event, Events->end());
128ad97ccf6SSam McCall ASSERT_EQ(++Prop, Root->end());
129ad97ccf6SSam McCall }
130ad97ccf6SSam McCall
TEST(MetricsTracer,LatencyTest)131e64f99c5SKadir Cetinkaya TEST(MetricsTracer, LatencyTest) {
132e64f99c5SKadir Cetinkaya trace::TestTracer Tracer;
133e64f99c5SKadir Cetinkaya constexpr llvm::StringLiteral MetricName = "span_latency";
134e64f99c5SKadir Cetinkaya constexpr llvm::StringLiteral OpName = "op_name";
135e64f99c5SKadir Cetinkaya {
136e64f99c5SKadir Cetinkaya // A span should record latencies to span_latency by default.
137e64f99c5SKadir Cetinkaya trace::Span SpanWithLat(OpName);
138e64f99c5SKadir Cetinkaya EXPECT_THAT(Tracer.takeMetric(MetricName, OpName), SizeIs(0));
139e64f99c5SKadir Cetinkaya }
140e64f99c5SKadir Cetinkaya EXPECT_THAT(Tracer.takeMetric(MetricName, OpName), SizeIs(1));
141e64f99c5SKadir Cetinkaya }
142e64f99c5SKadir Cetinkaya
1439b88a190SSam McCall class CSVMetricsTracerTest : public ::testing::Test {
1449b88a190SSam McCall protected:
CSVMetricsTracerTest()1459b88a190SSam McCall CSVMetricsTracerTest()
1469b88a190SSam McCall : OS(Output), Tracer(trace::createCSVMetricTracer(OS)), Session(*Tracer) {
1479b88a190SSam McCall }
1489b88a190SSam McCall trace::Metric Dist = {"dist", trace::Metric::Distribution, "lbl"};
1499b88a190SSam McCall trace::Metric Counter = {"cnt", trace::Metric::Counter};
1509b88a190SSam McCall
outputLines()1515bc0c8f0SSam McCall std::vector<std::string> outputLines() {
1529b88a190SSam McCall // Deliberately don't flush output stream, the tracer should do that.
1539b88a190SSam McCall // This is important when clangd crashes.
154ee02e20cSKirill Bobyrev llvm::SmallVector<llvm::StringRef> Lines;
1559b88a190SSam McCall llvm::StringRef(Output).split(Lines, "\r\n");
1569b88a190SSam McCall return {Lines.begin(), Lines.end()};
1579b88a190SSam McCall }
1589b88a190SSam McCall
1599b88a190SSam McCall std::string Output;
1609b88a190SSam McCall llvm::raw_string_ostream OS;
1619b88a190SSam McCall std::unique_ptr<trace::EventTracer> Tracer;
1629b88a190SSam McCall trace::Session Session;
1639b88a190SSam McCall };
1649b88a190SSam McCall
TEST_F(CSVMetricsTracerTest,RecordsValues)1659b88a190SSam McCall TEST_F(CSVMetricsTracerTest, RecordsValues) {
1669b88a190SSam McCall Dist.record(1, "x");
1679b88a190SSam McCall Counter.record(1, "");
1689b88a190SSam McCall Dist.record(2, "y");
1699b88a190SSam McCall
1707ebf7d91SSam McCall ASSERT_THAT(outputLines(),
1719b88a190SSam McCall ElementsAre("Kind,Metric,Label,Value,Timestamp",
17261559d04SSam McCall StartsWith("d,dist,x,1.000000e+00,"),
1739b88a190SSam McCall StartsWith("c,cnt,,1.000000e+00,"),
1749b88a190SSam McCall StartsWith("d,dist,y,2.000000e+00,"), ""));
1759b88a190SSam McCall }
1769b88a190SSam McCall
TEST_F(CSVMetricsTracerTest,Escaping)1779b88a190SSam McCall TEST_F(CSVMetricsTracerTest, Escaping) {
1789b88a190SSam McCall Dist.record(1, ",");
1799b88a190SSam McCall Dist.record(1, "a\"b");
1809b88a190SSam McCall Dist.record(1, "a\nb");
1819b88a190SSam McCall
1829b88a190SSam McCall EXPECT_THAT(outputLines(), ElementsAre(_, StartsWith(R"(d,dist,",",1)"),
1839b88a190SSam McCall StartsWith(R"(d,dist,"a""b",1)"),
1849b88a190SSam McCall StartsWith("d,dist,\"a\nb\",1"), ""));
1859b88a190SSam McCall }
1869b88a190SSam McCall
TEST_F(CSVMetricsTracerTest,IgnoresArgs)1878f1de22cSSam McCall TEST_F(CSVMetricsTracerTest, IgnoresArgs) {
1888f1de22cSSam McCall trace::Span Tracer("Foo");
1898f1de22cSSam McCall EXPECT_EQ(nullptr, Tracer.Args);
1908f1de22cSSam McCall }
1918f1de22cSSam McCall
192ad97ccf6SSam McCall } // namespace
193ad97ccf6SSam McCall } // namespace clangd
194ad97ccf6SSam McCall } // namespace clang
195