1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30// symupload.m: Upload a symbol file to a HTTP server. The upload is sent as 31// a multipart/form-data POST request with the following parameters: 32// code_file: the basename of the module, e.g. "app" 33// debug_file: the basename of the debugging file, e.g. "app" 34// debug_identifier: the debug file's identifier, usually consisting of 35// the guid and age embedded in the pdb, e.g. 36// "11111111BBBB3333DDDD555555555555F" 37// os: the operating system that the module was built for 38// cpu: the CPU that the module was built for (x86 or ppc) 39// symbol_file: the contents of the breakpad-format symbol file 40 41#include <fcntl.h> 42#include <sys/stat.h> 43#include <unistd.h> 44 45#include <Foundation/Foundation.h> 46#include "HTTPMultipartUpload.h" 47 48typedef struct { 49 NSString *symbolsPath; 50 NSString *uploadURLStr; 51 BOOL success; 52} Options; 53 54//============================================================================= 55static NSArray *ModuleDataForSymbolFile(NSString *file) { 56 NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:file]; 57 NSData *data = [fh readDataOfLength:1024]; 58 NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 59 NSScanner *scanner = [NSScanner scannerWithString:str]; 60 NSString *line; 61 NSMutableArray *parts = nil; 62 const int MODULE_ID_INDEX = 3; 63 64 if ([scanner scanUpToString:@"\n" intoString:&line]) { 65 parts = [[NSMutableArray alloc] init]; 66 NSScanner *moduleInfoScanner = [NSScanner scannerWithString:line]; 67 NSString *moduleInfo; 68 // Get everything BEFORE the module name. None of these properties 69 // can have spaces. 70 for (int i = 0; i <= MODULE_ID_INDEX; i++) { 71 [moduleInfoScanner scanUpToString:@" " intoString:&moduleInfo]; 72 [parts addObject:moduleInfo]; 73 } 74 75 // Now get the module name. This can have a space so we scan to 76 // the end of the line. 77 [moduleInfoScanner scanUpToString:@"\n" intoString:&moduleInfo]; 78 [parts addObject:moduleInfo]; 79 } 80 81 [str release]; 82 83 return parts; 84} 85 86//============================================================================= 87static void Start(Options *options) { 88 NSURL *url = [NSURL URLWithString:options->uploadURLStr]; 89 HTTPMultipartUpload *ul = [[HTTPMultipartUpload alloc] initWithURL:url]; 90 NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; 91 NSArray *moduleParts = ModuleDataForSymbolFile(options->symbolsPath); 92 NSMutableString *compactedID = 93 [NSMutableString stringWithString:[moduleParts objectAtIndex:3]]; 94 [compactedID replaceOccurrencesOfString:@"-" withString:@"" options:0 95 range:NSMakeRange(0, [compactedID length])]; 96 97 // Add parameters 98 [parameters setObject:compactedID forKey:@"debug_identifier"]; 99 100 // MODULE <os> <cpu> <uuid> <module-name> 101 // 0 1 2 3 4 102 [parameters setObject:[moduleParts objectAtIndex:1] forKey:@"os"]; 103 [parameters setObject:[moduleParts objectAtIndex:2] forKey:@"cpu"]; 104 [parameters setObject:[moduleParts objectAtIndex:4] forKey:@"debug_file"]; 105 [parameters setObject:[moduleParts objectAtIndex:4] forKey:@"code_file"]; 106 [ul setParameters:parameters]; 107 108 NSArray *keys = [parameters allKeys]; 109 int count = [keys count]; 110 for (int i = 0; i < count; ++i) { 111 NSString *key = [keys objectAtIndex:i]; 112 NSString *value = [parameters objectForKey:key]; 113 fprintf(stdout, "'%s' = '%s'\n", [key UTF8String], 114 [value UTF8String]); 115 } 116 117 // Add file 118 [ul addFileAtPath:options->symbolsPath name:@"symbol_file"]; 119 120 // Send it 121 NSError *error = nil; 122 NSData *data = [ul send:&error]; 123 NSString *result = [[NSString alloc] initWithData:data 124 encoding:NSUTF8StringEncoding]; 125 int status = [[ul response] statusCode]; 126 127 fprintf(stdout, "Send: %s\n", error ? [[error description] UTF8String] : 128 "No Error"); 129 fprintf(stdout, "Response: %d\n", status); 130 fprintf(stdout, "Result: %lu bytes\n%s\n", 131 (unsigned long)[data length], [result UTF8String]); 132 133 [result release]; 134 [ul release]; 135 options->success = !error && status==200; 136} 137 138//============================================================================= 139static void 140Usage(int argc, const char *argv[]) { 141 fprintf(stderr, "Submit symbol information.\n"); 142 fprintf(stderr, "Usage: %s <symbols> <upload-URL>\n", argv[0]); 143 fprintf(stderr, "<symbols> should be created by using the dump_syms tool.\n"); 144 fprintf(stderr, "<upload-URL> is the destination for the upload\n"); 145 fprintf(stderr, "\t-h: Usage\n"); 146 fprintf(stderr, "\t-?: Usage\n"); 147} 148 149//============================================================================= 150static void 151SetupOptions(int argc, const char *argv[], Options *options) { 152 extern int optind; 153 char ch; 154 155 while ((ch = getopt(argc, (char * const *)argv, "h?")) != -1) { 156 switch (ch) { 157 default: 158 Usage(argc, argv); 159 exit(0); 160 break; 161 } 162 } 163 164 if ((argc - optind) != 2) { 165 fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]); 166 Usage(argc, argv); 167 exit(1); 168 } 169 170 int fd = open(argv[optind], O_RDONLY); 171 if (fd < 0) { 172 fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno)); 173 exit(1); 174 } 175 176 struct stat statbuf; 177 if (fstat(fd, &statbuf) < 0) { 178 fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], strerror(errno)); 179 close(fd); 180 exit(1); 181 } 182 close(fd); 183 184 if (!S_ISREG(statbuf.st_mode)) { 185 fprintf(stderr, "%s: %s: not a regular file\n", argv[0], argv[optind]); 186 exit(1); 187 } 188 189 options->symbolsPath = [NSString stringWithUTF8String:argv[optind]]; 190 options->uploadURLStr = [NSString stringWithUTF8String:argv[optind + 1]]; 191} 192 193//============================================================================= 194int main (int argc, const char * argv[]) { 195 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 196 Options options; 197 198 bzero(&options, sizeof(Options)); 199 SetupOptions(argc, argv, &options); 200 Start(&options); 201 202 [pool release]; 203 return options.success ? 0 : 1; 204} 205