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