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