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