1 /* 2 * Copyright (C) 2015 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 package com.android.traceur; 18 19 import android.system.Os; 20 import android.util.Log; 21 22 import java.io.File; 23 import java.nio.file.Files; 24 import java.nio.file.Paths; 25 import java.util.ArrayList; 26 import java.util.Collection; 27 import java.util.List; 28 import java.util.TreeMap; 29 import java.util.concurrent.TimeUnit; 30 31 import perfetto.protos.DataSourceDescriptorOuterClass.DataSourceDescriptor; 32 import perfetto.protos.FtraceDescriptorOuterClass.FtraceDescriptor.AtraceCategory; 33 import perfetto.protos.TraceConfigOuterClass.TraceConfig; 34 import perfetto.protos.TracingServiceStateOuterClass.TracingServiceState; 35 import perfetto.protos.TracingServiceStateOuterClass.TracingServiceState.DataSource; 36 37 /** 38 * Utility functions for calling Perfetto 39 */ 40 public class PerfettoUtils { 41 42 static final String TAG = "Traceur"; 43 public static final String NAME = "PERFETTO"; 44 45 private static final String OUTPUT_EXTENSION = "perfetto-trace"; 46 private static final String TEMP_DIR = "/data/local/traces/"; 47 private static final String TEMP_TRACE_LOCATION = "/data/local/traces/.trace-in-progress.trace"; 48 49 private static final String PERFETTO_TAG = "traceur"; 50 private static final String MARKER = "PERFETTO_ARGUMENTS"; 51 private static final int LIST_TIMEOUT_MS = 10000; 52 private static final int STARTUP_TIMEOUT_MS = 10000; 53 private static final int STOP_TIMEOUT_MS = 30000; 54 private static final long MEGABYTES_TO_BYTES = 1024L * 1024L; 55 private static final long SECONDS_TO_MILLISECONDS = 1000L; 56 private static final long MINUTES_TO_MILLISECONDS = 60L * 1000L; 57 58 // This is 2x larger than the default buffer size to account for multiple processes being 59 // specified. 60 private static final int HEAP_DUMP_PER_CPU_BUFFER_SIZE_KB = 32 * 1024; 61 private static final int HEAP_DUMP_MAX_LONG_TRACE_SIZE_MB = 10 * 1024; 62 private static final int HEAP_DUMP_MAX_LONG_TRACE_DURATION_MINUTES = 10; 63 64 // The total amount of memory allocated to the two target buffers will be divided according to a 65 // ratio of (BUFFER_SIZE_RATIO - 1) to 1. 66 private static final int BUFFER_SIZE_RATIO = 8; 67 68 private static final int SYSTEM_INFO_BUFFER_SIZE_KB = 512; 69 70 // atrace trace categories that will result in added data sources in the Perfetto config. 71 private static final String CAMERA_TAG = "camera"; 72 private static final String GFX_TAG = "gfx"; 73 private static final String MEMORY_TAG = "memory"; 74 private static final String NETWORK_TAG = "network"; 75 private static final String POWER_TAG = "power"; 76 private static final String SCHED_TAG = "sched"; 77 private static final String WEBVIEW_TAG = "webview"; 78 private static final String WINDOW_MANAGER_TAG = "wm"; 79 80 // Custom trace categories. 81 private static final String SYS_STATS_TAG = "sys_stats"; 82 private static final String LOG_TAG = "logs"; 83 private static final String CPU_TAG = "cpu"; 84 85 // Statsd atoms. Values should be aligned with frameworks/proto_logging/stats/atoms.proto. 86 private static final int DESKTOP_MODE_UI_CHANGED = 818; 87 private static final int DESKTOP_MODE_SESSION_TASK_UPDATE = 819; 88 private static final int DESKTOP_MODE_TASK_SIZE_UPDATED = 935; 89 getName()90 public String getName() { 91 return NAME; 92 } 93 getOutputExtension()94 public String getOutputExtension() { 95 return OUTPUT_EXTENSION; 96 } 97 98 // Traceur will not verify that the input TraceConfig will start properly before attempting to 99 // record a trace. traceStart(TraceConfig config)100 public boolean traceStart(TraceConfig config) { 101 if (isTracingOn()) { 102 Log.e(TAG, "Attempting to start perfetto trace but trace is already in progress"); 103 return false; 104 } else { 105 recoverExistingRecording(); 106 } 107 108 return startPerfettoWithProtoConfig(config); 109 } 110 traceStart(Collection<String> tags, int bufferSizeKb, boolean winscope, boolean apps, boolean longTrace, boolean attachToBugreport, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)111 public boolean traceStart(Collection<String> tags, int bufferSizeKb, boolean winscope, 112 boolean apps, boolean longTrace, boolean attachToBugreport, int maxLongTraceSizeMb, 113 int maxLongTraceDurationMinutes) { 114 if (isTracingOn()) { 115 Log.e(TAG, "Attempting to start perfetto trace but trace is already in progress"); 116 return false; 117 } else { 118 recoverExistingRecording(); 119 } 120 121 StringBuilder config = new StringBuilder(); 122 appendBaseConfigOptions(config, attachToBugreport, longTrace, maxLongTraceSizeMb, 123 maxLongTraceDurationMinutes); 124 125 // The user chooses a per-CPU buffer size due to atrace limitations. 126 // So we use this to ensure that we reserve the correctly-sized buffer. 127 int numCpus = Runtime.getRuntime().availableProcessors(); 128 129 // Allots 1 / BUFFER_SIZE_RATIO to the small buffer and the remainder to the large buffer, 130 // (less the size of the buffer reserved for unchanging system information). 131 int totalBufferSizeKb = numCpus * bufferSizeKb - SYSTEM_INFO_BUFFER_SIZE_KB; 132 int targetBuffer1Kb = totalBufferSizeKb / BUFFER_SIZE_RATIO; 133 int targetBuffer0Kb = totalBufferSizeKb - targetBuffer1Kb; 134 135 // This is target_buffer: 0, which is used for ftrace and the ftrace-derived 136 // android.gpu.memory. 137 appendTraceBuffer(config, targetBuffer0Kb); 138 139 // This is target_buffer: 1, which is used for additional data sources. 140 appendTraceBuffer(config, targetBuffer1Kb); 141 142 // This is target_buffer: 2, used for unchanging system information like the packages 143 // list. 144 appendTraceBuffer(config, SYSTEM_INFO_BUFFER_SIZE_KB); 145 146 appendFtraceConfig(config, tags, apps); 147 148 appendSystemPropertyConfig(config, tags); 149 appendPackagesListConfig(config); 150 appendStatsdConfig(config, tags); 151 appendProcStatsConfig(config, tags, /* targetBuffer = */ 1); 152 appendAdditionalDataSources(config, tags, winscope, longTrace, /* targetBuffer = */ 1); 153 154 return startPerfettoWithTextConfig(config.toString()); 155 } 156 appendPackagesListConfig(StringBuilder config)157 private void appendPackagesListConfig(StringBuilder config) { 158 config.append("data_sources: {\n") 159 .append(" config { \n") 160 .append(" name: \"android.packages_list\"\n") 161 .append(" target_buffer: 2\n") 162 .append(" }\n") 163 .append("}\n"); 164 } 165 appendSystemPropertyConfig(StringBuilder config, Collection<String> tags)166 private void appendSystemPropertyConfig(StringBuilder config, Collection<String> tags) { 167 if (tags.contains(WINDOW_MANAGER_TAG)) { 168 config.append("data_sources: {\n") 169 .append(" config { \n") 170 .append(" name: \"android.system_property\"\n") 171 .append(" target_buffer: 0\n") 172 .append(" android_system_property_config {\n") 173 .append(" property_name: \"debug.tracing.desktop_mode_visible_tasks\"\n") 174 .append(" }\n") 175 .append(" }\n") 176 .append("}\n"); 177 } 178 } 179 appendStatsdConfig(StringBuilder config, Collection<String> tags)180 private void appendStatsdConfig(StringBuilder config, Collection<String> tags) { 181 List<Integer> rawPushAtomIds = new ArrayList<>(); 182 if (tags.contains(WINDOW_MANAGER_TAG)) { 183 rawPushAtomIds.add(DESKTOP_MODE_UI_CHANGED); 184 rawPushAtomIds.add(DESKTOP_MODE_SESSION_TASK_UPDATE); 185 rawPushAtomIds.add(DESKTOP_MODE_TASK_SIZE_UPDATED); 186 } 187 188 if (rawPushAtomIds.size() > 0) { 189 config.append("data_sources: {\n") 190 .append(" config { \n") 191 .append(" name: \"android.statsd\"\n") 192 .append(" target_buffer: 1\n") 193 .append(" statsd_tracing_config {\n"); 194 195 for (int id : rawPushAtomIds) { 196 config.append(" raw_push_atom_id: " + id + "\n"); 197 } 198 199 config.append(" }\n") 200 .append(" }\n") 201 .append("}\n"); 202 } 203 } 204 205 stackSampleStart(boolean attachToBugreport)206 public boolean stackSampleStart(boolean attachToBugreport) { 207 if (isTracingOn()) { 208 Log.e(TAG, "Attemping to start stack sampling but perfetto is already active"); 209 return false; 210 } else { 211 recoverExistingRecording(); 212 } 213 214 StringBuilder config = new StringBuilder(); 215 appendBaseConfigOptions(config, attachToBugreport, /* longTrace = */ false, 216 /* maxLongTraceSizeMb */ 0, /* maxLongTraceDurationMinutes = */ 0); 217 218 // Number of cores * 16MiB. 16MiB was chosen as it is the default for Traceur traces. 219 int targetBufferKb = Runtime.getRuntime().availableProcessors() * (16 * 1024); 220 appendTraceBuffer(config, targetBufferKb); 221 222 appendLinuxPerfConfig(config, /* targetBuffer = */ 0); 223 appendProcStatsConfig(config, /* tags = */ null, /* targetBuffer = */ 0); 224 225 return startPerfettoWithTextConfig(config.toString()); 226 } 227 heapDumpStart(Collection<String> processes, boolean continuousDump, int dumpIntervalSeconds, boolean attachToBugreport)228 public boolean heapDumpStart(Collection<String> processes, boolean continuousDump, 229 int dumpIntervalSeconds, boolean attachToBugreport) { 230 if (isTracingOn()) { 231 Log.e(TAG, "Attempting to start heap dump but perfetto is already active"); 232 return false; 233 } else { 234 recoverExistingRecording(); 235 } 236 237 if (processes.isEmpty()) { 238 Log.e(TAG, "Attempting to start heap dump but list of processes is empty."); 239 return false; 240 } 241 242 StringBuilder config = new StringBuilder(); 243 244 // A long trace is used to avoid data loss. 245 appendBaseConfigOptions(config, attachToBugreport, /* longTrace = */ true, 246 HEAP_DUMP_MAX_LONG_TRACE_SIZE_MB, HEAP_DUMP_MAX_LONG_TRACE_DURATION_MINUTES); 247 248 int targetBufferKb = 249 Runtime.getRuntime().availableProcessors() * HEAP_DUMP_PER_CPU_BUFFER_SIZE_KB; 250 appendTraceBuffer(config, targetBufferKb); 251 252 appendAndroidJavaHprofConfig(config, processes, continuousDump, dumpIntervalSeconds, 253 /* targetBuffer = */ 0); 254 appendProcStatsConfig(config, /* tags = */ null, /* targetBuffer = */ 0); 255 256 return startPerfettoWithTextConfig(config.toString()); 257 } 258 traceStop()259 public void traceStop() { 260 Log.v(TAG, "Stopping perfetto trace."); 261 262 if (!isTracingOn()) { 263 Log.w(TAG, "No trace appears to be in progress. Stopping perfetto trace may not work."); 264 } 265 266 String cmd = "perfetto --stop --attach=" + PERFETTO_TAG; 267 try { 268 Process process = TraceUtils.execWithTimeout(cmd, null, STOP_TIMEOUT_MS); 269 if (process != null && process.exitValue() != 0) { 270 Log.e(TAG, "perfetto traceStop failed with: " + process.exitValue()); 271 } 272 } catch (Exception e) { 273 throw new RuntimeException(e); 274 } 275 } 276 traceDump(File outFile)277 public boolean traceDump(File outFile) { 278 traceStop(); 279 280 // Short-circuit if a trace was not stopped. 281 if (isTracingOn()) { 282 Log.e(TAG, "Trace was not stopped successfully, aborting trace dump."); 283 return false; 284 } 285 286 // Short-circuit if the file we're trying to dump to doesn't exist. 287 if (!Files.exists(Paths.get(TEMP_TRACE_LOCATION))) { 288 Log.e(TAG, "In-progress trace file doesn't exist, aborting trace dump."); 289 return false; 290 } 291 292 Log.v(TAG, "Saving perfetto trace to " + outFile); 293 294 try { 295 Os.rename(TEMP_TRACE_LOCATION, outFile.getCanonicalPath()); 296 } catch (Exception e) { 297 throw new RuntimeException(e); 298 } 299 300 outFile.setReadable(true, false); // (readable, ownerOnly) 301 outFile.setWritable(true, false); // (writable, ownerOnly) 302 return true; 303 } 304 isTracingOn()305 public boolean isTracingOn() { 306 String cmd = "perfetto --is_detached=" + PERFETTO_TAG; 307 308 try { 309 Process process = TraceUtils.exec(cmd); 310 311 // 0 represents a detached process exists with this name 312 // 2 represents no detached process with this name 313 // 1 (or other error code) represents an error 314 int result = process.waitFor(); 315 if (result == 0) { 316 return true; 317 } else if (result == 2) { 318 return false; 319 } else { 320 throw new RuntimeException("Perfetto error: " + result); 321 } 322 } catch (Exception e) { 323 throw new RuntimeException(e); 324 } 325 } 326 perfettoListCategories()327 public static TreeMap<String,String> perfettoListCategories() { 328 String cmd = "perfetto --query-raw"; 329 330 Log.v(TAG, "Listing tags: " + cmd); 331 try { 332 333 TreeMap<String, String> result = new TreeMap<>(); 334 335 // execWithTimeout() cannot be used because stdout must be consumed before the process 336 // is terminated. 337 Process perfetto = TraceUtils.exec(cmd, null, false); 338 TracingServiceState serviceState = 339 TracingServiceState.parseFrom(perfetto.getInputStream()); 340 341 // Destroy the perfetto process if it times out. 342 if (!perfetto.waitFor(LIST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 343 Log.e(TAG, "perfettoListCategories timed out after " + LIST_TIMEOUT_MS + " ms."); 344 perfetto.destroyForcibly(); 345 return result; 346 } 347 348 // The perfetto process completed and failed, but does not need to be destroyed. 349 if (perfetto.exitValue() != 0) { 350 Log.e(TAG, "perfettoListCategories failed with: " + perfetto.exitValue()); 351 } 352 353 List<AtraceCategory> categories = null; 354 355 for (DataSource dataSource : serviceState.getDataSourcesList()) { 356 DataSourceDescriptor dataSrcDescriptor = dataSource.getDsDescriptor(); 357 if (dataSrcDescriptor.getName().equals("linux.ftrace")){ 358 categories = dataSrcDescriptor.getFtraceDescriptor().getAtraceCategoriesList(); 359 break; 360 } 361 } 362 363 if (categories != null) { 364 for (AtraceCategory category : categories) { 365 result.put(category.getName(), category.getDescription()); 366 } 367 } 368 return result; 369 } catch (Exception e) { 370 throw new RuntimeException(e); 371 } 372 } 373 374 // Starts Perfetto with the provided config string. startPerfettoWithTextConfig(String config)375 private boolean startPerfettoWithTextConfig(String config) { 376 // If the here-doc ends early, within the config string, exit immediately. 377 // This should never happen. 378 if (config.contains(MARKER)) { 379 throw new RuntimeException("The arguments to the Perfetto command are malformed."); 380 } 381 382 String cmd = "perfetto --detach=" + PERFETTO_TAG 383 + " -o " + TEMP_TRACE_LOCATION 384 + " -c - --txt" 385 + " <<" + MARKER +"\n" + config + "\n" + MARKER; 386 387 Log.v(TAG, "Starting perfetto trace with text config."); 388 try { 389 Process process = TraceUtils.execWithTimeout(cmd, TEMP_DIR, STARTUP_TIMEOUT_MS); 390 if (process == null) { 391 return false; 392 } else if (process.exitValue() != 0) { 393 Log.e(TAG, "perfetto trace start failed with: " + process.exitValue()); 394 return false; 395 } 396 } catch (Exception e) { 397 throw new RuntimeException(e); 398 } 399 400 Log.v(TAG, "perfetto traceStart succeeded!"); 401 return true; 402 } 403 404 // Starts Perfetto with the provided TraceConfig proto. startPerfettoWithProtoConfig(TraceConfig config)405 private boolean startPerfettoWithProtoConfig(TraceConfig config) { 406 String cmd = "perfetto --detach=" + PERFETTO_TAG 407 + " -o " + TEMP_TRACE_LOCATION 408 + " -c - "; 409 Log.v(TAG, "Starting perfetto trace with proto config."); 410 try { 411 Process process = TraceUtils.execWithTimeout(cmd, TEMP_DIR, 412 STARTUP_TIMEOUT_MS, config.toByteArray()); 413 if (process == null) { 414 return false; 415 } else if (process.exitValue() != 0) { 416 Log.e(TAG, "perfetto trace start failed with: " + process.exitValue()); 417 return false; 418 } 419 } catch (Exception e) { 420 throw new RuntimeException(e); 421 } 422 423 Log.v(TAG, "perfetto traceStart succeeded!"); 424 return true; 425 } 426 427 // Saves an existing temporary recording under a "recovered" filename. recoverExistingRecording()428 private void recoverExistingRecording() { 429 File recoveredFile = TraceUtils.getOutputFile( 430 TraceUtils.getRecoveredFilename()); 431 if (!traceDump(recoveredFile)) { 432 Log.w(TAG, "Failed to recover in-progress trace."); 433 } 434 } 435 436 // Appends options that can be used in any of Traceur's Perfetto configs. appendBaseConfigOptions(StringBuilder config, boolean attachToBugreport, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)437 private void appendBaseConfigOptions(StringBuilder config, boolean attachToBugreport, 438 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 439 config.append("write_into_file: true\n"); 440 441 // Ensure that we flush ftrace every 30s even if cpus are idle. 442 config.append("flush_period_ms: 30000\n"); 443 444 // If the user has flagged that in-progress trace sessions should be grabbed during 445 // bugreports, and BetterBug is present. 446 if (attachToBugreport) { 447 config.append("bugreport_score: 500\n"); 448 } 449 450 // Indicates that Perfetto should notify Traceur if the tracing session's status changes. 451 config.append("notify_traceur: true\n"); 452 453 // Allow previous trace contents to be referenced instead of duplicating. 454 config.append("incremental_state_config {\n") 455 .append(" clear_period_ms: 15000\n") 456 .append("}\n"); 457 458 // Add long/short trace-specific options. 459 if (longTrace) { 460 if (maxLongTraceSizeMb != 0) { 461 config.append("max_file_size_bytes: " 462 + (maxLongTraceSizeMb * MEGABYTES_TO_BYTES) + "\n"); 463 } 464 if (maxLongTraceDurationMinutes != 0) { 465 config.append("duration_ms: " 466 + (maxLongTraceDurationMinutes * MINUTES_TO_MILLISECONDS) 467 + "\n"); 468 } 469 470 // Default value for long traces to write to file. 471 config.append("file_write_period_ms: 1000\n"); 472 } else { 473 // For short traces, we don't write to the file. 474 // So, always use the maximum value here: 7 days. 475 config.append("file_write_period_ms: 604800000\n"); 476 } 477 } 478 479 // Specifies an additional buffer of size bufferSizeKb. Data sources can reference specific 480 // buffers in the order that they are added by this method. appendTraceBuffer(StringBuilder config, int bufferSizeKb)481 private void appendTraceBuffer(StringBuilder config, int bufferSizeKb) { 482 config.append("buffers {\n") 483 .append(" size_kb: " + bufferSizeKb + "\n") 484 .append(" fill_policy: RING_BUFFER\n") 485 .append("}\n"); 486 } 487 488 // Appends ftrace-related data sources to buffer 0 (linux.ftrace, android.gpu.memory). appendFtraceConfig(StringBuilder config, Collection<String> tags, boolean apps)489 private void appendFtraceConfig(StringBuilder config, Collection<String> tags, boolean apps) { 490 config.append("data_sources {\n") 491 .append(" config {\n") 492 .append(" name: \"linux.ftrace\"\n") 493 .append(" target_buffer: 0\n") 494 .append(" ftrace_config {\n") 495 .append(" symbolize_ksyms: true\n"); 496 497 for (String tag : tags) { 498 // Tags are expected to be only letters, numbers, and underscores. 499 String cleanTag = tag.replaceAll("[^a-zA-Z0-9_]", ""); 500 if (!cleanTag.equals(tag)) { 501 Log.w(TAG, "Attempting to use an invalid tag: " + tag); 502 } 503 config.append(" atrace_categories: \"" + cleanTag + "\"\n"); 504 } 505 506 if (apps) { 507 config.append(" atrace_apps: \"*\"\n"); 508 } 509 510 // Request a dense encoding of the common sched events (sched_switch, sched_waking). 511 if (tags.contains(SCHED_TAG)) { 512 config.append(" compact_sched {\n"); 513 config.append(" enabled: true\n"); 514 config.append(" }\n"); 515 } 516 517 // These parameters affect only the kernel trace buffer size and how 518 // frequently it gets moved into the userspace buffer defined above. 519 config.append(" buffer_size_kb: 16384\n") 520 .append(" }\n") 521 .append(" }\n") 522 .append("}\n") 523 .append("\n"); 524 525 // Captures initial counter values, updates are captured in ftrace. 526 if (tags.contains(MEMORY_TAG) || tags.contains(GFX_TAG)) { 527 config.append("data_sources: {\n") 528 .append(" config { \n") 529 .append(" name: \"android.gpu.memory\"\n") 530 .append(" target_buffer: 0\n") 531 .append(" }\n") 532 .append("}\n"); 533 } 534 } 535 536 // Appends the linux.process_stats data source to the specified target buffer. appendProcStatsConfig(StringBuilder config, Collection<String> tags, int targetBuffer)537 private void appendProcStatsConfig(StringBuilder config, Collection<String> tags, 538 int targetBuffer) { 539 boolean tagsContainsMemory = (tags != null) ? tags.contains(MEMORY_TAG) : false; 540 // For process association. If the memory tag is enabled, poll periodically instead of just 541 // once at the beginning. 542 config.append("data_sources {\n") 543 .append(" config {\n") 544 .append(" name: \"linux.process_stats\"\n") 545 .append(" target_buffer: " + targetBuffer + "\n") 546 .append(" process_stats_config {\n"); 547 if (tagsContainsMemory) { 548 config.append(" proc_stats_poll_ms: 60000\n"); 549 } else { 550 config.append(" scan_all_processes_on_start: true\n"); 551 } 552 config.append(" }\n") 553 .append(" }\n") 554 .append("}\n"); 555 } 556 557 // Appends the callstack-sampling data source. Sampling frequency is measured in Hz. appendLinuxPerfConfig(StringBuilder config, int targetBuffer)558 private void appendLinuxPerfConfig(StringBuilder config, int targetBuffer) { 559 config.append("data_sources: {\n") 560 .append(" config {\n") 561 .append(" name: \"linux.perf\"\n") 562 .append(" target_buffer: " + targetBuffer + "\n") 563 .append(" perf_event_config {\n") 564 .append(" all_cpus: true\n") 565 .append(" sampling_frequency: 100\n") 566 .append(" }\n") 567 .append(" }\n") 568 .append("}\n"); 569 } 570 571 // Appends the heap dump data source. appendAndroidJavaHprofConfig(StringBuilder config, Collection<String> processes, boolean continuousDump, int dumpIntervalSeconds, int targetBuffer)572 private void appendAndroidJavaHprofConfig(StringBuilder config, Collection<String> processes, 573 boolean continuousDump, int dumpIntervalSeconds, int targetBuffer) { 574 config.append("data_sources: {\n") 575 .append(" config: {\n") 576 .append(" name: \"android.java_hprof\"\n") 577 .append(" target_buffer: " + targetBuffer + "\n") 578 .append(" java_hprof_config: {\n"); 579 for (String process : processes) { 580 config.append(" process_cmdline: \"" + process + "\"\n"); 581 } 582 config.append(" dump_smaps: true\n"); 583 if (continuousDump) { 584 config.append(" continuous_dump_config: {\n") 585 .append(" dump_phase_ms: 0\n") 586 .append(" dump_interval_ms: " 587 + (dumpIntervalSeconds * SECONDS_TO_MILLISECONDS) + "\n") 588 .append(" }\n"); 589 } 590 config.append(" }\n") 591 .append(" }\n") 592 .append("}\n"); 593 } 594 595 // Appends additional data sources to the specified extra buffer based on enabled trace tags. appendAdditionalDataSources(StringBuilder config, Collection<String> tags, boolean winscope, boolean longTrace, int targetBuffer)596 private void appendAdditionalDataSources(StringBuilder config, Collection<String> tags, 597 boolean winscope, boolean longTrace, int targetBuffer) { 598 if (tags.contains(POWER_TAG)) { 599 config.append("data_sources: {\n") 600 .append(" config { \n") 601 .append(" name: \"android.power\"\n") 602 .append(" target_buffer: " + targetBuffer + "\n") 603 .append(" android_power_config {\n"); 604 if (longTrace) { 605 config.append(" battery_poll_ms: 5000\n"); 606 } else { 607 config.append(" battery_poll_ms: 1000\n"); 608 } 609 config.append(" collect_power_rails: true\n") 610 .append(" battery_counters: BATTERY_COUNTER_CAPACITY_PERCENT\n") 611 .append(" battery_counters: BATTERY_COUNTER_CHARGE\n") 612 .append(" battery_counters: BATTERY_COUNTER_CURRENT\n") 613 .append(" }\n") 614 .append(" }\n") 615 .append("}\n"); 616 } 617 618 if (tags.contains(SYS_STATS_TAG)) { 619 config.append("data_sources: {\n") 620 .append(" config { \n") 621 .append(" name: \"linux.sys_stats\"\n") 622 .append(" target_buffer: " + targetBuffer + "\n") 623 .append(" sys_stats_config {\n") 624 .append(" meminfo_period_ms: 1000\n") 625 .append(" psi_period_ms: 1000\n") 626 .append(" vmstat_period_ms: 1000\n") 627 .append(" }\n") 628 .append(" }\n") 629 .append("}\n"); 630 } 631 632 if (tags.contains(LOG_TAG)) { 633 config.append("data_sources: {\n") 634 .append(" config {\n") 635 .append(" name: \"android.log\"\n") 636 .append(" target_buffer: " + targetBuffer + "\n") 637 .append(" }\n") 638 .append("}\n"); 639 } 640 641 if (tags.contains(CPU_TAG)) { 642 appendLinuxPerfConfig(config, /* targetBuffer = */ 1); 643 } 644 645 if (tags.contains(GFX_TAG)) { 646 config.append("data_sources: {\n") 647 .append(" config { \n") 648 .append(" name: \"android.surfaceflinger.frametimeline\"\n") 649 .append(" target_buffer: " + targetBuffer + "\n") 650 .append(" }\n") 651 .append("}\n"); 652 } 653 654 if (tags.contains(CAMERA_TAG)) { 655 config.append("data_sources: {\n") 656 .append(" config { \n") 657 .append(" name: \"android.hardware.camera\"\n") 658 .append(" target_buffer: " + targetBuffer + "\n") 659 .append(" }\n") 660 .append("}\n"); 661 } 662 663 if (tags.contains(NETWORK_TAG)) { 664 config.append("data_sources: {\n") 665 .append(" config { \n") 666 .append(" name: \"android.network_packets\"\n") 667 .append(" target_buffer: " + targetBuffer + "\n") 668 .append(" network_packet_trace_config {\n") 669 .append(" poll_ms: 250\n") 670 .append(" }\n") 671 .append(" }\n") 672 .append("}\n"); 673 } 674 675 // Also enable Chrome events when the WebView tag is enabled. 676 if (tags.contains(WEBVIEW_TAG)) { 677 String chromeTraceConfig = "{" + 678 "\\\"record_mode\\\":\\\"record-continuously\\\"," + 679 "\\\"included_categories\\\":[\\\"*\\\"]" + 680 "}"; 681 config.append("data_sources: {\n") 682 .append(" config {\n") 683 .append(" name: \"track_event\"\n") 684 .append(" target_buffer: " + targetBuffer + "\n") 685 .append(" chrome_config {\n") 686 .append(" trace_config: \"" + chromeTraceConfig + "\"\n") 687 .append(" }\n") 688 .append(" track_event_config {\n") 689 .append(" enabled_categories: \"*\"\n") 690 .append(" enabled_categories: \"__metadata\"\n") 691 .append(" timestamp_unit_multiplier: 1000\n") 692 .append(" filter_debug_annotations: false\n") 693 .append(" enable_thread_time_sampling: true\n") 694 .append(" filter_dynamic_event_names: false\n") 695 .append(" }\n") 696 .append(" }\n") 697 .append("}\n") 698 .append("data_sources: {\n") 699 .append(" config {\n") 700 .append(" name: \"org.chromium.trace_metadata\"\n") 701 .append(" target_buffer: " + targetBuffer + "\n") 702 .append(" chrome_config {\n") 703 .append(" trace_config: \"" + chromeTraceConfig + "\"\n") 704 .append(" }\n") 705 .append(" }\n") 706 .append("}\n"); 707 } 708 709 if (winscope) { 710 config.append("data_sources: {\n") 711 .append(" config {\n") 712 .append(" name: \"android.inputmethod\"\n") 713 .append(" target_buffer: " + targetBuffer + "\n") 714 .append(" }\n") 715 .append("}\n"); 716 717 config.append("data_sources: {\n") 718 .append(" config {\n") 719 .append(" name: \"android.surfaceflinger.layers\"\n") 720 .append(" target_buffer: " + targetBuffer + "\n") 721 .append(" surfaceflinger_layers_config: {\n") 722 .append(" mode: MODE_ACTIVE\n") 723 .append(" trace_flags: TRACE_FLAG_INPUT\n") 724 .append(" trace_flags: TRACE_FLAG_COMPOSITION\n") 725 .append(" trace_flags: TRACE_FLAG_VIRTUAL_DISPLAYS\n") 726 .append(" }\n") 727 .append(" }\n") 728 .append("}\n"); 729 730 config.append("data_sources: {\n") 731 .append(" config {\n") 732 .append(" name: \"android.surfaceflinger.transactions\"\n") 733 .append(" target_buffer: " + targetBuffer + "\n") 734 .append(" surfaceflinger_transactions_config: {\n") 735 .append(" mode: MODE_ACTIVE\n") 736 .append(" }\n") 737 .append(" }\n") 738 .append("}\n"); 739 740 config.append("data_sources: {\n") 741 .append(" config {\n") 742 .append(" name: \"com.android.wm.shell.transition\"\n") 743 .append(" target_buffer: " + targetBuffer + "\n") 744 .append(" }\n") 745 .append("}\n"); 746 747 config.append("data_sources: {\n") 748 .append(" config {\n") 749 .append(" name: \"android.protolog\"\n") 750 .append(" target_buffer: " + targetBuffer + "\n") 751 .append(" protolog_config: {\n") 752 .append(" tracing_mode: ENABLE_ALL\n") 753 .append(" }\n") 754 .append(" }\n") 755 .append("}\n"); 756 757 config.append("data_sources: {\n") 758 .append(" config {\n") 759 .append(" name: \"android.viewcapture\"\n") 760 .append(" target_buffer: " + targetBuffer + "\n") 761 .append(" }\n") 762 .append("}\n"); 763 764 config.append("data_sources: {\n") 765 .append(" config {\n") 766 .append(" name: \"android.windowmanager\"\n") 767 .append(" target_buffer: " + targetBuffer + "\n") 768 .append(" }\n") 769 .append("}\n"); 770 771 config.append("data_sources {\n") 772 .append(" config {\n") 773 .append(" name: \"android.input.inputevent\"\n") 774 .append(" target_buffer: 1\n") 775 .append(" android_input_event_config {\n") 776 .append(" mode: TRACE_MODE_USE_RULES\n") 777 .append(" rules {\n") 778 .append(" trace_level: TRACE_LEVEL_NONE\n") 779 .append(" match_secure: true\n") 780 .append(" }\n") 781 .append(" rules {\n") 782 .append(" trace_level: TRACE_LEVEL_COMPLETE\n") 783 .append(" match_all_packages: \"com.android.shell\"\n") 784 .append(" match_all_packages: \"com.android.systemui\"\n") 785 .append(" match_all_packages: \"com.android.launcher3\"\n") 786 .append(" match_all_packages: \"com.android.settings\"\n") 787 .append(" match_ime_connection_active: false\n") 788 .append(" }\n") 789 .append(" rules {\n") 790 .append(" trace_level: TRACE_LEVEL_REDACTED\n") 791 .append(" }\n") 792 .append(" trace_dispatcher_input_events: true\n") 793 .append(" trace_dispatcher_window_dispatch: true\n") 794 .append(" }\n") 795 .append(" }\n") 796 .append("}\n"); 797 } 798 } 799 } 800