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.annotations.VisibleForTesting; 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.device.DeviceNotAvailableException; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 24 import com.android.tradefed.util.FileUtil; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.text.SimpleDateFormat; 29 import java.util.Date; 30 import java.util.Locale; 31 import java.util.Map; 32 import java.util.Timer; 33 import java.util.TimerTask; 34 35 /** 36 * A {@link IMetricCollector} that allows to run a collection task periodically at a set interval. 37 */ 38 public abstract class ScheduledDeviceMetricCollector extends BaseDeviceMetricCollector { 39 40 @Option( 41 name = "fixed-schedule-rate", 42 description = "Schedule the timetask as a fixed schedule rate" 43 ) 44 private boolean mFixedScheduleRate = false; 45 46 @Option( 47 name = "interval", 48 description = "the interval between two tasks being scheduled", 49 isTimeVal = true 50 ) 51 private long mIntervalMs = 60 * 1000l; 52 53 private Timer timer; 54 55 @Override onTestRunStart(DeviceMetricData runData)56 public final void onTestRunStart(DeviceMetricData runData) { 57 CLog.d("starting with interval = %s", mIntervalMs); 58 onStart(runData); 59 timer = new Timer(); 60 TimerTask timerTask = 61 new TimerTask() { 62 @Override 63 public void run() { 64 try { 65 for (ITestDevice device : getDevices()) { 66 collect(device, runData); 67 } 68 } catch (InterruptedException e) { 69 timer.cancel(); 70 Thread.currentThread().interrupt(); 71 CLog.e("Interrupted exception thrown from task:"); 72 CLog.e(e); 73 } 74 } 75 }; 76 77 if (mFixedScheduleRate) { 78 timer.scheduleAtFixedRate(timerTask, 0, mIntervalMs); 79 } else { 80 timer.schedule(timerTask, 0, mIntervalMs); 81 } 82 } 83 84 @Override onTestRunEnd( DeviceMetricData runData, final Map<String, Metric> currentRunMetrics)85 public final void onTestRunEnd( 86 DeviceMetricData runData, final Map<String, Metric> currentRunMetrics) { 87 if (timer != null) { 88 timer.cancel(); 89 timer.purge(); 90 } 91 onEnd(runData); 92 CLog.d("finished"); 93 } 94 95 /** 96 * Task periodically & asynchronously run during the test running on a specific device. 97 * 98 * @param device the {@link ITestDevice} the metric is associated to. 99 * @param runData the {@link DeviceMetricData} where to put metrics. 100 * @throws InterruptedException 101 */ collect(ITestDevice device, DeviceMetricData runData)102 abstract void collect(ITestDevice device, DeviceMetricData runData) throws InterruptedException; 103 104 /** 105 * Executed when entering this collector. 106 * 107 * @param runData the {@link DeviceMetricData} where to put metrics. 108 */ onStart(DeviceMetricData runData)109 void onStart(DeviceMetricData runData) { 110 // Does nothing. 111 } 112 113 /** 114 * Executed when finishing this collector. 115 * 116 * @param runData the {@link DeviceMetricData} where to put metrics. 117 */ onEnd(DeviceMetricData runData)118 void onEnd(DeviceMetricData runData) { 119 // Does nothing. 120 } 121 122 /** 123 * Send all the output of a process from all the devices to a file. 124 * 125 * <p>Please note, metric collections should not overlap. 126 * 127 * @throws DeviceNotAvailableException 128 * @throws IOException 129 */ saveProcessOutput(ITestDevice device, String command, String outputFileName)130 File saveProcessOutput(ITestDevice device, String command, String outputFileName) 131 throws DeviceNotAvailableException, IOException { 132 String output = device.executeShellCommand(command); 133 134 // Create the output file and dump the output of the command to this file. 135 File outputFile = new File(outputFileName); 136 137 FileUtil.writeToFile(output, outputFile); 138 139 return outputFile; 140 } 141 142 /** 143 * Create a suffix string to be appended at the end of each metric file to keep the name unique 144 * at each run. 145 * 146 * @return suffix string in the format year-month-date-hour-minute-seconds-milliseconds. 147 */ 148 @VisibleForTesting getFileSuffix()149 String getFileSuffix() { 150 return new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US).format(new Date()); 151 } 152 153 /** 154 * Creates temporary directory to store the metric files. 155 * 156 * @return {@link File} directory with 'tmp' prefixed to its name to signify that its temporary. 157 * @throws IOException 158 */ 159 @VisibleForTesting createTempDir()160 File createTempDir() throws IOException { 161 return FileUtil.createTempDir(String.format("tmp_%s", getTag())); 162 } 163 } 164