mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-03-08 09:59:55 +00:00
This CL adds three features that will allow the client to upload the report file. Three main modifications are made : - Allow upload url to have a file:// scheme, and write the HTTP request to file in that case - Split the request in two parts in case of a file:// scheme, the request time and the response time. A new API [handleNetworkResponse] is added. - Give the opportunity to the client to get the configuration NSDictionary to be able to recreate the breakpad context at response time. Patch by Olivier Robin <olivierrobin@chromium.org> Review URL: https://breakpad.appspot.com/2764002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1368 4c0a9323-5329-0410-9bdc-e9ce6186880e
355 lines
11 KiB
Plaintext
355 lines
11 KiB
Plaintext
// Copyright (c) 2012, Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#import "BreakpadController.h"
|
|
|
|
#import <UIKit/UIKit.h>
|
|
#include <asl.h>
|
|
#include <execinfo.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <common/scoped_ptr.h>
|
|
|
|
#pragma mark -
|
|
#pragma mark Private Methods
|
|
|
|
@interface BreakpadController ()
|
|
|
|
// Init the singleton instance.
|
|
- (id)initSingleton;
|
|
|
|
// Load a crash report and send it to the server.
|
|
- (void)sendStoredCrashReports;
|
|
|
|
// Returns when a report can be sent. |-1| means never, |0| means that a report
|
|
// can be sent immediately, a positive number is the number of seconds to wait
|
|
// before being allowed to upload a report.
|
|
- (int)sendDelay;
|
|
|
|
// Notifies that a report will be sent, and update the last sending time
|
|
// accordingly.
|
|
- (void)reportWillBeSent;
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
#pragma mark Anonymous namespace
|
|
|
|
namespace {
|
|
|
|
// The name of the user defaults key for the last submission to the crash
|
|
// server.
|
|
NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission";
|
|
|
|
// Returns a NSString describing the current platform.
|
|
NSString* GetPlatform() {
|
|
// Name of the system call for getting the platform.
|
|
static const char kHwMachineSysctlName[] = "hw.machine";
|
|
|
|
NSString* result = nil;
|
|
|
|
size_t size = 0;
|
|
if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0)
|
|
return nil;
|
|
google_breakpad::scoped_array<char> machine(new char[size]);
|
|
if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0)
|
|
result = [NSString stringWithUTF8String:machine.get()];
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#pragma mark -
|
|
#pragma mark BreakpadController Implementation
|
|
|
|
@implementation BreakpadController
|
|
|
|
+ (BreakpadController*)sharedInstance {
|
|
@synchronized(self) {
|
|
static BreakpadController* sharedInstance_ =
|
|
[[BreakpadController alloc] initSingleton];
|
|
return sharedInstance_;
|
|
}
|
|
}
|
|
|
|
- (id)init {
|
|
return nil;
|
|
}
|
|
|
|
- (id)initSingleton {
|
|
self = [super init];
|
|
if (self) {
|
|
queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL);
|
|
enableUploads_ = NO;
|
|
started_ = NO;
|
|
[self resetConfiguration];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
// Since this class is a singleton, this method is not expected to be called.
|
|
- (void)dealloc {
|
|
assert(!breakpadRef_);
|
|
dispatch_release(queue_);
|
|
[configuration_ release];
|
|
[uploadTimeParameters_ release];
|
|
[super dealloc];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)start:(BOOL)onCurrentThread {
|
|
if (started_)
|
|
return;
|
|
started_ = YES;
|
|
void(^startBlock)() = ^{
|
|
assert(!breakpadRef_);
|
|
breakpadRef_ = BreakpadCreate(configuration_);
|
|
if (breakpadRef_) {
|
|
BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform());
|
|
}
|
|
};
|
|
if (onCurrentThread)
|
|
startBlock();
|
|
else
|
|
dispatch_async(queue_, startBlock);
|
|
}
|
|
|
|
- (void)stop {
|
|
if (!started_)
|
|
return;
|
|
started_ = NO;
|
|
dispatch_sync(queue_, ^{
|
|
if (breakpadRef_) {
|
|
BreakpadRelease(breakpadRef_);
|
|
breakpadRef_ = NULL;
|
|
}
|
|
});
|
|
}
|
|
|
|
// This method must be called from the breakpad queue.
|
|
- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
|
|
withBreakpadRef:(BreakpadRef)ref {
|
|
NSAssert(started_, @"The controller must be started before "
|
|
"threadUnsafeSendReportWithConfiguration is called");
|
|
if (breakpadRef_) {
|
|
BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_,
|
|
uploadTimeParameters_,
|
|
configuration);
|
|
}
|
|
}
|
|
|
|
- (void)setUploadingEnabled:(BOOL)enabled {
|
|
NSAssert(started_,
|
|
@"The controller must be started before setUploadingEnabled is called");
|
|
dispatch_async(queue_, ^{
|
|
if (enabled == enableUploads_)
|
|
return;
|
|
if (enabled) {
|
|
// Set this before calling doSendStoredCrashReport, because that
|
|
// calls sendDelay, which in turn checks this flag.
|
|
enableUploads_ = YES;
|
|
[self sendStoredCrashReports];
|
|
} else {
|
|
enableUploads_ = NO;
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
|
selector:@selector(sendStoredCrashReports)
|
|
object:nil];
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)updateConfiguration:(NSDictionary*)configuration {
|
|
NSAssert(!started_,
|
|
@"The controller must not be started when updateConfiguration is called");
|
|
[configuration_ addEntriesFromDictionary:configuration];
|
|
NSString* uploadInterval =
|
|
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
|
if (uploadInterval)
|
|
[self setUploadInterval:[uploadInterval intValue]];
|
|
}
|
|
|
|
- (void)resetConfiguration {
|
|
NSAssert(!started_,
|
|
@"The controller must not be started when resetConfiguration is called");
|
|
[configuration_ autorelease];
|
|
configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
|
|
NSString* uploadInterval =
|
|
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
|
|
[self setUploadInterval:[uploadInterval intValue]];
|
|
[self setParametersToAddAtUploadTime:nil];
|
|
}
|
|
|
|
- (void)setUploadingURL:(NSString*)url {
|
|
NSAssert(!started_,
|
|
@"The controller must not be started when setUploadingURL is called");
|
|
[configuration_ setValue:url forKey:@BREAKPAD_URL];
|
|
}
|
|
|
|
- (void)setUploadInterval:(int)intervalInSeconds {
|
|
NSAssert(!started_,
|
|
@"The controller must not be started when setUploadInterval is called");
|
|
[configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL];
|
|
uploadIntervalInSeconds_ = intervalInSeconds;
|
|
if (uploadIntervalInSeconds_ < 0)
|
|
uploadIntervalInSeconds_ = 0;
|
|
}
|
|
|
|
- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters {
|
|
NSAssert(!started_, @"The controller must not be started when "
|
|
"setParametersToAddAtUploadTime is called");
|
|
[uploadTimeParameters_ autorelease];
|
|
uploadTimeParameters_ = [uploadTimeParameters copy];
|
|
}
|
|
|
|
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key {
|
|
NSAssert(started_,
|
|
@"The controller must be started before addUploadParameter is called");
|
|
dispatch_async(queue_, ^{
|
|
if (breakpadRef_)
|
|
BreakpadAddUploadParameter(breakpadRef_, key, value);
|
|
});
|
|
}
|
|
|
|
- (void)removeUploadParameterForKey:(NSString*)key {
|
|
NSAssert(started_, @"The controller must be started before "
|
|
"removeUploadParameterForKey is called");
|
|
dispatch_async(queue_, ^{
|
|
if (breakpadRef_)
|
|
BreakpadRemoveUploadParameter(breakpadRef_, key);
|
|
});
|
|
}
|
|
|
|
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback {
|
|
NSAssert(started_,
|
|
@"The controller must be started before withBreakpadRef is called");
|
|
dispatch_async(queue_, ^{
|
|
callback(breakpadRef_);
|
|
});
|
|
}
|
|
|
|
- (void)hasReportToUpload:(void(^)(BOOL))callback {
|
|
NSAssert(started_, @"The controller must be started before "
|
|
"hasReportToUpload is called");
|
|
dispatch_async(queue_, ^{
|
|
callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0));
|
|
});
|
|
}
|
|
|
|
- (void)getCrashReportCount:(void(^)(int))callback {
|
|
NSAssert(started_, @"The controller must be started before "
|
|
"getCrashReportCount is called");
|
|
dispatch_async(queue_, ^{
|
|
callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0);
|
|
});
|
|
}
|
|
|
|
- (void)getNextReportConfigurationOrSendDelay:
|
|
(void(^)(NSDictionary*, int))callback {
|
|
NSAssert(started_, @"The controller must be started before "
|
|
"getNextReportConfigurationOrSendDelay is called");
|
|
dispatch_async(queue_, ^{
|
|
if (!breakpadRef_) {
|
|
callback(nil, -1);
|
|
return;
|
|
}
|
|
int delay = [self sendDelay];
|
|
if (delay != 0) {
|
|
callback(nil, delay);
|
|
return;
|
|
}
|
|
[self reportWillBeSent];
|
|
callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0);
|
|
});
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (int)sendDelay {
|
|
if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_)
|
|
return -1;
|
|
|
|
// To prevent overloading the crash server, crashes are not sent than one
|
|
// report every |uploadIntervalInSeconds_|. A value in the user defaults is
|
|
// used to keep the time of the last upload.
|
|
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
|
NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission];
|
|
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
|
|
NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime;
|
|
|
|
if (spanSeconds >= uploadIntervalInSeconds_)
|
|
return 0;
|
|
return uploadIntervalInSeconds_ - static_cast<int>(spanSeconds);
|
|
}
|
|
|
|
- (void)reportWillBeSent {
|
|
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
|
[userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()]
|
|
forKey:kLastSubmission];
|
|
[userDefaults synchronize];
|
|
}
|
|
|
|
- (void)sendStoredCrashReports {
|
|
dispatch_async(queue_, ^{
|
|
if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
|
|
return;
|
|
|
|
int timeToWait = [self sendDelay];
|
|
|
|
// Unable to ever send report.
|
|
if (timeToWait == -1)
|
|
return;
|
|
|
|
// A report can be sent now.
|
|
if (timeToWait == 0) {
|
|
[self reportWillBeSent];
|
|
BreakpadUploadNextReportWithParameters(breakpadRef_,
|
|
uploadTimeParameters_);
|
|
|
|
// If more reports must be sent, make sure this method is called again.
|
|
if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
|
|
timeToWait = uploadIntervalInSeconds_;
|
|
}
|
|
|
|
// A report must be sent later.
|
|
if (timeToWait > 0) {
|
|
// performSelector: doesn't work on queue_
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self performSelector:@selector(sendStoredCrashReports)
|
|
withObject:nil
|
|
afterDelay:timeToWait];
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
@end
|