• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.monkey;
18 
19 import com.android.loganalysis.item.AnrItem;
20 import com.android.loganalysis.item.BugreportItem;
21 import com.android.loganalysis.item.IItem;
22 import com.android.loganalysis.item.JavaCrashItem;
23 import com.android.loganalysis.item.LogcatItem;
24 import com.android.loganalysis.item.MonkeyLogItem;
25 import com.android.loganalysis.item.NativeCrashItem;
26 import com.android.loganalysis.parser.BugreportParser;
27 import com.android.loganalysis.parser.MonkeyLogParser;
28 import com.android.tradefed.log.LogUtil.CLog;
29 import com.android.tradefed.result.ITestInvocationListener;
30 import com.android.tradefed.result.InputStreamSource;
31 import com.android.tradefed.result.LogDataType;
32 import com.android.tradefed.result.ResultForwarder;
33 import com.android.tradefed.result.TestDescription;
34 
35 import com.google.common.base.Throwables;
36 
37 import org.junit.Assert;
38 
39 import java.io.BufferedReader;
40 import java.io.IOException;
41 import java.io.InputStreamReader;
42 import java.util.HashMap;
43 import java.util.Map;
44 
45 /**
46  * A {@link ResultForwarder} that intercepts monkey and bug report logs, extracts relevant metrics
47  * from them using brillopad, and forwards the results to the specified {@link
48  * ITestInvocationListener}.
49  */
50 public class MonkeyBrillopadForwarder extends ResultForwarder {
51 
52     private enum MonkeyStatus {
53         FINISHED("Money completed without errors."),
54         CRASHED("Monkey run stopped because of a crash."),
55         MISSING_COUNT(
56                 "Monkey run failed to complete due to an unknown reason. "
57                         + "Check logs for details."),
58         FALSE_COUNT("Monkey run reported an invalid count. " + "Check logs for details."),
59         UPTIME_FAILURE(
60                 "Monkey output is indicating an invalid uptime. "
61                         + "Device may have reset during run."),
62         TIMEOUT("Monkey did not complete within the specified time");
63 
64         private String mDescription;
65 
MonkeyStatus(String desc)66         MonkeyStatus(String desc) {
67             mDescription = desc;
68         }
69 
70         /** Returns a User friendly description of the status. */
getDescription()71         String getDescription() {
72             return mDescription;
73         }
74     }
75 
76     private BugreportItem mBugreport = null;
77     private MonkeyLogItem mMonkeyLog = null;
78     private final long mMonkeyTimeoutMs;
79 
MonkeyBrillopadForwarder(ITestInvocationListener listener, long monkeyTimeoutMs)80     public MonkeyBrillopadForwarder(ITestInvocationListener listener, long monkeyTimeoutMs) {
81         super(listener);
82         mMonkeyTimeoutMs = monkeyTimeoutMs;
83     }
84 
85     /** {@inheritDoc} */
86     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)87     public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
88         try {
89             // just parse the logs for now. Forwarding of results will happen on test completion
90             if (LogDataType.BUGREPORT.equals(dataType)) {
91                 CLog.i("Parsing %s", dataName);
92                 mBugreport =
93                         new BugreportParser()
94                                 .parse(
95                                         new BufferedReader(
96                                                 new InputStreamReader(
97                                                         dataStream.createInputStream())));
98             }
99             if (LogDataType.MONKEY_LOG.equals(dataType)) {
100                 CLog.i("Parsing %s", dataName);
101                 mMonkeyLog =
102                         new MonkeyLogParser()
103                                 .parse(
104                                         new BufferedReader(
105                                                 new InputStreamReader(
106                                                         dataStream.createInputStream())));
107             }
108         } catch (IOException e) {
109             CLog.e("Could not parse file %s", dataName);
110         }
111         super.testLog(dataName, dataType, dataStream);
112     }
113 
114     /** {@inheritDoc} */
115     @Override
testEnded(TestDescription monkeyTest, Map<String, String> metrics)116     public void testEnded(TestDescription monkeyTest, Map<String, String> metrics) {
117         Map<String, String> monkeyMetrics = new HashMap<String, String>();
118         try {
119             Assert.assertNotNull("Failed to parse or retrieve bug report", mBugreport);
120             Assert.assertNotNull(
121                     "Cannot report run to brillopad, monkey log does not exist", mMonkeyLog);
122             Assert.assertNotNull(
123                     "monkey log is missing start time info", mMonkeyLog.getStartUptimeDuration());
124             Assert.assertNotNull(
125                     "monkey log is missing stop time info", mMonkeyLog.getStopUptimeDuration());
126             LogcatItem systemLog = mBugreport.getSystemLog();
127 
128             MonkeyStatus status = reportMonkeyStats(mMonkeyLog, monkeyMetrics);
129             StringBuilder crashTrace = new StringBuilder();
130             reportAnrs(mMonkeyLog, monkeyMetrics, crashTrace);
131             reportJavaCrashes(mMonkeyLog, monkeyMetrics, crashTrace);
132             if (systemLog != null) {
133                 reportNativeCrashes(systemLog, monkeyMetrics, crashTrace);
134             } else {
135                 CLog.w("Failed to get system log from bugreport");
136             }
137 
138             if (!status.equals(MonkeyStatus.FINISHED)) {
139                 String failure =
140                         String.format("%s.\n%s", status.getDescription(), crashTrace.toString());
141                 super.testFailed(monkeyTest, failure);
142             }
143         } catch (AssertionError e) {
144             super.testFailed(monkeyTest, Throwables.getStackTraceAsString(e));
145         } catch (RuntimeException e) {
146             super.testFailed(monkeyTest, Throwables.getStackTraceAsString(e));
147         } finally {
148             super.testEnded(monkeyTest, monkeyMetrics);
149         }
150     }
151 
152     /** Report stats about the monkey run from the monkey log. */
reportMonkeyStats( MonkeyLogItem monkeyLog, Map<String, String> monkeyMetrics)153     private MonkeyStatus reportMonkeyStats(
154             MonkeyLogItem monkeyLog, Map<String, String> monkeyMetrics) {
155         MonkeyStatus status = getStatus(monkeyLog);
156         monkeyMetrics.put("throttle", convertToString(monkeyLog.getThrottle()));
157         monkeyMetrics.put("status", status.toString());
158         monkeyMetrics.put("target_count", convertToString(monkeyLog.getTargetCount()));
159         monkeyMetrics.put("injected_count", convertToString(monkeyLog.getFinalCount()));
160         monkeyMetrics.put("run_duration", convertToString(monkeyLog.getTotalDuration()));
161         monkeyMetrics.put(
162                 "uptime",
163                 convertToString(
164                         (monkeyLog.getStopUptimeDuration() - monkeyLog.getStartUptimeDuration())));
165         return status;
166     }
167 
168     /**
169      * A utility method that converts an {@link Integer} to a {@link String}, and that can handle
170      * null.
171      */
convertToString(Integer integer)172     private static String convertToString(Integer integer) {
173         return integer == null ? "" : integer.toString();
174     }
175 
176     /**
177      * A utility method that converts a {@link Long} to a {@link String}, and that can handle null.
178      */
convertToString(Long longInt)179     private static String convertToString(Long longInt) {
180         return longInt == null ? "" : longInt.toString();
181     }
182 
183     /** Report stats about Java crashes from the monkey log. */
reportJavaCrashes( MonkeyLogItem monkeyLog, Map<String, String> metrics, StringBuilder crashTrace)184     private void reportJavaCrashes(
185             MonkeyLogItem monkeyLog, Map<String, String> metrics, StringBuilder crashTrace) {
186 
187         if (monkeyLog.getCrash() != null && monkeyLog.getCrash() instanceof JavaCrashItem) {
188             JavaCrashItem jc = (JavaCrashItem) monkeyLog.getCrash();
189             metrics.put("java_crash", "1");
190             crashTrace.append("Detected java crash:\n");
191             crashTrace.append(jc.getStack());
192             crashTrace.append("\n");
193         }
194     }
195 
196     /** Report stats about the native crashes from the bugreport. */
reportNativeCrashes( LogcatItem systemLog, Map<String, String> metrics, StringBuilder crashTrace)197     private void reportNativeCrashes(
198             LogcatItem systemLog, Map<String, String> metrics, StringBuilder crashTrace) {
199         if (systemLog.getEvents().size() > 0) {
200             int nativeCrashes = 0;
201             for (IItem item : systemLog.getEvents()) {
202                 if (item instanceof NativeCrashItem) {
203                     nativeCrashes++;
204                     crashTrace.append("Detected native crash:\n");
205                     crashTrace.append(((NativeCrashItem) item).getStack());
206                     crashTrace.append("\n");
207                 }
208             }
209             metrics.put("native_crash", Integer.toString(nativeCrashes));
210         }
211     }
212 
213     /** Report stats about the ANRs from the monkey log. */
reportAnrs( MonkeyLogItem monkeyLog, Map<String, String> metrics, StringBuilder crashTrace)214     private void reportAnrs(
215             MonkeyLogItem monkeyLog, Map<String, String> metrics, StringBuilder crashTrace) {
216         if (monkeyLog.getCrash() != null && monkeyLog.getCrash() instanceof AnrItem) {
217             AnrItem anr = (AnrItem) monkeyLog.getCrash();
218             metrics.put("anr_crash", "1");
219             crashTrace.append("Detected ANR:\n");
220             crashTrace.append(anr.getStack());
221             crashTrace.append("\n");
222         }
223     }
224 
225     /** Return the {@link MonkeyStatus} based on how the monkey run ran. */
getStatus(MonkeyLogItem monkeyLog)226     private MonkeyStatus getStatus(MonkeyLogItem monkeyLog) {
227         // Uptime
228         try {
229             long startUptime = monkeyLog.getStartUptimeDuration();
230             long stopUptime = monkeyLog.getStopUptimeDuration();
231             long totalDuration = monkeyLog.getTotalDuration();
232             if (stopUptime - startUptime < totalDuration - MonkeyBase.UPTIME_BUFFER) {
233                 return MonkeyStatus.UPTIME_FAILURE;
234             }
235             if (totalDuration >= mMonkeyTimeoutMs) {
236                 return MonkeyStatus.TIMEOUT;
237             }
238         } catch (NullPointerException e) {
239             return MonkeyStatus.UPTIME_FAILURE;
240         }
241 
242         // False count
243         if (monkeyLog.getIsFinished()
244                 && monkeyLog.getIntermediateCount() + 100 < monkeyLog.getTargetCount()) {
245             return MonkeyStatus.FALSE_COUNT;
246         }
247 
248         // Finished
249         if (monkeyLog.getIsFinished()) {
250             return MonkeyStatus.FINISHED;
251         }
252 
253         // Crashed
254         if (monkeyLog.getFinalCount() != null) {
255             return MonkeyStatus.CRASHED;
256         }
257 
258         // Missing count
259         return MonkeyStatus.MISSING_COUNT;
260     }
261 }
262