[Breakpad iOS] Add a callback on report upload completion.

This CL adds a result callback on report upload completion.
On failure, Breakpad deletes the configuration file and does retry to
upload a report.
Using this callback, the client will be able to log some metrics and to
act on upload failure.

Bug: 954175
Change-Id: I95a3264b65d4c06ba5d8dde8377440d23f1e2081
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1572661
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Olivier Robin 2019-04-18 18:06:31 +02:00 committed by Mark Mentovai
parent 8c70c504b2
commit 1fc9cc0d0e
6 changed files with 88 additions and 23 deletions

View file

@ -62,6 +62,14 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
mach_port_t crashing_thread, mach_port_t crashing_thread,
void *context); void *context);
// Optional user-defined function that will be called after a network upload
// of a crash report.
// |report_id| will be the id returned by the server, or "ERR" if an error
// occurred.
// |error| will contain the error, or nil if no error occured.
typedef void (*BreakpadUploadCompletionCallback)(NSString *report_id,
NSError *error);
// Create a new BreakpadRef object and install it as an exception // Create a new BreakpadRef object and install it as an exception
// handler. The |parameters| will typically be the contents of your // handler. The |parameters| will typically be the contents of your
// bundle's Info.plist. // bundle's Info.plist.
@ -210,8 +218,10 @@ void BreakpadUploadNextReport(BreakpadRef ref);
// Upload next report to the server. // Upload next report to the server.
// |server_parameters| is additional server parameters to send. // |server_parameters| is additional server parameters to send.
void BreakpadUploadNextReportWithParameters(BreakpadRef ref, void BreakpadUploadNextReportWithParameters(
NSDictionary *server_parameters); BreakpadRef ref,
NSDictionary *server_parameters,
BreakpadUploadCompletionCallback callback);
// Upload a report to the server. // Upload a report to the server.
// |server_parameters| is additional server parameters to send. // |server_parameters| is additional server parameters to send.
@ -219,7 +229,8 @@ void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
void BreakpadUploadReportWithParametersAndConfiguration( void BreakpadUploadReportWithParametersAndConfiguration(
BreakpadRef ref, BreakpadRef ref,
NSDictionary *server_parameters, NSDictionary *server_parameters,
NSDictionary *configuration); NSDictionary *configuration,
BreakpadUploadCompletionCallback callback);
// Handles the network response of a breakpad upload. This function is needed if // Handles the network response of a breakpad upload. This function is needed if
// the actual upload is done by the Breakpad client. // the actual upload is done by the Breakpad client.

View file

