1 /*
2 * Copyright (C) 2018 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_config_muxer.h"
18
19 #include <memory>
20
21 #include "src/traced/probes/ftrace/atrace_wrapper.h"
22 #include "src/traced/probes/ftrace/compact_sched.h"
23 #include "src/traced/probes/ftrace/ftrace_procfs.h"
24 #include "src/traced/probes/ftrace/proto_translation_table.h"
25 #include "test/gtest_and_gmock.h"
26
27 using testing::_;
28 using testing::AnyNumber;
29 using testing::MatchesRegex;
30 using testing::Contains;
31 using testing::ElementsAreArray;
32 using testing::Eq;
33 using testing::IsEmpty;
34 using testing::NiceMock;
35 using testing::Not;
36 using testing::Return;
37
38 namespace perfetto {
39 namespace {
40
41 class MockFtraceProcfs : public FtraceProcfs {
42 public:
MockFtraceProcfs()43 MockFtraceProcfs() : FtraceProcfs("/root/") {
44 ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
45 ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
46 ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
47 EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
48 }
49
50 MOCK_METHOD2(WriteToFile,
51 bool(const std::string& path, const std::string& str));
52 MOCK_METHOD2(AppendToFile,
53 bool(const std::string& path, const std::string& str));
54 MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
55 MOCK_METHOD1(ClearFile, bool(const std::string& path));
56 MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
57 MOCK_CONST_METHOD0(NumberOfCpus, size_t());
58 MOCK_CONST_METHOD1(GetEventNamesForGroup,
59 const std::set<std::string>(const std::string& path));
60 };
61
62 struct MockRunAtrace {
MockRunAtraceperfetto::__anondff3b6b80111::MockRunAtrace63 MockRunAtrace() {
64 static MockRunAtrace* instance;
65 instance = this;
66 SetRunAtraceForTesting([](const std::vector<std::string>& args) {
67 return instance->RunAtrace(args);
68 });
69 }
70
~MockRunAtraceperfetto::__anondff3b6b80111::MockRunAtrace71 ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
72
73 MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
74 };
75
76 class MockProtoTranslationTable : public ProtoTranslationTable {
77 public:
MockProtoTranslationTable(NiceMock<MockFtraceProcfs> * ftrace_procfs,const std::vector<Event> & events,std::vector<Field> common_fields,FtracePageHeaderSpec ftrace_page_header_spec,CompactSchedEventFormat compact_sched_format)78 MockProtoTranslationTable(NiceMock<MockFtraceProcfs>* ftrace_procfs,
79 const std::vector<Event>& events,
80 std::vector<Field> common_fields,
81 FtracePageHeaderSpec ftrace_page_header_spec,
82 CompactSchedEventFormat compact_sched_format)
83 : ProtoTranslationTable(ftrace_procfs,
84 events,
85 common_fields,
86 ftrace_page_header_spec,
87 compact_sched_format) {}
88 MOCK_METHOD1(GetOrCreateEvent, Event*(const GroupAndName& group_and_name));
89 MOCK_CONST_METHOD1(GetEvent,
90 const Event*(const GroupAndName& group_and_name));
91 };
92
93 class FtraceConfigMuxerTest : public ::testing::Test {
94 protected:
GetMockTable()95 std::unique_ptr<MockProtoTranslationTable> GetMockTable() {
96 std::vector<Field> common_fields;
97 std::vector<Event> events;
98 return std::unique_ptr<MockProtoTranslationTable>(
99 new MockProtoTranslationTable(
100 &table_procfs_, events, std::move(common_fields),
101 ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
102 InvalidCompactSchedEventFormatForTesting()));
103 }
104
105 static constexpr int kFakeSchedSwitchEventId = 1;
106 static constexpr int kCgroupMkdirEventId = 12;
107 static constexpr int kFakePrintEventId = 20;
108
CreateFakeTable(CompactSchedEventFormat compact_format=InvalidCompactSchedEventFormatForTesting ())109 std::unique_ptr<ProtoTranslationTable> CreateFakeTable(
110 CompactSchedEventFormat compact_format =
111 InvalidCompactSchedEventFormatForTesting()) {
112 std::vector<Field> common_fields;
113 std::vector<Event> events;
114 {
115 Event event;
116 event.name = "sched_switch";
117 event.group = "sched";
118 event.ftrace_event_id = kFakeSchedSwitchEventId;
119 events.push_back(event);
120 }
121
122 {
123 Event event;
124 event.name = "sched_wakeup";
125 event.group = "sched";
126 event.ftrace_event_id = 10;
127 events.push_back(event);
128 }
129
130 {
131 Event event;
132 event.name = "sched_new";
133 event.group = "sched";
134 event.ftrace_event_id = 11;
135 events.push_back(event);
136 }
137
138 {
139 Event event;
140 event.name = "cgroup_mkdir";
141 event.group = "cgroup";
142 event.ftrace_event_id = kCgroupMkdirEventId;
143 events.push_back(event);
144 }
145
146 {
147 Event event;
148 event.name = "mm_vmscan_direct_reclaim_begin";
149 event.group = "vmscan";
150 event.ftrace_event_id = 13;
151 events.push_back(event);
152 }
153
154 {
155 Event event;
156 event.name = "lowmemory_kill";
157 event.group = "lowmemorykiller";
158 event.ftrace_event_id = 14;
159 events.push_back(event);
160 }
161
162 {
163 Event event;
164 event.name = "print";
165 event.group = "ftrace";
166 event.ftrace_event_id = kFakePrintEventId;
167 events.push_back(event);
168 }
169
170 return std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
171 &table_procfs_, events, std::move(common_fields),
172 ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
173 compact_format));
174 }
175
176 NiceMock<MockFtraceProcfs> table_procfs_;
177 std::unique_ptr<ProtoTranslationTable> table_ = CreateFakeTable();
178 };
179
TEST_F(FtraceConfigMuxerTest,ComputeCpuBufferSizeInPages)180 TEST_F(FtraceConfigMuxerTest, ComputeCpuBufferSizeInPages) {
181 static constexpr size_t kMaxBufSizeInPages = 16 * 1024u;
182 // No buffer size given: good default (128 pages = 2mb).
183 EXPECT_EQ(ComputeCpuBufferSizeInPages(0), 512u);
184 // Buffer size given way too big: good default.
185 EXPECT_EQ(ComputeCpuBufferSizeInPages(10 * 1024 * 1024), kMaxBufSizeInPages);
186 // The limit is 64mb per CPU, 512mb is too much.
187 EXPECT_EQ(ComputeCpuBufferSizeInPages(512 * 1024), kMaxBufSizeInPages);
188 // Your size ends up with less than 1 page per cpu -> 1 page.
189 EXPECT_EQ(ComputeCpuBufferSizeInPages(3), 1u);
190 // You picked a good size -> your size rounded to nearest page.
191 EXPECT_EQ(ComputeCpuBufferSizeInPages(42), 10u);
192 }
193
TEST_F(FtraceConfigMuxerTest,AddGenericEvent)194 TEST_F(FtraceConfigMuxerTest, AddGenericEvent) {
195 auto mock_table = GetMockTable();
196 MockFtraceProcfs ftrace;
197
198 FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
199
200 FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
201
202 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
203 .WillByDefault(Return("[local] global boot"));
204 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
205 .Times(AnyNumber());
206
207 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
208 .Times(2)
209 .WillRepeatedly(Return('0'));
210 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
211 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
212 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
213 EXPECT_CALL(ftrace,
214 WriteToFile("/root/events/power/cpu_frequency/enable", "1"));
215 EXPECT_CALL(*mock_table, GetEvent(GroupAndName("power", "cpu_frequency")))
216 .Times(AnyNumber());
217
218 static constexpr int kExpectedEventId = 77;
219 Event event_to_return;
220 event_to_return.name = "cpu_frequency";
221 event_to_return.group = "power";
222 event_to_return.ftrace_event_id = kExpectedEventId;
223 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("power", "cpu_frequency")))
224 .WillByDefault(Return(&event_to_return));
225 EXPECT_CALL(*mock_table,
226 GetOrCreateEvent(GroupAndName("power", "cpu_frequency")));
227
228 FtraceConfigId id = model.SetupConfig(config);
229 ASSERT_TRUE(model.ActivateConfig(id));
230
231 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
232 ASSERT_TRUE(ds_config);
233 ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
234 ElementsAreArray({kExpectedEventId}));
235
236 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
237 ASSERT_THAT(central_filter->GetEnabledEvents(),
238 ElementsAreArray({kExpectedEventId}));
239 }
240
TEST_F(FtraceConfigMuxerTest,AddSameNameEvents)241 TEST_F(FtraceConfigMuxerTest, AddSameNameEvents) {
242 auto mock_table = GetMockTable();
243 NiceMock<MockFtraceProcfs> ftrace;
244
245 FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
246
247 FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
248
249 static constexpr int kEventId1 = 1;
250 Event event1;
251 event1.name = "foo";
252 event1.group = "group_one";
253 event1.ftrace_event_id = kEventId1;
254 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")))
255 .WillByDefault(Return(&event1));
256 EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")));
257
258 static constexpr int kEventId2 = 2;
259 Event event2;
260 event2.name = "foo";
261 event2.group = "group_two";
262 event2.ftrace_event_id = kEventId2;
263 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")))
264 .WillByDefault(Return(&event2));
265 EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
266
267 FtraceConfigId id = model.SetupConfig(config);
268 ASSERT_TRUE(model.ActivateConfig(id));
269
270 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
271 ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
272 ElementsAreArray({kEventId1, kEventId2}));
273
274 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
275 ASSERT_THAT(central_filter->GetEnabledEvents(),
276 ElementsAreArray({kEventId1, kEventId2}));
277 }
278
TEST_F(FtraceConfigMuxerTest,AddAllEvents)279 TEST_F(FtraceConfigMuxerTest, AddAllEvents) {
280 auto mock_table = GetMockTable();
281 MockFtraceProcfs ftrace;
282
283 FtraceConfig config = CreateFtraceConfig({"sched/*"});
284
285 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
286 .WillByDefault(Return("[local] global boot"));
287 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
288 .Times(AnyNumber());
289
290 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
291 .Times(2)
292 .WillRepeatedly(Return('0'));
293 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
294 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
295 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
296 EXPECT_CALL(ftrace,
297 WriteToFile("/root/events/sched/sched_switch/enable", "1"));
298 EXPECT_CALL(ftrace,
299 WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
300
301 FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
302 std::set<std::string> n = {"sched_switch", "sched_new_event"};
303 ON_CALL(ftrace, GetEventNamesForGroup("events/sched"))
304 .WillByDefault(Return(n));
305 EXPECT_CALL(ftrace, GetEventNamesForGroup("events/sched")).Times(1);
306
307 // Non-generic event.
308 static constexpr int kSchedSwitchEventId = 1;
309 Event sched_switch = {"sched_switch", "sched", {}, 0, 0, 0};
310 sched_switch.ftrace_event_id = kSchedSwitchEventId;
311 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
312 .WillByDefault(Return(&sched_switch));
313 EXPECT_CALL(*mock_table,
314 GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
315 .Times(AnyNumber());
316
317 // Generic event.
318 static constexpr int kGenericEventId = 2;
319 Event event_to_return;
320 event_to_return.name = "sched_new_event";
321 event_to_return.group = "sched";
322 event_to_return.ftrace_event_id = kGenericEventId;
323 ON_CALL(*mock_table,
324 GetOrCreateEvent(GroupAndName("sched", "sched_new_event")))
325 .WillByDefault(Return(&event_to_return));
326 EXPECT_CALL(*mock_table,
327 GetOrCreateEvent(GroupAndName("sched", "sched_new_event")));
328
329 FtraceConfigId id = model.SetupConfig(config);
330 ASSERT_TRUE(id);
331 ASSERT_TRUE(model.ActivateConfig(id));
332
333 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
334 ASSERT_TRUE(ds_config);
335 ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
336 ElementsAreArray({kSchedSwitchEventId, kGenericEventId}));
337
338 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
339 ASSERT_THAT(central_filter->GetEnabledEvents(),
340 ElementsAreArray({kSchedSwitchEventId, kGenericEventId}));
341 }
342
TEST_F(FtraceConfigMuxerTest,TwoWildcardGroups)343 TEST_F(FtraceConfigMuxerTest, TwoWildcardGroups) {
344 auto mock_table = GetMockTable();
345 NiceMock<MockFtraceProcfs> ftrace;
346
347 FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
348
349 FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
350
351 std::set<std::string> event_names = {"foo"};
352 ON_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
353 .WillByDefault(Return(event_names));
354 EXPECT_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
355 .Times(AnyNumber());
356
357 ON_CALL(ftrace, GetEventNamesForGroup("events/group_two"))
358 .WillByDefault(Return(event_names));
359 EXPECT_CALL(ftrace, GetEventNamesForGroup("events/group_two"))
360 .Times(AnyNumber());
361
362 static constexpr int kEventId1 = 1;
363 Event event1;
364 event1.name = "foo";
365 event1.group = "group_one";
366 event1.ftrace_event_id = kEventId1;
367 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")))
368 .WillByDefault(Return(&event1));
369 EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")));
370
371 static constexpr int kEventId2 = 2;
372 Event event2;
373 event2.name = "foo";
374 event2.group = "group_two";
375 event2.ftrace_event_id = kEventId2;
376 ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")))
377 .WillByDefault(Return(&event2));
378 EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
379
380 FtraceConfigId id = model.SetupConfig(config);
381 ASSERT_TRUE(model.ActivateConfig(id));
382
383 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
384 ASSERT_TRUE(ds_config);
385 ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
386 ElementsAreArray({kEventId1, kEventId2}));
387
388 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
389 ASSERT_THAT(central_filter->GetEnabledEvents(),
390 ElementsAreArray({kEventId1, kEventId2}));
391 }
392
TEST_F(FtraceConfigMuxerTest,TurnFtraceOnOff)393 TEST_F(FtraceConfigMuxerTest, TurnFtraceOnOff) {
394 MockFtraceProcfs ftrace;
395
396 FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
397
398 FtraceConfigMuxer model(&ftrace, table_.get(), {});
399
400 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
401 .WillByDefault(Return("[local] global boot"));
402 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
403 .Times(AnyNumber());
404
405 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
406 .Times(2)
407 .WillRepeatedly(Return('0'));
408 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
409 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
410 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
411 EXPECT_CALL(ftrace,
412 WriteToFile("/root/events/sched/sched_switch/enable", "1"));
413
414 FtraceConfigId id = model.SetupConfig(config);
415 ASSERT_TRUE(id);
416 ASSERT_TRUE(model.ActivateConfig(id));
417
418 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
419 ASSERT_TRUE(ds_config);
420 ASSERT_THAT(
421 ds_config->event_filter.GetEnabledEvents(),
422 ElementsAreArray({FtraceConfigMuxerTest::kFakeSchedSwitchEventId}));
423
424 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
425 ASSERT_THAT(
426 central_filter->GetEnabledEvents(),
427 ElementsAreArray({FtraceConfigMuxerTest::kFakeSchedSwitchEventId}));
428
429 ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
430 EXPECT_CALL(ftrace, NumberOfCpus()).Times(AnyNumber());
431
432 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
433 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
434 EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
435 EXPECT_CALL(ftrace,
436 WriteToFile("/root/events/sched/sched_switch/enable", "0"));
437 EXPECT_CALL(ftrace, ClearFile("/root/trace"));
438 EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
439
440 ASSERT_TRUE(model.RemoveConfig(id));
441 }
442
TEST_F(FtraceConfigMuxerTest,FtraceIsAlreadyOn)443 TEST_F(FtraceConfigMuxerTest, FtraceIsAlreadyOn) {
444 MockFtraceProcfs ftrace;
445
446 FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
447
448 FtraceConfigMuxer model(&ftrace, table_.get(), {});
449
450 // If someone is using ftrace already don't stomp on what they are doing.
451 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
452 .WillOnce(Return('1'));
453 FtraceConfigId id = model.SetupConfig(config);
454 ASSERT_FALSE(id);
455 }
456
TEST_F(FtraceConfigMuxerTest,Atrace)457 TEST_F(FtraceConfigMuxerTest, Atrace) {
458 NiceMock<MockFtraceProcfs> ftrace;
459 MockRunAtrace atrace;
460
461 FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
462 *config.add_atrace_categories() = "sched";
463
464 FtraceConfigMuxer model(&ftrace, table_.get(), {});
465
466 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
467 .WillOnce(Return('0'));
468 EXPECT_CALL(atrace,
469 RunAtrace(ElementsAreArray(
470 {"atrace", "--async_start", "--only_userspace", "sched"})))
471 .WillOnce(Return(true));
472
473 FtraceConfigId id = model.SetupConfig(config);
474 ASSERT_TRUE(id);
475
476 // "ftrace" group events are always enabled, and therefore the "print" event
477 // will show up in the per data source event filter (as we want to record it),
478 // but not the central filter (as we're not enabling/disabling it).
479 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
480 ASSERT_TRUE(ds_config);
481 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
482 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
483 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
484 Contains(FtraceConfigMuxerTest::kFakePrintEventId));
485
486 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
487 EXPECT_THAT(central_filter->GetEnabledEvents(),
488 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
489
490 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
491 {"atrace", "--async_stop", "--only_userspace"})))
492 .WillOnce(Return(true));
493 ASSERT_TRUE(model.RemoveConfig(id));
494 }
495
TEST_F(FtraceConfigMuxerTest,AtraceTwoApps)496 TEST_F(FtraceConfigMuxerTest, AtraceTwoApps) {
497 NiceMock<MockFtraceProcfs> ftrace;
498 MockRunAtrace atrace;
499
500 FtraceConfig config = CreateFtraceConfig({});
501 *config.add_atrace_apps() = "com.google.android.gms.persistent";
502 *config.add_atrace_apps() = "com.google.android.gms";
503
504 FtraceConfigMuxer model(&ftrace, table_.get(), {});
505
506 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
507 .WillOnce(Return('0'));
508 EXPECT_CALL(
509 atrace,
510 RunAtrace(ElementsAreArray(
511 {"atrace", "--async_start", "--only_userspace", "-a",
512 "com.google.android.gms,com.google.android.gms.persistent"})))
513 .WillOnce(Return(true));
514
515 FtraceConfigId id = model.SetupConfig(config);
516 ASSERT_TRUE(id);
517
518 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
519 ASSERT_TRUE(ds_config);
520 ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
521 Contains(FtraceConfigMuxerTest::kFakePrintEventId));
522
523 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
524 {"atrace", "--async_stop", "--only_userspace"})))
525 .WillOnce(Return(true));
526 ASSERT_TRUE(model.RemoveConfig(id));
527 }
528
TEST_F(FtraceConfigMuxerTest,AtraceMultipleConfigs)529 TEST_F(FtraceConfigMuxerTest, AtraceMultipleConfigs) {
530 NiceMock<MockFtraceProcfs> ftrace;
531 MockRunAtrace atrace;
532
533 FtraceConfig config_a = CreateFtraceConfig({});
534 *config_a.add_atrace_apps() = "app_a";
535 *config_a.add_atrace_categories() = "cat_a";
536
537 FtraceConfig config_b = CreateFtraceConfig({});
538 *config_b.add_atrace_apps() = "app_b";
539 *config_b.add_atrace_categories() = "cat_b";
540
541 FtraceConfig config_c = CreateFtraceConfig({});
542 *config_c.add_atrace_apps() = "app_c";
543 *config_c.add_atrace_categories() = "cat_c";
544
545 FtraceConfigMuxer model(&ftrace, table_.get(), {});
546
547 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
548 "--only_userspace", "cat_a",
549 "-a", "app_a"})))
550 .WillOnce(Return(true));
551 FtraceConfigId id_a = model.SetupConfig(config_a);
552 ASSERT_TRUE(id_a);
553
554 EXPECT_CALL(
555 atrace,
556 RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
557 "cat_a", "cat_b", "-a", "app_a,app_b"})))
558 .WillOnce(Return(true));
559 FtraceConfigId id_b = model.SetupConfig(config_b);
560 ASSERT_TRUE(id_b);
561
562 EXPECT_CALL(atrace,
563 RunAtrace(ElementsAreArray({"atrace", "--async_start",
564 "--only_userspace", "cat_a", "cat_b",
565 "cat_c", "-a", "app_a,app_b,app_c"})))
566 .WillOnce(Return(true));
567 FtraceConfigId id_c = model.SetupConfig(config_c);
568 ASSERT_TRUE(id_c);
569
570 EXPECT_CALL(
571 atrace,
572 RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
573 "cat_a", "cat_c", "-a", "app_a,app_c"})))
574 .WillOnce(Return(true));
575 ASSERT_TRUE(model.RemoveConfig(id_b));
576
577 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
578 "--only_userspace", "cat_c",
579 "-a", "app_c"})))
580 .WillOnce(Return(true));
581 ASSERT_TRUE(model.RemoveConfig(id_a));
582
583 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
584 {"atrace", "--async_stop", "--only_userspace"})))
585 .WillOnce(Return(true));
586 ASSERT_TRUE(model.RemoveConfig(id_c));
587 }
588
TEST_F(FtraceConfigMuxerTest,AtraceFailedConfig)589 TEST_F(FtraceConfigMuxerTest, AtraceFailedConfig) {
590 NiceMock<MockFtraceProcfs> ftrace;
591 MockRunAtrace atrace;
592
593 FtraceConfig config_a = CreateFtraceConfig({});
594 *config_a.add_atrace_apps() = "app_1";
595 *config_a.add_atrace_apps() = "app_2";
596 *config_a.add_atrace_categories() = "cat_1";
597 *config_a.add_atrace_categories() = "cat_2";
598
599 FtraceConfig config_b = CreateFtraceConfig({});
600 *config_b.add_atrace_apps() = "app_fail";
601 *config_b.add_atrace_categories() = "cat_fail";
602
603 FtraceConfig config_c = CreateFtraceConfig({});
604 *config_c.add_atrace_apps() = "app_1";
605 *config_c.add_atrace_apps() = "app_3";
606 *config_c.add_atrace_categories() = "cat_1";
607 *config_c.add_atrace_categories() = "cat_3";
608
609 FtraceConfigMuxer model(&ftrace, table_.get(), {});
610
611 EXPECT_CALL(
612 atrace,
613 RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
614 "cat_1", "cat_2", "-a", "app_1,app_2"})))
615 .WillOnce(Return(true));
616 FtraceConfigId id_a = model.SetupConfig(config_a);
617 ASSERT_TRUE(id_a);
618
619 EXPECT_CALL(atrace,
620 RunAtrace(ElementsAreArray(
621 {"atrace", "--async_start", "--only_userspace", "cat_1",
622 "cat_2", "cat_fail", "-a", "app_1,app_2,app_fail"})))
623 .WillOnce(Return(false));
624 FtraceConfigId id_b = model.SetupConfig(config_b);
625 ASSERT_TRUE(id_b);
626
627 EXPECT_CALL(atrace,
628 RunAtrace(ElementsAreArray({"atrace", "--async_start",
629 "--only_userspace", "cat_1", "cat_2",
630 "cat_3", "-a", "app_1,app_2,app_3"})))
631 .WillOnce(Return(true));
632 FtraceConfigId id_c = model.SetupConfig(config_c);
633 ASSERT_TRUE(id_c);
634
635 EXPECT_CALL(
636 atrace,
637 RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
638 "cat_1", "cat_2", "-a", "app_1,app_2"})))
639 .WillOnce(Return(true));
640 ASSERT_TRUE(model.RemoveConfig(id_c));
641
642 // Removing the config we failed to enable doesn't change the atrace state
643 // so we don't expect a call here.
644 ASSERT_TRUE(model.RemoveConfig(id_b));
645
646 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
647 {"atrace", "--async_stop", "--only_userspace"})))
648 .WillOnce(Return(true));
649 ASSERT_TRUE(model.RemoveConfig(id_a));
650 }
651
TEST_F(FtraceConfigMuxerTest,AtraceDuplicateConfigs)652 TEST_F(FtraceConfigMuxerTest, AtraceDuplicateConfigs) {
653 NiceMock<MockFtraceProcfs> ftrace;
654 MockRunAtrace atrace;
655
656 FtraceConfig config_a = CreateFtraceConfig({});
657 *config_a.add_atrace_apps() = "app_1";
658 *config_a.add_atrace_categories() = "cat_1";
659
660 FtraceConfig config_b = CreateFtraceConfig({});
661 *config_b.add_atrace_apps() = "app_1";
662 *config_b.add_atrace_categories() = "cat_1";
663
664 FtraceConfigMuxer model(&ftrace, table_.get(), {});
665
666 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
667 "--only_userspace", "cat_1",
668 "-a", "app_1"})))
669 .WillOnce(Return(true));
670 FtraceConfigId id_a = model.SetupConfig(config_a);
671 ASSERT_TRUE(id_a);
672
673 FtraceConfigId id_b = model.SetupConfig(config_b);
674 ASSERT_TRUE(id_b);
675
676 ASSERT_TRUE(model.RemoveConfig(id_a));
677
678 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
679 {"atrace", "--async_stop", "--only_userspace"})))
680 .WillOnce(Return(true));
681 ASSERT_TRUE(model.RemoveConfig(id_b));
682 }
683
TEST_F(FtraceConfigMuxerTest,AtraceAndFtraceConfigs)684 TEST_F(FtraceConfigMuxerTest, AtraceAndFtraceConfigs) {
685 NiceMock<MockFtraceProcfs> ftrace;
686 MockRunAtrace atrace;
687
688 FtraceConfig config_a = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
689
690 FtraceConfig config_b = CreateFtraceConfig({"sched/sched_switch"});
691 *config_b.add_atrace_categories() = "b";
692
693 FtraceConfig config_c = CreateFtraceConfig({"sched/sched_switch"});
694
695 FtraceConfig config_d = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
696 *config_d.add_atrace_categories() = "d";
697
698 FtraceConfigMuxer model(&ftrace, table_.get(), {});
699
700 FtraceConfigId id_a = model.SetupConfig(config_a);
701 ASSERT_TRUE(id_a);
702
703 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
704 "--only_userspace", "b"})))
705 .WillOnce(Return(true));
706 FtraceConfigId id_b = model.SetupConfig(config_b);
707 ASSERT_TRUE(id_b);
708
709 FtraceConfigId id_c = model.SetupConfig(config_c);
710 ASSERT_TRUE(id_c);
711
712 EXPECT_CALL(atrace,
713 RunAtrace(ElementsAreArray(
714 {"atrace", "--async_start", "--only_userspace", "b", "d"})))
715 .WillOnce(Return(true));
716 FtraceConfigId id_d = model.SetupConfig(config_d);
717 ASSERT_TRUE(id_d);
718
719 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
720 "--only_userspace", "b"})))
721 .WillOnce(Return(true));
722 ASSERT_TRUE(model.RemoveConfig(id_d));
723
724 ASSERT_TRUE(model.RemoveConfig(id_c));
725
726 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
727 {"atrace", "--async_stop", "--only_userspace"})))
728 .WillOnce(Return(true));
729 ASSERT_TRUE(model.RemoveConfig(id_b));
730
731 ASSERT_TRUE(model.RemoveConfig(id_a));
732 }
733
TEST_F(FtraceConfigMuxerTest,SetupClockForTesting)734 TEST_F(FtraceConfigMuxerTest, SetupClockForTesting) {
735 MockFtraceProcfs ftrace;
736 FtraceConfig config;
737
738 FtraceConfigMuxer model(&ftrace, table_.get(), {});
739
740 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
741 .Times(AnyNumber());
742
743 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
744 .WillByDefault(Return("[local] global boot"));
745 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
746 model.SetupClockForTesting(config);
747
748 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
749 .WillByDefault(Return("[local] global"));
750 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "global"));
751 model.SetupClockForTesting(config);
752
753 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
754 .WillByDefault(Return(""));
755 model.SetupClockForTesting(config);
756
757 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
758 .WillByDefault(Return("local [global]"));
759 model.SetupClockForTesting(config);
760 }
761
TEST_F(FtraceConfigMuxerTest,GetFtraceEvents)762 TEST_F(FtraceConfigMuxerTest, GetFtraceEvents) {
763 MockFtraceProcfs ftrace;
764 FtraceConfigMuxer model(&ftrace, table_.get(), {});
765
766 FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
767 std::set<GroupAndName> events =
768 model.GetFtraceEventsForTesting(config, table_.get());
769
770 EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
771 EXPECT_THAT(events, Not(Contains(GroupAndName("ftrace", "print"))));
772 }
773
TEST_F(FtraceConfigMuxerTest,GetFtraceEventsAtrace)774 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
775 MockFtraceProcfs ftrace;
776 FtraceConfigMuxer model(&ftrace, table_.get(), {});
777
778 FtraceConfig config = CreateFtraceConfig({});
779 *config.add_atrace_categories() = "sched";
780 std::set<GroupAndName> events =
781 model.GetFtraceEventsForTesting(config, table_.get());
782
783 EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
784 EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
785 EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
786 }
787
TEST_F(FtraceConfigMuxerTest,GetFtraceEventsAtraceCategories)788 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
789 MockFtraceProcfs ftrace;
790 FtraceConfigMuxer model(&ftrace, table_.get(), {});
791
792 FtraceConfig config = CreateFtraceConfig({});
793 *config.add_atrace_categories() = "sched";
794 *config.add_atrace_categories() = "memreclaim";
795 std::set<GroupAndName> events =
796 model.GetFtraceEventsForTesting(config, table_.get());
797
798 EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
799 EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
800 EXPECT_THAT(events, Contains(GroupAndName("cgroup", "cgroup_mkdir")));
801 EXPECT_THAT(events, Contains(GroupAndName("vmscan",
802 "mm_vmscan_direct_reclaim_begin")));
803 EXPECT_THAT(events,
804 Contains(GroupAndName("lowmemorykiller", "lowmemory_kill")));
805 EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
806 }
807
808 // Tests the enabling fallback logic that tries to use the "set_event" interface
809 // if writing the individual xxx/enable file fails.
TEST_F(FtraceConfigMuxerTest,FallbackOnSetEvent)810 TEST_F(FtraceConfigMuxerTest, FallbackOnSetEvent) {
811 MockFtraceProcfs ftrace;
812 FtraceConfig config =
813 CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
814 FtraceConfigMuxer model(&ftrace, table_.get(), {});
815
816 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
817 .WillByDefault(Return("[local] global boot"));
818 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
819 .Times(AnyNumber());
820
821 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
822 .Times(2)
823 .WillRepeatedly(Return('0'));
824 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
825 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
826 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
827 EXPECT_CALL(ftrace,
828 WriteToFile("/root/events/sched/sched_switch/enable", "1"));
829 EXPECT_CALL(ftrace,
830 WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "1"))
831 .WillOnce(Return(false));
832 EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "cgroup:cgroup_mkdir"))
833 .WillOnce(Return(true));
834 FtraceConfigId id = model.SetupConfig(config);
835 ASSERT_TRUE(id);
836 ASSERT_TRUE(model.ActivateConfig(id));
837
838 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
839 ASSERT_TRUE(ds_config);
840 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
841 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
842 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
843 Contains(FtraceConfigMuxerTest::kCgroupMkdirEventId));
844
845 const EventFilter* central_filter = model.GetCentralEventFilterForTesting();
846 EXPECT_THAT(central_filter->GetEnabledEvents(),
847 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
848 EXPECT_THAT(central_filter->GetEnabledEvents(),
849 Contains(FtraceConfigMuxerTest::kCgroupMkdirEventId));
850
851 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
852 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
853 EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
854 EXPECT_CALL(ftrace,
855 WriteToFile("/root/events/sched/sched_switch/enable", "0"));
856 EXPECT_CALL(ftrace,
857 WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "0"))
858 .WillOnce(Return(false));
859 EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "!cgroup:cgroup_mkdir"))
860 .WillOnce(Return(true));
861 EXPECT_CALL(ftrace, ClearFile("/root/trace"));
862 EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
863 ASSERT_TRUE(model.RemoveConfig(id));
864 }
865
TEST_F(FtraceConfigMuxerTest,CompactSchedConfig)866 TEST_F(FtraceConfigMuxerTest, CompactSchedConfig) {
867 // Set scheduling event format as validated. The pre-parsed format itself
868 // doesn't need to be sensible, as the tests won't use it.
869 auto valid_compact_format =
870 CompactSchedEventFormat{/*format_valid=*/true, CompactSchedSwitchFormat{},
871 CompactSchedWakingFormat{}};
872
873 NiceMock<MockFtraceProcfs> ftrace;
874 table_ = CreateFakeTable(valid_compact_format);
875 FtraceConfigMuxer model(&ftrace, table_.get(), {});
876
877 // First data source - request compact encoding.
878 FtraceConfig config_enabled = CreateFtraceConfig({"sched/sched_switch"});
879 config_enabled.mutable_compact_sched()->set_enabled(true);
880
881 // Second data source - no compact encoding (default).
882 FtraceConfig config_disabled = CreateFtraceConfig({"sched/sched_switch"});
883
884 {
885 FtraceConfigId id = model.SetupConfig(config_enabled);
886 ASSERT_TRUE(id);
887 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
888 ASSERT_TRUE(ds_config);
889 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
890 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
891 EXPECT_TRUE(ds_config->compact_sched.enabled);
892 }
893 {
894 FtraceConfigId id = model.SetupConfig(config_disabled);
895 ASSERT_TRUE(id);
896 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
897 ASSERT_TRUE(ds_config);
898 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
899 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
900 EXPECT_FALSE(ds_config->compact_sched.enabled);
901 }
902 }
903
TEST_F(FtraceConfigMuxerTest,CompactSchedConfigWithInvalidFormat)904 TEST_F(FtraceConfigMuxerTest, CompactSchedConfigWithInvalidFormat) {
905 NiceMock<MockFtraceProcfs> ftrace;
906 FtraceConfigMuxer model(&ftrace, table_.get(), {});
907
908 // Request compact encoding.
909 FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
910 config.mutable_compact_sched()->set_enabled(true);
911
912 FtraceConfigId id = model.SetupConfig(config);
913 ASSERT_TRUE(id);
914
915 // The translation table says that the scheduling events' format didn't match
916 // compile-time assumptions, so we won't enable compact events even if
917 // requested.
918 const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
919 ASSERT_TRUE(ds_config);
920 EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
921 Contains(FtraceConfigMuxerTest::kFakeSchedSwitchEventId));
922 EXPECT_FALSE(ds_config->compact_sched.enabled);
923 }
924
925 } // namespace
926 } // namespace perfetto
927