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