• 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 "perfetto/ext/base/utils.h"
23 #include "src/traced/probes/ftrace/atrace_wrapper.h"
24 #include "src/traced/probes/ftrace/compact_sched.h"
25 #include "src/traced/probes/ftrace/ftrace_procfs.h"
26 #include "src/traced/probes/ftrace/ftrace_stats.h"
27 #include "src/traced/probes/ftrace/proto_translation_table.h"
28 #include "test/gtest_and_gmock.h"
29 
30 using testing::_;
31 using testing::AnyNumber;
32 using testing::Contains;
33 using testing::ElementsAreArray;
34 using testing::Eq;
35 using testing::Invoke;
36 using testing::IsEmpty;
37 using testing::MatchesRegex;
38 using testing::NiceMock;
39 using testing::Not;
40 using testing::Return;
41 using testing::UnorderedElementsAre;
42 
43 namespace perfetto {
44 namespace {
45 
46 constexpr int kFakeSchedSwitchEventId = 1;
47 constexpr int kCgroupMkdirEventId = 12;
48 constexpr int kFakePrintEventId = 20;
49 constexpr int kSysEnterId = 329;
50 
51 struct FakeSyscallTable {
52   static constexpr char names[] =
53       "sys_open\0"
54       "sys_read\0";
55   static constexpr SyscallTable::OffT offsets[]{0, 9};
56 };
57 
PageSizeKb()58 std::string PageSizeKb() {
59   return std::to_string(base::GetSysPageSize() / 1024);
60 }
61 
62 class MockFtraceProcfs : public FtraceProcfs {
63  public:
MockFtraceProcfs()64   MockFtraceProcfs() : FtraceProcfs("/root/") {
65     ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
66     ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
67     ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
68     EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
69   }
70 
71   MOCK_METHOD(bool,
72               WriteToFile,
73               (const std::string& path, const std::string& str),
74               (override));
75   MOCK_METHOD(bool,
76               AppendToFile,
77               (const std::string& path, const std::string& str),
78               (override));
79   MOCK_METHOD(char, ReadOneCharFromFile, (const std::string& path), (override));
80   MOCK_METHOD(bool, ClearFile, (const std::string& path), (override));
81   MOCK_METHOD(std::string,
82               ReadFileIntoString,
83               (const std::string& path),
84               (const, override));
85   MOCK_METHOD(size_t, NumberOfCpus, (), (const, override));
86   MOCK_METHOD(const std::set<std::string>,
87               GetEventNamesForGroup,
88               (const std::string& path),
89               (const, override));
90   MOCK_METHOD(std::string,
91               ReadEventFormat,
92               (const std::string& group, const std::string& name),
93               (const, override));
94 };
95 
96 class MockAtraceWrapper : public AtraceWrapper {
97  public:
98   MOCK_METHOD(bool, RunAtrace, (const std::vector<std::string>&, std::string*));
99   MOCK_METHOD(bool, SupportsUserspaceOnly, ());
100 };
101 
102 class MockProtoTranslationTable : public ProtoTranslationTable {
103  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)104   MockProtoTranslationTable(NiceMock<MockFtraceProcfs>* ftrace_procfs,
105                             const std::vector<Event>& events,
106                             std::vector<Field> common_fields,
107                             FtracePageHeaderSpec ftrace_page_header_spec,
108                             CompactSchedEventFormat compact_sched_format)
109       : ProtoTranslationTable(ftrace_procfs,
110                               events,
111                               common_fields,
112                               ftrace_page_header_spec,
113                               compact_sched_format,
114                               PrintkMap()) {}
115   MOCK_METHOD(Event*,
116               GetOrCreateEvent,
117               (const GroupAndName& group_and_name),
118               (override));
119   MOCK_METHOD(const Event*,
120               GetEvent,
121               (const GroupAndName& group_and_name),
122               (const, override));
123 };
124 
TEST(ComputeCpuBufferSizeInPagesTest,DifferentCases)125 TEST(ComputeCpuBufferSizeInPagesTest, DifferentCases) {
126   constexpr auto test = ComputeCpuBufferSizeInPages;
127   auto KbToPages = [](uint64_t kb) {
128     return kb * 1024 / base::GetSysPageSize();
129   };
130   auto kMaxBufSizePages = KbToPages(64 * 1024);
131   int64_t kNoRamInfo = 0;
132   bool kExactSize = false;
133   bool kLowerBoundSize = true;
134   int64_t kLowRamPages =
135       static_cast<int64_t>(KbToPages(3 * (1ULL << 20) + 512 * (1ULL << 10)));
136   int64_t kHighRamPages =
137       static_cast<int64_t>(KbToPages(7 * (1ULL << 20) + 512 * (1ULL << 10)));
138 
139   // No buffer size given: good default.
140   EXPECT_EQ(test(0, kExactSize, kNoRamInfo), KbToPages(2048));
141   // Default depends on device ram size.
142   EXPECT_EQ(test(0, kExactSize, kLowRamPages), KbToPages(2048));
143   EXPECT_EQ(test(0, kExactSize, kHighRamPages), KbToPages(8192));
144 
145   // buffer_size_lower_bound lets us choose a higher default than given.
146   // default > requested:
147   EXPECT_EQ(test(4096, kExactSize, kHighRamPages), KbToPages(4096));
148   EXPECT_EQ(test(4096, kLowerBoundSize, kHighRamPages), KbToPages(8192));
149   // requested > default:
150   EXPECT_EQ(test(4096, kExactSize, kLowRamPages), KbToPages(4096));
151   EXPECT_EQ(test(4096, kLowerBoundSize, kLowRamPages), KbToPages(4096));
152   // requested > default:
153   EXPECT_EQ(test(16384, kExactSize, kHighRamPages), KbToPages(16384));
154   EXPECT_EQ(test(16384, kLowerBoundSize, kHighRamPages), KbToPages(16384));
155 
156   // Buffer size given way too big: good default.
157   EXPECT_EQ(test(10 * (1ULL << 20), kExactSize, kNoRamInfo), kMaxBufSizePages);
158   EXPECT_EQ(test(512 * 1024, kExactSize, kNoRamInfo), kMaxBufSizePages);
159 
160   // Your size ends up with less than 1 page per cpu -> 1 page.
161   EXPECT_EQ(test(3, kExactSize, kNoRamInfo), 1u);
162   // You picked a good size -> your size rounded to nearest page.
163   EXPECT_EQ(test(42, kExactSize, kNoRamInfo), KbToPages(42));
164 
165   // Sysconf returning an error is ok.
166   EXPECT_EQ(test(0, kExactSize, -1), KbToPages(2048));
167   EXPECT_EQ(test(4096, kExactSize, -1), KbToPages(4096));
168 }
169 
170 // Base fixture that provides some dependencies but doesn't construct a
171 // FtraceConfigMuxer.
172 class FtraceConfigMuxerTest : public ::testing::Test {
173  protected:
FtraceConfigMuxerTest()174   FtraceConfigMuxerTest() {
175     ON_CALL(atrace_wrapper_, RunAtrace).WillByDefault(Return(true));
176     ON_CALL(atrace_wrapper_, SupportsUserspaceOnly).WillByDefault(Return(true));
177   }
178 
GetMockTable()179   std::unique_ptr<MockProtoTranslationTable> GetMockTable() {
180     std::vector<Field> common_fields;
181     std::vector<Event> events;
182     return std::unique_ptr<MockProtoTranslationTable>(
183         new MockProtoTranslationTable(
184             &ftrace_, events, std::move(common_fields),
185             ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
186             InvalidCompactSchedEventFormatForTesting()));
187   }
188 
GetSyscallTable()189   SyscallTable GetSyscallTable() {
190     return SyscallTable::Load<FakeSyscallTable>();
191   }
192 
CreateFakeTable(CompactSchedEventFormat compact_format=InvalidCompactSchedEventFormatForTesting ())193   std::unique_ptr<ProtoTranslationTable> CreateFakeTable(
194       CompactSchedEventFormat compact_format =
195           InvalidCompactSchedEventFormatForTesting()) {
196     std::vector<Field> common_fields;
197     std::vector<Event> events;
198     {
199       Event event = {};
200       event.name = "sched_switch";
201       event.group = "sched";
202       event.ftrace_event_id = kFakeSchedSwitchEventId;
203       events.push_back(event);
204     }
205 
206     {
207       Event event = {};
208       event.name = "sched_wakeup";
209       event.group = "sched";
210       event.ftrace_event_id = 10;
211       events.push_back(event);
212     }
213 
214     {
215       Event event = {};
216       event.name = "sched_new";
217       event.group = "sched";
218       event.ftrace_event_id = 11;
219       events.push_back(event);
220     }
221 
222     {
223       Event event = {};
224       event.name = "cgroup_mkdir";
225       event.group = "cgroup";
226       event.ftrace_event_id = kCgroupMkdirEventId;
227       events.push_back(event);
228     }
229 
230     {
231       Event event = {};
232       event.name = "mm_vmscan_direct_reclaim_begin";
233       event.group = "vmscan";
234       event.ftrace_event_id = 13;
235       events.push_back(event);
236     }
237 
238     {
239       Event event = {};
240       event.name = "lowmemory_kill";
241       event.group = "lowmemorykiller";
242       event.ftrace_event_id = 14;
243       events.push_back(event);
244     }
245 
246     {
247       Event event = {};
248       event.name = "print";
249       event.group = "ftrace";
250       event.ftrace_event_id = kFakePrintEventId;
251       events.push_back(event);
252     }
253 
254     {
255       Event event = {};
256       event.name = "sys_enter";
257       event.group = "raw_syscalls";
258       event.ftrace_event_id = kSysEnterId;
259       events.push_back(event);
260     }
261 
262     return std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
263         &ftrace_, events, std::move(common_fields),
264         ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
265         compact_format, PrintkMap()));
266   }
267 
268   NiceMock<MockFtraceProcfs> ftrace_;
269   NiceMock<MockAtraceWrapper> atrace_wrapper_;
270 };
271 
TEST_F(FtraceConfigMuxerTest,SecondaryInstanceDoNotSupportAtrace)272 TEST_F(FtraceConfigMuxerTest, SecondaryInstanceDoNotSupportAtrace) {
273   auto fake_table = CreateFakeTable();
274   FtraceConfigMuxer model(&ftrace_, &atrace_wrapper_, fake_table.get(),
275                           GetSyscallTable(), {},
276                           /* secondary_instance= */ true);
277 
278   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
279   *config.add_atrace_categories() = "sched";
280 
281   ASSERT_FALSE(model.SetupConfig(/* id= */ 73, config));
282 }
283 
TEST_F(FtraceConfigMuxerTest,CompactSchedConfig)284 TEST_F(FtraceConfigMuxerTest, CompactSchedConfig) {
285   // Set scheduling event format as validated. The pre-parsed format itself
286   // doesn't need to be sensible, as the tests won't use it.
287   auto format_with_id = CompactSchedSwitchFormat{};
288   format_with_id.event_id = kFakeSchedSwitchEventId;
289   auto valid_compact_format = CompactSchedEventFormat{
290       /*format_valid=*/true, format_with_id, CompactSchedWakingFormat{}};
291 
292   std::unique_ptr<ProtoTranslationTable> table =
293       CreateFakeTable(valid_compact_format);
294   FtraceConfigMuxer muxer(&ftrace_, &atrace_wrapper_, table.get(),
295                           GetSyscallTable(), {});
296 
297   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
298       .WillByDefault(Return("nop"));
299   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
300       .WillByDefault(Return("0"));
301 
302   {
303     // Explicitly enabled.
304     FtraceConfig cfg = CreateFtraceConfig({"sched/sched_switch"});
305     cfg.mutable_compact_sched()->set_enabled(true);
306 
307     FtraceConfigId id = 42;
308     ASSERT_TRUE(muxer.SetupConfig(id, cfg));
309     const FtraceDataSourceConfig* ds_config = muxer.GetDataSourceConfig(id);
310     ASSERT_TRUE(ds_config);
311     EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
312                 Contains(kFakeSchedSwitchEventId));
313     EXPECT_TRUE(ds_config->compact_sched.enabled);
314   }
315   {
316     // Implicitly enabled (default).
317     FtraceConfig cfg = CreateFtraceConfig({"sched/sched_switch"});
318 
319     FtraceConfigId id = 43;
320     ASSERT_TRUE(muxer.SetupConfig(id, cfg));
321     const FtraceDataSourceConfig* ds_config = muxer.GetDataSourceConfig(id);
322     ASSERT_TRUE(ds_config);
323     EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
324                 Contains(kFakeSchedSwitchEventId));
325     EXPECT_TRUE(ds_config->compact_sched.enabled);
326   }
327   {
328     // Explicitly disabled.
329     FtraceConfig cfg = CreateFtraceConfig({"sched/sched_switch"});
330     cfg.mutable_compact_sched()->set_enabled(false);
331 
332     FtraceConfigId id = 44;
333     ASSERT_TRUE(muxer.SetupConfig(id, cfg));
334     const FtraceDataSourceConfig* ds_config = muxer.GetDataSourceConfig(id);
335     ASSERT_TRUE(ds_config);
336     EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
337                 Contains(kFakeSchedSwitchEventId));
338     EXPECT_FALSE(ds_config->compact_sched.enabled);
339   }
340   {
341     // Disabled if not recording sched_switch.
342     FtraceConfig cfg = CreateFtraceConfig({});
343 
344     FtraceConfigId id = 45;
345     ASSERT_TRUE(muxer.SetupConfig(id, cfg));
346     const FtraceDataSourceConfig* ds_config = muxer.GetDataSourceConfig(id);
347     ASSERT_TRUE(ds_config);
348     EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
349                 Not(Contains(kFakeSchedSwitchEventId)));
350     EXPECT_FALSE(ds_config->compact_sched.enabled);
351   }
352 }
353 
354 // Fixture that constructs a FtraceConfigMuxer with a fake
355 // ProtoTranslationTable.
356 class FtraceConfigMuxerFakeTableTest : public FtraceConfigMuxerTest {
357  protected:
358   std::unique_ptr<ProtoTranslationTable> table_ = CreateFakeTable();
359   FtraceConfigMuxer model_ = FtraceConfigMuxer(&ftrace_,
360                                                &atrace_wrapper_,
361                                                table_.get(),
362                                                GetSyscallTable(),
363                                                {});
364 };
365 
TEST_F(FtraceConfigMuxerFakeTableTest,GenericSyscallFiltering)366 TEST_F(FtraceConfigMuxerFakeTableTest, GenericSyscallFiltering) {
367   FtraceConfig config = CreateFtraceConfig({"raw_syscalls/sys_enter"});
368   *config.add_syscall_events() = "sys_open";
369   *config.add_syscall_events() = "sys_read";
370 
371   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
372       .WillByDefault(Return("[local] global boot"));
373   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
374       .Times(AnyNumber());
375   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
376       .WillOnce(Return("nop"));
377   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
378       .WillOnce(Return('1'));
379   EXPECT_CALL(ftrace_, WriteToFile(_, _)).WillRepeatedly(Return(true));
380   EXPECT_CALL(ftrace_, WriteToFile("/root/events/raw_syscalls/sys_enter/filter",
381                                    "id == 0 || id == 1"));
382   EXPECT_CALL(ftrace_, WriteToFile("/root/events/raw_syscalls/sys_exit/filter",
383                                    "id == 0 || id == 1"));
384 
385   FtraceConfigId id = 37;
386   ASSERT_TRUE(model_.SetupConfig(id, config));
387   ASSERT_TRUE(model_.ActivateConfig(id));
388 
389   const std::set<size_t>& filter = model_.GetSyscallFilterForTesting();
390   ASSERT_THAT(filter, UnorderedElementsAre(0, 1));
391 }
392 
TEST_F(FtraceConfigMuxerFakeTableTest,UnknownSyscallFilter)393 TEST_F(FtraceConfigMuxerFakeTableTest, UnknownSyscallFilter) {
394   FtraceConfig config = CreateFtraceConfig({"raw_syscalls/sys_enter"});
395   config.add_syscall_events("sys_open");
396   config.add_syscall_events("sys_not_a_call");
397 
398   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
399       .WillByDefault(Return("[local] global boot"));
400   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
401       .Times(AnyNumber());
402   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
403       .WillOnce(Return("nop"));
404   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
405       .WillOnce(Return('1'));
406 
407   // Unknown syscall is ignored.
408   ASSERT_TRUE(model_.SetupConfig(/*id = */ 73, config));
409   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre(0));
410 }
411 
TEST_F(FtraceConfigMuxerFakeTableTest,SyscallFilterMuxing)412 TEST_F(FtraceConfigMuxerFakeTableTest, SyscallFilterMuxing) {
413   FtraceConfig empty_config = CreateFtraceConfig({});
414 
415   FtraceConfig syscall_config = empty_config;
416   syscall_config.add_ftrace_events("raw_syscalls/sys_enter");
417 
418   FtraceConfig syscall_open_config = syscall_config;
419   syscall_open_config.add_syscall_events("sys_open");
420 
421   FtraceConfig syscall_read_config = syscall_config;
422   syscall_read_config.add_syscall_events("sys_read");
423 
424   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
425       .WillByDefault(Return("nop"));
426 
427   // Expect no filter for non-syscall config.
428   ASSERT_TRUE(model_.SetupConfig(/* id= */ 179239, empty_config));
429   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre());
430 
431   // Expect no filter for syscall config with no specified events.
432   FtraceConfigId syscall_id = 73;
433   ASSERT_TRUE(model_.SetupConfig(syscall_id, syscall_config));
434   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre());
435 
436   // Still expect no filter to satisfy this and the above.
437   FtraceConfigId syscall_open_id = 101;
438   ASSERT_TRUE(model_.SetupConfig(syscall_open_id, syscall_open_config));
439   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre());
440 
441   // After removing the generic syscall trace, only the one with filter is left.
442   ASSERT_TRUE(model_.RemoveConfig(syscall_id));
443   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre(0));
444 
445   // With sys_read and sys_open traced separately, filter includes both.
446   FtraceConfigId syscall_read_id = 57;
447   ASSERT_TRUE(model_.SetupConfig(syscall_read_id, syscall_read_config));
448   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre(0, 1));
449 
450   // After removing configs with filters, filter is reset to empty.
451   ASSERT_TRUE(model_.RemoveConfig(syscall_open_id));
452   ASSERT_TRUE(model_.RemoveConfig(syscall_read_id));
453   ASSERT_THAT(model_.GetSyscallFilterForTesting(), UnorderedElementsAre());
454 }
455 
TEST_F(FtraceConfigMuxerFakeTableTest,TurnFtraceOnOff)456 TEST_F(FtraceConfigMuxerFakeTableTest, TurnFtraceOnOff) {
457   FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
458 
459   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
460       .WillOnce(Return("nop"));
461   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
462       .WillOnce(Return('1'));
463   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
464   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
465   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
466   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
467   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
468       .WillByDefault(Return("[local] global boot"));
469   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
470       .Times(AnyNumber());
471   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", _));
472   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "boot"));
473   EXPECT_CALL(ftrace_,
474               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
475 
476   FtraceConfigId id = 97;
477   ASSERT_TRUE(model_.SetupConfig(id, config));
478 
479   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
480   ASSERT_TRUE(model_.ActivateConfig(id));
481 
482   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
483   ASSERT_TRUE(ds_config);
484   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
485               ElementsAreArray({kFakeSchedSwitchEventId}));
486 
487   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
488   ASSERT_THAT(central_filter->GetEnabledEvents(),
489               ElementsAreArray({kFakeSchedSwitchEventId}));
490 
491   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace_));
492   EXPECT_CALL(ftrace_, NumberOfCpus()).Times(AnyNumber());
493   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_percent", _))
494       .WillRepeatedly(Return(true));
495 
496   EXPECT_CALL(ftrace_,
497               WriteToFile("/root/events/sched/sched_switch/enable", "0"));
498   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
499   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", PageSizeKb()));
500   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
501   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
502   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
503   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
504 
505   ASSERT_TRUE(model_.RemoveConfig(id));
506 }
507 
TEST_F(FtraceConfigMuxerFakeTableTest,FtraceIsAlreadyOn)508 TEST_F(FtraceConfigMuxerFakeTableTest, FtraceIsAlreadyOn) {
509   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
510 
511   // If someone is using ftrace already don't stomp on what they are doing.
512   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
513       .WillOnce(Return("function"));
514   ASSERT_FALSE(model_.SetupConfig(/* id= */ 123, config));
515 }
516 
TEST_F(FtraceConfigMuxerFakeTableTest,Atrace)517 TEST_F(FtraceConfigMuxerFakeTableTest, Atrace) {
518   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
519   *config.add_atrace_categories() = "sched";
520 
521   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
522       .WillByDefault(Return("nop"));
523   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
524       .WillByDefault(Return("0"));
525   EXPECT_CALL(atrace_wrapper_,
526               RunAtrace(ElementsAreArray({"atrace", "--async_start",
527                                           "--only_userspace", "sched"}),
528                         _))
529       .WillOnce(Return(true));
530 
531   FtraceConfigId id = 57;
532   ASSERT_TRUE(model_.SetupConfig(id, config));
533 
534   // "ftrace" group events are always enabled, and therefore the "print" event
535   // will show up in the per data source event filter (as we want to record it),
536   // but not the central filter (as we're not enabling/disabling it).
537   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
538   ASSERT_TRUE(ds_config);
539   EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
540               Contains(kFakeSchedSwitchEventId));
541   EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
542               Contains(kFakePrintEventId));
543 
544   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
545   EXPECT_THAT(central_filter->GetEnabledEvents(),
546               Contains(kFakeSchedSwitchEventId));
547 
548   EXPECT_CALL(
549       atrace_wrapper_,
550       RunAtrace(
551           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
552       .WillOnce(Return(true));
553   ASSERT_TRUE(model_.RemoveConfig(id));
554 }
555 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceTwoApps)556 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceTwoApps) {
557   FtraceConfig config = CreateFtraceConfig({});
558   *config.add_atrace_apps() = "com.google.android.gms.persistent";
559   *config.add_atrace_apps() = "com.google.android.gms";
560 
561   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
562       .WillByDefault(Return("nop"));
563   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
564       .WillByDefault(Return("0"));
565   EXPECT_CALL(
566       atrace_wrapper_,
567       RunAtrace(
568           ElementsAreArray(
569               {"atrace", "--async_start", "--only_userspace", "-a",
570                "com.google.android.gms,com.google.android.gms.persistent"}),
571           _))
572       .WillOnce(Return(true));
573 
574   FtraceConfigId id = 97;
575   ASSERT_TRUE(model_.SetupConfig(id, config));
576 
577   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
578   ASSERT_TRUE(ds_config);
579   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
580               Contains(kFakePrintEventId));
581 
582   EXPECT_CALL(
583       atrace_wrapper_,
584       RunAtrace(
585           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
586       .WillOnce(Return(true));
587   ASSERT_TRUE(model_.RemoveConfig(id));
588 }
589 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceMultipleConfigs)590 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceMultipleConfigs) {
591   FtraceConfig config_a = CreateFtraceConfig({});
592   *config_a.add_atrace_apps() = "app_a";
593   *config_a.add_atrace_categories() = "cat_a";
594 
595   FtraceConfig config_b = CreateFtraceConfig({});
596   *config_b.add_atrace_apps() = "app_b";
597   *config_b.add_atrace_categories() = "cat_b";
598 
599   FtraceConfig config_c = CreateFtraceConfig({});
600   *config_c.add_atrace_apps() = "app_c";
601   *config_c.add_atrace_categories() = "cat_c";
602 
603   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
604       .WillByDefault(Return("nop"));
605   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
606       .WillByDefault(Return("0"));
607   EXPECT_CALL(
608       atrace_wrapper_,
609       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
610                                   "cat_a", "-a", "app_a"}),
611                 _))
612       .WillOnce(Return(true));
613   FtraceConfigId id_a = 3;
614   ASSERT_TRUE(model_.SetupConfig(id_a, config_a));
615 
616   EXPECT_CALL(
617       atrace_wrapper_,
618       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
619                                   "cat_a", "cat_b", "-a", "app_a,app_b"}),
620                 _))
621       .WillOnce(Return(true));
622   FtraceConfigId id_b = 13;
623   ASSERT_TRUE(model_.SetupConfig(id_b, config_b));
624 
625   EXPECT_CALL(atrace_wrapper_,
626               RunAtrace(ElementsAreArray({"atrace", "--async_start",
627                                           "--only_userspace", "cat_a", "cat_b",
628                                           "cat_c", "-a", "app_a,app_b,app_c"}),
629                         _))
630       .WillOnce(Return(true));
631   FtraceConfigId id_c = 23;
632   ASSERT_TRUE(model_.SetupConfig(id_c, config_c));
633 
634   EXPECT_CALL(
635       atrace_wrapper_,
636       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
637                                   "cat_a", "cat_c", "-a", "app_a,app_c"}),
638                 _))
639       .WillOnce(Return(true));
640   ASSERT_TRUE(model_.RemoveConfig(id_b));
641 
642   EXPECT_CALL(
643       atrace_wrapper_,
644       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
645                                   "cat_c", "-a", "app_c"}),
646                 _))
647       .WillOnce(Return(true));
648   ASSERT_TRUE(model_.RemoveConfig(id_a));
649 
650   EXPECT_CALL(
651       atrace_wrapper_,
652       RunAtrace(
653           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
654       .WillOnce(Return(true));
655   ASSERT_TRUE(model_.RemoveConfig(id_c));
656 }
657 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceFailedConfig)658 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceFailedConfig) {
659   FtraceConfig config_a = CreateFtraceConfig({});
660   *config_a.add_atrace_apps() = "app_1";
661   *config_a.add_atrace_apps() = "app_2";
662   *config_a.add_atrace_categories() = "cat_1";
663   *config_a.add_atrace_categories() = "cat_2";
664 
665   FtraceConfig config_b = CreateFtraceConfig({});
666   *config_b.add_atrace_apps() = "app_fail";
667   *config_b.add_atrace_categories() = "cat_fail";
668 
669   FtraceConfig config_c = CreateFtraceConfig({});
670   *config_c.add_atrace_apps() = "app_1";
671   *config_c.add_atrace_apps() = "app_3";
672   *config_c.add_atrace_categories() = "cat_1";
673   *config_c.add_atrace_categories() = "cat_3";
674 
675   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
676       .WillByDefault(Return("nop"));
677   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
678       .WillByDefault(Return("0"));
679   EXPECT_CALL(
680       atrace_wrapper_,
681       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
682                                   "cat_1", "cat_2", "-a", "app_1,app_2"}),
683                 _))
684       .WillOnce(Return(true));
685   FtraceConfigId id_a = 7;
686   ASSERT_TRUE(model_.SetupConfig(id_a, config_a));
687 
688   EXPECT_CALL(
689       atrace_wrapper_,
690       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
691                                   "cat_1", "cat_2", "cat_fail", "-a",
692                                   "app_1,app_2,app_fail"}),
693                 _))
694       .WillOnce(Return(false));
695   FtraceConfigId id_b = 17;
696   ASSERT_TRUE(model_.SetupConfig(id_b, config_b));
697 
698   EXPECT_CALL(atrace_wrapper_,
699               RunAtrace(ElementsAreArray({"atrace", "--async_start",
700                                           "--only_userspace", "cat_1", "cat_2",
701                                           "cat_3", "-a", "app_1,app_2,app_3"}),
702                         _))
703       .WillOnce(Return(true));
704   FtraceConfigId id_c = 47;
705   ASSERT_TRUE(model_.SetupConfig(id_c, config_c));
706 
707   EXPECT_CALL(
708       atrace_wrapper_,
709       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
710                                   "cat_1", "cat_2", "-a", "app_1,app_2"}),
711                 _))
712       .WillOnce(Return(true));
713   ASSERT_TRUE(model_.RemoveConfig(id_c));
714 
715   // Removing the config we failed to enable doesn't change the atrace state
716   // so we don't expect a call here.
717   ASSERT_TRUE(model_.RemoveConfig(id_b));
718 
719   EXPECT_CALL(
720       atrace_wrapper_,
721       RunAtrace(
722           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
723       .WillOnce(Return(true));
724   ASSERT_TRUE(model_.RemoveConfig(id_a));
725 }
726 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceDuplicateConfigs)727 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceDuplicateConfigs) {
728   FtraceConfig config_a = CreateFtraceConfig({});
729   *config_a.add_atrace_apps() = "app_1";
730   *config_a.add_atrace_categories() = "cat_1";
731 
732   FtraceConfig config_b = CreateFtraceConfig({});
733   *config_b.add_atrace_apps() = "app_1";
734   *config_b.add_atrace_categories() = "cat_1";
735 
736   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
737       .WillByDefault(Return("nop"));
738   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
739       .WillByDefault(Return("0"));
740   EXPECT_CALL(
741       atrace_wrapper_,
742       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
743                                   "cat_1", "-a", "app_1"}),
744                 _))
745       .WillOnce(Return(true));
746   FtraceConfigId id_a = 19;
747   ASSERT_TRUE(model_.SetupConfig(id_a, config_a));
748 
749   FtraceConfigId id_b = 29;
750   ASSERT_TRUE(model_.SetupConfig(id_b, config_b));
751 
752   ASSERT_TRUE(model_.RemoveConfig(id_a));
753 
754   EXPECT_CALL(
755       atrace_wrapper_,
756       RunAtrace(
757           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
758       .WillOnce(Return(true));
759   ASSERT_TRUE(model_.RemoveConfig(id_b));
760 }
761 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceAndFtraceConfigs)762 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceAndFtraceConfigs) {
763   FtraceConfig config_a = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
764 
765   FtraceConfig config_b = CreateFtraceConfig({"sched/sched_switch"});
766   *config_b.add_atrace_categories() = "b";
767 
768   FtraceConfig config_c = CreateFtraceConfig({"sched/sched_switch"});
769 
770   FtraceConfig config_d = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
771   *config_d.add_atrace_categories() = "d";
772 
773   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
774       .WillByDefault(Return("nop"));
775   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
776       .WillByDefault(Return("0"));
777   FtraceConfigId id_a = 179;
778   ASSERT_TRUE(model_.SetupConfig(id_a, config_a));
779 
780   EXPECT_CALL(atrace_wrapper_,
781               RunAtrace(ElementsAreArray({"atrace", "--async_start",
782                                           "--only_userspace", "b"}),
783                         _))
784       .WillOnce(Return(true));
785   FtraceConfigId id_b = 239;
786   ASSERT_TRUE(model_.SetupConfig(id_b, config_b));
787 
788   FtraceConfigId id_c = 101;
789   ASSERT_TRUE(model_.SetupConfig(id_c, config_c));
790 
791   EXPECT_CALL(atrace_wrapper_,
792               RunAtrace(ElementsAreArray({"atrace", "--async_start",
793                                           "--only_userspace", "b", "d"}),
794                         _))
795       .WillOnce(Return(true));
796   FtraceConfigId id_d = 47;
797   ASSERT_TRUE(model_.SetupConfig(id_d, config_d));
798 
799   EXPECT_CALL(atrace_wrapper_,
800               RunAtrace(ElementsAreArray({"atrace", "--async_start",
801                                           "--only_userspace", "b"}),
802                         _))
803       .WillOnce(Return(true));
804   ASSERT_TRUE(model_.RemoveConfig(id_d));
805 
806   ASSERT_TRUE(model_.RemoveConfig(id_c));
807 
808   EXPECT_CALL(
809       atrace_wrapper_,
810       RunAtrace(
811           ElementsAreArray({"atrace", "--async_stop", "--only_userspace"}), _))
812       .WillOnce(Return(true));
813   ASSERT_TRUE(model_.RemoveConfig(id_b));
814 
815   ASSERT_TRUE(model_.RemoveConfig(id_a));
816 }
817 
TEST_F(FtraceConfigMuxerFakeTableTest,AtraceErrorsPropagated)818 TEST_F(FtraceConfigMuxerFakeTableTest, AtraceErrorsPropagated) {
819   FtraceConfig config = CreateFtraceConfig({});
820   *config.add_atrace_categories() = "cat_1";
821   *config.add_atrace_categories() = "cat_2";
822 
823   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
824       .WillByDefault(Return("nop"));
825   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
826       .WillByDefault(Return("0"));
827 
828   EXPECT_CALL(
829       atrace_wrapper_,
830       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
831                                   "cat_1", "cat_2"}),
832                 _))
833       .WillOnce(Invoke([](const std::vector<std::string>&, std::string* err) {
834         EXPECT_NE(err, nullptr);
835         if (err)
836           err->append("foo\nbar\n");
837         return true;
838       }));
839 
840   FtraceSetupErrors errors{};
841   FtraceConfigId id_a = 23;
842   ASSERT_TRUE(model_.SetupConfig(id_a, config, &errors));
843   EXPECT_EQ(errors.atrace_errors, "foo\nbar\n");
844 }
845 
TEST_F(FtraceConfigMuxerFakeTableTest,SetupClockForTesting)846 TEST_F(FtraceConfigMuxerFakeTableTest, SetupClockForTesting) {
847   FtraceConfig config;
848 
849   namespace pb0 = protos::pbzero;
850 
851   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
852       .Times(AnyNumber());
853 
854   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
855       .WillByDefault(Return("[local] global boot"));
856   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "boot"));
857   model_.SetupClockForTesting(config);
858   // unspecified = boot.
859   EXPECT_EQ(model_.ftrace_clock(),
860             static_cast<int>(pb0::FTRACE_CLOCK_UNSPECIFIED));
861 
862   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
863       .WillByDefault(Return("[local] global"));
864   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "global"));
865   model_.SetupClockForTesting(config);
866   EXPECT_EQ(model_.ftrace_clock(), static_cast<int>(pb0::FTRACE_CLOCK_GLOBAL));
867 
868   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
869       .WillByDefault(Return(""));
870   model_.SetupClockForTesting(config);
871   EXPECT_EQ(model_.ftrace_clock(), static_cast<int>(pb0::FTRACE_CLOCK_UNKNOWN));
872 
873   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
874       .WillByDefault(Return("local [global]"));
875   model_.SetupClockForTesting(config);
876   EXPECT_EQ(model_.ftrace_clock(), static_cast<int>(pb0::FTRACE_CLOCK_GLOBAL));
877 }
878 
TEST_F(FtraceConfigMuxerFakeTableTest,GetFtraceEvents)879 TEST_F(FtraceConfigMuxerFakeTableTest, GetFtraceEvents) {
880   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
881   std::set<GroupAndName> events =
882       model_.GetFtraceEventsForTesting(config, table_.get());
883 
884   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
885   EXPECT_THAT(events, Not(Contains(GroupAndName("ftrace", "print"))));
886 }
887 
TEST_F(FtraceConfigMuxerFakeTableTest,GetFtraceEventsAtrace)888 TEST_F(FtraceConfigMuxerFakeTableTest, GetFtraceEventsAtrace) {
889   FtraceConfig config = CreateFtraceConfig({});
890   *config.add_atrace_categories() = "sched";
891   std::set<GroupAndName> events =
892       model_.GetFtraceEventsForTesting(config, table_.get());
893 
894   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
895   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
896   EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
897 }
898 
TEST_F(FtraceConfigMuxerFakeTableTest,GetFtraceEventsAtraceCategories)899 TEST_F(FtraceConfigMuxerFakeTableTest, GetFtraceEventsAtraceCategories) {
900   FtraceConfig config = CreateFtraceConfig({});
901   *config.add_atrace_categories() = "sched";
902   *config.add_atrace_categories() = "memreclaim";
903   std::set<GroupAndName> events =
904       model_.GetFtraceEventsForTesting(config, table_.get());
905 
906   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_switch")));
907   EXPECT_THAT(events, Contains(GroupAndName("sched", "sched_cpu_hotplug")));
908   EXPECT_THAT(events, Contains(GroupAndName("cgroup", "cgroup_mkdir")));
909   EXPECT_THAT(events, Contains(GroupAndName("vmscan",
910                                             "mm_vmscan_direct_reclaim_begin")));
911   EXPECT_THAT(events,
912               Contains(GroupAndName("lowmemorykiller", "lowmemory_kill")));
913   EXPECT_THAT(events, Contains(GroupAndName("ftrace", "print")));
914 }
915 
916 // Tests the enabling fallback logic that tries to use the "set_event" interface
917 // if writing the individual xxx/enable file fails.
TEST_F(FtraceConfigMuxerFakeTableTest,FallbackOnSetEvent)918 TEST_F(FtraceConfigMuxerFakeTableTest, FallbackOnSetEvent) {
919   FtraceConfig config =
920       CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
921 
922   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_percent", _))
923       .WillRepeatedly(Return(true));
924 
925   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
926       .WillOnce(Return("nop"));
927   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
928       .WillOnce(Return('1'));
929   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
930   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
931   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
932   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
933   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
934       .WillByDefault(Return("[local] global boot"));
935   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
936       .Times(AnyNumber());
937   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", _));
938   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "boot"));
939   EXPECT_CALL(ftrace_,
940               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
941   EXPECT_CALL(ftrace_,
942               WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "1"))
943       .WillOnce(Return(false));
944   EXPECT_CALL(ftrace_, AppendToFile("/root/set_event", "cgroup:cgroup_mkdir"))
945       .WillOnce(Return(true));
946   FtraceConfigId id = 97;
947   ASSERT_TRUE(model_.SetupConfig(id, config));
948 
949   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
950   ASSERT_TRUE(model_.ActivateConfig(id));
951 
952   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
953   ASSERT_TRUE(ds_config);
954   EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
955               Contains(kFakeSchedSwitchEventId));
956   EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
957               Contains(kCgroupMkdirEventId));
958 
959   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
960   EXPECT_THAT(central_filter->GetEnabledEvents(),
961               Contains(kFakeSchedSwitchEventId));
962   EXPECT_THAT(central_filter->GetEnabledEvents(),
963               Contains(kCgroupMkdirEventId));
964 
965   EXPECT_CALL(ftrace_,
966               WriteToFile("/root/events/sched/sched_switch/enable", "0"));
967   EXPECT_CALL(ftrace_,
968               WriteToFile("/root/events/cgroup/cgroup_mkdir/enable", "0"))
969       .WillOnce(Return(false));
970   EXPECT_CALL(ftrace_, AppendToFile("/root/set_event", "!cgroup:cgroup_mkdir"))
971       .WillOnce(Return(true));
972   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
973   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", PageSizeKb()));
974   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
975   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
976   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
977   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
978   ASSERT_TRUE(model_.RemoveConfig(id));
979 }
980 
TEST_F(FtraceConfigMuxerFakeTableTest,CompactSchedConfigWithInvalidFormat)981 TEST_F(FtraceConfigMuxerFakeTableTest, CompactSchedConfigWithInvalidFormat) {
982   // Request compact encoding.
983   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
984   config.mutable_compact_sched()->set_enabled(true);
985 
986   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
987       .WillByDefault(Return("nop"));
988   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
989       .WillByDefault(Return("0"));
990 
991   FtraceConfigId id = 67;
992   ASSERT_TRUE(model_.SetupConfig(id, config));
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(FtraceConfigMuxerFakeTableTest,SkipGenericEventsOption)1004 TEST_F(FtraceConfigMuxerFakeTableTest, SkipGenericEventsOption) {
1005   static constexpr int kFtraceGenericEventId = 42;
1006   ON_CALL(ftrace_, ReadEventFormat("sched", "generic"))
1007       .WillByDefault(Return(R"(name: generic
1008 ID: 42
1009 format:
1010 	field:int common_pid;	offset:0;	size:4;	signed:1;
1011 
1012 	field:u32 field_a;	offset:4;	size:4;	signed:0;
1013 	field:int field_b;	offset:8;	size:4;	signed:1;
1014 
1015 print fmt: "unused")"));
1016 
1017   // Data source asking for one known and one generic event.
1018   FtraceConfig config_default =
1019       CreateFtraceConfig({"sched/sched_switch", "sched/generic"});
1020 
1021   // As above, but with an option to suppress generic events.
1022   FtraceConfig config_with_disable =
1023       CreateFtraceConfig({"sched/sched_switch", "sched/generic"});
1024   config_with_disable.set_disable_generic_events(true);
1025 
1026   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1027       .WillByDefault(Return("nop"));
1028   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
1029       .WillByDefault(Return("0"));
1030 
1031   {
1032     FtraceConfigId id = 123;
1033     ASSERT_TRUE(model_.SetupConfig(id, config_default));
1034     const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1035     ASSERT_TRUE(ds_config);
1036     // Both events enabled for the data source by default.
1037     EXPECT_THAT(
1038         ds_config->event_filter.GetEnabledEvents(),
1039         UnorderedElementsAre(kFakeSchedSwitchEventId, kFtraceGenericEventId));
1040   }
1041   {
1042     FtraceConfigId id = 321;
1043     ASSERT_TRUE(model_.SetupConfig(id, config_with_disable));
1044     const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1045     ASSERT_TRUE(ds_config);
1046     // Only the statically known event is enabled.
1047     EXPECT_THAT(ds_config->event_filter.GetEnabledEvents(),
1048                 UnorderedElementsAre(kFakeSchedSwitchEventId));
1049   }
1050 }
1051 
TEST_F(FtraceConfigMuxerFakeTableTest,Funcgraph)1052 TEST_F(FtraceConfigMuxerFakeTableTest, Funcgraph) {
1053   FtraceConfig config;
1054   config.set_enable_function_graph(true);
1055   *config.add_function_filters() = "sched*";
1056   *config.add_function_filters() = "handle_mm_fault";
1057 
1058   *config.add_function_graph_roots() = "sched*";
1059   *config.add_function_graph_roots() = "*mm_fault";
1060 
1061   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1062       .WillByDefault(Return("nop"));
1063 
1064   EXPECT_CALL(ftrace_, WriteToFile(_, _)).WillRepeatedly(Return(true));
1065 
1066   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
1067   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
1068 
1069   // Set up config, assert that the tracefs writes happened:
1070   EXPECT_CALL(ftrace_, ClearFile("/root/set_ftrace_filter"));
1071   EXPECT_CALL(ftrace_, ClearFile("/root/set_graph_function"));
1072   EXPECT_CALL(ftrace_, AppendToFile("/root/set_ftrace_filter",
1073                                     "sched*\nhandle_mm_fault"))
1074       .WillOnce(Return(true));
1075   EXPECT_CALL(ftrace_,
1076               AppendToFile("/root/set_graph_function", "sched*\n*mm_fault"))
1077       .WillOnce(Return(true));
1078   EXPECT_CALL(ftrace_, WriteToFile("/root/current_tracer", "function_graph"))
1079       .WillOnce(Return(true));
1080   FtraceConfigId id = 43;
1081   ASSERT_TRUE(model_.SetupConfig(id, config));
1082   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace_));
1083   // Toggle config on and off, tracer won't be reset yet:
1084   ASSERT_TRUE(model_.ActivateConfig(id));
1085   ASSERT_TRUE(model_.RemoveConfig(id));
1086   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace_));
1087 
1088   // Emulate ftrace_controller's call to ResetCurrentTracer (see impl comments
1089   // for why RemoveConfig is insufficient).
1090   EXPECT_CALL(ftrace_, ClearFile("/root/set_ftrace_filter"));
1091   EXPECT_CALL(ftrace_, ClearFile("/root/set_graph_function"));
1092   EXPECT_CALL(ftrace_, WriteToFile("/root/current_tracer", "nop"))
1093       .WillOnce(Return(true));
1094   ASSERT_TRUE(model_.ResetCurrentTracer());
1095   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace_));
1096 }
1097 
TEST_F(FtraceConfigMuxerFakeTableTest,PreserveFtraceBufferNotSetBufferSizeKb)1098 TEST_F(FtraceConfigMuxerFakeTableTest, PreserveFtraceBufferNotSetBufferSizeKb) {
1099   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
1100 
1101   config.set_preserve_ftrace_buffer(true);
1102   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
1103       .WillOnce(Return('1'));
1104   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1105       .WillByDefault(Return("[local] global boot"));
1106   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1107       .Times(AnyNumber());
1108   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", _)).Times(0);
1109   EXPECT_CALL(ftrace_,
1110               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
1111 
1112   FtraceConfigId id = 44;
1113   ASSERT_TRUE(model_.SetupConfig(id, config));
1114 }
1115 
1116 // Fixture that constructs a FtraceConfigMuxer with a mock
1117 // ProtoTranslationTable.
1118 class FtraceConfigMuxerMockTableTest : public FtraceConfigMuxerTest {
1119  protected:
1120   std::unique_ptr<MockProtoTranslationTable> mock_table_ = GetMockTable();
1121   FtraceConfigMuxer model_ = FtraceConfigMuxer(&ftrace_,
1122                                                &atrace_wrapper_,
1123                                                mock_table_.get(),
1124                                                GetSyscallTable(),
1125                                                {});
1126 };
1127 
TEST_F(FtraceConfigMuxerMockTableTest,AddGenericEvent)1128 TEST_F(FtraceConfigMuxerMockTableTest, AddGenericEvent) {
1129   FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
1130 
1131   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1132       .WillOnce(Return("nop"));
1133   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
1134       .WillOnce(Return('1'));
1135   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
1136   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
1137   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
1138   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
1139   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1140       .WillByDefault(Return("[local] global boot"));
1141   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1142       .Times(AnyNumber());
1143   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", _));
1144   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "boot"));
1145   EXPECT_CALL(ftrace_,
1146               WriteToFile("/root/events/power/cpu_frequency/enable", "1"));
1147   EXPECT_CALL(*mock_table_, GetEvent(GroupAndName("power", "cpu_frequency")))
1148       .Times(AnyNumber());
1149 
1150   static constexpr int kExpectedEventId = 77;
1151   Event event_to_return;
1152   event_to_return.name = "cpu_frequency";
1153   event_to_return.group = "power";
1154   event_to_return.ftrace_event_id = kExpectedEventId;
1155   ON_CALL(*mock_table_,
1156           GetOrCreateEvent(GroupAndName("power", "cpu_frequency")))
1157       .WillByDefault(Return(&event_to_return));
1158   EXPECT_CALL(*mock_table_,
1159               GetOrCreateEvent(GroupAndName("power", "cpu_frequency")));
1160 
1161   FtraceConfigId id = 7;
1162   ASSERT_TRUE(model_.SetupConfig(id, config));
1163 
1164   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
1165   ASSERT_TRUE(model_.ActivateConfig(id));
1166 
1167   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1168   ASSERT_TRUE(ds_config);
1169   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
1170               ElementsAreArray({kExpectedEventId}));
1171 
1172   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
1173   ASSERT_THAT(central_filter->GetEnabledEvents(),
1174               ElementsAreArray({kExpectedEventId}));
1175 }
1176 
TEST_F(FtraceConfigMuxerMockTableTest,AddAllEvents)1177 TEST_F(FtraceConfigMuxerMockTableTest, AddAllEvents) {
1178   FtraceConfig config = CreateFtraceConfig({"sched/*"});
1179 
1180   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1181       .WillOnce(Return("nop"));
1182   EXPECT_CALL(ftrace_, ReadOneCharFromFile("/root/tracing_on"))
1183       .WillOnce(Return('1'));
1184   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "0"));
1185   EXPECT_CALL(ftrace_, WriteToFile("/root/events/enable", "0"));
1186   EXPECT_CALL(ftrace_, ClearFile("/root/trace"));
1187   EXPECT_CALL(ftrace_, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
1188   ON_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1189       .WillByDefault(Return("[local] global boot"));
1190   EXPECT_CALL(ftrace_, ReadFileIntoString("/root/trace_clock"))
1191       .Times(AnyNumber());
1192   EXPECT_CALL(ftrace_, WriteToFile("/root/buffer_size_kb", _));
1193   EXPECT_CALL(ftrace_, WriteToFile("/root/trace_clock", "boot"));
1194   EXPECT_CALL(ftrace_,
1195               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
1196   EXPECT_CALL(ftrace_,
1197               WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
1198 
1199   std::set<std::string> n = {"sched_switch", "sched_new_event"};
1200   ON_CALL(ftrace_, GetEventNamesForGroup("events/sched"))
1201       .WillByDefault(Return(n));
1202   EXPECT_CALL(ftrace_, GetEventNamesForGroup("events/sched")).Times(1);
1203 
1204   // Non-generic event.
1205   static constexpr int kSchedSwitchEventId = 1;
1206   Event sched_switch = {"sched_switch", "sched", {}, 0, 0, 0};
1207   sched_switch.ftrace_event_id = kSchedSwitchEventId;
1208   ON_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
1209       .WillByDefault(Return(&sched_switch));
1210   EXPECT_CALL(*mock_table_,
1211               GetOrCreateEvent(GroupAndName("sched", "sched_switch")))
1212       .Times(AnyNumber());
1213 
1214   // Generic event.
1215   static constexpr int kGenericEventId = 2;
1216   Event event_to_return;
1217   event_to_return.name = "sched_new_event";
1218   event_to_return.group = "sched";
1219   event_to_return.ftrace_event_id = kGenericEventId;
1220   ON_CALL(*mock_table_,
1221           GetOrCreateEvent(GroupAndName("sched", "sched_new_event")))
1222       .WillByDefault(Return(&event_to_return));
1223   EXPECT_CALL(*mock_table_,
1224               GetOrCreateEvent(GroupAndName("sched", "sched_new_event")));
1225 
1226   FtraceConfigId id = 13;
1227   ASSERT_TRUE(model_.SetupConfig(id, config));
1228   ASSERT_TRUE(id);
1229 
1230   EXPECT_CALL(ftrace_, WriteToFile("/root/tracing_on", "1"));
1231   ASSERT_TRUE(model_.ActivateConfig(id));
1232 
1233   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1234   ASSERT_TRUE(ds_config);
1235   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
1236               ElementsAreArray({kSchedSwitchEventId, kGenericEventId}));
1237 
1238   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
1239   ASSERT_THAT(central_filter->GetEnabledEvents(),
1240               ElementsAreArray({kSchedSwitchEventId, kGenericEventId}));
1241 }
1242 
TEST_F(FtraceConfigMuxerMockTableTest,TwoWildcardGroups)1243 TEST_F(FtraceConfigMuxerMockTableTest, TwoWildcardGroups) {
1244   FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
1245 
1246   std::set<std::string> event_names = {"foo"};
1247   ON_CALL(ftrace_, GetEventNamesForGroup("events/group_one"))
1248       .WillByDefault(Return(event_names));
1249   EXPECT_CALL(ftrace_, GetEventNamesForGroup("events/group_one"))
1250       .Times(AnyNumber());
1251 
1252   ON_CALL(ftrace_, GetEventNamesForGroup("events/group_two"))
1253       .WillByDefault(Return(event_names));
1254   EXPECT_CALL(ftrace_, GetEventNamesForGroup("events/group_two"))
1255       .Times(AnyNumber());
1256 
1257   static constexpr int kEventId1 = 1;
1258   Event event1;
1259   event1.name = "foo";
1260   event1.group = "group_one";
1261   event1.ftrace_event_id = kEventId1;
1262   ON_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_one", "foo")))
1263       .WillByDefault(Return(&event1));
1264   EXPECT_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_one", "foo")));
1265 
1266   static constexpr int kEventId2 = 2;
1267   Event event2;
1268   event2.name = "foo";
1269   event2.group = "group_two";
1270   event2.ftrace_event_id = kEventId2;
1271   ON_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_two", "foo")))
1272       .WillByDefault(Return(&event2));
1273   EXPECT_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_two", "foo")));
1274 
1275   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1276       .WillByDefault(Return("nop"));
1277   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
1278       .WillByDefault(Return("0"));
1279 
1280   FtraceConfigId id = 23;
1281   ASSERT_TRUE(model_.SetupConfig(id, config));
1282   ASSERT_TRUE(model_.ActivateConfig(id));
1283 
1284   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1285   ASSERT_TRUE(ds_config);
1286   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
1287               ElementsAreArray({kEventId1, kEventId2}));
1288 
1289   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
1290   ASSERT_THAT(central_filter->GetEnabledEvents(),
1291               ElementsAreArray({kEventId1, kEventId2}));
1292 }
1293 
TEST_F(FtraceConfigMuxerMockTableTest,AddSameNameEvents)1294 TEST_F(FtraceConfigMuxerMockTableTest, AddSameNameEvents) {
1295   FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
1296 
1297   static constexpr int kEventId1 = 1;
1298   Event event1;
1299   event1.name = "foo";
1300   event1.group = "group_one";
1301   event1.ftrace_event_id = kEventId1;
1302   ON_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_one", "foo")))
1303       .WillByDefault(Return(&event1));
1304   EXPECT_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_one", "foo")));
1305 
1306   static constexpr int kEventId2 = 2;
1307   Event event2;
1308   event2.name = "foo";
1309   event2.group = "group_two";
1310   event2.ftrace_event_id = kEventId2;
1311   ON_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_two", "foo")))
1312       .WillByDefault(Return(&event2));
1313   EXPECT_CALL(*mock_table_, GetOrCreateEvent(GroupAndName("group_two", "foo")));
1314 
1315   ON_CALL(ftrace_, ReadFileIntoString("/root/current_tracer"))
1316       .WillByDefault(Return("nop"));
1317   ON_CALL(ftrace_, ReadFileIntoString("/root/events/enable"))
1318       .WillByDefault(Return("0"));
1319 
1320   FtraceConfigId id = 5;
1321   ASSERT_TRUE(model_.SetupConfig(id, config));
1322   ASSERT_TRUE(model_.ActivateConfig(id));
1323 
1324   const FtraceDataSourceConfig* ds_config = model_.GetDataSourceConfig(id);
1325   ASSERT_THAT(ds_config->event_filter.GetEnabledEvents(),
1326               ElementsAreArray({kEventId1, kEventId2}));
1327 
1328   const EventFilter* central_filter = model_.GetCentralEventFilterForTesting();
1329   ASSERT_THAT(central_filter->GetEnabledEvents(),
1330               ElementsAreArray({kEventId1, kEventId2}));
1331 }
1332 
1333 }  // namespace
1334 }  // namespace perfetto
1335