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