• 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 <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