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 <stdint.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <algorithm>
25 #include <iterator>
26
27 #include "perfetto/base/compiler.h"
28 #include "perfetto/ext/base/utils.h"
29 #include "src/traced/probes/ftrace/atrace_wrapper.h"
30 #include "src/traced/probes/ftrace/compact_sched.h"
31 #include "src/traced/probes/ftrace/ftrace_stats.h"
32
33 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
34
35 namespace perfetto {
36 namespace {
37
38 constexpr int kDefaultPerCpuBufferSizeKb = 2 * 1024; // 2mb
39 constexpr int kMaxPerCpuBufferSizeKb = 64 * 1024; // 64mb
40
41 // trace_clocks in preference order.
42 // If this list is changed, the FtraceClocks enum in ftrace_event_bundle.proto
43 // and FtraceConfigMuxer::SetupClock() should be also changed accordingly.
44 constexpr const char* kClocks[] = {"boot", "global", "local"};
45
AddEventGroup(const ProtoTranslationTable * table,const std::string & group,std::set<GroupAndName> * to)46 void AddEventGroup(const ProtoTranslationTable* table,
47 const std::string& group,
48 std::set<GroupAndName>* to) {
49 const std::vector<const Event*>* events = table->GetEventsByGroup(group);
50 if (!events)
51 return;
52 for (const Event* event : *events)
53 to->insert(GroupAndName(group, event->name));
54 }
55
ReadEventsInGroupFromFs(const FtraceProcfs & ftrace_procfs,const std::string & group)56 std::set<GroupAndName> ReadEventsInGroupFromFs(
57 const FtraceProcfs& ftrace_procfs,
58 const std::string& group) {
59 std::set<std::string> names =
60 ftrace_procfs.GetEventNamesForGroup("events/" + group);
61 std::set<GroupAndName> events;
62 for (const auto& name : names)
63 events.insert(GroupAndName(group, name));
64 return events;
65 }
66
EventToStringGroupAndName(const std::string & event)67 std::pair<std::string, std::string> EventToStringGroupAndName(
68 const std::string& event) {
69 auto slash_pos = event.find('/');
70 if (slash_pos == std::string::npos)
71 return std::make_pair("", event);
72 return std::make_pair(event.substr(0, slash_pos),
73 event.substr(slash_pos + 1));
74 }
75
UnionInPlace(const std::vector<std::string> & unsorted_a,std::vector<std::string> * out)76 void UnionInPlace(const std::vector<std::string>& unsorted_a,
77 std::vector<std::string>* out) {
78 std::vector<std::string> a = unsorted_a;
79 std::sort(a.begin(), a.end());
80 std::sort(out->begin(), out->end());
81 std::vector<std::string> v;
82 std::set_union(a.begin(), a.end(), out->begin(), out->end(),
83 std::back_inserter(v));
84 *out = std::move(v);
85 }
86
IntersectInPlace(const std::vector<std::string> & unsorted_a,std::vector<std::string> * out)87 void IntersectInPlace(const std::vector<std::string>& unsorted_a,
88 std::vector<std::string>* out) {
89 std::vector<std::string> a = unsorted_a;
90 std::sort(a.begin(), a.end());
91 std::sort(out->begin(), out->end());
92 std::vector<std::string> v;
93 std::set_intersection(a.begin(), a.end(), out->begin(), out->end(),
94 std::back_inserter(v));
95 *out = std::move(v);
96 }
97
98 // This is just to reduce binary size and stack frame size of the insertions.
99 // It effectively undoes STL's set::insert inlining.
InsertEvent(const char * group,const char * name,std::set<GroupAndName> * dst)100 void PERFETTO_NO_INLINE InsertEvent(const char* group,
101 const char* name,
102 std::set<GroupAndName>* dst) {
103 dst->insert(GroupAndName(group, name));
104 }
105
106 } // namespace
107
GetFtraceEvents(const FtraceConfig & request,const ProtoTranslationTable * table)108 std::set<GroupAndName> FtraceConfigMuxer::GetFtraceEvents(
109 const FtraceConfig& request,
110 const ProtoTranslationTable* table) {
111 std::set<GroupAndName> events;
112 for (const auto& config_value : request.ftrace_events()) {
113 std::string group;
114 std::string name;
115 std::tie(group, name) = EventToStringGroupAndName(config_value);
116 if (name == "*") {
117 for (const auto& event : ReadEventsInGroupFromFs(*ftrace_, group))
118 events.insert(event);
119 } else if (group.empty()) {
120 // If there is no group specified, find an event with that name and
121 // use it's group.
122 const Event* e = table->GetEventByName(name);
123 if (!e) {
124 PERFETTO_DLOG(
125 "Event doesn't exist: %s. Include the group in the config to allow "
126 "the event to be output as a generic event.",
127 name.c_str());
128 continue;
129 }
130 events.insert(GroupAndName(e->group, e->name));
131 } else {
132 events.insert(GroupAndName(group, name));
133 }
134 }
135 if (RequiresAtrace(request)) {
136 InsertEvent("ftrace", "print", &events);
137
138 // Ideally we should keep this code in sync with:
139 // platform/frameworks/native/cmds/atrace/atrace.cpp
140 // It's not a disaster if they go out of sync, we can always add the ftrace
141 // categories manually server side but this is user friendly and reduces the
142 // size of the configs.
143 for (const std::string& category : request.atrace_categories()) {
144 if (category == "gfx") {
145 AddEventGroup(table, "mdss", &events);
146 InsertEvent("mdss", "rotator_bw_ao_as_context", &events);
147 InsertEvent("mdss", "mdp_trace_counter", &events);
148 InsertEvent("mdss", "tracing_mark_write", &events);
149 InsertEvent("mdss", "mdp_cmd_wait_pingpong", &events);
150 InsertEvent("mdss", "mdp_cmd_kickoff", &events);
151 InsertEvent("mdss", "mdp_cmd_release_bw", &events);
152 InsertEvent("mdss", "mdp_cmd_readptr_done", &events);
153 InsertEvent("mdss", "mdp_cmd_pingpong_done", &events);
154 InsertEvent("mdss", "mdp_misr_crc", &events);
155 InsertEvent("mdss", "mdp_compare_bw", &events);
156 InsertEvent("mdss", "mdp_perf_update_bus", &events);
157 InsertEvent("mdss", "mdp_video_underrun_done", &events);
158 InsertEvent("mdss", "mdp_commit", &events);
159 InsertEvent("mdss", "mdp_mixer_update", &events);
160 InsertEvent("mdss", "mdp_perf_prefill_calc", &events);
161 InsertEvent("mdss", "mdp_perf_set_ot", &events);
162 InsertEvent("mdss", "mdp_perf_set_wm_levels", &events);
163 InsertEvent("mdss", "mdp_perf_set_panic_luts", &events);
164 InsertEvent("mdss", "mdp_perf_set_qos_luts", &events);
165 InsertEvent("mdss", "mdp_sspp_change", &events);
166 InsertEvent("mdss", "mdp_sspp_set", &events);
167 AddEventGroup(table, "mali", &events);
168 InsertEvent("mali", "tracing_mark_write", &events);
169
170 AddEventGroup(table, "sde", &events);
171 InsertEvent("sde", "tracing_mark_write", &events);
172 InsertEvent("sde", "sde_perf_update_bus", &events);
173 InsertEvent("sde", "sde_perf_set_qos_luts", &events);
174 InsertEvent("sde", "sde_perf_set_ot", &events);
175 InsertEvent("sde", "sde_perf_set_danger_luts", &events);
176 InsertEvent("sde", "sde_perf_crtc_update", &events);
177 InsertEvent("sde", "sde_perf_calc_crtc", &events);
178 InsertEvent("sde", "sde_evtlog", &events);
179 InsertEvent("sde", "sde_encoder_underrun", &events);
180 InsertEvent("sde", "sde_cmd_release_bw", &events);
181
182 AddEventGroup(table, "dpu", &events);
183 InsertEvent("dpu", "tracing_mark_write", &events);
184
185 AddEventGroup(table, "g2d", &events);
186 InsertEvent("g2d", "tracing_mark_write", &events);
187 InsertEvent("g2d", "g2d_perf_update_qos", &events);
188 continue;
189 }
190
191 if (category == "ion") {
192 InsertEvent("kmem", "ion_alloc_buffer_start", &events);
193 continue;
194 }
195
196 // Note: sched_wakeup intentionally removed (diverging from atrace), as it
197 // is high-volume, but mostly redundant when sched_waking is also enabled.
198 // The event can still be enabled explicitly when necessary.
199 if (category == "sched") {
200 InsertEvent("sched", "sched_switch", &events);
201 InsertEvent("sched", "sched_waking", &events);
202 InsertEvent("sched", "sched_blocked_reason", &events);
203 InsertEvent("sched", "sched_cpu_hotplug", &events);
204 InsertEvent("sched", "sched_pi_setprio", &events);
205 InsertEvent("sched", "sched_process_exit", &events);
206 AddEventGroup(table, "cgroup", &events);
207 InsertEvent("cgroup", "cgroup_transfer_tasks", &events);
208 InsertEvent("cgroup", "cgroup_setup_root", &events);
209 InsertEvent("cgroup", "cgroup_rmdir", &events);
210 InsertEvent("cgroup", "cgroup_rename", &events);
211 InsertEvent("cgroup", "cgroup_remount", &events);
212 InsertEvent("cgroup", "cgroup_release", &events);
213 InsertEvent("cgroup", "cgroup_mkdir", &events);
214 InsertEvent("cgroup", "cgroup_destroy_root", &events);
215 InsertEvent("cgroup", "cgroup_attach_task", &events);
216 InsertEvent("oom", "oom_score_adj_update", &events);
217 InsertEvent("task", "task_rename", &events);
218 InsertEvent("task", "task_newtask", &events);
219
220 AddEventGroup(table, "systrace", &events);
221 InsertEvent("systrace", "0", &events);
222
223 AddEventGroup(table, "scm", &events);
224 InsertEvent("scm", "scm_call_start", &events);
225 InsertEvent("scm", "scm_call_end", &events);
226 continue;
227 }
228
229 if (category == "irq") {
230 AddEventGroup(table, "irq", &events);
231 InsertEvent("irq", "tasklet_hi_exit", &events);
232 InsertEvent("irq", "tasklet_hi_entry", &events);
233 InsertEvent("irq", "tasklet_exit", &events);
234 InsertEvent("irq", "tasklet_entry", &events);
235 InsertEvent("irq", "softirq_raise", &events);
236 InsertEvent("irq", "softirq_exit", &events);
237 InsertEvent("irq", "softirq_entry", &events);
238 InsertEvent("irq", "irq_handler_exit", &events);
239 InsertEvent("irq", "irq_handler_entry", &events);
240 AddEventGroup(table, "ipi", &events);
241 InsertEvent("ipi", "ipi_raise", &events);
242 InsertEvent("ipi", "ipi_exit", &events);
243 InsertEvent("ipi", "ipi_entry", &events);
244 continue;
245 }
246
247 if (category == "irqoff") {
248 InsertEvent("preemptirq", "irq_enable", &events);
249 InsertEvent("preemptirq", "irq_disable", &events);
250 continue;
251 }
252
253 if (category == "preemptoff") {
254 InsertEvent("preemptirq", "preempt_enable", &events);
255 InsertEvent("preemptirq", "preempt_disable", &events);
256 continue;
257 }
258
259 if (category == "i2c") {
260 AddEventGroup(table, "i2c", &events);
261 InsertEvent("i2c", "i2c_read", &events);
262 InsertEvent("i2c", "i2c_write", &events);
263 InsertEvent("i2c", "i2c_result", &events);
264 InsertEvent("i2c", "i2c_reply", &events);
265 InsertEvent("i2c", "smbus_read", &events);
266 InsertEvent("i2c", "smbus_write", &events);
267 InsertEvent("i2c", "smbus_result", &events);
268 InsertEvent("i2c", "smbus_reply", &events);
269 continue;
270 }
271
272 if (category == "freq") {
273 InsertEvent("power", "cpu_frequency", &events);
274 InsertEvent("power", "gpu_frequency", &events);
275 InsertEvent("power", "clock_set_rate", &events);
276 InsertEvent("power", "clock_disable", &events);
277 InsertEvent("power", "clock_enable", &events);
278 InsertEvent("clk", "clk_set_rate", &events);
279 InsertEvent("clk", "clk_disable", &events);
280 InsertEvent("clk", "clk_enable", &events);
281 InsertEvent("power", "cpu_frequency_limits", &events);
282 InsertEvent("power", "suspend_resume", &events);
283 InsertEvent("cpuhp", "cpuhp_enter", &events);
284 InsertEvent("cpuhp", "cpuhp_exit", &events);
285 InsertEvent("cpuhp", "cpuhp_pause", &events);
286 AddEventGroup(table, "msm_bus", &events);
287 InsertEvent("msm_bus", "bus_update_request_end", &events);
288 InsertEvent("msm_bus", "bus_update_request", &events);
289 InsertEvent("msm_bus", "bus_rules_matches", &events);
290 InsertEvent("msm_bus", "bus_max_votes", &events);
291 InsertEvent("msm_bus", "bus_client_status", &events);
292 InsertEvent("msm_bus", "bus_bke_params", &events);
293 InsertEvent("msm_bus", "bus_bimc_config_limiter", &events);
294 InsertEvent("msm_bus", "bus_avail_bw", &events);
295 InsertEvent("msm_bus", "bus_agg_bw", &events);
296 continue;
297 }
298
299 if (category == "membus") {
300 AddEventGroup(table, "memory_bus", &events);
301 continue;
302 }
303
304 if (category == "idle") {
305 InsertEvent("power", "cpu_idle", &events);
306 continue;
307 }
308
309 if (category == "disk") {
310 InsertEvent("f2fs", "f2fs_sync_file_enter", &events);
311 InsertEvent("f2fs", "f2fs_sync_file_exit", &events);
312 InsertEvent("f2fs", "f2fs_write_begin", &events);
313 InsertEvent("f2fs", "f2fs_write_end", &events);
314 InsertEvent("ext4", "ext4_da_write_begin", &events);
315 InsertEvent("ext4", "ext4_da_write_end", &events);
316 InsertEvent("ext4", "ext4_sync_file_enter", &events);
317 InsertEvent("ext4", "ext4_sync_file_exit", &events);
318 InsertEvent("block", "block_rq_issue", &events);
319 InsertEvent("block", "block_rq_complete", &events);
320 continue;
321 }
322
323 if (category == "mmc") {
324 AddEventGroup(table, "mmc", &events);
325 continue;
326 }
327
328 if (category == "load") {
329 AddEventGroup(table, "cpufreq_interactive", &events);
330 continue;
331 }
332
333 if (category == "sync") {
334 // linux kernel < 4.9
335 AddEventGroup(table, "sync", &events);
336 InsertEvent("sync", "sync_pt", &events);
337 InsertEvent("sync", "sync_timeline", &events);
338 InsertEvent("sync", "sync_wait", &events);
339 // linux kernel == 4.9.x
340 AddEventGroup(table, "fence", &events);
341 InsertEvent("fence", "fence_annotate_wait_on", &events);
342 InsertEvent("fence", "fence_destroy", &events);
343 InsertEvent("fence", "fence_emit", &events);
344 InsertEvent("fence", "fence_enable_signal", &events);
345 InsertEvent("fence", "fence_init", &events);
346 InsertEvent("fence", "fence_signaled", &events);
347 InsertEvent("fence", "fence_wait_end", &events);
348 InsertEvent("fence", "fence_wait_start", &events);
349 // linux kernel > 4.9
350 AddEventGroup(table, "dma_fence", &events);
351 continue;
352 }
353
354 if (category == "workq") {
355 AddEventGroup(table, "workqueue", &events);
356 InsertEvent("workqueue", "workqueue_queue_work", &events);
357 InsertEvent("workqueue", "workqueue_execute_start", &events);
358 InsertEvent("workqueue", "workqueue_execute_end", &events);
359 InsertEvent("workqueue", "workqueue_activate_work", &events);
360 continue;
361 }
362
363 if (category == "memreclaim") {
364 InsertEvent("vmscan", "mm_vmscan_direct_reclaim_begin", &events);
365 InsertEvent("vmscan", "mm_vmscan_direct_reclaim_end", &events);
366 InsertEvent("vmscan", "mm_vmscan_kswapd_wake", &events);
367 InsertEvent("vmscan", "mm_vmscan_kswapd_sleep", &events);
368 AddEventGroup(table, "lowmemorykiller", &events);
369 InsertEvent("lowmemorykiller", "lowmemory_kill", &events);
370 continue;
371 }
372
373 if (category == "regulators") {
374 AddEventGroup(table, "regulator", &events);
375 events.insert(
376 GroupAndName("regulator", "regulator_set_voltage_complete"));
377 InsertEvent("regulator", "regulator_set_voltage", &events);
378 InsertEvent("regulator", "regulator_enable_delay", &events);
379 InsertEvent("regulator", "regulator_enable_complete", &events);
380 InsertEvent("regulator", "regulator_enable", &events);
381 InsertEvent("regulator", "regulator_disable_complete", &events);
382 InsertEvent("regulator", "regulator_disable", &events);
383 continue;
384 }
385
386 if (category == "binder_driver") {
387 InsertEvent("binder", "binder_transaction", &events);
388 InsertEvent("binder", "binder_transaction_received", &events);
389 InsertEvent("binder", "binder_transaction_alloc_buf", &events);
390 InsertEvent("binder", "binder_set_priority", &events);
391 continue;
392 }
393
394 if (category == "binder_lock") {
395 InsertEvent("binder", "binder_lock", &events);
396 InsertEvent("binder", "binder_locked", &events);
397 InsertEvent("binder", "binder_unlock", &events);
398 continue;
399 }
400
401 if (category == "pagecache") {
402 AddEventGroup(table, "filemap", &events);
403 events.insert(
404 GroupAndName("filemap", "mm_filemap_delete_from_page_cache"));
405 InsertEvent("filemap", "mm_filemap_add_to_page_cache", &events);
406 InsertEvent("filemap", "filemap_set_wb_err", &events);
407 InsertEvent("filemap", "file_check_and_advance_wb_err", &events);
408 continue;
409 }
410
411 if (category == "memory") {
412 // Use rss_stat_throttled if supported
413 if (ftrace_->SupportsRssStatThrottled()) {
414 InsertEvent("synthetic", "rss_stat_throttled", &events);
415 } else {
416 InsertEvent("kmem", "rss_stat", &events);
417 }
418 InsertEvent("kmem", "ion_heap_grow", &events);
419 InsertEvent("kmem", "ion_heap_shrink", &events);
420 // ion_stat supersedes ion_heap_grow / shrink for kernel 4.19+
421 InsertEvent("ion", "ion_stat", &events);
422 InsertEvent("mm_event", "mm_event_record", &events);
423 InsertEvent("dmabuf_heap", "dma_heap_stat", &events);
424 InsertEvent("gpu_mem", "gpu_mem_total", &events);
425 continue;
426 }
427
428 if (category == "thermal") {
429 InsertEvent("thermal", "thermal_temperature", &events);
430 InsertEvent("thermal", "cdev_update", &events);
431 continue;
432 }
433 }
434 }
435
436 // If throttle_rss_stat: true, use the rss_stat_throttled event if supported
437 if (request.throttle_rss_stat() && ftrace_->SupportsRssStatThrottled()) {
438 auto it = std::find_if(
439 events.begin(), events.end(), [](const GroupAndName& event) {
440 return event.group() == "kmem" && event.name() == "rss_stat";
441 });
442
443 if (it != events.end()) {
444 events.erase(it);
445 InsertEvent("synthetic", "rss_stat_throttled", &events);
446 }
447 }
448
449 return events;
450 }
451
452 // Post-conditions:
453 // 1. result >= 1 (should have at least one page per CPU)
454 // 2. result * 4 < kMaxTotalBufferSizeKb
455 // 3. If input is 0 output is a good default number.
ComputeCpuBufferSizeInPages(size_t requested_buffer_size_kb)456 size_t ComputeCpuBufferSizeInPages(size_t requested_buffer_size_kb) {
457 if (requested_buffer_size_kb == 0)
458 requested_buffer_size_kb = kDefaultPerCpuBufferSizeKb;
459 if (requested_buffer_size_kb > kMaxPerCpuBufferSizeKb) {
460 PERFETTO_ELOG(
461 "The requested ftrace buf size (%zu KB) is too big, capping to %d KB",
462 requested_buffer_size_kb, kMaxPerCpuBufferSizeKb);
463 requested_buffer_size_kb = kMaxPerCpuBufferSizeKb;
464 }
465
466 size_t pages = requested_buffer_size_kb / (base::kPageSize / 1024);
467 if (pages == 0)
468 return 1;
469
470 return pages;
471 }
472
FtraceConfigMuxer(FtraceProcfs * ftrace,ProtoTranslationTable * table,std::map<std::string,std::vector<GroupAndName>> vendor_events)473 FtraceConfigMuxer::FtraceConfigMuxer(
474 FtraceProcfs* ftrace,
475 ProtoTranslationTable* table,
476 std::map<std::string, std::vector<GroupAndName>> vendor_events)
477 : ftrace_(ftrace),
478 table_(table),
479 current_state_(),
480 ds_configs_(),
481 vendor_events_(vendor_events) {}
482 FtraceConfigMuxer::~FtraceConfigMuxer() = default;
483
SetupConfig(const FtraceConfig & request,FtraceSetupErrors * errors)484 FtraceConfigId FtraceConfigMuxer::SetupConfig(const FtraceConfig& request,
485 FtraceSetupErrors* errors) {
486 EventFilter filter;
487 bool is_ftrace_enabled = ftrace_->IsTracingEnabled();
488 if (ds_configs_.empty()) {
489 PERFETTO_DCHECK(active_configs_.empty());
490
491 // If someone outside of perfetto is using ftrace give up now.
492 if (is_ftrace_enabled && !IsOldAtrace()) {
493 PERFETTO_ELOG("ftrace in use by non-Perfetto.");
494 return 0;
495 }
496
497 // Setup ftrace, without starting it. Setting buffers can be quite slow
498 // (up to hundreds of ms).
499 SetupClock(request);
500 SetupBufferSize(request);
501 } else {
502 // Did someone turn ftrace off behind our back? If so give up.
503 if (!active_configs_.empty() && !is_ftrace_enabled && !IsOldAtrace()) {
504 PERFETTO_ELOG("ftrace disabled by non-Perfetto.");
505 return 0;
506 }
507 }
508
509 std::set<GroupAndName> events = GetFtraceEvents(request, table_);
510
511 // Vendors can provide a set of extra ftrace categories to be enabled when a
512 // specific atrace category is used (e.g. "gfx" -> ["my_hw/my_custom_event",
513 // "my_hw/my_special_gpu"]). Merge them with the hard coded events for each
514 // categories.
515 for (const std::string& category : request.atrace_categories()) {
516 if (vendor_events_.count(category)) {
517 for (const GroupAndName& event : vendor_events_[category]) {
518 events.insert(event);
519 }
520 }
521 }
522
523 if (RequiresAtrace(request)) {
524 if (IsOldAtrace() && !ds_configs_.empty()) {
525 PERFETTO_ELOG(
526 "Concurrent atrace sessions are not supported before Android P, "
527 "bailing out.");
528 return 0;
529 }
530 UpdateAtrace(request, errors ? &errors->atrace_errors : nullptr);
531 }
532
533 for (const auto& group_and_name : events) {
534 const Event* event = table_->GetOrCreateEvent(group_and_name);
535 if (!event) {
536 PERFETTO_DLOG("Can't enable %s, event not known",
537 group_and_name.ToString().c_str());
538 if (errors)
539 errors->unknown_ftrace_events.push_back(group_and_name.ToString());
540 continue;
541 }
542 // Niche option to skip events that are in the config, but don't have a
543 // dedicated proto for the event in perfetto. Otherwise such events will be
544 // encoded as GenericFtraceEvent.
545 if (request.disable_generic_events() &&
546 event->proto_field_id ==
547 protos::pbzero::FtraceEvent::kGenericFieldNumber) {
548 if (errors)
549 errors->failed_ftrace_events.push_back(group_and_name.ToString());
550 continue;
551 }
552 // Note: ftrace events are always implicitly enabled (and don't have an
553 // "enable" file). So they aren't tracked by the central event filter (but
554 // still need to be added to the per data source event filter to retain
555 // the events during parsing).
556 if (current_state_.ftrace_events.IsEventEnabled(event->ftrace_event_id) ||
557 std::string("ftrace") == event->group) {
558 filter.AddEnabledEvent(event->ftrace_event_id);
559 continue;
560 }
561 if (ftrace_->EnableEvent(event->group, event->name)) {
562 current_state_.ftrace_events.AddEnabledEvent(event->ftrace_event_id);
563 filter.AddEnabledEvent(event->ftrace_event_id);
564 } else {
565 PERFETTO_DPLOG("Failed to enable %s.", group_and_name.ToString().c_str());
566 if (errors)
567 errors->failed_ftrace_events.push_back(group_and_name.ToString());
568 }
569 }
570
571 auto compact_sched =
572 CreateCompactSchedConfig(request, table_->compact_sched_format());
573
574 std::vector<std::string> apps(request.atrace_apps());
575 std::vector<std::string> categories(request.atrace_categories());
576 FtraceConfigId id = ++last_id_;
577 ds_configs_.emplace(
578 std::piecewise_construct, std::forward_as_tuple(id),
579 std::forward_as_tuple(std::move(filter), compact_sched, std::move(apps),
580 std::move(categories), request.symbolize_ksyms()));
581 return id;
582 }
583
ActivateConfig(FtraceConfigId id)584 bool FtraceConfigMuxer::ActivateConfig(FtraceConfigId id) {
585 if (!id || ds_configs_.count(id) == 0) {
586 PERFETTO_DFATAL("Config not found");
587 return false;
588 }
589
590 if (active_configs_.empty()) {
591 if (ftrace_->IsTracingEnabled() && !IsOldAtrace()) {
592 // If someone outside of perfetto is using ftrace give up now.
593 PERFETTO_ELOG("ftrace in use by non-Perfetto.");
594 return false;
595 }
596 if (!ftrace_->EnableTracing()) {
597 PERFETTO_ELOG("Failed to enable ftrace.");
598 return false;
599 }
600 }
601
602 active_configs_.insert(id);
603 return true;
604 }
605
RemoveConfig(FtraceConfigId config_id)606 bool FtraceConfigMuxer::RemoveConfig(FtraceConfigId config_id) {
607 if (!config_id || !ds_configs_.erase(config_id))
608 return false;
609 EventFilter expected_ftrace_events;
610 std::vector<std::string> expected_apps;
611 std::vector<std::string> expected_categories;
612 for (const auto& id_config : ds_configs_) {
613 const perfetto::FtraceDataSourceConfig& config = id_config.second;
614 expected_ftrace_events.EnableEventsFrom(config.event_filter);
615 UnionInPlace(config.atrace_apps, &expected_apps);
616 UnionInPlace(config.atrace_categories, &expected_categories);
617 }
618 // At this point expected_{apps,categories} contains the union of the
619 // leftover configs (if any) that should be still on. However we did not
620 // necessarily succeed in turning on atrace for each of those configs
621 // previously so we now intersect the {apps,categories} that we *did* manage
622 // to turn on with those we want on to determine the new state we should aim
623 // for:
624 IntersectInPlace(current_state_.atrace_apps, &expected_apps);
625 IntersectInPlace(current_state_.atrace_categories, &expected_categories);
626 // Work out if there is any difference between the current state and the
627 // desired state: It's sufficient to compare sizes here (since we know from
628 // above that expected_{apps,categories} is now a subset of
629 // atrace_{apps,categories}:
630 bool atrace_changed =
631 (current_state_.atrace_apps.size() != expected_apps.size()) ||
632 (current_state_.atrace_categories.size() != expected_categories.size());
633
634 // Disable any events that are currently enabled, but are not in any configs
635 // anymore.
636 std::set<size_t> event_ids = current_state_.ftrace_events.GetEnabledEvents();
637 for (size_t id : event_ids) {
638 if (expected_ftrace_events.IsEventEnabled(id))
639 continue;
640 const Event* event = table_->GetEventById(id);
641 // Any event that was enabled must exist.
642 PERFETTO_DCHECK(event);
643 if (ftrace_->DisableEvent(event->group, event->name))
644 current_state_.ftrace_events.DisableEvent(event->ftrace_event_id);
645 }
646
647 // If there aren't any more active configs, disable ftrace.
648 auto active_it = active_configs_.find(config_id);
649 if (active_it != active_configs_.end()) {
650 active_configs_.erase(active_it);
651 if (active_configs_.empty()) {
652 // This was the last active config, disable ftrace.
653 if (!ftrace_->DisableTracing())
654 PERFETTO_ELOG("Failed to disable ftrace.");
655 }
656 }
657
658 // Even if we don't have any other active configs, we might still have idle
659 // configs around. Tear down the rest of the ftrace config only if all
660 // configs are removed.
661 if (ds_configs_.empty()) {
662 if (ftrace_->SetCpuBufferSizeInPages(1))
663 current_state_.cpu_buffer_size_pages = 1;
664 ftrace_->DisableAllEvents();
665 ftrace_->ClearTrace();
666 }
667
668 if (current_state_.atrace_on) {
669 if (expected_apps.empty() && expected_categories.empty()) {
670 DisableAtrace();
671 } else if (atrace_changed) {
672 // Update atrace to remove the no longer wanted categories/apps. For
673 // some categories this won't disable them (e.g. categories that just
674 // enable ftrace events) for those there is nothing we can do till the
675 // last ftrace config is removed.
676 if (StartAtrace(expected_apps, expected_categories,
677 /*atrace_errors=*/nullptr)) {
678 // Update current_state_ to reflect this change.
679 current_state_.atrace_apps = expected_apps;
680 current_state_.atrace_categories = expected_categories;
681 }
682 }
683 }
684
685 return true;
686 }
687
GetDataSourceConfig(FtraceConfigId id)688 const FtraceDataSourceConfig* FtraceConfigMuxer::GetDataSourceConfig(
689 FtraceConfigId id) {
690 if (!ds_configs_.count(id))
691 return nullptr;
692 return &ds_configs_.at(id);
693 }
694
SetupClock(const FtraceConfig &)695 void FtraceConfigMuxer::SetupClock(const FtraceConfig&) {
696 std::string current_clock = ftrace_->GetClock();
697 std::set<std::string> clocks = ftrace_->AvailableClocks();
698
699 for (size_t i = 0; i < base::ArraySize(kClocks); i++) {
700 std::string clock = std::string(kClocks[i]);
701 if (!clocks.count(clock))
702 continue;
703 if (current_clock == clock)
704 break;
705 ftrace_->SetClock(clock);
706 current_clock = clock;
707 break;
708 }
709
710 namespace pb0 = protos::pbzero;
711 if (current_clock == "boot") {
712 // "boot" is the default expectation on modern kernels, which is why we
713 // don't have an explicit FTRACE_CLOCK_BOOT enum and leave it unset.
714 // See comments in ftrace_event_bundle.proto.
715 current_state_.ftrace_clock = pb0::FTRACE_CLOCK_UNSPECIFIED;
716 } else if (current_clock == "global") {
717 current_state_.ftrace_clock = pb0::FTRACE_CLOCK_GLOBAL;
718 } else if (current_clock == "local") {
719 current_state_.ftrace_clock = pb0::FTRACE_CLOCK_LOCAL;
720 } else {
721 current_state_.ftrace_clock = pb0::FTRACE_CLOCK_UNKNOWN;
722 }
723 }
724
SetupBufferSize(const FtraceConfig & request)725 void FtraceConfigMuxer::SetupBufferSize(const FtraceConfig& request) {
726 size_t pages = ComputeCpuBufferSizeInPages(request.buffer_size_kb());
727 ftrace_->SetCpuBufferSizeInPages(pages);
728 current_state_.cpu_buffer_size_pages = pages;
729 }
730
GetPerCpuBufferSizePages()731 size_t FtraceConfigMuxer::GetPerCpuBufferSizePages() {
732 return current_state_.cpu_buffer_size_pages;
733 }
734
UpdateAtrace(const FtraceConfig & request,std::string * atrace_errors)735 void FtraceConfigMuxer::UpdateAtrace(const FtraceConfig& request,
736 std::string* atrace_errors) {
737 // We want to avoid poisoning current_state_.atrace_{categories, apps}
738 // if for some reason these args make atrace unhappy so we stash the
739 // union into temps and only update current_state_ if we successfully
740 // run atrace.
741
742 std::vector<std::string> combined_categories = request.atrace_categories();
743 UnionInPlace(current_state_.atrace_categories, &combined_categories);
744
745 std::vector<std::string> combined_apps = request.atrace_apps();
746 UnionInPlace(current_state_.atrace_apps, &combined_apps);
747
748 if (current_state_.atrace_on &&
749 combined_apps.size() == current_state_.atrace_apps.size() &&
750 combined_categories.size() == current_state_.atrace_categories.size()) {
751 return;
752 }
753
754 if (StartAtrace(combined_apps, combined_categories, atrace_errors)) {
755 current_state_.atrace_categories = combined_categories;
756 current_state_.atrace_apps = combined_apps;
757 current_state_.atrace_on = true;
758 }
759 }
760
761 // static
StartAtrace(const std::vector<std::string> & apps,const std::vector<std::string> & categories,std::string * atrace_errors)762 bool FtraceConfigMuxer::StartAtrace(const std::vector<std::string>& apps,
763 const std::vector<std::string>& categories,
764 std::string* atrace_errors) {
765 PERFETTO_DLOG("Update atrace config...");
766
767 std::vector<std::string> args;
768 args.push_back("atrace"); // argv0 for exec()
769 args.push_back("--async_start");
770 if (!IsOldAtrace())
771 args.push_back("--only_userspace");
772
773 for (const auto& category : categories)
774 args.push_back(category);
775
776 if (!apps.empty()) {
777 args.push_back("-a");
778 std::string arg = "";
779 for (const auto& app : apps) {
780 arg += app;
781 arg += ",";
782 }
783 arg.resize(arg.size() - 1);
784 args.push_back(arg);
785 }
786
787 bool result = RunAtrace(args, atrace_errors);
788 PERFETTO_DLOG("...done (%s)", result ? "success" : "fail");
789 return result;
790 }
791
DisableAtrace()792 void FtraceConfigMuxer::DisableAtrace() {
793 PERFETTO_DCHECK(current_state_.atrace_on);
794
795 PERFETTO_DLOG("Stop atrace...");
796
797 std::vector<std::string> args{"atrace", "--async_stop"};
798 if (!IsOldAtrace())
799 args.push_back("--only_userspace");
800 if (RunAtrace(args, /*atrace_errors=*/nullptr)) {
801 current_state_.atrace_categories.clear();
802 current_state_.atrace_apps.clear();
803 current_state_.atrace_on = false;
804 }
805
806 PERFETTO_DLOG("...done");
807 }
808
809 } // namespace perfetto
810