blob: b4df4c4604867262455db7166edc666a69a2cb30 [file] [log] [blame]
/*
* 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-<device serial>-<random string>.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);
}
}