1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 
7 #include <array>
8 #include <memory>
9 
10 #include <lib/async-loop/cpp/loop.h>
11 #include <trace-engine/context.h>
12 #include <trace-engine/handler.h>
13 #include <trace-engine/types.h>
14 #include <trace-provider/handler.h>
15 #include <trace-test-utils/compare_records.h>
16 #include <trace-test-utils/read_records.h>
17 #include <unittest/unittest.h>
18 
19 #include "trace-vthread/event_vthread.h"
20 
21 // Helper macros for writing tests.
22 #define STR_ARGS1 "k1", TA_STRING("v1")
23 #define STR_ARGS4 "k1", TA_STRING("v1"), "k2", TA_STRING("v2"), \
24                   "k3", TA_STRING("v3"), "k4", TA_STRING("v4")
25 
26 class TraceFixture : private trace::TraceHandler {
27 public:
28     static constexpr trace_buffering_mode_t kBufferingMode =
29         TRACE_BUFFERING_MODE_ONESHOT;
30 
31     static constexpr size_t kBufferSize = 1024 * 1024;
32 
TraceFixture()33     TraceFixture() {
34         buffer_.reset(new std::array<uint8_t, kBufferSize>);
35     }
36 
StartTracing()37     bool StartTracing() {
38         zx_status_t status = trace_start_engine(loop_.dispatcher(), this,
39                                                 kBufferingMode,
40                                                 buffer_->data(), buffer_->size());
41         return status == ZX_OK;
42     }
43 
StopTracing()44     bool StopTracing() {
45         zx_status_t status = trace_stop_engine(ZX_OK);
46         loop_.RunUntilIdle();
47         return status == ZX_OK;
48     }
49 
CompareBuffer(const char * expected)50     bool CompareBuffer(const char* expected) {
51         BEGIN_HELPER;
52         fbl::Vector<trace::Record> records;
53         ASSERT_TRUE(trace_testing::ReadRecords(buffer_->data(), buffer_->size(),
54                                                &records));
55         ASSERT_TRUE(trace_testing::CompareBuffer(records, expected));
56         END_HELPER;
57     }
58 
59 private:
60     async::Loop loop_{&kAsyncLoopConfigAttachToThread};
61 
62     std::unique_ptr<std::array<uint8_t, kBufferSize>> buffer_;
63 };
64 
TestVthreadDurationBegin()65 bool TestVthreadDurationBegin() {
66     TraceFixture fixture;
67 
68     BEGIN_TEST;
69 
70     ASSERT_TRUE(fixture.StartTracing());
71 
72     TRACE_VTHREAD_DURATION_BEGIN("+enabled", "name", "virtual-thread", 1u, zx_ticks_get());
73     TRACE_VTHREAD_DURATION_BEGIN("+enabled", "name", "virtual-thread", 1u, zx_ticks_get(), STR_ARGS1);
74     TRACE_VTHREAD_DURATION_BEGIN("+enabled", "name", "virtual-thread", 1u, zx_ticks_get(), STR_ARGS4);
75 
76     ASSERT_TRUE(fixture.StopTracing());
77 
78     ASSERT_TRUE(fixture.CompareBuffer("\
79 String(index: 1, \"+enabled\")\n\
80 String(index: 2, \"process\")\n\
81 KernelObject(koid: <>, type: thread, name: \"virtual-thread\", {process: koid(<>)})\n\
82 Thread(index: 1, <>)\n\
83 String(index: 3, \"name\")\n\
84 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationBegin, {})\n\
85 String(index: 4, \"k1\")\n\
86 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationBegin, {k1: string(\"v1\")})\n\
87 String(index: 5, \"k2\")\n\
88 String(index: 6, \"k3\")\n\
89 String(index: 7, \"k4\")\n\
90 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationBegin, {k1: string(\"v1\"), k2: string(\"v2\"), k3: string(\"v3\"), k4: string(\"v4\")})\n\
91 "));
92 
93     END_TEST;
94 }
95 
TestVthreadDurationEnd()96 bool TestVthreadDurationEnd() {
97     TraceFixture fixture;
98 
99     BEGIN_TEST;
100 
101     ASSERT_TRUE(fixture.StartTracing());
102 
103     TRACE_VTHREAD_DURATION_END("+enabled", "name", "virtual-thread", 1u, zx_ticks_get());
104     TRACE_VTHREAD_DURATION_END("+enabled", "name", "virtual-thread", 1u, zx_ticks_get(), STR_ARGS1);
105     TRACE_VTHREAD_DURATION_END("+enabled", "name", "virtual-thread", 1u, zx_ticks_get(), STR_ARGS4);
106 
107     ASSERT_TRUE(fixture.StopTracing());
108 
109     ASSERT_TRUE(fixture.CompareBuffer("\
110 String(index: 1, \"+enabled\")\n\
111 String(index: 2, \"process\")\n\
112 KernelObject(koid: <>, type: thread, name: \"virtual-thread\", {process: koid(<>)})\n\
113 Thread(index: 1, <>)\n\
114 String(index: 3, \"name\")\n\
115 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationEnd, {})\n\
116 String(index: 4, \"k1\")\n\
117 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationEnd, {k1: string(\"v1\")})\n\
118 String(index: 5, \"k2\")\n\
119 String(index: 6, \"k3\")\n\
120 String(index: 7, \"k4\")\n\
121 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", DurationEnd, {k1: string(\"v1\"), k2: string(\"v2\"), k3: string(\"v3\"), k4: string(\"v4\")})\n\
122 "));
123 
124     END_TEST;
125 }
126 
TestVthreadFlowBegin()127 bool TestVthreadFlowBegin() {
128     TraceFixture fixture;
129 
130     BEGIN_TEST;
131 
132     ASSERT_TRUE(fixture.StartTracing());
133 
134     TRACE_VTHREAD_FLOW_BEGIN("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get());
135     TRACE_VTHREAD_FLOW_BEGIN("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS1);
136     TRACE_VTHREAD_FLOW_BEGIN("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS4);
137 
138     ASSERT_TRUE(fixture.StopTracing());
139 
140     ASSERT_TRUE(fixture.CompareBuffer("\
141 String(index: 1, \"+enabled\")\n\
142 String(index: 2, \"process\")\n\
143 KernelObject(koid: <>, type: thread, name: \"virtual-thread\", {process: koid(<>)})\n\
144 Thread(index: 1, <>)\n\
145 String(index: 3, \"name\")\n\
146 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowBegin(id: 2), {})\n\
147 String(index: 4, \"k1\")\n\
148 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowBegin(id: 2), {k1: string(\"v1\")})\n\
149 String(index: 5, \"k2\")\n\
150 String(index: 6, \"k3\")\n\
151 String(index: 7, \"k4\")\n\
152 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowBegin(id: 2), {k1: string(\"v1\"), k2: string(\"v2\"), k3: string(\"v3\"), k4: string(\"v4\")})\n\
153 "));
154 
155     END_TEST;
156 }
157 
TestVthreadFlowStep()158 bool TestVthreadFlowStep() {
159     TraceFixture fixture;
160 
161     BEGIN_TEST;
162 
163     ASSERT_TRUE(fixture.StartTracing());
164 
165     TRACE_VTHREAD_FLOW_STEP("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get());
166     TRACE_VTHREAD_FLOW_STEP("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS1);
167     TRACE_VTHREAD_FLOW_STEP("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS4);
168 
169     ASSERT_TRUE(fixture.StopTracing());
170 
171     ASSERT_TRUE(fixture.CompareBuffer("\
172 String(index: 1, \"+enabled\")\n\
173 String(index: 2, \"process\")\n\
174 KernelObject(koid: <>, type: thread, name: \"virtual-thread\", {process: koid(<>)})\n\
175 Thread(index: 1, <>)\n\
176 String(index: 3, \"name\")\n\
177 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowStep(id: 2), {})\n\
178 String(index: 4, \"k1\")\n\
179 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowStep(id: 2), {k1: string(\"v1\")})\n\
180 String(index: 5, \"k2\")\n\
181 String(index: 6, \"k3\")\n\
182 String(index: 7, \"k4\")\n\
183 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowStep(id: 2), {k1: string(\"v1\"), k2: string(\"v2\"), k3: string(\"v3\"), k4: string(\"v4\")})\n\
184 "));
185 
186     END_TEST;
187 }
188 
TestVthreadFlowEnd()189 bool TestVthreadFlowEnd() {
190     TraceFixture fixture;
191 
192     BEGIN_TEST;
193 
194     ASSERT_TRUE(fixture.StartTracing());
195 
196     TRACE_VTHREAD_FLOW_END("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get());
197     TRACE_VTHREAD_FLOW_END("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS1);
198     TRACE_VTHREAD_FLOW_END("+enabled", "name", "virtual-thread", 1u, 2u, zx_ticks_get(), STR_ARGS4);
199 
200     ASSERT_TRUE(fixture.StopTracing());
201 
202     ASSERT_TRUE(fixture.CompareBuffer("\
203 String(index: 1, \"+enabled\")\n\
204 String(index: 2, \"process\")\n\
205 KernelObject(koid: <>, type: thread, name: \"virtual-thread\", {process: koid(<>)})\n\
206 Thread(index: 1, <>)\n\
207 String(index: 3, \"name\")\n\
208 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowEnd(id: 2), {})\n\
209 String(index: 4, \"k1\")\n\
210 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowEnd(id: 2), {k1: string(\"v1\")})\n\
211 String(index: 5, \"k2\")\n\
212 String(index: 6, \"k3\")\n\
213 String(index: 7, \"k4\")\n\
214 Event(ts: <>, pt: <>, category: \"+enabled\", name: \"name\", FlowEnd(id: 2), {k1: string(\"v1\"), k2: string(\"v2\"), k3: string(\"v3\"), k4: string(\"v4\")})\n\
215 "));
216 
217     END_TEST;
218 }
219 
220 BEGIN_TEST_CASE(event_thread_tests)
RUN_TEST(TestVthreadDurationBegin)221 RUN_TEST(TestVthreadDurationBegin)
222 RUN_TEST(TestVthreadDurationEnd)
223 RUN_TEST(TestVthreadFlowBegin)
224 RUN_TEST(TestVthreadFlowStep)
225 RUN_TEST(TestVthreadFlowEnd)
226 END_TEST_CASE(event_thread_tests)
227 
228 int main(int argc, char** argv) {
229     return unittest_run_all_tests(argc, argv) ? 0 : -1;
230 }
231