blob: 28dbfc74b743e43080d88b2dc2013fd79a231515 [file] [log] [blame]
Primiano Tucci4f9b6d72017-12-05 20:59:16 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "cpu_reader.h"
18
19#include "ftrace_procfs.h"
20#include "gtest/gtest.h"
21#include "proto_translation_table.h"
22
23#include "perfetto/protozero/scattered_stream_writer.h"
Primiano Tuccib03ba362017-12-06 09:47:41 +000024#include "src/ftrace_reader/test/scattered_stream_delegate_for_testing.h"
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000025
26#include "protos/ftrace/ftrace_event.pb.h"
27#include "protos/ftrace/ftrace_event_bundle.pb.h"
28#include "protos/ftrace/ftrace_event_bundle.pbzero.h"
29
30namespace perfetto {
31
32namespace {
33
34const size_t kPageSize = 4096;
Hector Dearman71372ea2017-12-06 17:13:37 +000035const uint64_t kNanoInSecond = 1000 * 1000 * 1000;
36const uint64_t kNanoInMicro = 1000;
37
38::testing::AssertionResult WithinOneMicrosecond(uint64_t actual_ns,
39 uint64_t expected_s,
40 uint64_t expected_us) {
41 // Round to closest us.
42 uint64_t actual_us = (actual_ns + kNanoInMicro / 2) / kNanoInMicro;
43 uint64_t total_expected_us = expected_s * 1000 * 1000 + expected_us;
44 if (actual_us == total_expected_us) {
45 return ::testing::AssertionSuccess();
46 } else {
47 return ::testing::AssertionFailure()
48 << actual_ns / kNanoInSecond << "."
49 << (actual_ns % kNanoInSecond) / kNanoInMicro << " vs. "
50 << expected_s << "." << expected_us;
51 }
52}
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000053
Hector Dearman5add6542017-12-06 15:47:42 +000054struct ExamplePage {
55 // The name of the format file set used in the collection of this example
56 // page. Should name a directory under src/ftrace_reader/test/data
57 const char* name;
58 // The non-zero prefix of xxd'ing the page.
59 const char* data;
60};
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000061
Hector Dearman5add6542017-12-06 15:47:42 +000062// Single class to manage the whole protozero -> scattered stream -> chunks ->
63// single buffer -> real proto dance. Has a method: writer() to get an
64// protozero ftrace bundle writer and a method GetBundle() to attempt to
65// parse whatever has been written so far into a proto message.
66class BundleProvider {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000067 public:
Hector Dearman5add6542017-12-06 15:47:42 +000068 explicit BundleProvider(size_t chunk_size)
69 : chunk_size_(chunk_size), delegate_(chunk_size_), stream_(&delegate_) {
70 delegate_.set_writer(&stream_);
71 writer_.Reset(&stream_);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000072 }
Hector Dearman5add6542017-12-06 15:47:42 +000073 ~BundleProvider() = default;
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000074
Hector Dearman5add6542017-12-06 15:47:42 +000075 protos::pbzero::FtraceEventBundle* writer() { return &writer_; }
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000076
Hector Dearman5add6542017-12-06 15:47:42 +000077 // Stitch together the scattered chunks into a single buffer then attempt
78 // to parse the buffer as a FtraceEventBundle. Returns the FtraceEventBundle
79 // on success and nullptr on failure.
80 std::unique_ptr<protos::FtraceEventBundle> GetBundle() {
81 auto bundle = std::unique_ptr<protos::FtraceEventBundle>(
82 new protos::FtraceEventBundle());
83 size_t msg_size =
84 delegate_.chunks().size() * chunk_size_ - stream_.bytes_available();
85 std::unique_ptr<uint8_t[]> buffer = delegate_.StitchChunks(msg_size);
86 if (!bundle->ParseFromArray(buffer.get(), static_cast<int>(msg_size)))
87 return nullptr;
88 return bundle;
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000089 }
90
91 private:
Hector Dearman5add6542017-12-06 15:47:42 +000092 BundleProvider(const BundleProvider&) = delete;
93 BundleProvider& operator=(const BundleProvider&) = delete;
94
95 size_t chunk_size_;
96 perfetto::ScatteredStreamDelegateForTesting delegate_;
97 protozero::ScatteredStreamWriter stream_;
98 protos::pbzero::FtraceEventBundle writer_;
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000099};
100
Hector Dearman5add6542017-12-06 15:47:42 +0000101// Create a ProtoTranslationTable uing the fomat files in
102// directory |name|. Caches the table for subsequent lookups.
103std::map<std::string, std::unique_ptr<ProtoTranslationTable>>* g_tables;
104ProtoTranslationTable* GetTable(const std::string& name) {
105 if (!g_tables)
106 g_tables =
107 new std::map<std::string, std::unique_ptr<ProtoTranslationTable>>();
108 if (!g_tables->count(name)) {
109 std::string path = "src/ftrace_reader/test/data/" + name + "/";
110 FtraceProcfs ftrace(path);
111 auto table = ProtoTranslationTable::Create(&ftrace);
112 g_tables->emplace(name, std::move(table));
113 }
114 return g_tables->at(name).get();
115}
116
117// Convert xxd output into binary data.
118std::unique_ptr<uint8_t[]> PageFromXxd(const std::string& text) {
119 auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[kPageSize]);
120 const char* ptr = text.data();
121 memset(buffer.get(), 0xfa, kPageSize);
122 uint8_t* out = buffer.get();
123 while (*ptr != '\0') {
124 if (*(ptr++) != ':')
125 continue;
126 for (int i = 0; i < 8; i++) {
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800127 PERFETTO_CHECK(text.size() >=
128 static_cast<size_t>((ptr - text.data()) + 5));
Hector Dearman5add6542017-12-06 15:47:42 +0000129 PERFETTO_CHECK(*(ptr++) == ' ');
130 int n = sscanf(ptr, "%02hhx%02hhx", out, out + 1);
131 PERFETTO_CHECK(n == 2);
132 out += n;
133 ptr += 4;
134 }
135 while (*ptr != '\n')
136 ptr++;
137 }
138 return buffer;
139}
140
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000141} // namespace
142
Hector Dearman5add6542017-12-06 15:47:42 +0000143TEST(PageFromXxdTest, OneLine) {
144 std::string text = R"(
145 00000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
146 00000000: 0000 0000 5600 0000 0000 0000 0000 0000 ................
147 )";
148 auto page = PageFromXxd(text);
149 EXPECT_EQ(page.get()[0x14], 0x56);
150}
151
152TEST(PageFromXxdTest, ManyLines) {
153 std::string text = R"(
154 00000000: 1234 0000 0000 0000 0000 0000 0000 0056 ................
155 00000010: 7800 0000 0000 0000 0000 0000 0000 009a ................
156 00000020: 0000 0000 bc00 0000 00de 0000 0000 009a ................
157 )";
158 auto page = PageFromXxd(text);
159 EXPECT_EQ(page.get()[0x00], 0x12);
160 EXPECT_EQ(page.get()[0x01], 0x34);
161 EXPECT_EQ(page.get()[0x0f], 0x56);
162 EXPECT_EQ(page.get()[0x10], 0x78);
163 EXPECT_EQ(page.get()[0x1f], 0x9a);
164 EXPECT_EQ(page.get()[0x24], 0xbc);
165 EXPECT_EQ(page.get()[0x29], 0xde);
166}
167
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000168TEST(EventFilterTest, EventFilter) {
169 using Event = ProtoTranslationTable::Event;
170 using Field = ProtoTranslationTable::Field;
171
172 std::vector<Field> common_fields;
173 std::vector<Event> events;
174
175 {
176 Event event;
177 event.name = "foo";
178 event.ftrace_event_id = 1;
179 events.push_back(event);
180 }
181
182 {
183 Event event;
184 event.name = "bar";
185 event.ftrace_event_id = 10;
186 events.push_back(event);
187 }
188
189 ProtoTranslationTable table(events, std::move(common_fields));
190 EventFilter filter(table, std::set<std::string>({"foo"}));
191
192 EXPECT_TRUE(filter.IsEventEnabled(1));
193 EXPECT_FALSE(filter.IsEventEnabled(2));
194 EXPECT_FALSE(filter.IsEventEnabled(10));
195}
196
Hector Dearman5add6542017-12-06 15:47:42 +0000197TEST(ReadAndAdvanceTest, Number) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000198 uint64_t expected = 42;
199 uint64_t actual = 0;
200 uint8_t buffer[8] = {};
201 const uint8_t* start = buffer;
202 const uint8_t* ptr = buffer;
203 memcpy(&buffer, &expected, 8);
204 EXPECT_TRUE(CpuReader::ReadAndAdvance<uint64_t>(&ptr, ptr + 8, &actual));
205 EXPECT_EQ(ptr, start + 8);
206 EXPECT_EQ(actual, expected);
207}
208
Hector Dearman5add6542017-12-06 15:47:42 +0000209TEST(ReadAndAdvanceTest, PlainStruct) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000210 struct PlainStruct {
211 uint64_t timestamp;
212 uint64_t length;
213 };
214
215 uint64_t expected[2] = {42, 999};
216 PlainStruct actual;
217 uint8_t buffer[16] = {};
218 const uint8_t* start = buffer;
219 const uint8_t* ptr = buffer;
220 memcpy(&buffer, &expected, 16);
221 EXPECT_TRUE(CpuReader::ReadAndAdvance<PlainStruct>(&ptr, ptr + 16, &actual));
222 EXPECT_EQ(ptr, start + 16);
223 EXPECT_EQ(actual.timestamp, 42ul);
224 EXPECT_EQ(actual.length, 999ul);
225}
226
Hector Dearman5add6542017-12-06 15:47:42 +0000227TEST(ReadAndAdvanceTest, ComplexStruct) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000228 struct ComplexStruct {
229 uint64_t timestamp;
230 uint32_t length;
231 uint32_t : 24;
232 uint32_t overwrite : 8;
233 };
234
235 uint64_t expected[2] = {42, 0xcdffffffabababab};
236 ComplexStruct actual = {};
237 uint8_t buffer[16] = {};
238 const uint8_t* start = buffer;
239 const uint8_t* ptr = buffer;
240 memcpy(&buffer, &expected, 16);
241 EXPECT_TRUE(
242 CpuReader::ReadAndAdvance<ComplexStruct>(&ptr, ptr + 16, &actual));
243 EXPECT_EQ(ptr, start + 16);
244 EXPECT_EQ(actual.timestamp, 42ul);
245 EXPECT_EQ(actual.length, 0xabababab);
246 EXPECT_EQ(actual.overwrite, 0xCDu);
247}
248
Hector Dearman5add6542017-12-06 15:47:42 +0000249TEST(ReadAndAdvanceTest, Overruns) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000250 uint64_t result = 42;
251 uint8_t buffer[7] = {};
252 const uint8_t* start = buffer;
253 const uint8_t* ptr = buffer;
254 EXPECT_FALSE(CpuReader::ReadAndAdvance<uint64_t>(&ptr, ptr + 7, &result));
255 EXPECT_EQ(ptr, start);
256 EXPECT_EQ(result, 42ul);
257}
258
Hector Dearman5add6542017-12-06 15:47:42 +0000259TEST(ReadAndAdvanceTest, AtEnd) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000260 uint8_t result = 42;
261 uint8_t buffer[8] = {};
262 const uint8_t* start = buffer;
263 const uint8_t* ptr = buffer;
264 EXPECT_FALSE(CpuReader::ReadAndAdvance<uint8_t>(&ptr, ptr, &result));
265 EXPECT_EQ(ptr, start);
266 EXPECT_EQ(result, 42);
267}
268
Hector Dearman5add6542017-12-06 15:47:42 +0000269TEST(ReadAndAdvanceTest, Underruns) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000270 uint64_t expected = 42;
271 uint64_t actual = 0;
272 uint8_t buffer[9] = {};
273 const uint8_t* start = buffer;
274 const uint8_t* ptr = buffer;
275 memcpy(&buffer, &expected, 8);
276 EXPECT_TRUE(CpuReader::ReadAndAdvance<uint64_t>(&ptr, ptr + 8, &actual));
277 EXPECT_EQ(ptr, start + 8);
278 EXPECT_EQ(actual, expected);
279}
280
Hector Dearman5add6542017-12-06 15:47:42 +0000281// # tracer: nop
282// #
283// # entries-in-buffer/entries-written: 1/1 #P:8
284// #
285// # _-----=> irqs-off
286// # / _----=> need-resched
287// # | / _---=> hardirq/softirq
288// # || / _--=> preempt-depth
289// # ||| / delay
290// # TASK-PID CPU# |||| TIMESTAMP FUNCTION
291// # | | | |||| | |
292// sh-28712 [000] ...1 608934.535199: tracing_mark_write: Hello, world!
293ExamplePage g_single_print{
294 "synthetic",
295 R"(
296 00000000: ba12 6a33 c628 0200 2c00 0000 0000 0000 ..j3.(..,.......
297 00000010: def0 ec67 8d21 0000 0800 0000 0500 0001 ...g.!..........
298 00000020: 2870 0000 ac5d 1661 86ff ffff 4865 6c6c (p...].a....Hell
299 00000030: 6f2c 2077 6f72 6c64 210a 00ff 0000 0000 o, world!.......
300 )",
301};
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000302
Hector Dearman5add6542017-12-06 15:47:42 +0000303TEST(CpuReaderTest, ParseSinglePrint) {
304 const ExamplePage* test_case = &g_single_print;
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000305
Hector Dearman5add6542017-12-06 15:47:42 +0000306 BundleProvider bundle_provider(kPageSize);
307 ProtoTranslationTable* table = GetTable(test_case->name);
308 auto page = PageFromXxd(test_case->data);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000309
310 EventFilter filter(*table, std::set<std::string>({"print"}));
311
Hector Dearman6fd5e5c2017-12-06 15:48:58 +0000312 CpuReader::ParsePage(42 /* cpu number */, page.get(), &filter,
Hector Dearman5add6542017-12-06 15:47:42 +0000313 bundle_provider.writer(), table);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000314
Hector Dearman5add6542017-12-06 15:47:42 +0000315 auto bundle = bundle_provider.GetBundle();
316 ASSERT_TRUE(bundle);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800317 EXPECT_EQ(bundle->cpu(), 42ul);
Hector Dearman5add6542017-12-06 15:47:42 +0000318 ASSERT_EQ(bundle->event().size(), 1);
319 const protos::FtraceEvent& event = bundle->event().Get(0);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800320 EXPECT_EQ(event.pid(), 28712ul);
Hector Dearman71372ea2017-12-06 17:13:37 +0000321 EXPECT_TRUE(WithinOneMicrosecond(event.timestamp(), 608934, 535199));
Hector Dearman5add6542017-12-06 15:47:42 +0000322 EXPECT_EQ(event.print().buf(), "Hello, world!\n");
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000323}
324
Hector Dearman71372ea2017-12-06 17:13:37 +0000325// # tracer: nop
326// #
327// # entries-in-buffer/entries-written: 3/3 #P:8
328// #
329// # _-----=> irqs-off
330// # / _----=> need-resched
331// # | / _---=> hardirq/softirq
332// # || / _--=> preempt-depth
333// # ||| / delay
334// # TASK-PID CPU# |||| TIMESTAMP FUNCTION
335// # | | | |||| | |
336// sh-30693 [000] ...1 615436.216806: tracing_mark_write: Hello, world!
337// sh-30693 [000] ...1 615486.377232: tracing_mark_write: Good afternoon, world!
338// sh-30693 [000] ...1 615495.632679: tracing_mark_write: Goodbye, world!
339ExamplePage g_three_prints{
340 "synthetic",
341 R"(
342 00000000: a3ab 1569 bc2f 0200 9400 0000 0000 0000 ...i./..........
343 00000010: 1e00 0000 0000 0000 0800 0000 0500 0001 ................
344 00000020: e577 0000 ac5d 1661 86ff ffff 4865 6c6c .w...].a....Hell
345 00000030: 6f2c 2077 6f72 6c64 210a 0000 5e32 6bb9 o, world!...^2k.
346 00000040: 7501 0000 0b00 0000 0500 0001 e577 0000 u............w..
347 00000050: ac5d 1661 86ff ffff 476f 6f64 2061 6674 .].a....Good aft
348 00000060: 6572 6e6f 6f6e 2c20 776f 726c 6421 0a00 ernoon, world!..
349 00000070: 0000 0000 9e6a 5df5 4400 0000 0900 0000 .....j].D.......
350 00000080: 0500 0001 e577 0000 ac5d 1661 86ff ffff .....w...].a....
351 00000090: 476f 6f64 6279 652c 2077 6f72 6c64 210a Goodbye, world!.
352 000000a0: 0051 0000 0000 0000 0000 0000 0000 0000 .Q..............
353 )",
354};
355
356TEST(CpuReaderTest, ParseThreePrint) {
357 const ExamplePage* test_case = &g_three_prints;
358
359 BundleProvider bundle_provider(kPageSize);
360 ProtoTranslationTable* table = GetTable(test_case->name);
361 auto page = PageFromXxd(test_case->data);
362
363 EventFilter filter(*table, std::set<std::string>({"print"}));
364
365 CpuReader::ParsePage(42 /* cpu number */, page.get(), &filter,
366 bundle_provider.writer(), table);
367
368 auto bundle = bundle_provider.GetBundle();
369 ASSERT_TRUE(bundle);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800370 EXPECT_EQ(bundle->cpu(), 42ul);
Hector Dearman71372ea2017-12-06 17:13:37 +0000371 ASSERT_EQ(bundle->event().size(), 3);
372
373 {
374 const protos::FtraceEvent& event = bundle->event().Get(0);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800375 EXPECT_EQ(event.pid(), 30693ul);
Hector Dearman71372ea2017-12-06 17:13:37 +0000376 EXPECT_TRUE(WithinOneMicrosecond(event.timestamp(), 615436, 216806));
377 EXPECT_EQ(event.print().buf(), "Hello, world!\n");
378 }
379
380 {
381 const protos::FtraceEvent& event = bundle->event().Get(1);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800382 EXPECT_EQ(event.pid(), 30693ul);
Hector Dearman71372ea2017-12-06 17:13:37 +0000383 EXPECT_TRUE(WithinOneMicrosecond(event.timestamp(), 615486, 377232));
384 EXPECT_EQ(event.print().buf(), "Good afternoon, world!\n");
385 }
386
387 {
388 const protos::FtraceEvent& event = bundle->event().Get(2);
Oystein Eftevaaged4df192017-12-07 10:52:58 -0800389 EXPECT_EQ(event.pid(), 30693ul);
Hector Dearman71372ea2017-12-06 17:13:37 +0000390 EXPECT_TRUE(WithinOneMicrosecond(event.timestamp(), 615495, 632679));
391 EXPECT_EQ(event.print().buf(), "Goodbye, world!\n");
392 }
393}
394
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000395} // namespace perfetto