1 /*
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 "src/traced/probes/ftrace/ftrace_controller.h"
18
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22
23 #include "perfetto/ext/base/file_utils.h"
24 #include "src/traced/probes/ftrace/compact_sched.h"
25 #include "src/traced/probes/ftrace/cpu_reader.h"
26 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
27 #include "src/traced/probes/ftrace/ftrace_config_utils.h"
28 #include "src/traced/probes/ftrace/ftrace_data_source.h"
29 #include "src/traced/probes/ftrace/ftrace_procfs.h"
30 #include "src/traced/probes/ftrace/proto_translation_table.h"
31 #include "src/tracing/core/trace_writer_for_testing.h"
32 #include "test/gtest_and_gmock.h"
33
34 #include "protos/perfetto/trace/ftrace/ftrace_stats.gen.h"
35 #include "protos/perfetto/trace/ftrace/ftrace_stats.pbzero.h"
36 #include "protos/perfetto/trace/trace_packet.gen.h"
37 #include "protos/perfetto/trace/trace_packet.pbzero.h"
38
39 using testing::_;
40 using testing::AnyNumber;
41 using testing::ByMove;
42 using testing::ElementsAre;
43 using testing::Invoke;
44 using testing::IsEmpty;
45 using testing::MatchesRegex;
46 using testing::Mock;
47 using testing::NiceMock;
48 using testing::Pair;
49 using testing::Return;
50 using testing::UnorderedElementsAre;
51
52 using Table = perfetto::ProtoTranslationTable;
53
54 namespace perfetto {
55
56 namespace {
57
58 constexpr char kFooEnablePath[] = "/root/events/group/foo/enable";
59 constexpr char kBarEnablePath[] = "/root/events/group/bar/enable";
60
61 class MockTaskRunner : public base::TaskRunner {
62 public:
63 MOCK_METHOD1(PostTask, void(std::function<void()>));
64 MOCK_METHOD2(PostDelayedTask, void(std::function<void()>, uint32_t delay_ms));
65 MOCK_METHOD2(AddFileDescriptorWatch, void(int fd, std::function<void()>));
66 MOCK_METHOD1(RemoveFileDescriptorWatch, void(int fd));
67 MOCK_CONST_METHOD0(RunsTasksOnCurrentThread, bool());
68 };
69
FakeTable(FtraceProcfs * ftrace)70 std::unique_ptr<Table> FakeTable(FtraceProcfs* ftrace) {
71 std::vector<Field> common_fields;
72 std::vector<Event> events;
73 {
74 events.push_back(Event{});
75 auto& event = events.back();
76 event.name = "foo";
77 event.group = "group";
78 event.ftrace_event_id = 1;
79 }
80 {
81 events.push_back(Event{});
82 auto& event = events.back();
83 event.name = "bar";
84 event.group = "group";
85 event.ftrace_event_id = 10;
86 }
87
88 return std::unique_ptr<Table>(
89 new Table(ftrace, events, std::move(common_fields),
90 ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
91 InvalidCompactSchedEventFormatForTesting(), PrintkMap()));
92 }
93
FakeModel(FtraceProcfs * ftrace,ProtoTranslationTable * table)94 std::unique_ptr<FtraceConfigMuxer> FakeModel(FtraceProcfs* ftrace,
95 ProtoTranslationTable* table) {
96 return std::unique_ptr<FtraceConfigMuxer>(
97 new FtraceConfigMuxer(ftrace, table, {}));
98 }
99
100 class MockFtraceProcfs : public FtraceProcfs {
101 public:
MockFtraceProcfs(size_t cpu_count=1)102 explicit MockFtraceProcfs(size_t cpu_count = 1) : FtraceProcfs("/root/") {
103 ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(cpu_count));
104 EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
105
106 ON_CALL(*this, ReadFileIntoString("/root/trace_clock"))
107 .WillByDefault(Return("local global [boot]"));
108 EXPECT_CALL(*this, ReadFileIntoString("/root/trace_clock"))
109 .Times(AnyNumber());
110
111 ON_CALL(*this, ReadFileIntoString("/root/per_cpu/cpu0/stats"))
112 .WillByDefault(Return(""));
113 EXPECT_CALL(*this, ReadFileIntoString("/root/per_cpu/cpu0/stats"))
114 .Times(AnyNumber());
115
116 ON_CALL(*this, ReadFileIntoString("/root/events//not_an_event/format"))
117 .WillByDefault(Return(""));
118 EXPECT_CALL(*this, ReadFileIntoString("/root/events//not_an_event/format"))
119 .Times(AnyNumber());
120
121 ON_CALL(*this, ReadFileIntoString("/root/events/group/bar/format"))
122 .WillByDefault(Return(""));
123 EXPECT_CALL(*this, ReadFileIntoString("/root/events/group/bar/format"))
124 .Times(AnyNumber());
125
126 ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
127 ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
128
129 ON_CALL(*this, WriteToFile("/root/tracing_on", _))
130 .WillByDefault(Invoke(this, &MockFtraceProcfs::WriteTracingOn));
131 ON_CALL(*this, ReadOneCharFromFile("/root/tracing_on"))
132 .WillByDefault(Invoke(this, &MockFtraceProcfs::ReadTracingOn));
133 EXPECT_CALL(*this, ReadOneCharFromFile("/root/tracing_on"))
134 .Times(AnyNumber());
135 }
136
WriteTracingOn(const std::string &,const std::string & value)137 bool WriteTracingOn(const std::string& /*path*/, const std::string& value) {
138 PERFETTO_CHECK(value == "1" || value == "0");
139 tracing_on_ = value == "1";
140 return true;
141 }
142
ReadTracingOn(const std::string &)143 char ReadTracingOn(const std::string& /*path*/) {
144 return tracing_on_ ? '1' : '0';
145 }
146
OpenPipeForCpu(size_t)147 base::ScopedFile OpenPipeForCpu(size_t /*cpu*/) override {
148 return base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY));
149 }
150
151 MOCK_METHOD2(WriteToFile,
152 bool(const std::string& path, const std::string& str));
153 MOCK_CONST_METHOD0(NumberOfCpus, size_t());
154 MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
155 MOCK_METHOD1(ClearFile, bool(const std::string& path));
156 MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
157
is_tracing_on()158 bool is_tracing_on() { return tracing_on_; }
159
160 private:
161 bool tracing_on_ = false;
162 };
163
164 } // namespace
165
166 class TestFtraceController : public FtraceController,
167 public FtraceController::Observer {
168 public:
TestFtraceController(std::unique_ptr<MockFtraceProcfs> ftrace_procfs,std::unique_ptr<Table> table,std::unique_ptr<FtraceConfigMuxer> model,std::unique_ptr<MockTaskRunner> runner,MockFtraceProcfs * raw_procfs)169 TestFtraceController(std::unique_ptr<MockFtraceProcfs> ftrace_procfs,
170 std::unique_ptr<Table> table,
171 std::unique_ptr<FtraceConfigMuxer> model,
172 std::unique_ptr<MockTaskRunner> runner,
173 MockFtraceProcfs* raw_procfs)
174 : FtraceController(std::move(ftrace_procfs),
175 std::move(table),
176 std::move(model),
177 runner.get(),
178 /*observer=*/this),
179 runner_(std::move(runner)),
180 procfs_(raw_procfs) {}
181
runner()182 MockTaskRunner* runner() { return runner_.get(); }
procfs()183 MockFtraceProcfs* procfs() { return procfs_; }
NowMs() const184 uint64_t NowMs() const override { return now_ms; }
drain_period_ms()185 uint32_t drain_period_ms() { return GetDrainPeriodMs(); }
186
AddFakeDataSource(const FtraceConfig & cfg)187 std::unique_ptr<FtraceDataSource> AddFakeDataSource(const FtraceConfig& cfg) {
188 std::unique_ptr<FtraceDataSource> data_source(new FtraceDataSource(
189 GetWeakPtr(), 0 /* session id */, cfg, nullptr /* trace_writer */));
190 if (!AddDataSource(data_source.get()))
191 return nullptr;
192 return data_source;
193 }
194
OnFtraceDataWrittenIntoDataSourceBuffers()195 void OnFtraceDataWrittenIntoDataSourceBuffers() override {}
196
197 uint64_t now_ms = 0;
198
199 private:
200 TestFtraceController(const TestFtraceController&) = delete;
201 TestFtraceController& operator=(const TestFtraceController&) = delete;
202
203 std::unique_ptr<MockTaskRunner> runner_;
204 MockFtraceProcfs* procfs_;
205 };
206
207 namespace {
208
CreateTestController(bool procfs_is_nice_mock,size_t cpu_count=1)209 std::unique_ptr<TestFtraceController> CreateTestController(
210 bool procfs_is_nice_mock,
211 size_t cpu_count = 1) {
212 std::unique_ptr<MockTaskRunner> runner =
213 std::unique_ptr<MockTaskRunner>(new NiceMock<MockTaskRunner>());
214
215 std::unique_ptr<MockFtraceProcfs> ftrace_procfs;
216 if (procfs_is_nice_mock) {
217 ftrace_procfs = std::unique_ptr<MockFtraceProcfs>(
218 new NiceMock<MockFtraceProcfs>(cpu_count));
219 } else {
220 ftrace_procfs =
221 std::unique_ptr<MockFtraceProcfs>(new MockFtraceProcfs(cpu_count));
222 }
223
224 auto table = FakeTable(ftrace_procfs.get());
225
226 auto model = FakeModel(ftrace_procfs.get(), table.get());
227
228 MockFtraceProcfs* raw_procfs = ftrace_procfs.get();
229 return std::unique_ptr<TestFtraceController>(new TestFtraceController(
230 std::move(ftrace_procfs), std::move(table), std::move(model),
231 std::move(runner), raw_procfs));
232 }
233
234 } // namespace
235
TEST(FtraceControllerTest,NonExistentEventsDontCrash)236 TEST(FtraceControllerTest, NonExistentEventsDontCrash) {
237 auto controller = CreateTestController(true /* nice procfs */);
238
239 FtraceConfig config = CreateFtraceConfig({"not_an_event"});
240 EXPECT_TRUE(controller->AddFakeDataSource(config));
241 }
242
TEST(FtraceControllerTest,RejectsBadEventNames)243 TEST(FtraceControllerTest, RejectsBadEventNames) {
244 auto controller = CreateTestController(true /* nice procfs */);
245
246 FtraceConfig config = CreateFtraceConfig({"../try/to/escape"});
247 EXPECT_FALSE(controller->AddFakeDataSource(config));
248 config = CreateFtraceConfig({"/event"});
249 EXPECT_FALSE(controller->AddFakeDataSource(config));
250 config = CreateFtraceConfig({"event/"});
251 EXPECT_FALSE(controller->AddFakeDataSource(config));
252 }
253
TEST(FtraceControllerTest,OneSink)254 TEST(FtraceControllerTest, OneSink) {
255 auto controller = CreateTestController(false /* nice procfs */);
256
257 // No read tasks posted as part of adding the data source.
258 EXPECT_CALL(*controller->runner(), PostDelayedTask(_, _)).Times(0);
259
260 FtraceConfig config = CreateFtraceConfig({"group/foo"});
261
262 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
263 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
264 auto data_source = controller->AddFakeDataSource(config);
265 ASSERT_TRUE(data_source);
266
267 // Verify that no read tasks have been posted. And set up expectation that
268 // a single recurring read task will be posted as part of starting the data
269 // source.
270 Mock::VerifyAndClearExpectations(controller->runner());
271 EXPECT_CALL(*controller->runner(), PostDelayedTask(_, _)).Times(1);
272
273 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
274 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
275
276 // Verify single posted read task.
277 Mock::VerifyAndClearExpectations(controller->runner());
278
279 // State clearing on tracing teardown.
280 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
281 EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
282 .WillOnce(Return(true));
283 EXPECT_CALL(*controller->procfs(),
284 ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
285 .WillRepeatedly(Return(true));
286 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
287 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
288 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
289 EXPECT_TRUE(controller->procfs()->is_tracing_on());
290
291 data_source.reset();
292 EXPECT_FALSE(controller->procfs()->is_tracing_on());
293 }
294
TEST(FtraceControllerTest,MultipleSinks)295 TEST(FtraceControllerTest, MultipleSinks) {
296 auto controller = CreateTestController(false /* nice procfs */);
297
298 FtraceConfig configA = CreateFtraceConfig({"group/foo"});
299 FtraceConfig configB = CreateFtraceConfig({"group/foo", "group/bar"});
300
301 // No read tasks posted as part of adding the data sources.
302 EXPECT_CALL(*controller->runner(), PostDelayedTask(_, _)).Times(0);
303
304 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
305 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
306 auto data_sourceA = controller->AddFakeDataSource(configA);
307 EXPECT_CALL(*controller->procfs(), WriteToFile(kBarEnablePath, "1"));
308 auto data_sourceB = controller->AddFakeDataSource(configB);
309
310 // Verify that no read tasks have been posted. And set up expectation that
311 // a single recurring read task will be posted as part of starting the data
312 // sources.
313 Mock::VerifyAndClearExpectations(controller->runner());
314 EXPECT_CALL(*controller->runner(), PostDelayedTask(_, _)).Times(1);
315
316 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
317 ASSERT_TRUE(controller->StartDataSource(data_sourceA.get()));
318 ASSERT_TRUE(controller->StartDataSource(data_sourceB.get()));
319
320 // Verify single posted read task.
321 Mock::VerifyAndClearExpectations(controller->runner());
322
323 data_sourceA.reset();
324
325 // State clearing on tracing teardown.
326 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
327 EXPECT_CALL(*controller->procfs(), WriteToFile(kBarEnablePath, "0"));
328 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
329 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
330 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
331 EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"));
332 EXPECT_CALL(*controller->procfs(),
333 ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
334 data_sourceB.reset();
335 }
336
TEST(FtraceControllerTest,ControllerMayDieFirst)337 TEST(FtraceControllerTest, ControllerMayDieFirst) {
338 auto controller = CreateTestController(false /* nice procfs */);
339
340 FtraceConfig config = CreateFtraceConfig({"group/foo"});
341
342 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
343 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
344 auto data_source = controller->AddFakeDataSource(config);
345
346 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
347 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
348
349 // State clearing on tracing teardown.
350 EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
351 EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
352 .WillOnce(Return(true));
353 EXPECT_CALL(*controller->procfs(),
354 ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
355 .WillRepeatedly(Return(true));
356 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
357 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
358 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
359 controller.reset();
360 data_source.reset();
361 }
362
TEST(FtraceControllerTest,BufferSize)363 TEST(FtraceControllerTest, BufferSize) {
364 auto controller = CreateTestController(false /* nice procfs */);
365
366 // For this test we don't care about most calls to WriteToFile/ClearFile.
367 EXPECT_CALL(*controller->procfs(), WriteToFile(_, _)).Times(AnyNumber());
368 EXPECT_CALL(*controller->procfs(), ClearFile(_)).Times(AnyNumber());
369
370 // Every time a fake data source is destroyed, the controller will reset the
371 // buffer size to a single page.
372 EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"))
373 .Times(AnyNumber());
374
375 {
376 // No buffer size -> good default.
377 EXPECT_CALL(*controller->procfs(),
378 WriteToFile("/root/buffer_size_kb", "2048"));
379 FtraceConfig config = CreateFtraceConfig({"group/foo"});
380 auto data_source = controller->AddFakeDataSource(config);
381 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
382 }
383
384 {
385 // Way too big buffer size -> max size.
386 EXPECT_CALL(*controller->procfs(),
387 WriteToFile("/root/buffer_size_kb", "65536"));
388 FtraceConfig config = CreateFtraceConfig({"group/foo"});
389 config.set_buffer_size_kb(10 * 1024 * 1024);
390 auto data_source = controller->AddFakeDataSource(config);
391 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
392 }
393
394 {
395 // The limit is 64mb, 65mb is too much.
396 EXPECT_CALL(*controller->procfs(),
397 WriteToFile("/root/buffer_size_kb", "65536"));
398 FtraceConfig config = CreateFtraceConfig({"group/foo"});
399 ON_CALL(*controller->procfs(), NumberOfCpus()).WillByDefault(Return(2));
400 config.set_buffer_size_kb(65 * 1024);
401 auto data_source = controller->AddFakeDataSource(config);
402 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
403 }
404
405 {
406 // Your size ends up with less than 1 page per cpu -> 1 page (gmock already
407 // covered by the cleanup expectation above).
408 FtraceConfig config = CreateFtraceConfig({"group/foo"});
409 config.set_buffer_size_kb(1);
410 auto data_source = controller->AddFakeDataSource(config);
411 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
412 }
413
414 {
415 // You picked a good size -> your size rounded to nearest page.
416 EXPECT_CALL(*controller->procfs(),
417 WriteToFile("/root/buffer_size_kb", "40"));
418 FtraceConfig config = CreateFtraceConfig({"group/foo"});
419 config.set_buffer_size_kb(42);
420 auto data_source = controller->AddFakeDataSource(config);
421 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
422 }
423
424 {
425 // You picked a good size -> your size rounded to nearest page.
426 EXPECT_CALL(*controller->procfs(),
427 WriteToFile("/root/buffer_size_kb", "40"));
428 FtraceConfig config = CreateFtraceConfig({"group/foo"});
429 ON_CALL(*controller->procfs(), NumberOfCpus()).WillByDefault(Return(2));
430 config.set_buffer_size_kb(42);
431 auto data_source = controller->AddFakeDataSource(config);
432 ASSERT_TRUE(controller->StartDataSource(data_source.get()));
433 }
434 }
435
TEST(FtraceControllerTest,PeriodicDrainConfig)436 TEST(FtraceControllerTest, PeriodicDrainConfig) {
437 auto controller = CreateTestController(false /* nice procfs */);
438
439 // For this test we don't care about calls to WriteToFile/ClearFile.
440 EXPECT_CALL(*controller->procfs(), WriteToFile(_, _)).Times(AnyNumber());
441 EXPECT_CALL(*controller->procfs(), ClearFile(_)).Times(AnyNumber());
442
443 {
444 // No period -> good default.
445 FtraceConfig config = CreateFtraceConfig({"group/foo"});
446 auto data_source = controller->AddFakeDataSource(config);
447 EXPECT_EQ(100u, controller->drain_period_ms());
448 }
449
450 {
451 // Pick a tiny value -> good default.
452 FtraceConfig config = CreateFtraceConfig({"group/foo"});
453 config.set_drain_period_ms(0);
454 auto data_source = controller->AddFakeDataSource(config);
455 EXPECT_EQ(100u, controller->drain_period_ms());
456 }
457
458 {
459 // Pick a huge value -> good default.
460 FtraceConfig config = CreateFtraceConfig({"group/foo"});
461 config.set_drain_period_ms(1000 * 60 * 60);
462 auto data_source = controller->AddFakeDataSource(config);
463 EXPECT_EQ(100u, controller->drain_period_ms());
464 }
465
466 {
467 // Pick a resonable value -> get that value.
468 FtraceConfig config = CreateFtraceConfig({"group/foo"});
469 config.set_drain_period_ms(200);
470 auto data_source = controller->AddFakeDataSource(config);
471 EXPECT_EQ(200u, controller->drain_period_ms());
472 }
473 }
474
TEST(FtraceMetadataTest,Clear)475 TEST(FtraceMetadataTest, Clear) {
476 FtraceMetadata metadata;
477 metadata.inode_and_device.insert(std::make_pair(1, 1));
478 metadata.pids.insert(2);
479 metadata.last_seen_device_id = 100;
480 metadata.Clear();
481 EXPECT_THAT(metadata.inode_and_device, IsEmpty());
482 EXPECT_THAT(metadata.pids, IsEmpty());
483 EXPECT_EQ(BlockDeviceID(0), metadata.last_seen_device_id);
484 }
485
TEST(FtraceMetadataTest,AddDevice)486 TEST(FtraceMetadataTest, AddDevice) {
487 FtraceMetadata metadata;
488 metadata.AddDevice(1);
489 EXPECT_EQ(BlockDeviceID(1), metadata.last_seen_device_id);
490 metadata.AddDevice(3);
491 EXPECT_EQ(BlockDeviceID(3), metadata.last_seen_device_id);
492 }
493
TEST(FtraceMetadataTest,AddInode)494 TEST(FtraceMetadataTest, AddInode) {
495 FtraceMetadata metadata;
496 metadata.AddCommonPid(getpid() + 1);
497 metadata.AddDevice(3);
498 metadata.AddInode(2);
499 metadata.AddInode(1);
500 metadata.AddCommonPid(getpid() + 1);
501 metadata.AddDevice(4);
502 metadata.AddInode(3);
503
504 // Check activity from ourselves is excluded.
505 metadata.AddCommonPid(getpid());
506 metadata.AddDevice(5);
507 metadata.AddInode(5);
508
509 EXPECT_THAT(metadata.inode_and_device,
510 UnorderedElementsAre(Pair(2, 3), Pair(1, 3), Pair(3, 4)));
511 }
512
TEST(FtraceMetadataTest,AddPid)513 TEST(FtraceMetadataTest, AddPid) {
514 FtraceMetadata metadata;
515 metadata.AddPid(1);
516 metadata.AddPid(2);
517 metadata.AddPid(2);
518 metadata.AddPid(3);
519 EXPECT_THAT(metadata.pids, ElementsAre(1, 2, 3));
520 }
521
TEST(FtraceStatsTest,Write)522 TEST(FtraceStatsTest, Write) {
523 FtraceStats stats{};
524 FtraceCpuStats cpu_stats{};
525 cpu_stats.cpu = 0;
526 cpu_stats.entries = 1;
527 cpu_stats.overrun = 2;
528 stats.cpu_stats.push_back(cpu_stats);
529
530 std::unique_ptr<TraceWriterForTesting> writer =
531 std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
532 {
533 auto packet = writer->NewTracePacket();
534 auto* out = packet->set_ftrace_stats();
535 stats.Write(out);
536 }
537
538 protos::gen::TracePacket result_packet = writer->GetOnlyTracePacket();
539 auto result = result_packet.ftrace_stats().cpu_stats()[0];
540 EXPECT_EQ(result.cpu(), 0u);
541 EXPECT_EQ(result.entries(), 1u);
542 EXPECT_EQ(result.overrun(), 2u);
543 }
544
545 } // namespace perfetto
546