aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorqsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-11-16 17:04:28 +0000
committerqsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-11-16 17:04:28 +0000
commit976930db64c56d476b02ebe86602a9600248ac7f (patch)
treeeb0f6ac5c12fdb44b7419fa58bc29105b0689ab2 /src
parentFix several error-case leaks on the Mac found by clang analysis (diff)
downloadbreakpad-976930db64c56d476b02ebe86602a9600248ac7f.tar.xz
Add an API to Breakpad to upload custom file to the crash server.
On iOS, sending logs using the usual breakpad behavior is not possible, because tar is not available. This allow to use Breakpad to send any file to the crash server. R=mark@chromium.org Review URL: http://breakpad.appspot.com/327001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@885 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/client/ios/Breakpad.h5
-rw-r--r--src/client/ios/Breakpad.mm35
-rw-r--r--src/client/mac/Breakpad.xcodeproj/project.pbxproj8
-rw-r--r--src/client/mac/sender/uploader.h10
-rw-r--r--src/client/mac/sender/uploader.mm (renamed from src/client/mac/sender/uploader.m)226
5 files changed, 178 insertions, 106 deletions
diff --git a/src/client/ios/Breakpad.h b/src/client/ios/Breakpad.h
index 685dea32..1483e860 100644
--- a/src/client/ios/Breakpad.h
+++ b/src/client/ios/Breakpad.h
@@ -196,6 +196,11 @@ bool BreakpadHasCrashReportToUpload(BreakpadRef ref);
// Upload next report to the server.
void BreakpadUploadNextReport(BreakpadRef ref);
+// Upload a file to the server. |data| is the content of the file to sent.
+// |server_parameters| is additional server parameters to send.
+void BreakpadUploadData(BreakpadRef ref, NSData *data,
+ NSDictionary *server_parameters);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/client/ios/Breakpad.mm b/src/client/ios/Breakpad.mm
index d2f5652f..0b9cabbb 100644
--- a/src/client/ios/Breakpad.mm
+++ b/src/client/ios/Breakpad.mm
@@ -152,6 +152,7 @@ class Breakpad {
void RemoveKeyValue(NSString *key);
NSString *NextCrashReportToUpload();
void UploadNextReport();
+ void UploadData(NSData *data, NSDictionary *server_parameters);
private:
Breakpad()
@@ -426,6 +427,25 @@ void Breakpad::UploadNextReport() {
}
//=============================================================================
+void Breakpad::UploadData(NSData *data, NSDictionary *server_parameters) {
+ NSMutableDictionary *config = [NSMutableDictionary dictionary];
+
+ SimpleStringDictionaryIterator it(*config_params_);
+ while (const KeyValueEntry *next = it.Next()) {
+ [config setValue:[NSString stringWithUTF8String:next->GetValue()]
+ forKey:[NSString stringWithUTF8String:next->GetKey()]];
+ }
+
+ Uploader *uploader =
+ [[[Uploader alloc] initWithConfig:config] autorelease];
+ for (NSString *key in server_parameters) {
+ [uploader addServerParameter:[server_parameters objectForKey:key]
+ forKey:key];
+ }
+ [uploader uploadData:data];
+}
+
+//=============================================================================
bool Breakpad::HandleMinidump(const char *dump_dir,
const char *minidump_id) {
DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n");
@@ -681,3 +701,18 @@ void BreakpadUploadNextReport(BreakpadRef ref) {
fprintf(stderr, "BreakpadUploadNextReport() : error\n");
}
}
+
+//=============================================================================
+void BreakpadUploadData(BreakpadRef ref, NSData *data,
+ NSDictionary *server_parameters) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad) {
+ breakpad->UploadData(data, server_parameters);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadUploadData() : error\n");
+ }
+}
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
index c927c7a7..848d4f52 100644
--- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -38,8 +38,8 @@
163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 163201D41443019E00C4DBF5 /* ConfigFile.h */; };
163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; };
163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; };
- 163202451443201300C4DBF5 /* uploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 163202441443201300C4DBF5 /* uploader.m */; };
1632058314442E9000C4DBF5 /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 1632058214442E9000C4DBF5 /* BreakpadDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 16E02DB8147410F0008C604D /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16E02DB4147410D4008C604D /* uploader.mm */; };
3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; };
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; };
@@ -552,8 +552,8 @@
163201D41443019E00C4DBF5 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigFile.h; path = crash_generation/ConfigFile.h; sourceTree = "<group>"; };
163201D51443019E00C4DBF5 /* ConfigFile.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ConfigFile.mm; path = crash_generation/ConfigFile.mm; sourceTree = "<group>"; };
163202431443201300C4DBF5 /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uploader.h; path = sender/uploader.h; sourceTree = "<group>"; };
- 163202441443201300C4DBF5 /* uploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = uploader.m; path = sender/uploader.m; sourceTree = "<group>"; };
1632058214442E9000C4DBF5 /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpadDefines.h; path = Framework/BreakpadDefines.h; sourceTree = "<group>"; };
+ 16E02DB4147410D4008C604D /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = uploader.mm; path = sender/uploader.mm; sourceTree = "<group>"; };
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
3329D4EC0FA16D820007BBC5 /* Breakpad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Breakpad.xib; path = sender/Breakpad.xib; sourceTree = "<group>"; };
33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -998,8 +998,8 @@
F92C56A60ECE04B6009BE4BA /* sender */ = {
isa = PBXGroup;
children = (
+ 16E02DB4147410D4008C604D /* uploader.mm */,
163202431443201300C4DBF5 /* uploader.h */,
- 163202441443201300C4DBF5 /* uploader.m */,
F9B6309F100FF96B00D0F4AC /* goArrow.png */,
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
@@ -1741,7 +1741,7 @@
F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */,
F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */,
F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */,
- 163202451443201300C4DBF5 /* uploader.m in Sources */,
+ 16E02DB8147410F0008C604D /* uploader.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/src/client/mac/sender/uploader.h b/src/client/mac/sender/uploader.h
index af1c19fc..318165c9 100644
--- a/src/client/mac/sender/uploader.h
+++ b/src/client/mac/sender/uploader.h
@@ -44,7 +44,6 @@ extern NSString *const kDefaultServerType;
@interface Uploader : NSObject {
@private
- int configFile_; // File descriptor for config file
NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
NSData *minidumpContents_; // The data in the minidump (STRONG)
NSData *logFileData_; // An NSdata for the tar,
@@ -66,8 +65,17 @@ extern NSString *const kDefaultServerType;
- (id)initWithConfigFile:(const char *)configFile;
+- (id)initWithConfig:(NSDictionary *)config;
+
- (NSMutableDictionary *)parameters;
- (void)report;
+// Upload the given data to the crash server.
+- (void)uploadData:(NSData *)data name:(NSString *)name;
+
+// This method adds a key/value pair to the dictionary that
+// will be uploaded to the crash server.
+- (void)addServerParameter:(id)value forKey:(NSString *)key;
+
@end
diff --git a/src/client/mac/sender/uploader.m b/src/client/mac/sender/uploader.mm
index f215018e..0e084fb1 100644
--- a/src/client/mac/sender/uploader.m
+++ b/src/client/mac/sender/uploader.mm
@@ -52,12 +52,99 @@ NSString *const kDefaultServerType = @"google";
#pragma mark -
+namespace {
+// Read one line from the configuration file.
+NSString *readString(int fileId) {
+ NSMutableString *str = [NSMutableString stringWithCapacity:32];
+ char ch[2] = { 0 };
+
+ while (read(fileId, &ch[0], 1) == 1) {
+ if (ch[0] == '\n') {
+ // Break if this is the first newline after reading some other string
+ // data.
+ if ([str length])
+ break;
+ } else {
+ [str appendString:[NSString stringWithUTF8String:ch]];
+ }
+ }
+
+ return str;
+}
+
+//=============================================================================
+// Read |length| of binary data from the configuration file. This method will
+// returns |nil| in case of error.
+NSData *readData(int fileId, ssize_t length) {
+ NSMutableData *data = [NSMutableData dataWithLength:length];
+ char *bytes = (char *)[data bytes];
+
+ if (read(fileId, bytes, length) != length)
+ return nil;
+
+ return data;
+}
+
+//=============================================================================
+// Read the configuration from the config file.
+NSDictionary *readConfigurationData(const char *configFile) {
+ int fileId = open(configFile, O_RDONLY, 0600);
+ if (fileId == -1) {
+ GTMLoggerDebug(@"Couldn't open config file %s - %s",
+ configFile,
+ strerror(errno));
+ }
+
+ // we want to avoid a build-up of old config files even if they
+ // have been incorrectly written by the framework
+ if (unlink(configFile)) {
+ GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
+ configFile,
+ strerror(errno));
+ }
+
+ if (fileId == -1) {
+ return nil;
+ }
+
+ NSMutableDictionary *config = [NSMutableDictionary dictionary];
+
+ while (1) {
+ NSString *key = readString(fileId);
+
+ if (![key length])
+ break;
+
+ // Read the data. Try to convert to a UTF-8 string, or just save
+ // the data
+ NSString *lenStr = readString(fileId);
+ ssize_t len = [lenStr intValue];
+ NSData *data = readData(fileId, len);
+ id value = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+
+ [config setObject:(value ? value : data) forKey:key];
+ [value release];
+ }
+
+ close(fileId);
+ return config;
+}
+} // namespace
+
+#pragma mark -
+
@interface Uploader(PrivateMethods)
-- (NSString *)readString;
-- (NSData *)readData:(ssize_t)length;
-- (BOOL)readConfigurationData;
+// Update |parameters_| as well as the server parameters using |config|.
+- (void)translateConfigurationData:(NSDictionary *)config;
+
+// Read the minidump referenced in |parameters_| and update |minidumpContents_|
+// with its content.
- (BOOL)readMinidumpData;
+
+// Read the log files referenced in |parameters_| and update |logFileData_|
+// with their content.
- (BOOL)readLogFileData;
// Returns a unique client id (user-specific), creating a persistent
@@ -80,41 +167,24 @@ NSString *const kDefaultServerType = @"google";
// Accessor method for the URL parameter dictionary
- (NSMutableDictionary *)urlParameterDictionary;
-// This method adds a key/value pair to the dictionary that
-// will be uploaded to the crash server.
-- (void)addServerParameter:(id)value forKey:(NSString *)key;
-
// Records the uploaded crash ID to the log file.
- (void)logUploadWithID:(const char *)uploadID;
-
@end
@implementation Uploader
//=============================================================================
- (id)initWithConfigFile:(const char *)configFile {
- if ((self = [super init])) {
-
- configFile_ = open(configFile, O_RDONLY, 0600);
- if (configFile_ == -1) {
- GTMLoggerDebug(@"Couldn't open config file %s - %s",
- configFile,
- strerror(errno));
- }
-
- // we want to avoid a build-up of old config files even if they
- // have been incorrectly written by the framework
- if (unlink(configFile)) {
- GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
- configFile,
- strerror(errno));
- }
+ NSDictionary *config = readConfigurationData(configFile);
+ if (!config)
+ return nil;
- if (configFile_ == -1) {
- [self release];
- return nil;
- }
+ return [self initWithConfig:config];
+}
+//=============================================================================
+- (id)initWithConfig:(NSDictionary *)config {
+ if ((self = [super init])) {
// Because the reporter is embedded in the framework (and many copies
// of the framework may exist) its not completely certain that the OS
// will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
@@ -126,68 +196,21 @@ NSString *const kDefaultServerType = @"google";
[self createServerParameterDictionaries];
- if (![self readConfigurationData]) {
- GTMLoggerDebug(@"uploader readConfigurationData failed");
- [self release];
- return nil;
- }
+ [self translateConfigurationData:config];
// Read the minidump into memory.
[self readMinidumpData];
[self readLogFileData];
-
}
return self;
}
//=============================================================================
-- (NSString *)readString {
- NSMutableString *str = [NSMutableString stringWithCapacity:32];
- char ch[2] = { 0 };
-
- while (read(configFile_, &ch[0], 1) == 1) {
- if (ch[0] == '\n') {
- // Break if this is the first newline after reading some other string
- // data.
- if ([str length])
- break;
- } else {
- [str appendString:[NSString stringWithUTF8String:ch]];
- }
- }
-
- return str;
-}
-
-//=============================================================================
-- (NSData *)readData:(ssize_t)length {
- NSMutableData *data = [NSMutableData dataWithLength:length];
- char *bytes = (char *)[data bytes];
-
- if (read(configFile_, bytes, length) != length)
- return nil;
-
- return data;
-}
-
-//=============================================================================
-- (BOOL)readConfigurationData {
+- (void)translateConfigurationData:(NSDictionary *)config {
parameters_ = [[NSMutableDictionary alloc] init];
- while (1) {
- NSString *key = [self readString];
-
- if (![key length])
- break;
-
- // Read the data. Try to convert to a UTF-8 string, or just save
- // the data
- NSString *lenStr = [self readString];
- ssize_t len = [lenStr intValue];
- NSData *data = [self readData:len];
- id value = [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
-
+ NSEnumerator *it = [config keyEnumerator];
+ while (NSString *key = [it nextObject]) {
// If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX
// that indicates that it should be uploaded to the server along
// with the minidump, so we treat it specially.
@@ -195,29 +218,24 @@ NSString *const kDefaultServerType = @"google";
NSString *urlParameterKey =
[key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]];
if ([urlParameterKey length]) {
- if (value) {
- [self addServerParameter:value
+ id value = [config objectForKey:key];
+ if ([value isKindOfClass:[NSString class]]) {
+ [self addServerParameter:(NSString *)value
forKey:urlParameterKey];
} else {
- [self addServerParameter:data
+ [self addServerParameter:(NSData *)value
forKey:urlParameterKey];
}
}
} else {
- [parameters_ setObject:(value ? value : data) forKey:key];
+ [parameters_ setObject:[config objectForKey:key] forKey:key];
}
- [value release];
}
// generate a unique client ID based on this host's MAC address
// then add a key/value pair for it
NSString *clientID = [self clientID];
[parameters_ setObject:clientID forKey:@"guid"];
-
- close(configFile_);
- configFile_ = -1;
-
- return YES;
}
// Per user per machine
@@ -528,21 +546,27 @@ NSString *const kDefaultServerType = @"google";
}
if (logFileData_) {
- HTTPMultipartUpload *logUpload =
- [[HTTPMultipartUpload alloc] initWithURL:url];
+ [self uploadData:logFileData_ name:@"log" url:url];
+ }
- [uploadParameters setObject:@"log" forKey:@"type"];
- [logUpload setParameters:uploadParameters];
- [logUpload addFileContents:logFileData_ name:@"log"];
+ [upload release];
+}
- NSError *error = nil;
- NSData *data = [logUpload send:&error];
- NSString *result = [[NSString alloc] initWithData:data
- encoding:NSUTF8StringEncoding];
- [result release];
- [logUpload release];
- }
+- (void)uploadData:(NSData *)data name:(NSString *)name {
+ NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
+ NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
+
+ if (![self populateServerDictionary:uploadParameters])
+ return;
+
+ HTTPMultipartUpload *upload =
+ [[HTTPMultipartUpload alloc] initWithURL:url];
+
+ [uploadParameters setObject:name forKey:@"type"];
+ [upload setParameters:uploadParameters];
+ [upload addFileContents:data name:name];
+ [upload send:nil];
[upload release];
}