• 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 "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "src/traced/probes/ftrace/atrace_wrapper.h"
24 #include "src/traced/probes/ftrace/ftrace_procfs.h"
25 #include "src/traced/probes/ftrace/proto_translation_table.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 using testing::UnorderedElementsAre;
38 
39 namespace perfetto {
40 namespace {
41 
42 class MockFtraceProcfs : public FtraceProcfs {
43  public:
MockFtraceProcfs()44   MockFtraceProcfs() : FtraceProcfs("/root/") {
45     ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
46     ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
47     ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
48     EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
49   }
50 
51   MOCK_METHOD2(WriteToFile,
52                bool(const std::string& path, const std::string& str));
53   MOCK_METHOD2(AppendToFile,
54                bool(const std::string& path, const std::string& str));
55   MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
56   MOCK_METHOD1(ClearFile, bool(const std::string& path));
57   MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
58   MOCK_CONST_METHOD0(NumberOfCpus, size_t());
59   MOCK_CONST_METHOD1(GetEventNamesForGroup,
60                      const std::set<std::string>(const std::string& path));
61 };
62 
63 struct MockRunAtrace {
MockRunAtraceperfetto::__anon935c1af60111::MockRunAtrace64   MockRunAtrace() {
65     static MockRunAtrace* instance;
66     instance = this;
67     SetRunAtraceForTesting([](const std::vector<std::string>& args) {
68       return instance->RunAtrace(args);
69     });
70   }
71 
~MockRunAtraceperfetto::__anon935c1af60111::MockRunAtrace72   ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
73 
74   MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
75 };
76 
77 class MockProtoTranslationTable : public ProtoTranslationTable {
78  public:
MockProtoTranslationTable(NiceMock<MockFtraceProcfs> * ftrace_procfs,const std::vector<Event> & events,std::vector<Field> common_fields,FtracePageHeaderSpec ftrace_page_header_spec)79   MockProtoTranslationTable(NiceMock<MockFtraceProcfs>* ftrace_procfs,
80                             const std::vector<Event>& events,
81                             std::vector<Field> common_fields,
82                             FtracePageHeaderSpec ftrace_page_header_spec)
83       : ProtoTranslationTable(ftrace_procfs,
84                               events,
85                               common_fields,
86                               ftrace_page_header_spec) {}
87   MOCK_METHOD1(GetOrCreateEvent, Event*(const GroupAndName& group_and_name));
88   MOCK_CONST_METHOD1(GetEvent,
89                      const Event*(const GroupAndName& group_and_name));
90 };
91 
92 class FtraceConfigMuxerTest : public ::testing::Test {
93  protected:
GetMockTable()94   std::unique_ptr<MockProtoTranslationTable> GetMockTable() {
95     std::vector<Field> common_fields;
96     std::vector<Event> events;
97     return std::unique_ptr<MockProtoTranslationTable>(
98         new MockProtoTranslationTable(
99             &table_procfs_, events, std::move(common_fields),
100             ProtoTranslationTable::DefaultPageHeaderSpecForTesting()));
101   }
CreateFakeTable()102   std::unique_ptr<ProtoTranslationTable> CreateFakeTable() {
103     std::vector<Field> common_fields;
104     std::vector<Event> events;
105     {
106       Event event;
107       event.name = "sched_switch";
108       event.group = "sched";
109       event.ftrace_event_id = 1;
110       events.push_back(event);
111     }
112 
113     {
114       Event event;
115       event.name = "sched_wakeup";
116       event.group = "sched";
117       event.ftrace_event_id = 10;
118       events.push_back(event);
119     }
120 
121     {
122       Event event;
123       event.name = "sched_new";
124       event.group = "sched";
125       event.ftrace_event_id = 11;
126       events.push_back(event);
127     }
128 
129     {
130       Event event;
131       event.name = "cgroup_mkdir";
132       event.group = "cgroup";
133       event.ftrace_event_id = 12;
134       events.push_back(event);
135     }
136 
137     {
138       Event event;
139       event.name = "mm_vmscan_direct_reclaim_begin";
140       event.group = "vmscan";
141       event.ftrace_event_id = 13;
142       events.push_back(event);
143     }
144 
145     {
146       Event event;
147       event.name = "lowmemory_kill";
148       event.group = "lowmemorykiller";
149       event.ftrace_event_id = 14;
150       events.push_back(event);
151     }
152 
153     {
154       Event event;
155       event.name = "print";
156       event.group = "ftrace";
157       event.ftrace_event_id = 20;
158       events.push_back(event);
159     }
160 
161     return std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
162         &table_procfs_, events, std::move(common_fields),
163         ProtoTranslationTable::DefaultPageHeaderSpecForTesting()));
164   }
165 
166   NiceMock<MockFtraceProcfs> table_procfs_;
167   std::unique_ptr<ProtoTranslationTable> table_ = CreateFakeTable();
168 };
169 
TEST_F(FtraceConfigMuxerTest,ComputeCpuBufferSizeInPages)170 TEST_F(FtraceConfigMuxerTest, ComputeCpuBufferSizeInPages) {
171   static constexpr size_t kMaxBufSizeInPages = 16 * 1024u;
172   // No buffer size given: good default (128 pages = 2mb).
173   EXPECT_EQ(ComputeCpuBufferSizeInPages(0), 512u);
174   // Buffer size given way too big: good default.
175   EXPECT_EQ(ComputeCpuBufferSizeInPages(10 * 1024 * 1024), kMaxBufSizeInPages);
176   // The limit is 64mb per CPU, 512mb is too much.
177   EXPECT_EQ(ComputeCpuBufferSizeInPages(512 * 1024), kMaxBufSizeInPages);
178   // Your size ends up with less than 1 page per cpu -> 1 page.
179   EXPECT_EQ(ComputeCpuBufferSizeInPages(3), 1u);
180   // You picked a good size -> your size rounded to nearest page.
181   EXPECT_EQ(ComputeCpuBufferSizeInPages(42), 10u);
182 }
183 
TEST_F(FtraceConfigMuxerTest,AddGenericEvent)184 TEST_F(FtraceConfigMuxerTest, AddGenericEvent) {
185   auto mock_table = GetMockTable();
186   MockFtraceProcfs ftrace;
187 
188   FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
189 
190   FtraceConfigMuxer model(&ftrace, mock_table.get());
191 
192   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
193       .WillByDefault(Return("[local] global boot"));
194   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
195       .Times(AnyNumber());
196 
197   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
198       .Times(2)
199       .WillRepeatedly(Return('0'));
200   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
201   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
202   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
203   EXPECT_CALL(ftrace,
204               WriteToFile("/root/events/power/cpu_frequency/enable", "1"));
205   EXPECT_CALL(*mock_table, GetEvent(GroupAndName("power", "cpu_frequency")))
206       .Times(AnyNumber());
207 
208   Event event_to_return;
209   event_to_return.name = "cpu_frequency";
210   event_to_return.group = "power";
211   event_to_return.ftrace_event_id = 1;
212   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("power", "cpu_frequency")))
213       .WillByDefault(Return(&event_to_return));
214   EXPECT_CALL(*mock_table,
215               GetOrCreateEvent(GroupAndName("power", "cpu_frequency")));
216 
217   FtraceConfigId id = model.SetupConfig(config);
218   ASSERT_TRUE(model.ActivateConfig(id));
219   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
220   EXPECT_TRUE(actual_config);
221   EXPECT_THAT(actual_config->ftrace_events(), Contains("power/cpu_frequency"));
222 }
223 
TEST_F(FtraceConfigMuxerTest,AddSameNameEvents)224 TEST_F(FtraceConfigMuxerTest, AddSameNameEvents) {
225   auto mock_table = GetMockTable();
226   NiceMock<MockFtraceProcfs> ftrace;
227 
228   FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
229 
230   FtraceConfigMuxer model(&ftrace, mock_table.get());
231 
232   Event event1;
233   event1.name = "foo";
234   event1.group = "group_one";
235   event1.ftrace_event_id = 1;
236   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")))
237       .WillByDefault(Return(&event1));
238   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")));
239 
240   Event event2;
241   event2.name = "foo";
242   event2.group = "group_two";
243   event2.ftrace_event_id = 2;
244   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")))
245       .WillByDefault(Return(&event2));
246   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
247 
248   FtraceConfigId id = model.SetupConfig(config);
249   ASSERT_TRUE(model.ActivateConfig(id));
250   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
251   EXPECT_TRUE(actual_config);
252   EXPECT_THAT(actual_config->ftrace_events(), Contains("group_one/foo"));
253   EXPECT_THAT(actual_config->ftrace_events(), Contains("group_two/foo"));
254 }
255 
TEST_F(FtraceConfigMuxerTest,AddAllEvents)256 TEST_F(FtraceConfigMuxerTest, AddAllEvents) {
257   auto mock_table = GetMockTable();
258   MockFtraceProcfs ftrace;
259 
260   FtraceConfig config = CreateFtraceConfig({"sched/*"});
261 
262   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
263       .WillByDefault(Return("[local] global boot"));
264   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
265       .Times(AnyNumber());
266 
267   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
268       .Times(2)
269       .WillRepeatedly(Return('0'));
270   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
271   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
272   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
273   EXPECT_CALL(ftrace,
274               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
275   EXPECT_CALL(ftrace,
276               WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
277 
278   FtraceConfigMuxer model(&ftrace, mock_table.get());
279   std::set<std::string> n = {"sched_switch", "sched_new_event"};
280   ON_CALL(ftrace, GetEventNamesForGroup("events/sched"))
281       .WillByDefault(Return(n));
282   EXPECT_CALL(ftrace, GetEventNamesForGroup("events/sched")).Times(1);
283 
284   // Non-generic event.
285   std::map<std::string, const Event*> events;
286   Event sched_switch = {"sched_switch", "sched"};
287   sched_switch.ftrace_event_id = 1;
288   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
289       .WillByDefault(Return(&sched_switch));
290   EXPECT_CALL(*mock_table,
291               GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
292       .Times(AnyNumber());
293 
294   // Generic event.
295   Event event_to_return;
296   event_to_return.name = "sched_new_event";
297   event_to_return.group = "sched";
298   event_to_return.ftrace_event_id = 2;
299   ON_CALL(*mock_table,
300           GetOrCreateEvent(GroupAndName("sched", "sched_new_event")))
301       .WillByDefault(Return(&event_to_return));
302   EXPECT_CALL(*mock_table,
303               GetOrCreateEvent(GroupAndName("sched", "sched_new_event")));
304 
305   FtraceConfigId id = model.SetupConfig(config);
306   ASSERT_TRUE(id);
307   ASSERT_TRUE(model.ActivateConfig(id));
308 
309   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
310   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched/sched_switch"));
311   EXPECT_THAT(actual_config->ftrace_events(),
312               Contains("sched/sched_new_event"));
313 }
314 
TEST_F(FtraceConfigMuxerTest,TwoWildcardGroups)315 TEST_F(FtraceConfigMuxerTest, TwoWildcardGroups) {
316   auto mock_table = GetMockTable();
317   NiceMock<MockFtraceProcfs> ftrace;
318 
319   FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
320 
321   FtraceConfigMuxer model(&ftrace, mock_table.get());
322 
323   std::set<std::string> event_names = {"foo"};
324   ON_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
325       .WillByDefault(Return(event_names));
326   EXPECT_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
327       .Times(AnyNumber());
328 
329   ON_CALL(ftrace, GetEventNamesForGroup("events/group_two"))
330       .WillByDefault(Return(event_names));
331   EXPECT_CALL(ftrace, GetEventNamesForGroup("events/group_two"))
332       .Times(AnyNumber());
333 
334   Event event1;
335   event1.name = "foo";
336   event1.group = "group_one";
337   event1.ftrace_event_id = 1;
338   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")))
339       .WillByDefault(Return(&event1));
340   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_one", "foo")));
341 
342   Event event2;
343   event2.name = "foo";
344   event2.group = "group_two";
345   event2.ftrace_event_id = 2;
346   ON_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")))
347       .WillByDefault(Return(&event2));
348   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
349 
350   FtraceConfigId id = model.SetupConfig(config);
351   ASSERT_TRUE(model.ActivateConfig(id));
352   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
353   EXPECT_TRUE(actual_config);
354   EXPECT_THAT(actual_config->ftrace_events(), Contains("group_one/foo"));
355   EXPECT_THAT(actual_config->ftrace_events(), Contains("group_two/foo"));
356 }
357 
TEST_F(FtraceConfigMuxerTest,TurnFtraceOnOff)358 TEST_F(FtraceConfigMuxerTest, TurnFtraceOnOff) {
359   MockFtraceProcfs ftrace;
360 
361   FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
362 
363   FtraceConfigMuxer model(&ftrace, table_.get());
364 
365   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
366       .WillByDefault(Return("[local] global boot"));
367   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
368       .Times(AnyNumber());
369 
370   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
371       .Times(2)
372       .WillRepeatedly(Return('0'));
373   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
374   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
375   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
376   EXPECT_CALL(ftrace,
377               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
378   FtraceConfigId id = model.SetupConfig(config);
379   ASSERT_TRUE(id);
380   ASSERT_TRUE(model.ActivateConfig(id));
381 
382   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
383   EXPECT_TRUE(actual_config);
384   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched/sched_switch"));
385   EXPECT_THAT(actual_config->ftrace_events(), Not(Contains("foo")));
386 
387   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
388   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "0"));
389   EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
390   EXPECT_CALL(ftrace,
391               WriteToFile("/root/events/sched/sched_switch/enable", "0"));
392   EXPECT_CALL(ftrace, ClearFile("/root/trace"));
393   EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
394   ASSERT_TRUE(model.RemoveConfig(id));
395 }
396 
TEST_F(FtraceConfigMuxerTest,FtraceIsAlreadyOn)397 TEST_F(FtraceConfigMuxerTest, FtraceIsAlreadyOn) {
398   MockFtraceProcfs ftrace;
399 
400   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
401 
402   FtraceConfigMuxer model(&ftrace, table_.get());
403 
404   // If someone is using ftrace already don't stomp on what they are doing.
405   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
406       .WillOnce(Return('1'));
407   FtraceConfigId id = model.SetupConfig(config);
408   ASSERT_FALSE(id);
409 }
410 
TEST_F(FtraceConfigMuxerTest,Atrace)411 TEST_F(FtraceConfigMuxerTest, Atrace) {
412   NiceMock<MockFtraceProcfs> ftrace;
413   MockRunAtrace atrace;
414 
415   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
416   *config.add_atrace_categories() = "sched";
417 
418   FtraceConfigMuxer model(&ftrace, table_.get());
419 
420   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
421       .WillOnce(Return('0'));
422   EXPECT_CALL(atrace,
423               RunAtrace(ElementsAreArray(
424                   {"atrace", "--async_start", "--only_userspace", "sched"})))
425       .WillOnce(Return(true));
426 
427   FtraceConfigId id = model.SetupConfig(config);
428   ASSERT_TRUE(id);
429 
430   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
431   EXPECT_TRUE(actual_config);
432   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched/sched_switch"));
433   EXPECT_THAT(actual_config->ftrace_events(), Contains("ftrace/print"));
434 
435   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
436                           {"atrace", "--async_stop", "--only_userspace"})))
437       .WillOnce(Return(true));
438   ASSERT_TRUE(model.RemoveConfig(id));
439 }
440 
TEST_F(FtraceConfigMuxerTest,AtraceTwoApps)441 TEST_F(FtraceConfigMuxerTest, AtraceTwoApps) {
442   NiceMock<MockFtraceProcfs> ftrace;
443   MockRunAtrace atrace;
444 
445   FtraceConfig config = CreateFtraceConfig({});
446   *config.add_atrace_apps() = "com.google.android.gms.persistent";
447   *config.add_atrace_apps() = "com.google.android.gms";
448 
449   FtraceConfigMuxer model(&ftrace, table_.get());
450 
451   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
452       .WillOnce(Return('0'));
453   EXPECT_CALL(
454       atrace,
455       RunAtrace(ElementsAreArray(
456           {"atrace", "--async_start", "--only_userspace", "-a",
457            "com.google.android.gms.persistent,com.google.android.gms"})))
458       .WillOnce(Return(true));
459 
460   FtraceConfigId id = model.SetupConfig(config);
461   ASSERT_TRUE(id);
462 
463   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
464   EXPECT_TRUE(actual_config);
465   EXPECT_THAT(actual_config->ftrace_events(), Contains("ftrace/print"));
466 
467   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
468                           {"atrace", "--async_stop", "--only_userspace"})))
469       .WillOnce(Return(true));
470   ASSERT_TRUE(model.RemoveConfig(id));
471 }
472 
TEST_F(FtraceConfigMuxerTest,SetupClockForTesting)473 TEST_F(FtraceConfigMuxerTest, SetupClockForTesting) {
474   MockFtraceProcfs ftrace;
475   FtraceConfig config;
476 
477   FtraceConfigMuxer model(&ftrace, table_.get());
478 
479   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
480       .Times(AnyNumber());
481 
482   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
483       .WillByDefault(Return("[local] global boot"));
484   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
485   model.SetupClockForTesting(config);
486 
487   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
488       .WillByDefault(Return("[local] global"));
489   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "global"));
490   model.SetupClockForTesting(config);
491 
492   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
493       .WillByDefault(Return(""));
494   model.SetupClockForTesting(config);
495 
496   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
497       .WillByDefault(Return("local [global]"));
498   model.SetupClockForTesting(config);
499 }
500 
TEST_F(FtraceConfigMuxerTest,GetFtraceEvents)501 TEST_F(FtraceConfigMuxerTest, GetFtraceEvents) {
502   MockFtraceProcfs ftrace;
503   FtraceConfigMuxer model(&ftrace, table_.get());
504 
505   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
506   std::set<GroupAndName> events =
507       model.GetFtraceEventsForTesting(config, table_.get());
508 
509   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
510   EXPECT_THAT(events, Not(Contains(GroupAndName("ftrace", "print"))));
511 }
512 
TEST_F(FtraceConfigMuxerTest,GetFtraceEventsAtrace)513 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
514   MockFtraceProcfs ftrace;
515   FtraceConfigMuxer model(&ftrace, table_.get());
516 
517   FtraceConfig config = CreateFtraceConfig({});
518   *config.add_atrace_categories() = "sched";
519   std::set<GroupAndName> events =
520       model.GetFtraceEventsForTesting(config, table_.get());
521 
522   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
523   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
524   EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
525 }
526 
TEST_F(FtraceConfigMuxerTest,GetFtraceEventsAtraceCategories)527 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
528   MockFtraceProcfs ftrace;
529   FtraceConfigMuxer model(&ftrace, table_.get());
530 
531   FtraceConfig config = CreateFtraceConfig({});
532   *config.add_atrace_categories() = "sched";
533   *config.add_atrace_categories() = "memreclaim";
534   std::set<GroupAndName> events =
535       model.GetFtraceEventsForTesting(config, table_.get());
536 
537   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
538   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
539   EXPECT_THAT(events, Contains(GroupAndName("cgroup", "cgroup_mkdir")));
540   EXPECT_THAT(events, Contains(GroupAndName("vmscan",
541                                             "mm_vmscan_direct_reclaim_begin")));
542   EXPECT_THAT(events,
543               Contains(GroupAndName("lowmemorykiller", "lowmemory_kill")));
544   EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
545 }
546 
547 // Tests the enabling fallback logic that tries to use the "set_event" interface
548 // if writing the individual xxx/enable file fails.
TEST_F(FtraceConfigMuxerTest,FallbackOnSetEvent)549 TEST_F(FtraceConfigMuxerTest, FallbackOnSetEvent) {
550   MockFtraceProcfs ftrace;
551   FtraceConfig config =
552       CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
553   FtraceConfigMuxer model(&ftrace, table_.get());
554 
555   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
556       .WillByDefault(Return("[local] global boot"));
557   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
558       .Times(AnyNumber());
559 
560   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
561       .Times(2)
562       .WillRepeatedly(Return('0'));
563   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
564   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
565   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
566   EXPECT_CALL(ftrace,
567               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
568   EXPECT_CALL(ftrace,
569               WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "1"))
570       .WillOnce(Return(false));
571   EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "cgroup:cgroup_mkdir"))
572       .WillOnce(Return(true));
573   FtraceConfigId id = model.SetupConfig(config);
574   ASSERT_TRUE(id);
575   ASSERT_TRUE(model.ActivateConfig(id));
576 
577   const FtraceConfig* actual_config = model.GetConfigForTesting(id);
578   EXPECT_TRUE(actual_config);
579   EXPECT_THAT(actual_config->ftrace_events(), Contains("sched/sched_switch"));
580   EXPECT_THAT(actual_config->ftrace_events(), Contains("cgroup/cgroup_mkdir"));
581 
582   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
583   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "0"));
584   EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
585   EXPECT_CALL(ftrace,
586               WriteToFile("/root/events/sched/sched_switch/enable", "0"));
587   EXPECT_CALL(ftrace,
588               WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "0"))
589       .WillOnce(Return(false));
590   EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "!cgroup:cgroup_mkdir"))
591       .WillOnce(Return(true));
592   EXPECT_CALL(ftrace, ClearFile("/root/trace"));
593   EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
594   ASSERT_TRUE(model.RemoveConfig(id));
595 }
596 
597 }  // namespace
598 }  // namespace perfetto
599