• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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