/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.monkey; import com.android.tradefed.log.ITestLogger; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.result.FileInputStreamSource; import com.android.tradefed.result.InputStreamSource; import com.android.tradefed.result.LogDataType; import com.android.tradefed.util.CommandResult; import com.android.tradefed.util.CommandStatus; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.RunUtil; import java.io.File; import java.io.IOException; /** * A utility class that encapsulates details of calling post-processing scripts to generate monkey * ANR reports. */ public class AnrReportGenerator { private static final long REPORT_GENERATION_TIMEOUT = 30 * 1000; // 30s private File mCachedMonkeyLog = null; private File mCachedBugreport = null; private final String mReportScriptPath; private final String mReportBasePath; private final String mReportUrlPrefix; private final String mReportPath; private final String mDeviceSerial; private String mBuildId = null; private String mBuildFlavor = null; /** * Constructs the instance with details of report script and output location information. See * matching options on {@link MonkeyBase} for more info. */ public AnrReportGenerator( String reportScriptPath, String reportBasePath, String reportUrlPrefix, String reportPath, String buildId, String buildFlavor, String deviceSerial) { mReportScriptPath = reportScriptPath; mReportBasePath = reportBasePath; mReportUrlPrefix = reportUrlPrefix; mReportPath = reportPath; mBuildId = buildId; mBuildFlavor = buildFlavor; mDeviceSerial = deviceSerial; if (mReportBasePath == null || mReportPath == null || mReportScriptPath == null || mReportUrlPrefix == null) { throw new IllegalArgumentException( "ANR post-processing enabled but missing " + "required parameters!"); } } /** * Return the storage sub path based on build info. The path will not include trailing path * separator. */ private String getPerBuildStoragePath() { if (mBuildId == null) { mBuildId = "-1"; } if (mBuildFlavor == null) { mBuildFlavor = "unknown_flavor"; } return String.format("%s/%s", mBuildId, mBuildFlavor); } /** * Sets bugreport information for ANR post-processing script * * @param bugreportStream */ public void setBugReportInfo(InputStreamSource bugreportStream) throws IOException { if (mCachedBugreport != null) { CLog.w( "A bugreport for this invocation already existed at %s, overriding anyways", mCachedBugreport.getAbsolutePath()); } mCachedBugreport = FileUtil.createTempFile("monkey-anr-report-bugreport", ".txt"); FileUtil.writeToFile(bugreportStream.createInputStream(), mCachedBugreport); } /** * Sets monkey log information for ANR post-processing script * * @param monkeyLogStream */ public void setMonkeyLogInfo(InputStreamSource monkeyLogStream) throws IOException { if (mCachedMonkeyLog != null) { CLog.w( "A monkey log for this invocation already existed at %s, overriding anyways", mCachedMonkeyLog.getAbsolutePath()); } mCachedMonkeyLog = FileUtil.createTempFile("monkey-anr-report-monkey-log", ".txt"); FileUtil.writeToFile(monkeyLogStream.createInputStream(), mCachedMonkeyLog); } public boolean genereateAnrReport(ITestLogger logger) { if (mCachedMonkeyLog == null || mCachedBugreport == null) { CLog.w("Cannot generate report: bugreport or monkey log not populated yet."); return false; } // generate monkey report and log it File reportPath = new File( mReportBasePath, String.format("%s/%s", mReportPath, getPerBuildStoragePath())); if (reportPath.exists()) { if (!reportPath.isDirectory()) { CLog.w( "The expected report storage path is not a directory: %s", reportPath.getAbsolutePath()); return false; } } else { if (!reportPath.mkdirs()) { CLog.w( "Failed to create report storage directory: %s", reportPath.getAbsolutePath()); return false; } } // now we should have the storage path, calculate the HTML report path // HTML report file should be named as: // monkey-anr-report--.html // under the pre-constructed base report storage path File htmlReport = null; try { htmlReport = FileUtil.createTempFile( String.format("monkey-anr-report-%s-", mDeviceSerial), ".html", reportPath); } catch (IOException ioe) { CLog.e("Error getting place holder file for HTML report."); CLog.e(ioe); return false; } // now ready to call the script String htmlReportPath = htmlReport.getAbsolutePath(); String command[] = { mReportScriptPath, "--monkey", mCachedMonkeyLog.getAbsolutePath(), "--html", htmlReportPath, mCachedBugreport.getAbsolutePath() }; CommandResult cr = RunUtil.getDefault().runTimedCmdSilently(REPORT_GENERATION_TIMEOUT, command); if (cr.getStatus() == CommandStatus.SUCCESS) { // Test log the generated HTML report try (InputStreamSource source = new FileInputStreamSource(htmlReport)) { logger.testLog("monkey-anr-report", LogDataType.HTML, source); } // Clean up and declare success! FileUtil.deleteFile(htmlReport); return true; } else { CLog.w(cr.getStderr()); return false; } } public void cleanTempFiles() { FileUtil.deleteFile(mCachedBugreport); FileUtil.deleteFile(mCachedMonkeyLog); } }