@ -164,7 +164,8 @@ class Breakpad {
NSDate *DateOfMostRecentCrashReport(); NSDate *DateOfMostRecentCrashReport();
void UploadNextReport(NSDictionary *server_parameters); void UploadNextReport(NSDictionary *server_parameters);
void UploadReportWithConfiguration(NSDictionary *configuration, void UploadReportWithConfiguration(NSDictionary *configuration,
NSDictionary *server_parameters); NSDictionary *server_parameters,
BreakpadUploadCompletionCallback callback);
void UploadData(NSData *data, NSString *name, void UploadData(NSData *data, NSString *name,
NSDictionary *server_parameters); NSDictionary *server_parameters);
void HandleNetworkResponse(NSDictionary *configuration, void HandleNetworkResponse(NSDictionary *configuration,
@ -502,8 +503,10 @@ void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
} }
//============================================================================= //=============================================================================
void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration, void Breakpad::UploadReportWithConfiguration(
NSDictionary *server_parameters) { NSDictionary *configuration,
NSDictionary *server_parameters,
BreakpadUploadCompletionCallback callback) {
Uploader *uploader = [[[Uploader alloc] Uploader *uploader = [[[Uploader alloc]
initWithConfig:configuration] autorelease]; initWithConfig:configuration] autorelease];
if (!uploader) if (!uploader)
@ -512,6 +515,13 @@ void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
[uploader addServerParameter:[server_parameters objectForKey:key] [uploader addServerParameter:[server_parameters objectForKey:key]
forKey:key]; forKey:key];
} }
if (callback) {
[uploader setUploadCompletionBlock:^(NSString *report_id, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
callback(report_id, error);
});
}];
}
[uploader report]; [uploader report];
} }
@ -519,7 +529,8 @@ void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
void Breakpad::UploadNextReport(NSDictionary *server_parameters) { void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
NSDictionary *configuration = NextCrashReportConfiguration(); NSDictionary *configuration = NextCrashReportConfiguration();
if (configuration) { if (configuration) {
return UploadReportWithConfiguration(configuration, server_parameters); return UploadReportWithConfiguration(configuration, server_parameters,
nullptr);
} }
} }
@ -850,7 +861,7 @@ int BreakpadGetCrashReportCount(BreakpadRef ref) {
//============================================================================= //=============================================================================
void BreakpadUploadNextReport(BreakpadRef ref) { void BreakpadUploadNextReport(BreakpadRef ref) {
BreakpadUploadNextReportWithParameters(ref, nil); BreakpadUploadNextReportWithParameters(ref, nil, nullptr);
} }
//============================================================================= //=============================================================================
@ -882,22 +893,25 @@ NSDate *BreakpadGetDateOfMostRecentCrashReport(BreakpadRef ref) {
void BreakpadUploadReportWithParametersAndConfiguration( void BreakpadUploadReportWithParametersAndConfiguration(
BreakpadRef ref, BreakpadRef ref,
NSDictionary *server_parameters, NSDictionary *server_parameters,
NSDictionary *configuration) { NSDictionary *configuration,
BreakpadUploadCompletionCallback callback) {
try { try {
Breakpad *breakpad = (Breakpad *)ref; Breakpad *breakpad = (Breakpad *)ref;
if (!breakpad || !configuration) if (!breakpad || !configuration)
return; return;
breakpad->UploadReportWithConfiguration(configuration, server_parameters); breakpad->UploadReportWithConfiguration(configuration, server_parameters,
callback);
} catch(...) { // don't let exceptions leave this C API } catch(...) { // don't let exceptions leave this C API
fprintf(stderr, fprintf(stderr,
"BreakpadUploadReportWithParametersAndConfiguration() : error\n"); "BreakpadUploadReportWithParametersAndConfiguration() : error\n");
} }
} }
//============================================================================= //=============================================================================
void BreakpadUploadNextReportWithParameters(BreakpadRef ref, void BreakpadUploadNextReportWithParameters(
NSDictionary *server_parameters) { BreakpadRef ref,
NSDictionary *server_parameters,
BreakpadUploadCompletionCallback callback) {
try { try {
Breakpad *breakpad = (Breakpad *)ref; Breakpad *breakpad = (Breakpad *)ref;
if (!breakpad) if (!breakpad)
@ -905,9 +919,8 @@ void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
NSDictionary *configuration = breakpad->NextCrashReportConfiguration(); NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
if (!configuration) if (!configuration)
return; return;
return BreakpadUploadReportWithParametersAndConfiguration(ref, return BreakpadUploadReportWithParametersAndConfiguration(
server_parameters, ref, server_parameters, configuration, callback);
configuration);
} catch(...) { // don't let exceptions leave this C API } catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n"); fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
} }

View file

@ -66,6 +66,9 @@
// The dictionary that contains additional server parameters to send when // The dictionary that contains additional server parameters to send when
// uploading crash reports. // uploading crash reports.
NSDictionary* uploadTimeParameters_; NSDictionary* uploadTimeParameters_;
// The callback to call on report upload completion.
BreakpadUploadCompletionCallback uploadCompleteCallback_;
} }
// Singleton. // Singleton.
@ -95,6 +98,10 @@
// crash report is generated. See |BreakpadAddUploadParameter|. // crash report is generated. See |BreakpadAddUploadParameter|.
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key; - (void)addUploadParameter:(NSString*)value forKey:(NSString*)key;
// Sets the callback to be called after uploading a crash report to the server.
// Only the latest callback registered will be called.
- (void)setUploadCallback:(BreakpadUploadCompletionCallback)callback;
// Remove a previously-added parameter from the upload parameter set. See // Remove a previously-added parameter from the upload parameter set. See
// |BreakpadRemoveUploadParameter|. // |BreakpadRemoveUploadParameter|.
- (void)removeUploadParameterForKey:(NSString*)key; - (void)removeUploadParameterForKey:(NSString*)key;

View file

@ -166,9 +166,9 @@ NSString* GetPlatform() {
NSAssert(started_, @"The controller must be started before " NSAssert(started_, @"The controller must be started before "
"threadUnsafeSendReportWithConfiguration is called"); "threadUnsafeSendReportWithConfiguration is called");
if (breakpadRef_) { if (breakpadRef_) {
BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_, BreakpadUploadReportWithParametersAndConfiguration(
uploadTimeParameters_, breakpadRef_, uploadTimeParameters_, configuration,
configuration); uploadCompleteCallback_);
} }
} }
@ -195,7 +195,7 @@ NSString* GetPlatform() {
NSAssert(!started_, NSAssert(!started_,
@"The controller must not be started when updateConfiguration is called"); @"The controller must not be started when updateConfiguration is called");
[configuration_ addEntriesFromDictionary:configuration]; [configuration_ addEntriesFromDictionary:configuration];
NSString* uploadInterval = NSString *uploadInterval =
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
if (uploadInterval) if (uploadInterval)
[self setUploadInterval:[uploadInterval intValue]]; [self setUploadInterval:[uploadInterval intValue]];
@ -206,7 +206,7 @@ NSString* GetPlatform() {
@"The controller must not be started when resetConfiguration is called"); @"The controller must not be started when resetConfiguration is called");
[configuration_ autorelease]; [configuration_ autorelease];
configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy]; configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
NSString* uploadInterval = NSString *uploadInterval =
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
[self setUploadInterval:[uploadInterval intValue]]; [self setUploadInterval:[uploadInterval intValue]];
[self setParametersToAddAtUploadTime:nil]; [self setParametersToAddAtUploadTime:nil];
@ -243,6 +243,15 @@ NSString* GetPlatform() {
}); });
} }
- (void)setUploadCallback:(BreakpadUploadCompletionCallback)callback {
NSAssert(started_,
@"The controller must not be started before setUploadCallback is "
"called");
dispatch_async(queue_, ^{
uploadCompleteCallback_ = callback;
});
}
- (void)removeUploadParameterForKey:(NSString*)key { - (void)removeUploadParameterForKey:(NSString*)key {
NSAssert(started_, @"The controller must be started before " NSAssert(started_, @"The controller must be started before "
"removeUploadParameterForKey is called"); "removeUploadParameterForKey is called");
@ -345,8 +354,8 @@ NSString* GetPlatform() {
// A report can be sent now. // A report can be sent now.
if (timeToWait == 0) { if (timeToWait == 0) {
[self reportWillBeSent]; [self reportWillBeSent];
BreakpadUploadNextReportWithParameters(breakpadRef_, BreakpadUploadNextReportWithParameters(breakpadRef_, uploadTimeParameters_,
uploadTimeParameters_); uploadCompleteCallback_);
// If more reports must be sent, make sure this method is called again. // If more reports must be sent, make sure this method is called again.
if (BreakpadGetCrashReportCount(breakpadRef_) > 0) if (BreakpadGetCrashReportCount(breakpadRef_) > 0)

View file

@ -42,6 +42,13 @@ extern NSString *const kGoogleServerType;
extern NSString *const kSocorroServerType; extern NSString *const kSocorroServerType;
extern NSString *const kDefaultServerType; extern NSString *const kDefaultServerType;
// Optional user-defined function that will be called after a network upload
// of a crash report.
// |report_id| will be the id returned by the server, or "ERR" if an error
// occurred.
// |error| will contain the error, or nil if no error occured.
typedef void (^UploadCompletionBlock)(NSString *reportId, NSError *error);
@interface Uploader : NSObject { @interface Uploader : NSObject {
@private @private
NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
@ -61,6 +68,12 @@ extern NSString *const kDefaultServerType;
// that are uploaded to the // that are uploaded to the
// crash server with the // crash server with the
// minidump. // minidump.
UploadCompletionBlock uploadCompletion_; // A block called on network upload
// completion. Parameters are:
// The report ID returned by the
// server,
// the NSError triggered during
// upload.
} }
- (id)initWithConfigFile:(const char *)configFile; - (id)initWithConfigFile:(const char *)configFile;
@ -86,4 +99,8 @@ extern NSString *const kDefaultServerType;
// new ID. // new ID.
- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error; - (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error;
// Sets the callback to be called after uploading a crash report to the server.
// Only the latest callback registered will be called.
- (void)setUploadCompletionBlock:(UploadCompletionBlock)uploadCompletion;
@end @end

View file

@ -511,6 +511,9 @@ NSDictionary *readConfigurationData(const char *configFile) {
reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
[self logUploadWithID:reportID]; [self logUploadWithID:reportID];
} }
if (uploadCompletion_) {
uploadCompletion_([NSString stringWithUTF8String:reportID], error);
}
// rename the minidump file according to the id returned from the server // rename the minidump file according to the id returned from the server
NSString *minidumpDir = NSString *minidumpDir =
@ -547,6 +550,11 @@ NSDictionary *readConfigurationData(const char *configFile) {
return [NSURLQueryItem queryItemWithName:queryItemName value:escapedValue]; return [NSURLQueryItem queryItemWithName:queryItemName value:escapedValue];
} }
//=============================================================================
- (void)setUploadCompletionBlock:(UploadCompletionBlock)uploadCompletion {
uploadCompletion_ = uploadCompletion;
}
//============================================================================= //=============================================================================
- (void)report { - (void)report {
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];