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 */ 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 */ 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 */ 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 */ 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 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 171 public void cleanTempFiles() { 172 FileUtil.deleteFile(mCachedBugreport); 173 FileUtil.deleteFile(mCachedMonkeyLog); 174 } 175 } 176