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