Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Unified Diff: chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java

Issue 620673003: Chromecast: adds crash handling for Android build. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address lei's comments Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
diff --git a/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc1631ccb62f75e036e15d9eb2b0889aed9abebd
--- /dev/null
+++ b/chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
@@ -0,0 +1,223 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chromecast.shell;
+
+import android.net.http.AndroidHttpClient;
+import android.util.Log;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.InputStreamEntity;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.SequenceInputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Crash crashdump uploader. Scans the crash dump location provided by CastCrashReporterClient
+ * for dump files, attempting to upload all crash dumps to the crash server.
+ *
+ * Uploading is intended to happen in a background thread, and this method will likely be called
+ * on startup, looking for crash dumps from previous runs, since Chromium's crash code
+ * explicitly blocks any post-dump hooks or uploading for Android builds.
+ */
+public final class CastCrashUploader {
+ private static final String TAG = "CastCrashUploader";
+ private static final String CRASH_REPORT_HOST = "clients2.google.com";
+ private static final String CAST_SHELL_USER_AGENT = android.os.Build.MODEL + "/CastShell";
+
+ private final ExecutorService mExecutorService;
+ private final String mCrashDumpPath;
+ private final String mCrashReportUploadUrl;
+
+ public CastCrashUploader(String crashDumpPath, boolean isDebugBuild) {
+ this.mCrashDumpPath = crashDumpPath;
+ mCrashReportUploadUrl = isDebugBuild ?
+ "http://clients2.google.com/cr/staging_report" :
+ "http://clients2.google.com/cr/report";
+ mExecutorService = Executors.newFixedThreadPool(1);
+ }
+
+ public void removeCrashDumpsSync() {
+ mExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ File crashDumpDirectory = new File(mCrashDumpPath);
+ for (File potentialDump : crashDumpDirectory.listFiles()) {
+ if (potentialDump.getName().matches(".*\\.dmp\\d*")) {
+ potentialDump.delete();
+ }
+ }
+ }
+ });
+ waitForTasksToFinish();
+ }
+
+ /**
+ * Synchronously uploads the crash dump from the current process, along with an attached
+ * log file.
+ * @param logFilePath Full path to the log file for the current process.
+ */
+ public void uploadCurrentProcessDumpSync(String logFilePath) {
+ int pid = android.os.Process.myPid();
+ Log.d(TAG, "Immediately attempting a crash upload with logs, looking for: .dmp" + pid);
+
+ queueAllCrashDumpUpload(".*\\.dmp" + pid, new File(logFilePath));
+ waitForTasksToFinish();
+ }
+
+ private void waitForTasksToFinish() {
+ try {
+ mExecutorService.shutdown();
+ boolean finished = mExecutorService.awaitTermination(60, TimeUnit.SECONDS);
+ if (!finished) {
+ Log.d(TAG, "Crash dump handling did not finish executing in time. Exiting.");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted waiting for asynchronous execution", e);
+ }
+ }
+
+ /**
+ * Scans the given location for crash dump files and queues background
+ * uploads of each file.
+ */
+ public void uploadRecentCrashesAsync() {
+ mExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ // Multipart dump filename has format "[random string].dmp[pid]", e.g.
+ // 20597a65-b822-008e-31f8fc8e-02bb45c0.dmp18169
+ queueAllCrashDumpUpload(".*\\.dmp\\d+", null);
+ }
+ });
+ }
+
+ /**
+ * Searches for files matching the given regex in the crash dump folder, queueing each
+ * one for upload.
+ * @param dumpFileRegex Regex to test against the name of each file in the crash dump folder.
+ * @param logFile Log file to include, if any.
+ */
+ private void queueAllCrashDumpUpload(String dumpFileRegex, File logFile) {
+ if (mCrashDumpPath == null) return;
+ final AndroidHttpClient httpClient = AndroidHttpClient.newInstance(CAST_SHELL_USER_AGENT);
+ File crashDumpDirectory = new File(mCrashDumpPath);
+ for (File potentialDump : crashDumpDirectory.listFiles()) {
+ if (potentialDump.getName().matches(dumpFileRegex)) {
+ queueCrashDumpUpload(httpClient, potentialDump, logFile);
+ }
+ }
+
+ // When finished uploading all of the queued dumps, release httpClient
+ mExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ httpClient.close();
+ }
+ });
+ }
+
+ /**
+ * Enqueues a background task to upload a single crash dump file.
+ */
+ private void queueCrashDumpUpload(final AndroidHttpClient httpClient, final File dumpFile,
+ final File logFile) {
+ mExecutorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "Uploading dump crash log: " + dumpFile.getName());
+
+ try {
+ // Dump file is already in multipart MIME format and has a boundary throughout.
+ // Scrape the first line, remove two dashes, call that the "boundary" and add it
+ // to the content-type.
+ FileInputStream dumpFileStream = new FileInputStream(dumpFile);
+ String dumpFirstLine = getFirstLine(dumpFileStream);
+ String mimeBoundary = dumpFirstLine.substring(2);
+
+ InputStream uploadCrashDumpStream = new FileInputStream(dumpFile);
+ InputStream logFileStream = null;
+
+ if (logFile != null) {
+ Log.d(TAG, "Including log file: " + logFile.getName());
+ StringBuffer logHeader = new StringBuffer();
+ logHeader.append(dumpFirstLine);
+ logHeader.append("\n");
+ logHeader.append(
+ "Content-Disposition: form-data; name=\"log\"; filename=\"log\"\n");
+ logHeader.append("Content-Type: text/plain\n\n");
+ InputStream logHeaderStream =
+ new ByteArrayInputStream(logHeader.toString().getBytes());
+ logFileStream = new FileInputStream(logFile);
+
+ // Upload: prepend the log file for uploading
+ uploadCrashDumpStream = new SequenceInputStream(
+ new SequenceInputStream(logHeaderStream, logFileStream),
+ uploadCrashDumpStream);
+ }
+
+ InputStreamEntity entity = new InputStreamEntity(uploadCrashDumpStream, -1);
+ entity.setContentType("multipart/form-data; boundary=" + mimeBoundary);
+
+ HttpPost uploadRequest = new HttpPost(mCrashReportUploadUrl);
+ uploadRequest.setEntity(entity);
+ HttpResponse response =
+ httpClient.execute(new HttpHost(CRASH_REPORT_HOST), uploadRequest);
+
+ // Expect a report ID as the entire response
+ String responseLine = getFirstLine(response.getEntity().getContent());
+
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.SC_OK) {
+ Log.e(TAG, "Failed response (" + statusCode + "): " + responseLine);
+ return;
+ }
+
+ Log.d(TAG, "Successfully uploaded as report ID: " + responseLine);
+
+ // Delete the file so we don't re-upload it next time.
+ dumpFileStream.close();
+ dumpFile.delete();
+ } catch (IOException e) {
+ Log.e(TAG, "File I/O error trying to upload crash dump", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Gets the first line from an input stream, opening and closing readers to do so.
+ * We're targeting Java 6, so this is still tedious to do.
+ * @return First line of the input stream.
+ * @throws IOException
+ */
+ private String getFirstLine(InputStream inputStream) throws IOException {
+ InputStreamReader streamReader = null;
+ BufferedReader reader = null;
+ try {
+ streamReader = new InputStreamReader(inputStream);
+ reader = new BufferedReader(streamReader);
+ return reader.readLine();
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ if (streamReader != null) {
+ streamReader.close();
+ }
+ }
+ }
+}
« no previous file with comments | « chromecast/shell/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java ('k') | chromecast/shell/app/DEPS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698