1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include <sstream>
19
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
23#include "phosphor/trace_event.h"
24#include "phosphor/platform/thread.h"
25
26using phosphor::TraceEvent;
27using phosphor::TraceArgument;
28
29/*
30 * Basic tracepoint_info used in tests
31 */
32phosphor::tracepoint_info tpi = {
33    "category",
34    "name",
35    {{"arg1", "arg2"}}
36};
37
38TEST(TraceEvent, create) {
39    TraceEvent def;
40    (void)def;
41    TraceEvent event(
42        &tpi,
43        TraceEvent::Type::Instant,
44        0,
45        {{0, 0}},
46        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
47}
48
49TEST(TraceEvent, string_check) {
50    TraceEvent event(
51        &tpi,
52        TraceEvent::Type::Instant,
53        0,
54        {{0, 0}},
55        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
56
57    auto event_regex = testing::MatchesRegex(
58#if GTEST_USES_POSIX_RE
59        "TraceEvent<[0-9]+d [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{9}, "
60        "category, name, type=Instant, thread_id=0, "
61        "arg1=\"Type::is_none\", arg2=\"Type::is_none\">");
62#else
63        "TraceEvent<\\d+d \\d+:\\d+:\\d+.\\d+, "
64        "category, name, type=Instant, thread_id=0, "
65        "arg1=\"Type::is_none\", arg2=\"Type::is_none\">");
66#endif
67
68    // This should probably require linking against GoogleMock
69    // as well but I think we'll get away with it..
70    EXPECT_THAT(event.to_string(), event_regex);
71
72    std::stringstream s;
73    s << event;
74    EXPECT_THAT(s.str(), event_regex);
75}
76
77TEST(TraceEvent, typeToString) {
78    EXPECT_STREQ("AsyncStart",
79                 TraceEvent::typeToString(TraceEvent::Type::AsyncStart));
80    EXPECT_STREQ("AsyncEnd", TraceEvent::typeToString(TraceEvent::Type::AsyncEnd));
81    EXPECT_STREQ("SyncStart",
82                 TraceEvent::typeToString(TraceEvent::Type::SyncStart));
83    EXPECT_STREQ("SyncEnd", TraceEvent::typeToString(TraceEvent::Type::SyncEnd));
84    EXPECT_STREQ("Instant", TraceEvent::typeToString(TraceEvent::Type::Instant));
85    EXPECT_STREQ("GlobalInstant",
86                 TraceEvent::typeToString(TraceEvent::Type::GlobalInstant));
87    EXPECT_STREQ("Complete",
88                 TraceEvent::typeToString(TraceEvent::Type::Complete));
89    EXPECT_THROW(TraceEvent::typeToString(static_cast<TraceEvent::Type>(0xFF)),
90                 std::invalid_argument);
91}
92
93TEST(TraceEvent, toJSON) {
94    TraceEvent event(
95        &tpi,
96        TraceEvent::Type::Instant,
97        0,
98        {{0, 0}},
99        {{TraceArgument::Type::is_bool, TraceArgument::Type::is_none}});
100
101    auto event_regex = testing::MatchesRegex(
102#if GTEST_USES_POSIX_RE
103        "\\{\"name\":\"name\",\"cat\":\"category\",\"ph\":\"i\",\"s\":\"t\","
104        "\"ts\":[0-9]+,\"pid\":" +
105        std::to_string(phosphor::platform::getCurrentProcessID()) +
106        ",\"tid\":0,"
107        "\"args\":\\{\"arg1\":false\\}\\}");
108#else
109        "\\{\"name\":\"name\",\"cat\":\"category\",\"ph\":\"i\",\"s\":\"t\","
110        "\"ts\":\\d+,\"pid\":" +
111        std::to_string(phosphor::platform::getCurrentProcessID()) +
112        ",\"tid\":0,"
113        "\"args\":\\{\"arg1\":false\\}\\}");
114#endif
115    EXPECT_THAT(event.to_json(), event_regex);
116}
117
118TEST(TraceEvent, toJSONAlt) {
119    TraceEvent event(
120        &tpi,
121        TraceEvent::Type::SyncEnd,
122        0,
123        {{0, 0}},
124        {{TraceArgument::Type::is_bool, TraceArgument::Type::is_bool}});
125
126    auto event_regex = testing::MatchesRegex(
127#if GTEST_USES_POSIX_RE
128        "\\{\"name\":\"name\",\"cat\":\"category\",\"ph\":\"E\","
129        "\"ts\":[0-9]+,\"pid\":" +
130        std::to_string(phosphor::platform::getCurrentProcessID()) +
131        ",\"tid\":0,"
132        "\"args\":\\{\"arg1\":false,\"arg2\":false\\}\\}");
133#else
134        "\\{\"name\":\"name\",\"cat\":\"category\",\"ph\":\"E\","
135        "\"ts\":\\d+,\"pid\":" +
136        std::to_string(phosphor::platform::getCurrentProcessID()) +
137        ",\"tid\":0,"
138        "\"args\":\\{\"arg1\":false,\"arg2\":false\\}\\}");
139#endif
140    EXPECT_THAT(event.to_json(), event_regex);
141}
142
143class MockTraceEvent : public TraceEvent {
144public:
145    using TraceEvent::typeToJSON;
146
147    MockTraceEvent(
148        const phosphor::tracepoint_info* _tpi,
149        Type _type,
150        uint64_t _thread_id,
151        std::array<TraceArgument, phosphor::arg_count>&& _args,
152        std::array<TraceArgument::Type, phosphor::arg_count>&& _arg_types)
153        : TraceEvent(_tpi,
154                     _type,
155                     _thread_id,
156                     std::move(_args),
157                     std::move(_arg_types)) {}
158
159    MockTraceEvent(
160            const phosphor::tracepoint_info* _tpi,
161            uint64_t _thread_id,
162            std::chrono::steady_clock::time_point _start,
163            std::chrono::steady_clock::duration _duration,
164            std::array<TraceArgument, phosphor::arg_count>&& _args,
165            std::array<TraceArgument::Type, phosphor::arg_count>&& _arg_types)
166        : TraceEvent(_tpi,
167                     _thread_id,
168                     _start,
169                     _duration,
170                     std::move(_args),
171                     std::move(_arg_types)) {
172    }
173};
174
175TEST(TraceEventTypeToJSON, Instant) {
176    MockTraceEvent event(
177        &tpi,
178        TraceEvent::Type::Instant,
179        0,
180        {{0, 0}},
181        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
182    auto res = event.typeToJSON();
183    EXPECT_EQ("i", std::string(res.type));
184    EXPECT_EQ(",\"s\":\"t\"", res.extras);
185}
186
187TEST(TraceEventTypeToJSON, SyncStart) {
188    MockTraceEvent event(
189        &tpi,
190        TraceEvent::Type::SyncStart,
191        0,
192        {{0, 0}},
193        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
194    auto res = event.typeToJSON();
195    EXPECT_EQ("B", std::string(res.type));
196    EXPECT_EQ("", res.extras);
197}
198
199TEST(TraceEventTypeToJSON, SyncEnd) {
200    MockTraceEvent event(
201        &tpi,
202        TraceEvent::Type::SyncEnd,
203        0,
204        {{0, 0}},
205        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
206    auto res = event.typeToJSON();
207    EXPECT_EQ("E", std::string(res.type));
208    EXPECT_EQ("", res.extras);
209}
210
211TEST(TraceEventTypeToJSON, AsyncStart) {
212    MockTraceEvent event(
213        &tpi,
214        TraceEvent::Type::AsyncStart,
215        0,
216        {{0, 0}},
217        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
218    auto res = event.typeToJSON();
219    EXPECT_EQ("b", std::string(res.type));
220    EXPECT_EQ(",\"id\": \"0x0\"", res.extras);
221}
222
223TEST(TraceEventTypeToJSON, AsyncEnd) {
224    MockTraceEvent event(
225        &tpi,
226        TraceEvent::Type::AsyncEnd,
227        0,
228        {{0, 0}},
229        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
230    auto res = event.typeToJSON();
231    EXPECT_EQ("e", std::string(res.type));
232    EXPECT_EQ(",\"id\": \"0x0\"", res.extras);
233}
234
235TEST(TraceEventTypeToJSON, GlobalInstant) {
236    MockTraceEvent event(
237        &tpi,
238        TraceEvent::Type::GlobalInstant,
239        0,
240        {{0, 0}},
241        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
242    auto res = event.typeToJSON();
243    EXPECT_EQ("i", std::string(res.type));
244    EXPECT_EQ(",\"s\":\"g\"", res.extras);
245}
246
247TEST(TraceEventTypeToJSON, Complete) {
248    MockTraceEvent event(
249            &tpi,
250            0,
251            std::chrono::steady_clock::now(),
252            std::chrono::microseconds(1),
253            {{0, 0}},
254            {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
255    auto res = event.typeToJSON();
256    EXPECT_EQ("X", std::string(res.type));
257    EXPECT_EQ(",\"dur\":1.000", res.extras);
258}
259
260TEST(TraceEventTypeToJSON, Invalid) {
261    MockTraceEvent event(
262        &tpi,
263        static_cast<TraceEvent::Type>(0xFF),
264        0,
265        {{0, 0}},
266        {{TraceArgument::Type::is_none, TraceArgument::Type::is_none}});
267    EXPECT_THROW(event.typeToJSON(), std::invalid_argument);
268}
269
270TEST(TraceEventTypeToJSON, testProperties) {
271    using namespace testing;
272
273    phosphor::tracepoint_info tpi2 = {
274        "my_category",
275        "my_name",
276        {{"my_arg1", "my_arg2"}}
277    };
278
279    TraceEvent event(
280            &tpi2,
281            TraceEvent::Type::Instant,
282            0,
283            {{0, 4.5}},
284            {{TraceArgument::Type::is_int, TraceArgument::Type::is_double}});
285    EXPECT_STREQ("my_category", event.getCategory());
286    EXPECT_STREQ("my_name", event.getName());
287    EXPECT_THAT(event.getArgNames(), testing::ElementsAre(StrEq("my_arg1"),
288                                                          StrEq("my_arg2")));
289    EXPECT_EQ(TraceEvent::Type::Instant, event.getType());
290    EXPECT_EQ(0, event.getArgs()[0].as_int);
291    EXPECT_EQ(4.5, event.getArgs()[1].as_double);
292    EXPECT_EQ(TraceArgument::Type::is_int, event.getArgTypes()[0]);
293    EXPECT_EQ(TraceArgument::Type::is_double, event.getArgTypes()[1]);
294}
295