blob: f825e634374999265caf6760a6a14607e8a190ac [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.crash;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.Log;
import org.chromium.components.minidump_uploader.CrashFileManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
/** Prepends a logcat file to a minidump file for upload. */
public class MinidumpLogcatPrepender {
private static final String TAG = "LogcatPrepender";
@VisibleForTesting
public static final String LOGCAT_CONTENT_DISPOSITION =
"Content-Disposition: form-data; name=\"logcat\"; filename=\"logcat\"";
@VisibleForTesting public static final String LOGCAT_CONTENT_TYPE = "Content-Type: text/plain";
private final CrashFileManager mFileManager;
private final File mMinidumpFile;
private final List<String> mLogcat;
public MinidumpLogcatPrepender(
CrashFileManager fileManager, File minidumpFile, List<String> logcat) {
mFileManager = fileManager;
mMinidumpFile = minidumpFile;
mLogcat = logcat;
}
/** Read the boundary from the first line of the file. */
private static String getBoundary(File minidumpFile) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(minidumpFile));
return reader.readLine();
} finally {
if (reader != null) {
reader.close();
}
}
}
/**
* Write the logcat data to the specified target {@link File}.
*
* Target file is overwritten, not appended to the end.
*
* @param targetFile File to which logcat data should be written.
* @param logcat The lines of the logcat output.
* @param boundary String MIME boundary to prepend.
* @throws IOException if something goes wrong.
*/
private static void writeLogcat(File targetFile, List<String> logcat, String boundary)
throws IOException {
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(targetFile, false));
writer.write(boundary);
writer.newLine();
// Next we write the logcat data in a MIME block.
writer.write(LOGCAT_CONTENT_DISPOSITION);
writer.newLine();
writer.write(LOGCAT_CONTENT_TYPE);
writer.newLine();
writer.newLine();
// Emits the contents of the buffer into the output file.
for (String ln : logcat) {
writer.write(ln);
writer.newLine();
}
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* Append the minidump file data to the specified target {@link File}.
*
* @param minidumpFile File containing data to append.
* @param targetFile File to which data should be appended.
* @throws IOException when standard IO errors occur.
*/
private static void appendMinidump(File minidumpFile, File targetFile) throws IOException {
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
byte[] buf = new byte[256];
in = new BufferedInputStream(new FileInputStream(minidumpFile));
out = new BufferedOutputStream(new FileOutputStream(targetFile, true));
int count;
while ((count = in.read(buf)) != -1) {
out.write(buf, 0, count);
}
} finally {
if (in != null) in.close();
if (out != null) out.close();
}
}
/**
* Prepends the logcat output to the minidump file.
* @return On success, returns the file containing the combined logcat and minidump output.
* On failure, returns the original file containing just the minidump.
*/
public File run() {
if (mLogcat.isEmpty()) return mMinidumpFile;
String targetFileName = mMinidumpFile.getName() + ".try0";
File targetFile = null;
boolean success = false;
try {
String boundary = getBoundary(mMinidumpFile);
if (boundary == null) {
return mMinidumpFile;
}
targetFile = mFileManager.createNewTempFile(targetFileName);
writeLogcat(targetFile, mLogcat, boundary);
// Finally reopen and append the original minidump MIME sections, including the leading
// boundary.
appendMinidump(mMinidumpFile, targetFile);
success = true;
} catch (IOException e) {
Log.w(
TAG,
"Error while trying to annotate minidump file %s with logcat data",
mMinidumpFile.getAbsoluteFile(),
e);
if (targetFile != null) {
CrashFileManager.deleteFile(targetFile);
}
}
if (!success) return mMinidumpFile;
// Try to clean up the previous file. Note that this step is best-effort, and failing to
// perform the cleanup does not count as an overall failure to prepend the logcat output.
if (!mMinidumpFile.delete()) {
Log.w(TAG, "Failed to delete minidump file: " + mMinidumpFile.getName());
}
assert targetFile != null;
assert targetFile.exists();
return targetFile;
}
}