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 <stdint.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <algorithm>
25
26 #include "perfetto/base/utils.h"
27 #include "src/ftrace_reader/atrace_wrapper.h"
28 #include "src/ftrace_reader/proto_translation_table.h"
29
30 namespace perfetto {
31 namespace {
32
33 // trace_clocks in preference order.
34 constexpr const char* kClocks[] = {"boot", "global", "local"};
35
36 constexpr int kDefaultPerCpuBufferSizeKb = 512; // 512kb
37 constexpr int kMaxPerCpuBufferSizeKb = 64 * 1024; // 64mb
38
difference(const std::set<std::string> & a,const std::set<std::string> & b)39 std::vector<std::string> difference(const std::set<std::string>& a,
40 const std::set<std::string>& b) {
41 std::vector<std::string> result;
42 result.reserve(std::max(b.size(), a.size()));
43 std::set_difference(a.begin(), a.end(), b.begin(), b.end(),
44 std::inserter(result, result.begin()));
45 return result;
46 }
47
AddEventGroup(const ProtoTranslationTable * table,const std::string & group,std::set<std::string> * to)48 void AddEventGroup(const ProtoTranslationTable* table,
49 const std::string& group,
50 std::set<std::string>* to) {
51 const std::vector<const Event*>* events = table->GetEventsByGroup(group);
52 if (!events)
53 return;
54 for (const Event* event : *events)
55 to->insert(event->name);
56 }
57
58 } // namespace
59
GetFtraceEvents(const FtraceConfig & request,const ProtoTranslationTable * table)60 std::set<std::string> GetFtraceEvents(const FtraceConfig& request,
61 const ProtoTranslationTable* table) {
62 std::set<std::string> events;
63 events.insert(request.ftrace_events().begin(), request.ftrace_events().end());
64 if (RequiresAtrace(request)) {
65 events.insert("print");
66
67 // Ideally we should keep this code in sync with:
68 // platform/frameworks/native/cmds/atrace/atrace.cpp
69 // It's not a disaster if they go out of sync, we can always add the ftrace
70 // categories manually server side but this is user friendly and reduces the
71 // size of the configs.
72 for (const std::string& category : request.atrace_categories()) {
73 if (category == "gfx") {
74 AddEventGroup(table, "mdss", &events);
75 AddEventGroup(table, "sde", &events);
76 continue;
77 }
78
79 if (category == "sched") {
80 events.insert("sched_switch");
81 events.insert("sched_wakeup");
82 events.insert("sched_waking");
83 events.insert("sched_blocked_reason");
84 events.insert("sched_cpu_hotplug");
85 AddEventGroup(table, "cgroup", &events);
86 continue;
87 }
88
89 if (category == "irq") {
90 AddEventGroup(table, "irq", &events);
91 AddEventGroup(table, "ipi", &events);
92 continue;
93 }
94
95 if (category == "irqoff") {
96 events.insert("irq_enable");
97 events.insert("irq_disable");
98 continue;
99 }
100
101 if (category == "preemptoff") {
102 events.insert("preempt_enable");
103 events.insert("preempt_disable");
104 continue;
105 }
106
107 if (category == "i2c") {
108 AddEventGroup(table, "i2c", &events);
109 continue;
110 }
111
112 if (category == "freq") {
113 events.insert("cpu_frequency");
114 events.insert("clock_set_rate");
115 events.insert("clock_disable");
116 events.insert("clock_enable");
117 events.insert("clk_set_rate");
118 events.insert("clk_disable");
119 events.insert("clk_enable");
120 events.insert("cpu_frequency_limits");
121 continue;
122 }
123
124 if (category == "membus") {
125 AddEventGroup(table, "memory_bus", &events);
126 continue;
127 }
128
129 if (category == "idle") {
130 events.insert("cpu_idle");
131 continue;
132 }
133
134 if (category == "disk") {
135 events.insert("f2fs_sync_file_enter");
136 events.insert("f2fs_sync_file_exit");
137 events.insert("f2fs_write_begin");
138 events.insert("f2fs_write_end");
139 events.insert("ext4_da_write_begin");
140 events.insert("ext4_da_write_end");
141 events.insert("ext4_sync_file_enter");
142 events.insert("ext4_sync_file_exit");
143 events.insert("block_rq_issue");
144 events.insert("block_rq_complete");
145 continue;
146 }
147
148 if (category == "mmc") {
149 AddEventGroup(table, "mmc", &events);
150 continue;
151 }
152
153 if (category == "load") {
154 AddEventGroup(table, "cpufreq_interactive", &events);
155 continue;
156 }
157
158 if (category == "sync") {
159 AddEventGroup(table, "sync", &events);
160 continue;
161 }
162
163 if (category == "workq") {
164 AddEventGroup(table, "workqueue", &events);
165 continue;
166 }
167
168 if (category == "memreclaim") {
169 events.insert("mm_vmscan_direct_reclaim_begin");
170 events.insert("mm_vmscan_direct_reclaim_end");
171 events.insert("mm_vmscan_kswapd_wake");
172 events.insert("mm_vmscan_kswapd_sleep");
173 AddEventGroup(table, "lowmemorykiller", &events);
174 continue;
175 }
176
177 if (category == "regulators") {
178 AddEventGroup(table, "regulator", &events);
179 continue;
180 }
181
182 if (category == "binder_driver") {
183 events.insert("binder_transaction");
184 events.insert("binder_transaction_received");
185 events.insert("binder_set_priority");
186 continue;
187 }
188
189 if (category == "binder_lock") {
190 events.insert("binder_lock");
191 events.insert("binder_locked");
192 events.insert("binder_unlock");
193 continue;
194 }
195
196 if (category == "pagecache") {
197 AddEventGroup(table, "pagecache", &events);
198 continue;
199 }
200 }
201 }
202 return events;
203 }
204
205 // Post-conditions:
206 // 1. result >= 1 (should have at least one page per CPU)
207 // 2. result * 4 < kMaxTotalBufferSizeKb
208 // 3. If input is 0 output is a good default number.
ComputeCpuBufferSizeInPages(size_t requested_buffer_size_kb)209 size_t ComputeCpuBufferSizeInPages(size_t requested_buffer_size_kb) {
210 if (requested_buffer_size_kb == 0)
211 requested_buffer_size_kb = kDefaultPerCpuBufferSizeKb;
212 if (requested_buffer_size_kb > kMaxPerCpuBufferSizeKb) {
213 PERFETTO_ELOG(
214 "The requested ftrace buf size (%zu KB) is too big, capping to %d KB",
215 requested_buffer_size_kb, kMaxPerCpuBufferSizeKb);
216 requested_buffer_size_kb = kMaxPerCpuBufferSizeKb;
217 }
218
219 size_t pages = requested_buffer_size_kb / (base::kPageSize / 1024);
220 if (pages == 0)
221 return 1;
222
223 return pages;
224 }
225
FtraceConfigMuxer(FtraceProcfs * ftrace,const ProtoTranslationTable * table)226 FtraceConfigMuxer::FtraceConfigMuxer(FtraceProcfs* ftrace,
227 const ProtoTranslationTable* table)
228 : ftrace_(ftrace), table_(table), current_state_(), configs_() {}
229 FtraceConfigMuxer::~FtraceConfigMuxer() = default;
230
RequestConfig(const FtraceConfig & request)231 FtraceConfigId FtraceConfigMuxer::RequestConfig(const FtraceConfig& request) {
232 FtraceConfig actual;
233
234 bool is_ftrace_enabled = ftrace_->IsTracingEnabled();
235 if (configs_.empty()) {
236 PERFETTO_DCHECK(!current_state_.tracing_on);
237
238 // If someone outside of perfetto is using ftrace give up now.
239 if (is_ftrace_enabled)
240 return 0;
241
242 // If we're about to turn tracing on use this opportunity do some setup:
243 SetupClock(request);
244 SetupBufferSize(request);
245 } else {
246 // Did someone turn ftrace off behind our back? If so give up.
247 if (!is_ftrace_enabled)
248 return 0;
249 }
250
251 std::set<std::string> events = GetFtraceEvents(request, table_);
252
253 if (RequiresAtrace(request))
254 UpdateAtrace(request);
255
256 for (auto& name : events) {
257 const Event* event = table_->GetEventByName(name);
258 if (!event) {
259 PERFETTO_DLOG("Can't enable %s, event not known", name.c_str());
260 continue;
261 }
262 if (current_state_.ftrace_events.count(name) ||
263 std::string("ftrace") == event->group) {
264 *actual.add_ftrace_events() = name;
265 continue;
266 }
267 if (ftrace_->EnableEvent(event->group, event->name)) {
268 current_state_.ftrace_events.insert(name);
269 *actual.add_ftrace_events() = name;
270 } else {
271 PERFETTO_DPLOG("Failed to enable %s.", name.c_str());
272 }
273 }
274
275 if (configs_.empty()) {
276 PERFETTO_DCHECK(!current_state_.tracing_on);
277 ftrace_->EnableTracing();
278 current_state_.tracing_on = true;
279 }
280
281 FtraceConfigId id = ++last_id_;
282 configs_.emplace(id, std::move(actual));
283 return id;
284 }
285
RemoveConfig(FtraceConfigId id)286 bool FtraceConfigMuxer::RemoveConfig(FtraceConfigId id) {
287 if (!id || !configs_.erase(id))
288 return false;
289
290 std::set<std::string> expected_ftrace_events;
291 for (const auto& id_config : configs_) {
292 const FtraceConfig& config = id_config.second;
293 expected_ftrace_events.insert(config.ftrace_events().begin(),
294 config.ftrace_events().end());
295 }
296
297 std::vector<std::string> events_to_disable =
298 difference(current_state_.ftrace_events, expected_ftrace_events);
299
300 for (auto& name : events_to_disable) {
301 const Event* event = table_->GetEventByName(name);
302 if (!event)
303 continue;
304 if (ftrace_->DisableEvent(event->group, event->name))
305 current_state_.ftrace_events.erase(name);
306 }
307
308 if (configs_.empty()) {
309 PERFETTO_DCHECK(current_state_.tracing_on);
310 ftrace_->DisableTracing();
311 ftrace_->SetCpuBufferSizeInPages(0);
312 ftrace_->DisableAllEvents();
313 ftrace_->ClearTrace();
314 current_state_.tracing_on = false;
315 if (current_state_.atrace_on)
316 DisableAtrace();
317 }
318
319 return true;
320 }
321
GetConfig(FtraceConfigId id)322 const FtraceConfig* FtraceConfigMuxer::GetConfig(FtraceConfigId id) {
323 if (!configs_.count(id))
324 return nullptr;
325 return &configs_.at(id);
326 }
327
SetupClock(const FtraceConfig &)328 void FtraceConfigMuxer::SetupClock(const FtraceConfig&) {
329 std::string current_clock = ftrace_->GetClock();
330 std::set<std::string> clocks = ftrace_->AvailableClocks();
331
332 for (size_t i = 0; i < base::ArraySize(kClocks); i++) {
333 std::string clock = std::string(kClocks[i]);
334 if (!clocks.count(clock))
335 continue;
336 if (current_clock == clock)
337 break;
338 ftrace_->SetClock(clock);
339 break;
340 }
341 }
342
SetupBufferSize(const FtraceConfig & request)343 void FtraceConfigMuxer::SetupBufferSize(const FtraceConfig& request) {
344 size_t pages = ComputeCpuBufferSizeInPages(request.buffer_size_kb());
345 ftrace_->SetCpuBufferSizeInPages(pages);
346 current_state_.cpu_buffer_size_pages = pages;
347 }
348
UpdateAtrace(const FtraceConfig & request)349 void FtraceConfigMuxer::UpdateAtrace(const FtraceConfig& request) {
350 PERFETTO_DLOG("Update atrace config...");
351
352 std::vector<std::string> args;
353 args.push_back("atrace"); // argv0 for exec()
354 args.push_back("--async_start");
355 args.push_back("--only_userspace");
356 for (const auto& category : request.atrace_categories())
357 args.push_back(category);
358 if (!request.atrace_apps().empty()) {
359 args.push_back("-a");
360 for (const auto& app : request.atrace_apps())
361 args.push_back(app);
362 }
363
364 if (RunAtrace(args))
365 current_state_.atrace_on = true;
366
367 PERFETTO_DLOG("...done");
368 }
369
DisableAtrace()370 void FtraceConfigMuxer::DisableAtrace() {
371 PERFETTO_DCHECK(current_state_.atrace_on);
372
373 PERFETTO_DLOG("Stop atrace...");
374
375 if (RunAtrace({"atrace", "--async_stop", "--only_userspace"}))
376 current_state_.atrace_on = false;
377
378 PERFETTO_DLOG("...done");
379 }
380
381 } // namespace perfetto
382