• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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