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