1 /* 2 * Copyright (C) 2017 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.tradefed.device.metric; 17 18 import com.android.tradefed.config.Option; 19 import com.android.tradefed.device.DeviceNotAvailableException; 20 import com.android.tradefed.device.ITestDevice; 21 import com.android.tradefed.log.LogUtil.CLog; 22 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 23 import com.android.tradefed.util.FileUtil; 24 import com.android.tradefed.util.proto.TfMetricProtoUtil; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.util.AbstractMap.SimpleEntry; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.regex.Pattern; 34 35 /** 36 * A {@link BaseDeviceMetricCollector} that listen for metrics key coming from the device and pull 37 * them as a file from the device. Can be extended for extra-processing of the file. 38 */ 39 public abstract class FilePullerDeviceMetricCollector extends BaseDeviceMetricCollector { 40 41 @Option( 42 name = "pull-pattern-keys", 43 description = "The pattern key name to be pull from the device as a file. Can be repeated." 44 ) 45 private List<String> mKeys = new ArrayList<>(); 46 47 @Option( 48 name = "directory-keys", 49 description = "Path to the directory on the device that contains the metrics." 50 ) 51 protected List<String> mDirectoryKeys = new ArrayList<>(); 52 53 @Option( 54 name = "clean-up", 55 description = "Whether to delete the file from the device after pulling it or not." 56 ) 57 private boolean mCleanUp = true; 58 59 @Override onTestRunEnd( DeviceMetricData runData, final Map<String, Metric> currentRunMetrics)60 public void onTestRunEnd( 61 DeviceMetricData runData, final Map<String, Metric> currentRunMetrics) { 62 processMetricRequest(runData, TfMetricProtoUtil.compatibleConvert(currentRunMetrics)); 63 } 64 65 @Override onTestEnd(DeviceMetricData testData, Map<String, Metric> currentTestCaseMetrics)66 public void onTestEnd(DeviceMetricData testData, Map<String, Metric> currentTestCaseMetrics) { 67 processMetricRequest(testData, TfMetricProtoUtil.compatibleConvert(currentTestCaseMetrics)); 68 } 69 70 /** 71 * Implementation of the method should allow to log the file, parse it for metrics to be put in 72 * {@link DeviceMetricData}. 73 * 74 * @param key the option key associated to the file that was pulled. 75 * @param metricFile the {@link File} pulled from the device matching the option key. 76 * @param runData the run {@link DeviceMetricData} where metrics can be stored. 77 */ processMetricFile(String key, File metricFile, DeviceMetricData runData)78 public abstract void processMetricFile(String key, File metricFile, DeviceMetricData runData); 79 80 /** 81 * Implementation of the method should allow to log the directory, parse it for metrics to be 82 * put in {@link DeviceMetricData}. 83 * 84 * @param key the option key associated to the directory that was pulled. 85 * @param metricDirectory the {@link File} pulled from the device matching the option key. 86 * @param runData the run {@link DeviceMetricData} where metrics can be stored. 87 */ processMetricDirectory( String key, File metricDirectory, DeviceMetricData runData)88 public abstract void processMetricDirectory( 89 String key, File metricDirectory, DeviceMetricData runData); 90 processMetricRequest(DeviceMetricData data, Map<String, String> currentMetrics)91 private void processMetricRequest(DeviceMetricData data, Map<String, String> currentMetrics) { 92 if (mKeys.isEmpty() && mDirectoryKeys.isEmpty()) { 93 return; 94 } 95 for (String key : mKeys) { 96 Entry<String, File> pulledMetrics = pullMetricFile(key, currentMetrics); 97 if (pulledMetrics != null) { 98 processMetricFile(pulledMetrics.getKey(), pulledMetrics.getValue(), data); 99 } 100 } 101 102 for (String key : mDirectoryKeys) { 103 Entry<String, File> pulledMetrics = pullMetricDirectory(key); 104 if (pulledMetrics != null) { 105 processMetricDirectory(pulledMetrics.getKey(), pulledMetrics.getValue(), data); 106 } 107 } 108 109 } 110 pullMetricFile( String pattern, final Map<String, String> currentRunMetrics)111 private Entry<String, File> pullMetricFile( 112 String pattern, final Map<String, String> currentRunMetrics) { 113 Pattern p = Pattern.compile(pattern); 114 for (Entry<String, String> entry : currentRunMetrics.entrySet()) { 115 if (p.matcher(entry.getKey()).find()) { 116 for (ITestDevice device : getDevices()) { 117 try { 118 File attemptPull = device.pullFile(entry.getValue()); 119 if (attemptPull != null) { 120 if (mCleanUp) { 121 device.executeShellCommand( 122 String.format("rm -f %s", entry.getValue())); 123 } 124 // Return the actual key and the file associated 125 return new SimpleEntry<String, File>(entry.getKey(), attemptPull); 126 } 127 } catch (DeviceNotAvailableException e) { 128 CLog.e( 129 "Exception when pulling metric file '%s' from %s", 130 entry.getValue(), device.getSerialNumber()); 131 CLog.e(e); 132 } 133 } 134 } 135 } 136 CLog.e("Could not find a device file associated to pattern '%s'.", pattern); 137 return null; 138 } 139 140 /** 141 * Pulls the directory and all its content from the device and save it in the 142 * host under the host_tmp folder. 143 * 144 * @param keyDirectory path to the source directory in the device. 145 * @return Key,value pair of the directory name and path to the directory in the 146 * local host. 147 */ pullMetricDirectory(String keyDirectory)148 private Entry<String, File> pullMetricDirectory(String keyDirectory) { 149 try { 150 File tmpDestDir = FileUtil.createTempDir("host_tmp"); 151 for (ITestDevice device : getDevices()) { 152 try { 153 if (device.pullDir(keyDirectory, tmpDestDir)) { 154 if (mCleanUp) { 155 device.executeShellCommand( 156 String.format("rm -rf %s", keyDirectory)); 157 } 158 return new SimpleEntry<String, File>(keyDirectory, tmpDestDir); 159 } 160 } catch (DeviceNotAvailableException e) { 161 CLog.e( 162 "Exception when pulling directory '%s' from %s", 163 keyDirectory, device.getSerialNumber()); 164 CLog.e(e); 165 } 166 } 167 } catch (IOException ioe) { 168 CLog.e("Exception while creating the local directory"); 169 CLog.e(ioe); 170 } 171 CLog.e("Could not find a device directory associated to path '%s'.", keyDirectory); 172 return null; 173 } 174 175 } 176