Home | History | Annotate | Download | only in ftrace_reader
      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 
     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 
     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 
     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.
    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 
    226 FtraceConfigMuxer::FtraceConfigMuxer(FtraceProcfs* ftrace,
    227                                      const ProtoTranslationTable* table)
    228     : ftrace_(ftrace), table_(table), current_state_(), configs_() {}
    229 FtraceConfigMuxer::~FtraceConfigMuxer() = default;
    230 
    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 
    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 
    322 const FtraceConfig* FtraceConfigMuxer::GetConfig(FtraceConfigId id) {
    323   if (!configs_.count(id))
    324     return nullptr;
    325   return &configs_.at(id);
    326 }
    327 
    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 
    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 
    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 
    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