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