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