diff options
author | qsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2011-11-16 17:04:28 +0000 |
---|---|---|
committer | qsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2011-11-16 17:04:28 +0000 |
commit | 976930db64c56d476b02ebe86602a9600248ac7f (patch) | |
tree | eb0f6ac5c12fdb44b7419fa58bc29105b0689ab2 /src/client/mac | |
parent | Fix several error-case leaks on the Mac found by clang analysis (diff) | |
download | breakpad-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/client/mac')
-rw-r--r-- | src/client/mac/Breakpad.xcodeproj/project.pbxproj | 8 | ||||
-rw-r--r-- | src/client/mac/sender/uploader.h | 10 | ||||
-rw-r--r-- | src/client/mac/sender/uploader.mm (renamed from src/client/mac/sender/uploader.m) | 226 |
3 files changed, 138 insertions, 106 deletions
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]; } |