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/ftrace_reader/ftrace_config_muxer.h"
18
19 #include <memory>
20
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "src/ftrace_reader/atrace_wrapper.h"
24 #include "src/ftrace_reader/ftrace_procfs.h"
25 #include "src/ftrace_reader/proto_translation_table.h"
26
27 using testing::_;
28 using testing::AnyNumber;
29 using testing::Contains;
30 using testing::ElementsAreArray;
31 using testing::Eq;
32 using testing::IsEmpty;
33 using testing::NiceMock;
34 using testing::Not;
35 using testing::Return;
36 using testing::UnorderedElementsAre;
37
38 namespace perfetto {
39 namespace {
40
41 class MockFtraceProcfs : public FtraceProcfs {
42 public:
MockFtraceProcfs()43 MockFtraceProcfs() : FtraceProcfs("/root/") {
44 ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
45 ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
46 ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
47 EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
48 }
49
50 MOCK_METHOD2(WriteToFile,
51 bool(const std::string& path, const std::string& str));
52 MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
53 MOCK_METHOD1(ClearFile, bool(const std::string& path));
54 MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
55 MOCK_CONST_METHOD0(NumberOfCpus, size_t());
56 };
57
58 struct MockRunAtrace {
MockRunAtraceperfetto::__anon3804e6600111::MockRunAtrace59 MockRunAtrace() {
60 static MockRunAtrace* instance;
61 instance = this;
62 SetRunAtraceForTesting([](const std::vector<std::string>& args) {
63 return instance->RunAtrace(args);
64 });
65 }
66
~MockRunAtraceperfetto::__anon3804e6600111::MockRunAtrace67 ~MockRunAtrace() { SetRunAtraceForTesting(nullptr); }
68
69 MOCK_METHOD1(RunAtrace, bool(const std::vector<std::string>&));
70 };
71
CreateFakeTable()72 std::unique_ptr<ProtoTranslationTable> CreateFakeTable() {
73 std::vector<Field> common_fields;
74 std::vector<Event> events;
75
76 {
77 Event event;
78 event.name = "sched_switch";
79 event.group = "sched";
80 event.ftrace_event_id = 1;
81 events.push_back(event);
82 }
83
84 {
85 Event event;
86 event.name = "sched_wakeup";
87 event.group = "sched";
88 event.ftrace_event_id = 10;
89 events.push_back(event);
90 }
91
92 {
93 Event event;
94 event.name = "sched_new";
95 event.group = "sched";
96 event.ftrace_event_id = 11;
97 events.push_back(event);
98 }
99
100 {
101 Event event;
102 event.name = "cgroup_mkdir";
103 event.group = "cgroup";
104 event.ftrace_event_id = 12;
105 events.push_back(event);
106 }
107
108 {
109 Event event;
110 event.name = "mm_vmscan_direct_reclaim_begin";
111 event.group = "vmscan";
112 event.ftrace_event_id = 13;
113 events.push_back(event);
114 }
115
116 {
117 Event event;
118 event.name = "lowmemory_kill";
119 event.group = "lowmemorykiller";
120 event.ftrace_event_id = 14;
121 events.push_back(event);
122 }
123
124 {
125 Event event;
126 event.name = "print";
127 event.group = "ftrace";
128 event.ftrace_event_id = 20;
129 events.push_back(event);
130 }
131
132 return std::unique_ptr<ProtoTranslationTable>(
133 new ProtoTranslationTable(events, std::move(common_fields)));
134 }
135
TEST(FtraceConfigMuxerTest,ComputeCpuBufferSizeInPages)136 TEST(FtraceConfigMuxerTest, ComputeCpuBufferSizeInPages) {
137 static constexpr size_t kMaxBufSizeInPages = 16 * 1024u;
138 // No buffer size given: good default (128 pages = 512kb).
139 EXPECT_EQ(ComputeCpuBufferSizeInPages(0), 128u);
140 // Buffer size given way too big: good default.
141 EXPECT_EQ(ComputeCpuBufferSizeInPages(10 * 1024 * 1024), kMaxBufSizeInPages);
142 // The limit is 64mb per CPU, 512mb is too much.
143 EXPECT_EQ(ComputeCpuBufferSizeInPages(512 * 1024), kMaxBufSizeInPages);
144 // Your size ends up with less than 1 page per cpu -> 1 page.
145 EXPECT_EQ(ComputeCpuBufferSizeInPages(3), 1u);
146 // You picked a good size -> your size rounded to nearest page.
147 EXPECT_EQ(ComputeCpuBufferSizeInPages(42), 10u);
148 }
149
TEST(FtraceConfigMuxerTest,TurnFtraceOnOff)150 TEST(FtraceConfigMuxerTest, TurnFtraceOnOff) {
151 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
152 MockFtraceProcfs ftrace;
153
154 FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
155
156 FtraceConfigMuxer model(&ftrace, table.get());
157
158 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
159 .WillByDefault(Return("[local] global boot"));
160 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
161 .Times(AnyNumber());
162
163 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
164 .WillOnce(Return('0'));
165 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "512"));
166 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
167 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
168 EXPECT_CALL(ftrace,
169 WriteToFile("/root/events/sched/sched_switch/enable", "1"));
170 FtraceConfigId id = model.RequestConfig(config);
171 ASSERT_TRUE(id);
172
173 const FtraceConfig* actual_config = model.GetConfig(id);
174 EXPECT_TRUE(actual_config);
175 EXPECT_THAT(actual_config->ftrace_events(), Contains("sched_switch"));
176 EXPECT_THAT(actual_config->ftrace_events(), Not(Contains("foo")));
177
178 EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
179 EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "0"));
180 EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
181 EXPECT_CALL(ftrace,
182 WriteToFile("/root/events/sched/sched_switch/enable", "0"));
183 EXPECT_CALL(ftrace, ClearFile("/root/trace"));
184 ASSERT_TRUE(model.RemoveConfig(id));
185 }
186
TEST(FtraceConfigMuxerTest,FtraceIsAlreadyOn)187 TEST(FtraceConfigMuxerTest, FtraceIsAlreadyOn) {
188 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
189 MockFtraceProcfs ftrace;
190
191 FtraceConfig config = CreateFtraceConfig({"sched_switch"});
192
193 FtraceConfigMuxer model(&ftrace, table.get());
194
195 // If someone is using ftrace already don't stomp on what they are doing.
196 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
197 .WillOnce(Return('1'));
198 FtraceConfigId id = model.RequestConfig(config);
199 ASSERT_FALSE(id);
200 }
201
TEST(FtraceConfigMuxerTest,Atrace)202 TEST(FtraceConfigMuxerTest, Atrace) {
203 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
204 NiceMock<MockFtraceProcfs> ftrace;
205 MockRunAtrace atrace;
206
207 FtraceConfig config = CreateFtraceConfig({"sched_switch"});
208 *config.add_atrace_categories() = "sched";
209
210 FtraceConfigMuxer model(&ftrace, table.get());
211
212 EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
213 .WillOnce(Return('0'));
214 EXPECT_CALL(atrace,
215 RunAtrace(ElementsAreArray(
216 {"atrace", "--async_start", "--only_userspace", "sched"})))
217 .WillOnce(Return(true));
218
219 FtraceConfigId id = model.RequestConfig(config);
220 ASSERT_TRUE(id);
221
222 const FtraceConfig* actual_config = model.GetConfig(id);
223 EXPECT_TRUE(actual_config);
224 EXPECT_THAT(actual_config->ftrace_events(), Contains("sched_switch"));
225 EXPECT_THAT(actual_config->ftrace_events(), Contains("print"));
226
227 EXPECT_CALL(atrace, RunAtrace(ElementsAreArray(
228 {"atrace", "--async_stop", "--only_userspace"})))
229 .WillOnce(Return(true));
230 ASSERT_TRUE(model.RemoveConfig(id));
231 }
232
TEST(FtraceConfigMuxerTest,SetupClockForTesting)233 TEST(FtraceConfigMuxerTest, SetupClockForTesting) {
234 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
235 MockFtraceProcfs ftrace;
236 FtraceConfig config;
237
238 FtraceConfigMuxer model(&ftrace, table.get());
239
240 EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
241 .Times(AnyNumber());
242
243 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
244 .WillByDefault(Return("[local] global boot"));
245 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
246 model.SetupClockForTesting(config);
247
248 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
249 .WillByDefault(Return("[local] global"));
250 EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "global"));
251 model.SetupClockForTesting(config);
252
253 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
254 .WillByDefault(Return(""));
255 model.SetupClockForTesting(config);
256
257 ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
258 .WillByDefault(Return("local [global]"));
259 model.SetupClockForTesting(config);
260 }
261
TEST(FtraceConfigMuxerTest,GetFtraceEvents)262 TEST(FtraceConfigMuxerTest, GetFtraceEvents) {
263 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
264 FtraceConfig config = CreateFtraceConfig({"sched_switch"});
265 std::set<std::string> events = GetFtraceEvents(config, table.get());
266
267 EXPECT_THAT(events, Contains("sched_switch"));
268 EXPECT_THAT(events, Not(Contains("print")));
269 }
270
TEST(FtraceConfigMuxerTest,GetFtraceEventsAtrace)271 TEST(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
272 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
273 FtraceConfig config = CreateFtraceConfig({});
274 *config.add_atrace_categories() = "sched";
275 std::set<std::string> events = GetFtraceEvents(config, table.get());
276
277 EXPECT_THAT(events, Contains("sched_switch"));
278 EXPECT_THAT(events, Contains("sched_cpu_hotplug"));
279 EXPECT_THAT(events, Contains("print"));
280 }
281
TEST(FtraceConfigMuxerTest,GetFtraceEventsAtraceCategories)282 TEST(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
283 std::unique_ptr<ProtoTranslationTable> table = CreateFakeTable();
284 FtraceConfig config = CreateFtraceConfig({});
285 *config.add_atrace_categories() = "sched";
286 *config.add_atrace_categories() = "memreclaim";
287 std::set<std::string> events = GetFtraceEvents(config, table.get());
288
289 EXPECT_THAT(events, Contains("sched_switch"));
290 EXPECT_THAT(events, Contains("sched_cpu_hotplug"));
291 EXPECT_THAT(events, Contains("cgroup_mkdir"));
292 EXPECT_THAT(events, Contains("mm_vmscan_direct_reclaim_begin"));
293 EXPECT_THAT(events, Contains("lowmemory_kill"));
294 EXPECT_THAT(events, Contains("print"));
295 }
296
297 } // namespace
298 } // namespace perfetto
299