• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 package com.android.performance;
17 
18 import com.android.tradefed.config.Option;
19 import com.android.tradefed.config.OptionClass;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.invoker.TestInformation;
23 import com.android.tradefed.log.LogUtil.CLog;
24 import com.android.tradefed.targetprep.BaseTargetPreparer;
25 import com.android.tradefed.targetprep.TargetSetupError;
26 
27 import java.io.File;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 
31 /** A {@link ITargetPreparer} that generate Java heap profile for perfetto config */
32 @OptionClass(alias = "perfetto-java-heap-config")
33 public class PerfettoJavaHeapConfigTargetPreparer extends BaseTargetPreparer {
34 
35     @Option(
36             name = "push-trace-config-file",
37             description = "Full path to push the trace on the device")
38     private String mOutputFile = "/data/misc/perfetto-traces/trace_config_java_heap.textproto";
39 
40     @Option(
41             name = "process-names-to-profile",
42             description = "Comma-separated list of process names to profile.")
43     private String mProcessNames = "com.android.systemui";
44 
45     @Option(
46             name = "buffer-size-kb",
47             description = "Buffer size in memory that store the whole java heap graph in kb")
48     private int mBufferSizeKb = 256000;
49 
50     /** {@inheritDoc} */
51     @Override
setUp(TestInformation testInfo)52     public void setUp(TestInformation testInfo)
53             throws TargetSetupError, DeviceNotAvailableException {
54         File tempFile = null;
55         try {
56             tempFile = File.createTempFile("trace_config_java_heap", ".textproto");
57             writeTraceConfig(tempFile);
58             pushFile(testInfo.getDevice(), tempFile, mOutputFile);
59         } catch (IOException e) {
60             CLog.e("Error when creating Perfetto config", e);
61         } finally {
62             if (tempFile != null) {
63                 tempFile.delete();
64             }
65         }
66     }
67 
writeTraceConfig(File srcFile)68     private void writeTraceConfig(File srcFile) {
69         CLog.i("Writing perfetto trace config for heap dump collection");
70         String result = generateConfig(mProcessNames, mBufferSizeKb);
71         CLog.i(String.format("Command result = %s", result));
72 
73         FileWriter fileWriter = null;
74         try {
75             fileWriter = new FileWriter(srcFile, true);
76             storeToFile(srcFile.getName(), result, fileWriter);
77         } catch (IOException e) {
78             CLog.e(String.format("Unable to update file %s ", srcFile.getName()), e);
79         } finally {
80             if (fileWriter != null) {
81                 try {
82                     fileWriter.close();
83                 } catch (IOException closeException) {
84                     CLog.e(
85                             String.format("Unable to close file %s ", srcFile.getName()),
86                             closeException);
87                 }
88             }
89         }
90     }
91 
generateConfig(String processNames, int bufferSizeKb)92     private String generateConfig(String processNames, int bufferSizeKb) {
93         return "buffers {\n"
94                 + "  size_kb: "
95                 + bufferSizeKb
96                 + "\n"
97                 + "  fill_policy: DISCARD\n"
98                 + "}\n"
99                 + "\n"
100                 + "data_sources {\n"
101                 + "  config {\n"
102                 + "    name: \"android.java_hprof\"\n"
103                 + "    java_hprof_config {\n"
104                 + "      process_cmdline: \""
105                 + processNames
106                 + "\"\n"
107                 + "      dump_smaps: true\n"
108                 + "    }\n"
109                 + "  }\n"
110                 + "}\n"
111                 + "\n"
112                 + "data_source_stop_timeout_ms: 100000\n"
113                 + "data_sources {\n"
114                 + "  config {\n"
115                 + "    name: \"android.packages_list\"\n"
116                 + "  }\n"
117                 + "}\n"
118                 + "\n"
119                 + "data_sources: {\n"
120                 + "  config {\n"
121                 + "    name: \"linux.process_stats\"\n"
122                 + "    process_stats_config {\n"
123                 + "      scan_all_processes_on_start: true\n"
124                 + "    }\n"
125                 + "  }\n"
126                 + "}";
127     }
128 
pushFile(ITestDevice device, File src, String remotePath)129     private void pushFile(ITestDevice device, File src, String remotePath)
130             throws DeviceNotAvailableException {
131         if (!device.pushFile(src, remotePath)) {
132             CLog.e(
133                     String.format(
134                             "Failed to push local '%s' to remote '%s'", src.getPath(), remotePath));
135         }
136     }
137 
storeToFile(String targetFileName, String content, FileWriter target)138     private void storeToFile(String targetFileName, String content, FileWriter target)
139             throws RuntimeException {
140         try {
141             target.write('\n');
142             target.write(content);
143             target.write('\n');
144         } catch (IOException e) {
145             throw new RuntimeException(
146                     String.format("Unable to write file %s ", targetFileName), e);
147         }
148     }
149 }
150