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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java

Issue 2737263006: [Android Crash Reporting] Allow uploading minidumps via the JobScheduler (Closed)
Patch Set: Assert that job scheduled successfully Created 3 years, 9 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.crash; 5 package org.chromium.chrome.browser.crash;
6 6
7 import android.annotation.SuppressLint;
7 import android.app.IntentService; 8 import android.app.IntentService;
9 import android.app.job.JobInfo;
10 import android.content.ComponentName;
8 import android.content.Context; 11 import android.content.Context;
9 import android.content.Intent; 12 import android.content.Intent;
13 import android.os.Build;
14 import android.os.PersistableBundle;
10 import android.support.annotation.StringDef; 15 import android.support.annotation.StringDef;
11 16
12 import org.chromium.base.Log; 17 import org.chromium.base.Log;
13 import org.chromium.base.StreamUtil; 18 import org.chromium.base.StreamUtil;
14 import org.chromium.base.VisibleForTesting; 19 import org.chromium.base.VisibleForTesting;
15 import org.chromium.base.annotations.CalledByNative; 20 import org.chromium.base.annotations.CalledByNative;
16 import org.chromium.base.metrics.RecordHistogram; 21 import org.chromium.base.metrics.RecordHistogram;
22 import org.chromium.chrome.browser.ChromeFeatureList;
17 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; 23 import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
18 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager ; 24 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager ;
25 import org.chromium.components.background_task_scheduler.TaskIds;
19 import org.chromium.components.minidump_uploader.CrashFileManager; 26 import org.chromium.components.minidump_uploader.CrashFileManager;
20 import org.chromium.components.minidump_uploader.MinidumpUploadCallable; 27 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
28 import org.chromium.components.minidump_uploader.MinidumpUploadJobService;
21 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager; 29 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionMa nager;
22 30
23 import java.io.BufferedReader; 31 import java.io.BufferedReader;
24 import java.io.File; 32 import java.io.File;
25 import java.io.FileReader; 33 import java.io.FileReader;
26 import java.io.IOException; 34 import java.io.IOException;
27 35
28 /** 36 /**
29 * Service that is responsible for uploading crash minidumps to the Google crash server. 37 * Service that is responsible for uploading crash minidumps to the Google crash server.
30 */ 38 */
(...skipping 30 matching lines...) Expand all
61 static final String OTHER = "Other"; 69 static final String OTHER = "Other";
62 70
63 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER}; 71 static final String[] TYPES = {BROWSER, RENDERER, GPU, OTHER};
64 72
65 public MinidumpUploadService() { 73 public MinidumpUploadService() {
66 super(TAG); 74 super(TAG);
67 setIntentRedelivery(true); 75 setIntentRedelivery(true);
68 } 76 }
69 77
70 /** 78 /**
79 * @return Whether to use the JobSchduler API to upload crash reports, rathe r than directly
80 * creating a service for uploading.
81 */
82 public static boolean shouldUseJobSchedulerForUploads() {
83 // The JobScheduler API is only available as of Android M.
84 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
85
86 return ChromeFeatureList.isEnabled(
87 ChromeFeatureList.UPLOAD_CRASH_REPORTS_USING_JOB_SCHEDULER);
88 }
89
90 /**
91 * Schedules uploading of all pending minidumps, using the JobScheduler API.
92 */
93 @SuppressLint("NewApi")
94 public static void scheduleUploadJob(Context context) {
95 assert shouldUseJobSchedulerForUploads();
96
97 CrashReportingPermissionManager permissionManager = PrivacyPreferencesMa nager.getInstance();
98 PersistableBundle permissions = new PersistableBundle();
99 permissions.putBoolean(ChromeMinidumpUploaderDelegate.IS_CLIENT_IN_METRI CS_SAMPLE,
100 permissionManager.isClientInMetricsSample());
101 permissions.putBoolean(
102 ChromeMinidumpUploaderDelegate.IS_CRASH_UPLOAD_DISABLED_BY_COMMA ND_LINE,
103 permissionManager.isCrashUploadDisabledByCommandLine());
104 permissions.putBoolean(ChromeMinidumpUploaderDelegate.IS_UPLOAD_ENABLED_ FOR_TESTS,
105 permissionManager.isUploadEnabledForTests());
106
107 JobInfo.Builder builder =
108 new JobInfo
109 .Builder(TaskIds.CHROME_MINIDUMP_UPLOADING_JOB_ID,
110 new ComponentName(context, ChromeMinidumpUploadJ obService.class))
111 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
112 .setExtras(permissions);
113 MinidumpUploadJobService.scheduleUpload(context, builder);
114 }
115
116 /**
71 * Stores the successes and failures from uploading crash to UMA, 117 * Stores the successes and failures from uploading crash to UMA,
72 */ 118 */
73 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) { 119 public static void storeBreakpadUploadStatsInUma(ChromePreferenceManager pre f) {
74 for (String type : TYPES) { 120 for (String type : TYPES) {
75 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) { 121 for (int success = pref.getCrashSuccessUploadCount(type); success > 0; success--) {
76 RecordHistogram.recordEnumeratedHistogram( 122 RecordHistogram.recordEnumeratedHistogram(
77 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX); 123 HISTOGRAM_NAME_PREFIX + type, SUCCESS, HISTOGRAM_MAX);
78 } 124 }
79 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) { 125 for (int fail = pref.getCrashFailureUploadCount(type); fail > 0; fai l--) {
80 RecordHistogram.recordEnumeratedHistogram( 126 RecordHistogram.recordEnumeratedHistogram(
(...skipping 18 matching lines...) Expand all
99 Log.w(TAG, "Cannot upload crash data since minidump is absent."); 145 Log.w(TAG, "Cannot upload crash data since minidump is absent.");
100 return; 146 return;
101 } 147 }
102 File minidumpFile = new File(minidumpFileName); 148 File minidumpFile = new File(minidumpFileName);
103 if (!minidumpFile.isFile()) { 149 if (!minidumpFile.isFile()) {
104 Log.w(TAG, "Cannot upload crash data since specified minidump " 150 Log.w(TAG, "Cannot upload crash data since specified minidump "
105 + minidumpFileName + " is not present."); 151 + minidumpFileName + " is not present.");
106 return; 152 return;
107 } 153 }
108 int tries = CrashFileManager.readAttemptNumber(minidumpFileName); 154 int tries = CrashFileManager.readAttemptNumber(minidumpFileName);
109 // -1 means no attempt number was read.
110 if (tries == -1) {
111 tries = 0;
112 }
113 155
114 // Since we do not rename a file after reaching max number of tries, 156 // Since we do not rename a file after reaching max number of tries,
115 // files that have maxed out tries will NOT reach this. 157 // files that have maxed out tries will NOT reach this.
116 if (tries >= MAX_TRIES_ALLOWED || tries < 0) { 158 if (tries >= MAX_TRIES_ALLOWED || tries < 0) {
117 // Reachable only if the file naming is incorrect by current standar d. 159 // Reachable only if the file naming is incorrect by current standar d.
118 // Thus we log an error instead of recording failure to UMA. 160 // Thus we log an error instead of recording failure to UMA.
119 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName 161 Log.e(TAG, "Giving up on trying to upload " + minidumpFileName
120 + " after failing to read a valid attempt number."); 162 + " after failing to read a valid attempt number.");
121 return; 163 return;
122 } 164 }
123 165
124 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY); 166 String logfileName = intent.getStringExtra(UPLOAD_LOG_KEY);
125 File logfile = new File(logfileName); 167 File logfile = new File(logfileName);
126 168
127 // Try to upload minidump 169 // Try to upload minidump
128 MinidumpUploadCallable minidumpUploadCallable = 170 MinidumpUploadCallable minidumpUploadCallable =
129 createMinidumpUploadCallable(minidumpFile, logfile); 171 createMinidumpUploadCallable(minidumpFile, logfile);
130 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus = 172 @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus =
131 minidumpUploadCallable.call(); 173 minidumpUploadCallable.call();
132 174
133 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) { 175 if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) {
134 // Only update UMA stats if an intended and successful upload. 176 // Only update UMA stats if an intended and successful upload.
135 incrementCrashSuccessUploadCount(getNewNameAfterSuccessfulUpload(min idumpFileName)); 177 incrementCrashSuccessUploadCount(minidumpFileName);
136 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) { 178 } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) {
137 // Unable to upload minidump. Incrementing try number and restarting . 179 // Unable to upload minidump. Incrementing try number and restarting .
180 ++tries;
181 if (tries == MAX_TRIES_ALLOWED) {
182 // Only record failure to UMA after we have maxed out the allott ed tries.
183 incrementCrashFailureUploadCount(minidumpFileName);
184 }
138 185
139 // Only create another attempt if we have successfully renamed 186 // Only create another attempt if we have successfully renamed the f ile.
140 // the file.
141 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File); 187 String newName = CrashFileManager.tryIncrementAttemptNumber(minidump File);
142 if (newName != null) { 188 if (newName != null) {
143 if (++tries < MAX_TRIES_ALLOWED) { 189 if (tries < MAX_TRIES_ALLOWED) {
144 // TODO(nyquist): Do this as an exponential backoff. 190 // TODO(nyquist): Do this as an exponential backoff.
145 MinidumpUploadRetry.scheduleRetry( 191 MinidumpUploadRetry.scheduleRetry(
146 getApplicationContext(), getCrashReportingPermission Manager()); 192 getApplicationContext(), getCrashReportingPermission Manager());
147 } else { 193 } else {
148 // Only record failure to UMA after we have maxed out the al lotted tries.
149 incrementCrashFailureUploadCount(newName);
150 Log.d(TAG, "Giving up on trying to upload " + minidumpFileNa me + "after " 194 Log.d(TAG, "Giving up on trying to upload " + minidumpFileNa me + "after "
151 + tries + " number of tries."); 195 + tries + " number of tries.");
152 } 196 }
153 } else { 197 } else {
154 Log.w(TAG, "Failed to rename minidump " + minidumpFileName); 198 Log.w(TAG, "Failed to rename minidump " + minidumpFileName);
155 } 199 }
156 } 200 }
157 } 201 }
158 202
159 /** 203 /**
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 } 245 }
202 } catch (IOException e) { 246 } catch (IOException e) {
203 Log.w(TAG, "Error while reading crash file %s: %s", fileName, e.toSt ring()); 247 Log.w(TAG, "Error while reading crash file %s: %s", fileName, e.toSt ring());
204 } finally { 248 } finally {
205 StreamUtil.closeQuietly(fileReader); 249 StreamUtil.closeQuietly(fileReader);
206 } 250 }
207 return OTHER; 251 return OTHER;
208 } 252 }
209 253
210 /** 254 /**
211 * Increment the count of success/failure by 1 and distinguish between diffe rent types of 255 * Increments the count of successful uploads by 1. Distinguishes between di fferent types of
212 * crashes by looking into the file. 256 * crashes by looking into the file contents. Because this code can execute in a context when
213 * @param fileName is the name of a minidump file that contains the type of crash. 257 * the main Chrome activity is no longer running, the counts are stored in s hared preferences;
258 * they are later read and recorded as metrics by the main Chrome activity.
259 * NOTE: This method should be called *after* renaming the file, since renam ing occurs as a
260 * side-effect of a successful upload.
261 * @param originalFilename The name of the successfully uploaded minidump, * prior* to uploading.
214 */ 262 */
215 private void incrementCrashSuccessUploadCount(String fileName) { 263 public static void incrementCrashSuccessUploadCount(String originalFilename) {
216 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount( 264 ChromePreferenceManager.getInstance().incrementCrashSuccessUploadCount(
217 getCrashType(fileName)); 265 getCrashType(getNewNameAfterSuccessfulUpload(originalFilename))) ;
218 }
219
220 private void incrementCrashFailureUploadCount(String fileName) {
221 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount(
222 getCrashType(fileName));
223 } 266 }
224 267
225 /** 268 /**
269 * Increments the count of failed uploads by 1. Distinguishes between differ ent types of crashes
270 * by looking into the file contents. Because this code can execute in a con text when the main
271 * Chrome activity is no longer running, the counts are stored in shared pre ferences; they are
272 * later read and recorded as metrics by the main Chrome activity.
273 * NOTE: This method should be called *prior* to renaming the file.
274 * @param originalFilename The name of the successfully uploaded minidump, * prior* to uploading.
275 */
276 public static void incrementCrashFailureUploadCount(String originalFilename) {
277 ChromePreferenceManager.getInstance().incrementCrashFailureUploadCount(
278 getCrashType(originalFilename));
279 }
280
281 /**
226 * Factory method for creating minidump callables. 282 * Factory method for creating minidump callables.
227 * 283 *
228 * This may be overridden for tests. 284 * This may be overridden for tests.
229 * 285 *
230 * @param minidumpFile the File to upload. 286 * @param minidumpFile the File to upload.
231 * @param logfile the Log file to write to upon successful uploads. 287 * @param logfile the Log file to write to upon successful uploads.
232 * @return a new MinidumpUploadCallable. 288 * @return a new MinidumpUploadCallable.
233 */ 289 */
234 @VisibleForTesting 290 @VisibleForTesting
235 MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFile, File logfile) { 291 MinidumpUploadCallable createMinidumpUploadCallable(File minidumpFile, File logfile) {
236 return new MinidumpUploadCallable( 292 return new MinidumpUploadCallable(
237 minidumpFile, logfile, getCrashReportingPermissionManager()); 293 minidumpFile, logfile, getCrashReportingPermissionManager());
238 } 294 }
239 295
240 /** 296 /**
241 * Attempts to upload the specified {@param minidumpFile}. 297 * Attempts to upload the specified {@param minidumpFile}.
242 * 298 *
243 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will 299 * Note that this method is asynchronous. All that is guaranteed is that an upload attempt will
244 * be enqueued. 300 * be enqueued.
245 * 301 *
246 * @param context The application context, in which to initiate the crash re port upload. 302 * @param context The application context, in which to initiate the crash re port upload.
247 * @throws A security excpetion if the caller doesn't have permission to sta rt the upload 303 * @throws A security excpetion if the caller doesn't have permission to sta rt the upload
248 * service. This can only happen on KitKat and below, due to a frame work bug. 304 * service. This can only happen on KitKat and below, due to a frame work bug.
249 */ 305 */
250 public static void tryUploadCrashDump(Context context, File minidumpFile) 306 public static void tryUploadCrashDump(Context context, File minidumpFile)
251 throws SecurityException { 307 throws SecurityException {
308 assert !shouldUseJobSchedulerForUploads();
252 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 309 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
253 Intent intent = new Intent(context, MinidumpUploadService.class); 310 Intent intent = new Intent(context, MinidumpUploadService.class);
254 intent.setAction(ACTION_UPLOAD); 311 intent.setAction(ACTION_UPLOAD);
255 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath()); 312 intent.putExtra(FILE_TO_UPLOAD_KEY, minidumpFile.getAbsolutePath());
256 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath()); 313 intent.putExtra(UPLOAD_LOG_KEY, fileManager.getCrashUploadLogFile().getA bsolutePath());
257 context.startService(intent); 314 context.startService(intent);
258 } 315 }
259 316
260 /** 317 /**
261 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}. 318 * Attempts to upload all minidump files using the given {@link android.cont ent.Context}.
262 * 319 *
263 * Note that this method is asynchronous. All that is guaranteed is that 320 * Note that this method is asynchronous. All that is guaranteed is that
264 * upload attempts will be enqueued. 321 * upload attempts will be enqueued.
265 * 322 *
266 * This method is safe to call from the UI thread. 323 * This method is safe to call from the UI thread.
267 * 324 *
268 * @param context Context of the application. 325 * @param context Context of the application.
269 */ 326 */
270 public static void tryUploadAllCrashDumps(Context context) { 327 public static void tryUploadAllCrashDumps(Context context) {
328 assert !shouldUseJobSchedulerForUploads();
271 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( )); 329 CrashFileManager fileManager = new CrashFileManager(context.getCacheDir( ));
272 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED); 330 File[] minidumps = fileManager.getAllMinidumpFiles(MAX_TRIES_ALLOWED);
273 Log.i(TAG, "Attempting to upload accumulated crash dumps."); 331 Log.i(TAG, "Attempting to upload accumulated crash dumps.");
274 for (File minidump : minidumps) { 332 for (File minidump : minidumps) {
275 MinidumpUploadService.tryUploadCrashDump(context, minidump); 333 tryUploadCrashDump(context, minidump);
276 } 334 }
277 } 335 }
278 336
279 /** 337 /**
280 * Attempts to upload the crash report with the given local ID. 338 * Attempts to upload the crash report with the given local ID.
281 * 339 *
282 * Note that this method is asynchronous. All that is guaranteed is that 340 * Note that this method is asynchronous. All that is guaranteed is that
283 * upload attempts will be enqueued. 341 * upload attempts will be enqueued.
284 * 342 *
285 * This method is safe to call from the UI thread. 343 * This method is safe to call from the UI thread.
(...skipping 12 matching lines...) Expand all
298 File minidumpFile = fileManager.getCrashFileWithLocalId(localId); 356 File minidumpFile = fileManager.getCrashFileWithLocalId(localId);
299 if (minidumpFile == null) { 357 if (minidumpFile == null) {
300 Log.w(TAG, "Could not find a crash dump with local ID " + localId); 358 Log.w(TAG, "Could not find a crash dump with local ID " + localId);
301 return; 359 return;
302 } 360 }
303 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile); 361 File renamedMinidumpFile = CrashFileManager.trySetForcedUpload(minidumpF ile);
304 if (renamedMinidumpFile == null) { 362 if (renamedMinidumpFile == null) {
305 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload"); 363 Log.w(TAG, "Could not rename the file " + minidumpFile.getName() + " for re-upload");
306 return; 364 return;
307 } 365 }
308 MinidumpUploadService.tryUploadCrashDump(context, renamedMinidumpFile); 366
367 if (shouldUseJobSchedulerForUploads()) {
368 scheduleUploadJob(context);
369 } else {
370 tryUploadCrashDump(context, renamedMinidumpFile);
371 }
309 } 372 }
310 } 373 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698