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