1 /* 2 * Copyright (C) 2020 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.helpers; 18 19 import android.os.SystemClock; 20 import android.support.test.uiautomator.UiDevice; 21 import android.util.Log; 22 import androidx.test.InstrumentationRegistry; 23 24 import java.io.IOException; 25 import java.io.File; 26 import java.nio.file.Path; 27 import java.nio.file.Paths; 28 29 /** 30 * SimpleperfHelper is used to start and stop simpleperf sample collection and move the output 31 * sample file to the destination folder. 32 */ 33 public class SimpleperfHelper { 34 35 private static final String LOG_TAG = SimpleperfHelper.class.getSimpleName(); 36 private static final String SIMPLEPERF_TMP_FILE_PATH = "/data/local/tmp/perf.data"; 37 38 private static final String SIMPLEPERF_START_CMD = 39 "simpleperf record -o %s -g --post-unwind=yes -f 500 -a --exclude-perf"; 40 private static final String SIMPLEPERF_STOP_CMD = "pkill -INT simpleperf"; 41 private static final String SIMPLEPERF_PROC_ID_CMD = "pidof simpleperf"; 42 private static final String REMOVE_CMD = "rm %s"; 43 private static final String MOVE_CMD = "mv %s %s"; 44 45 private static final int SIMPLEPERF_STOP_WAIT_COUNT = 12; 46 private static final long SIMPLEPERF_STOP_WAIT_TIME = 5000; 47 48 private UiDevice mUiDevice; 49 startCollecting()50 public boolean startCollecting() { 51 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 52 try { 53 // Cleanup any running simpleperf sessions. 54 Log.i(LOG_TAG, "Cleanup simpleperf before starting."); 55 if (isSimpleperfRunning()) { 56 Log.i(LOG_TAG, "Simpleperf is already running. Stopping simpleperf."); 57 if (!stopSimpleperf()) { 58 return false; 59 } 60 } 61 62 Log.i(LOG_TAG, String.format("Starting simpleperf")); 63 new Thread() { 64 @Override 65 public void run() { 66 UiDevice uiDevice = 67 UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 68 try { 69 uiDevice.executeShellCommand( 70 String.format(SIMPLEPERF_START_CMD, SIMPLEPERF_TMP_FILE_PATH)); 71 } catch (IOException e) { 72 Log.e(LOG_TAG, "Failed to start simpleperf."); 73 } 74 } 75 }.start(); 76 77 if (!isSimpleperfRunning()) { 78 Log.e(LOG_TAG, "Simpleperf sampling failed to start."); 79 return false; 80 } 81 } catch (IOException e) { 82 Log.e(LOG_TAG, "Unable to start simpleperf sampling due to :" + e.getMessage()); 83 return false; 84 } 85 Log.i(LOG_TAG, "Simpleperf sampling started successfully."); 86 return true; 87 } 88 89 /** 90 * Stop the simpleperf sample collection under /data/local/tmp/perf.data and copy the output to 91 * the destination file. 92 * 93 * @param destinationFile file to copy the simpleperf sample file to. 94 * @return true if the trace collection is successful otherwise false. 95 */ stopCollecting(String destinationFile)96 public boolean stopCollecting(String destinationFile) { 97 Log.i(LOG_TAG, "Stopping simpleperf."); 98 try { 99 if (stopSimpleperf()) { 100 if (!copyFileOutput(destinationFile)) { 101 return false; 102 } 103 } else { 104 Log.e(LOG_TAG, "Simpleperf failed to stop"); 105 return false; 106 } 107 } catch (IOException e) { 108 Log.e(LOG_TAG, "Unable to stop the simpleperf samping due to " + e.getMessage()); 109 return false; 110 } 111 return true; 112 } 113 114 /** 115 * Utility method for sending the signal to stop simpleperf. 116 * 117 * @return true if simpleperf is successfully stopped. 118 */ stopSimpleperf()119 public boolean stopSimpleperf() throws IOException { 120 if (!isSimpleperfRunning()) { 121 Log.e(LOG_TAG, "Simpleperf stop called, but simpleperf is not running."); 122 return false; 123 } 124 125 String stopOutput = mUiDevice.executeShellCommand(SIMPLEPERF_STOP_CMD); 126 Log.i(LOG_TAG, String.format("Simpleperf stop command ran")); 127 int waitCount = 0; 128 while (isSimpleperfRunning()) { 129 if (waitCount < SIMPLEPERF_STOP_WAIT_COUNT) { 130 SystemClock.sleep(SIMPLEPERF_STOP_WAIT_TIME); 131 waitCount++; 132 continue; 133 } 134 return false; 135 } 136 Log.e(LOG_TAG, "Simpleperf stopped successfully."); 137 return true; 138 } 139 140 /** 141 * Check if there is a simpleperf instance running. 142 * 143 * @return true if there is a running simpleperf instance, otherwise false. 144 */ isSimpleperfRunning()145 private boolean isSimpleperfRunning() { 146 try { 147 String simpleperfProcId = mUiDevice.executeShellCommand(SIMPLEPERF_PROC_ID_CMD); 148 Log.i(LOG_TAG, String.format("Simpleperf process id - %s", simpleperfProcId)); 149 if (simpleperfProcId.isEmpty()) { 150 return false; 151 } 152 } catch (IOException e) { 153 Log.e(LOG_TAG, "Unable to check simpleperf status: " + e.getMessage()); 154 return false; 155 } 156 return true; 157 } 158 159 /** 160 * Copy the temporary simpleperf output file to the given destinationFile. 161 * 162 * @param destinationFile file to copy simpleperf output into. 163 * @return true if the simpleperf file copied successfully, otherwise false. 164 */ copyFileOutput(String destinationFile)165 private boolean copyFileOutput(String destinationFile) { 166 Path path = Paths.get(destinationFile); 167 String destDirectory = path.getParent().toString(); 168 // Check if directory already exists 169 File directory = new File(destDirectory); 170 if (!directory.exists()) { 171 boolean success = directory.mkdirs(); 172 if (!success) { 173 Log.e( 174 LOG_TAG, 175 String.format( 176 "Result output directory %s not created successfully.", 177 destDirectory)); 178 return false; 179 } 180 } 181 182 // Copy the collected trace from /data/local/tmp to the destinationFile. 183 try { 184 String moveResult = 185 mUiDevice.executeShellCommand( 186 String.format(MOVE_CMD, SIMPLEPERF_TMP_FILE_PATH, destinationFile)); 187 if (!moveResult.isEmpty()) { 188 Log.e( 189 LOG_TAG, 190 String.format( 191 "Unable to move simpleperf output file from %s to %s due to %s", 192 SIMPLEPERF_TMP_FILE_PATH, destinationFile, moveResult)); 193 return false; 194 } 195 } catch (IOException e) { 196 Log.e( 197 LOG_TAG, 198 "Unable to move the simpleperf sample file to destination file." 199 + e.getMessage()); 200 return false; 201 } 202 return true; 203 } 204 } 205