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(String reportScriptPath, String reportBasePath, 57 String reportUrlPrefix, String reportPath, 58 String buildId, String buildFlavor, String deviceSerial) { 59 mReportScriptPath = reportScriptPath; 60 mReportBasePath = reportBasePath; 61 mReportUrlPrefix = reportUrlPrefix; 62 mReportPath = reportPath; 63 mBuildId = buildId; 64 mBuildFlavor = buildFlavor; 65 mDeviceSerial = deviceSerial; 66 67 if (mReportBasePath == null || mReportPath == null || mReportScriptPath == null 68 || mReportUrlPrefix == null) { 69 throw new IllegalArgumentException("ANR post-processing enabled but missing " 70 + "required parameters!"); 71 } 72 } 73 74 /** 75 * Return the storage sub path based on build info. The path will not include trailing path 76 * separator. 77 */ getPerBuildStoragePath()78 private String getPerBuildStoragePath() { 79 if (mBuildId == null) { 80 mBuildId = "-1"; 81 } 82 if (mBuildFlavor == null) { 83 mBuildFlavor = "unknown_flavor"; 84 } 85 return String.format("%s/%s", mBuildId, mBuildFlavor); 86 } 87 88 /** 89 * Sets bugreport information for ANR post-processing script 90 * @param bugreportStream 91 */ setBugReportInfo(InputStreamSource bugreportStream)92 public void setBugReportInfo(InputStreamSource bugreportStream) throws IOException { 93 if (mCachedBugreport != null) { 94 CLog.w("A bugreport for this invocation already existed at %s, overriding anyways", 95 mCachedBugreport.getAbsolutePath()); 96 } 97 mCachedBugreport = FileUtil.createTempFile("monkey-anr-report-bugreport", ".txt"); 98 FileUtil.writeToFile(bugreportStream.createInputStream(), mCachedBugreport); 99 } 100 101 /** 102 * Sets monkey log information for ANR post-processing script 103 * @param monkeyLogStream 104 */ setMonkeyLogInfo(InputStreamSource monkeyLogStream)105 public void setMonkeyLogInfo(InputStreamSource monkeyLogStream) throws IOException { 106 if (mCachedMonkeyLog != null) { 107 CLog.w("A monkey log for this invocation already existed at %s, overriding anyways", 108 mCachedMonkeyLog.getAbsolutePath()); 109 } 110 mCachedMonkeyLog = FileUtil.createTempFile("monkey-anr-report-monkey-log", ".txt"); 111 FileUtil.writeToFile(monkeyLogStream.createInputStream(), mCachedMonkeyLog); 112 } 113 genereateAnrReport(ITestLogger logger)114 public boolean genereateAnrReport(ITestLogger logger) { 115 if (mCachedMonkeyLog == null || mCachedBugreport == null) { 116 CLog.w("Cannot generate report: bugreport or monkey log not populated yet."); 117 return false; 118 } 119 // generate monkey report and log it 120 File reportPath = new File(mReportBasePath, 121 String.format("%s/%s", mReportPath, getPerBuildStoragePath())); 122 if (reportPath.exists()) { 123 if (!reportPath.isDirectory()) { 124 CLog.w("The expected report storage path is not a directory: %s", 125 reportPath.getAbsolutePath()); 126 return false; 127 } 128 } else { 129 if (!reportPath.mkdirs()) { 130 CLog.w("Failed to create report storage directory: %s", 131 reportPath.getAbsolutePath()); 132 return false; 133 } 134 } 135 // now we should have the storage path, calculate the HTML report path 136 // HTML report file should be named as: 137 // monkey-anr-report-<device serial>-<random string>.html 138 // under the pre-constructed base report storage path 139 File htmlReport = null; 140 try { 141 htmlReport = FileUtil.createTempFile( 142 String.format("monkey-anr-report-%s-", mDeviceSerial), ".html", 143 reportPath); 144 } catch (IOException ioe) { 145 CLog.e("Error getting place holder file for HTML report."); 146 CLog.e(ioe); 147 return false; 148 } 149 // now ready to call the script 150 String htmlReportPath = htmlReport.getAbsolutePath(); 151 String command[] = { 152 mReportScriptPath, "--monkey", mCachedMonkeyLog.getAbsolutePath(), "--html", 153 htmlReportPath, mCachedBugreport.getAbsolutePath() 154 }; 155 CommandResult cr = RunUtil.getDefault().runTimedCmdSilently(REPORT_GENERATION_TIMEOUT, 156 command); 157 if (cr.getStatus() == CommandStatus.SUCCESS) { 158 // Test log the generated HTML report 159 try (InputStreamSource source = new FileInputStreamSource(htmlReport)) { 160 logger.testLog("monkey-anr-report", LogDataType.HTML, source); 161 } 162 // Clean up and declare success! 163 FileUtil.deleteFile(htmlReport); 164 return true; 165 } else { 166 CLog.w(cr.getStderr()); 167 return false; 168 } 169 } 170 cleanTempFiles()171 public void cleanTempFiles() { 172 FileUtil.deleteFile(mCachedBugreport); 173 FileUtil.deleteFile(mCachedMonkeyLog); 174 } 175 } 176