• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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