1 /* 2 * Copyright (C) 2019 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.util.Log; 20 21 import androidx.annotation.VisibleForTesting; 22 import androidx.test.InstrumentationRegistry; 23 import androidx.test.uiautomator.UiDevice; 24 25 import java.io.BufferedReader; 26 import java.io.File; 27 import java.io.FileReader; 28 import java.io.IOException; 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.regex.Matcher; 32 import java.util.regex.Pattern; 33 34 /** 35 * PwrStatsUtilHelper runs and collects power stats HAL metrics from the Pixel-specific 36 * pwrstats_util tool that is already included on the device in userdebug builds. 37 */ 38 public class PwrStatsUtilHelper implements ICollectorHelper<Long> { 39 private static final String LOG_TAG = PwrStatsUtilHelper.class.getSimpleName(); 40 41 private UiDevice mDevice = null; 42 43 @VisibleForTesting protected int mUtilPid = 0; 44 @VisibleForTesting protected File mLogFile; 45 46 @Override startCollecting()47 public boolean startCollecting() { 48 Log.i(LOG_TAG, "Starting pwrstats_util collection..."); 49 // Create temp log file for pwrstats_util 50 try { 51 mLogFile = File.createTempFile("pwrstats_output", ".txt"); 52 } catch (IOException e) { 53 Log.e(LOG_TAG, e.toString()); 54 return false; 55 } 56 mLogFile.delete(); 57 58 // Kick off pwrstats_util and get its PID 59 final String pid_regex = "^pid = (\\d+)$"; 60 final Pattern pid_pattern = Pattern.compile(pid_regex); 61 String output; 62 try { 63 output = executeShellCommand("pwrstats_util -d " + mLogFile.getAbsolutePath()); 64 } catch (IOException e) { 65 Log.e(LOG_TAG, e.toString()); 66 return false; 67 } 68 Matcher m = pid_pattern.matcher(output); 69 if (m.find()) { 70 mUtilPid = Integer.parseInt(m.group(1)); 71 } 72 return true; 73 } 74 75 @Override stopCollecting()76 public boolean stopCollecting() { 77 Log.i(LOG_TAG, "Ending pwrstats_util collection..."); 78 try { 79 executeShellCommand("kill -INT " + mUtilPid); 80 } catch (IOException e) { 81 Log.e(LOG_TAG, e.toString()); 82 return false; 83 } 84 try { 85 Thread.sleep(1000); 86 } catch (InterruptedException e) { 87 Log.e(LOG_TAG, e.toString()); 88 } 89 return true; 90 } 91 92 @Override getMetrics()93 public Map<String, Long> getMetrics() { 94 stopCollecting(); 95 96 Log.i(LOG_TAG, "Getting metrics..."); 97 return processMetricsFromLogFile(); 98 } 99 100 @VisibleForTesting 101 /* Execute a shell command and return its output. */ executeShellCommand(String command)102 protected String executeShellCommand(String command) throws IOException { 103 if (mDevice == null) { 104 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 105 } 106 107 Log.i(LOG_TAG, "Running '" + command + "'"); 108 return mDevice.executeShellCommand(command); 109 } 110 111 /* Parse each of the metrics into a key value pair and delete the log file. */ processMetricsFromLogFile()112 private Map<String, Long> processMetricsFromLogFile() { 113 final String PATTERN = "^(.*)=(\\d+)$"; 114 final Pattern r = Pattern.compile(PATTERN); 115 Map<String, Long> metrics = new HashMap<>(); 116 BufferedReader br = null; 117 try { 118 br = new BufferedReader(new FileReader(mLogFile)); 119 // First line is elapsed time 120 String line = br.readLine(); 121 Matcher m; 122 while ((line = br.readLine()) != null) { 123 m = r.matcher(line); 124 if (m.find()) { 125 Log.i(LOG_TAG, m.group(1) + "=" + m.group(2)); 126 metrics.put(m.group(1), Long.parseLong(m.group(2))); 127 } 128 } 129 mLogFile.delete(); 130 } catch (IOException e) { 131 Log.e(LOG_TAG, e.toString()); 132 } 133 134 try { 135 if (br != null) br.close(); 136 } catch (IOException e) { 137 Log.e(LOG_TAG, e.toString()); 138 } 139 return metrics; 140 } 141 } 142