1 /* 2 * Copyright (C) 2016 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.tradefed.log.ITestLogger; 20 import com.android.tradefed.log.LogUtil.CLog; 21 import com.android.tradefed.result.FileInputStreamSource; 22 import com.android.tradefed.result.InputStreamSource; 23 import com.android.tradefed.result.LogDataType; 24 import com.android.tradefed.util.CommandResult; 25 import com.android.tradefed.util.CommandStatus; 26 import com.android.tradefed.util.FileUtil; 27 import com.android.tradefed.util.RunUtil; 28 29 import java.io.File; 30 import java.io.IOException; 31 32 /** 33 * A utility class that encapsulates details of calling post-processing scripts to generate monkey 34 * ANR reports. 35 */ 36 public class AnrReportGenerator { 37 38 private static final long REPORT_GENERATION_TIMEOUT = 30 * 1000; // 30s 39 40 private File mCachedMonkeyLog = null; 41 private File mCachedBugreport = null; 42 43 private final String mReportScriptPath; 44 private final String mReportBasePath; 45 private final String mReportUrlPrefix; 46 private final String mReportPath; 47 private final String mDeviceSerial; 48 49 private String mBuildId = null; 50 private String mBuildFlavor = null; 51 52 /** 53 * Constructs the instance with details of report script and output location information. See 54 * matching options on {@link MonkeyBase} for more info. 55 */ AnrReportGenerator( String reportScriptPath, String reportBasePath, String reportUrlPrefix, String reportPath, String buildId, String buildFlavor, String deviceSerial)56 public AnrReportGenerator( 57 String reportScriptPath, 58 String reportBasePath, 59 String reportUrlPrefix, 60 String reportPath, 61 String buildId, 62 String buildFlavor, 63 String deviceSerial) { 64 mReportScriptPath = reportScriptPath; 65 mReportBasePath = reportBasePath; 66 mReportUrlPrefix = reportUrlPrefix; 67 mReportPath = reportPath; 68 mBuildId = buildId; 69 mBuildFlavor = buildFlavor; 70 mDeviceSerial = deviceSerial; 71 72 if (mReportBasePath == null 73 || mReportPath == null 74 || mReportScriptPath == null 75 || mReportUrlPrefix == null) { 76 throw new IllegalArgumentException( 77 "ANR post-processing enabled but missing " + "required parameters!"); 78 } 79 } 80 81 /** 82 * Return the storage sub path based on build info. The path will not include trailing path 83 * separator. 84 */ getPerBuildStoragePath()85 private String getPerBuildStoragePath() { 86 if (mBuildId == null) { 87 mBuildId = "-1"; 88 } 89 if (mBuildFlavor == null) { 90 mBuildFlavor = "unknown_flavor"; 91 } 92 return String.format("%s/%s", mBuildId, mBuildFlavor); 93 } 94 95 /** 96 * Sets bugreport information for ANR post-processing script 97 * 98 * @param bugreportStream 99 */ setBugReportInfo(InputStreamSource bugreportStream)100 public void setBugReportInfo(InputStreamSource bugreportStream) throws IOException { 101 if (mCachedBugreport != null) { 102 CLog.w( 103 "A bugreport for this invocation already existed at %s, overriding anyways", 104 mCachedBugreport.getAbsolutePath()); 105 } 106 mCachedBugreport = FileUtil.createTempFile("monkey-anr-report-bugreport", ".txt"); 107 FileUtil.writeToFile(bugreportStream.createInputStream(), mCachedBugreport); 108 } 109 110 /** 111 * Sets monkey log information for ANR post-processing script 112 * 113 * @param monkeyLogStream 114 */ setMonkeyLogInfo(InputStreamSource monkeyLogStream)115 public void setMonkeyLogInfo(InputStreamSource monkeyLogStream) throws IOException { 116 if (mCachedMonkeyLog != null) { 117 CLog.w( 118 "A monkey log for this invocation already existed at %s, overriding anyways", 119 mCachedMonkeyLog.getAbsolutePath()); 120 } 121 mCachedMonkeyLog = FileUtil.createTempFile("monkey-anr-report-monkey-log", ".txt"); 122 FileUtil.writeToFile(monkeyLogStream.createInputStream(), mCachedMonkeyLog); 123 } 124 genereateAnrReport(ITestLogger logger)125 public boolean genereateAnrReport(ITestLogger logger) { 126 if (mCachedMonkeyLog == null || mCachedBugreport == null) { 127 CLog.w("Cannot generate report: bugreport or monkey log not populated yet."); 128 return false; 129 } 130 // generate monkey report and log it 131 File reportPath = 132 new File( 133 mReportBasePath, 134 String.format("%s/%s", mReportPath, getPerBuildStoragePath())); 135 if (reportPath.exists()) { 136 if (!reportPath.isDirectory()) { 137 CLog.w( 138 "The expected report storage path is not a directory: %s", 139 reportPath.getAbsolutePath()); 140 return false; 141 } 142 } else { 143 if (!reportPath.mkdirs()) { 144 CLog.w( 145 "Failed to create report storage directory: %s", 146 reportPath.getAbsolutePath()); 147 return false; 148 } 149 } 150 // now we should have the storage path, calculate the HTML report path 151 // HTML report file should be named as: 152 // monkey-anr-report-<device serial>-<random string>.html 153 // under the pre-constructed base report storage path 154 File htmlReport = null; 155 try { 156 htmlReport = 157 FileUtil.createTempFile( 158 String.format("monkey-anr-report-%s-", mDeviceSerial), 159 ".html", 160 reportPath); 161 } catch (IOException ioe) { 162 CLog.e("Error getting place holder file for HTML report."); 163 CLog.e(ioe); 164 return false; 165 } 166 // now ready to call the script 167 String htmlReportPath = htmlReport.getAbsolutePath(); 168 String command[] = { 169 mReportScriptPath, 170 "--monkey", 171 mCachedMonkeyLog.getAbsolutePath(), 172 "--html", 173 htmlReportPath, 174 mCachedBugreport.getAbsolutePath() 175 }; 176 CommandResult cr = 177 RunUtil.getDefault().runTimedCmdSilently(REPORT_GENERATION_TIMEOUT, command); 178 if (cr.getStatus() == CommandStatus.SUCCESS) { 179 // Test log the generated HTML report 180 try (InputStreamSource source = new FileInputStreamSource(htmlReport)) { 181 logger.testLog("monkey-anr-report", LogDataType.HTML, source); 182 } 183 // Clean up and declare success! 184 FileUtil.deleteFile(htmlReport); 185 return true; 186 } else { 187 CLog.w(cr.getStderr()); 188 return false; 189 } 190 } 191 cleanTempFiles()192 public void cleanTempFiles() { 193 FileUtil.deleteFile(mCachedBugreport); 194 FileUtil.deleteFile(mCachedMonkeyLog); 195 } 196 } 197