toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2011, Google Inc.
michael@0 2 // All rights reserved.
michael@0 3 //
michael@0 4 // Redistribution and use in source and binary forms, with or without
michael@0 5 // modification, are permitted provided that the following conditions are
michael@0 6 // met:
michael@0 7 //
michael@0 8 // * Redistributions of source code must retain the above copyright
michael@0 9 // notice, this list of conditions and the following disclaimer.
michael@0 10 // * Redistributions in binary form must reproduce the above
michael@0 11 // copyright notice, this list of conditions and the following disclaimer
michael@0 12 // in the documentation and/or other materials provided with the
michael@0 13 // distribution.
michael@0 14 // * Neither the name of Google Inc. nor the names of its
michael@0 15 // contributors may be used to endorse or promote products derived from
michael@0 16 // this software without specific prior written permission.
michael@0 17 //
michael@0 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30 #define VERBOSE 0
michael@0 31
michael@0 32 #if VERBOSE
michael@0 33 static bool gDebugLog = true;
michael@0 34 #else
michael@0 35 static bool gDebugLog = false;
michael@0 36 #endif
michael@0 37
michael@0 38 #define DEBUGLOG if (gDebugLog) fprintf
michael@0 39 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
michael@0 40
michael@0 41 #import "client/ios/Breakpad.h"
michael@0 42
michael@0 43 #import <Foundation/Foundation.h>
michael@0 44 #include <pthread.h>
michael@0 45 #include <sys/stat.h>
michael@0 46 #include <sys/sysctl.h>
michael@0 47
michael@0 48 #import "client/mac/crash_generation/ConfigFile.h"
michael@0 49 #import "client/mac/handler/exception_handler.h"
michael@0 50 #import "client/mac/handler/minidump_generator.h"
michael@0 51 #import "client/mac/sender/uploader.h"
michael@0 52 #import "common/mac/SimpleStringDictionary.h"
michael@0 53 #import "client/ios/handler/ios_exception_minidump_generator.h"
michael@0 54 #import "client/mac/handler/protected_memory_allocator.h"
michael@0 55
michael@0 56 #ifndef __EXCEPTIONS
michael@0 57 // This file uses C++ try/catch (but shouldn't). Duplicate the macros from
michael@0 58 // <c++/4.2.1/exception_defines.h> allowing this file to work properly with
michael@0 59 // exceptions disabled even when other C++ libraries are used. #undef the try
michael@0 60 // and catch macros first in case libstdc++ is in use and has already provided
michael@0 61 // its own definitions.
michael@0 62 #undef try
michael@0 63 #define try if (true)
michael@0 64 #undef catch
michael@0 65 #define catch(X) if (false)
michael@0 66 #endif // __EXCEPTIONS
michael@0 67
michael@0 68 using google_breakpad::ConfigFile;
michael@0 69 using google_breakpad::EnsureDirectoryPathExists;
michael@0 70 using google_breakpad::KeyValueEntry;
michael@0 71 using google_breakpad::SimpleStringDictionary;
michael@0 72 using google_breakpad::SimpleStringDictionaryIterator;
michael@0 73
michael@0 74 //=============================================================================
michael@0 75 // We want any memory allocations which are used by breakpad during the
michael@0 76 // exception handling process (after a crash has happened) to be read-only
michael@0 77 // to prevent them from being smashed before a crash occurs. Unfortunately
michael@0 78 // we cannot protect against smashes to our exception handling thread's
michael@0 79 // stack.
michael@0 80 //
michael@0 81 // NOTE: Any memory allocations which are not used during the exception
michael@0 82 // handling process may be allocated in the normal ways.
michael@0 83 //
michael@0 84 // The ProtectedMemoryAllocator class provides an Allocate() method which
michael@0 85 // we'll using in conjunction with placement operator new() to control
michael@0 86 // allocation of C++ objects. Note that we don't use operator delete()
michael@0 87 // but instead call the objects destructor directly: object->~ClassName();
michael@0 88 //
michael@0 89 ProtectedMemoryAllocator *gMasterAllocator = NULL;
michael@0 90 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
michael@0 91 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
michael@0 92
michael@0 93 // Mutex for thread-safe access to the key/value dictionary used by breakpad.
michael@0 94 // It's a global instead of an instance variable of Breakpad
michael@0 95 // since it can't live in a protected memory area.
michael@0 96 pthread_mutex_t gDictionaryMutex;
michael@0 97
michael@0 98 //=============================================================================
michael@0 99 // Stack-based object for thread-safe access to a memory-protected region.
michael@0 100 // It's assumed that normally the memory block (allocated by the allocator)
michael@0 101 // is protected (read-only). Creating a stack-based instance of
michael@0 102 // ProtectedMemoryLocker will unprotect this block after taking the lock.
michael@0 103 // Its destructor will first re-protect the memory then release the lock.
michael@0 104 class ProtectedMemoryLocker {
michael@0 105 public:
michael@0 106 // allocator may be NULL, in which case no Protect() or Unprotect() calls
michael@0 107 // will be made, but a lock will still be taken
michael@0 108 ProtectedMemoryLocker(pthread_mutex_t *mutex,
michael@0 109 ProtectedMemoryAllocator *allocator)
michael@0 110 : mutex_(mutex), allocator_(allocator) {
michael@0 111 // Lock the mutex
michael@0 112 assert(pthread_mutex_lock(mutex_) == 0);
michael@0 113
michael@0 114 // Unprotect the memory
michael@0 115 if (allocator_ ) {
michael@0 116 allocator_->Unprotect();
michael@0 117 }
michael@0 118 }
michael@0 119
michael@0 120 ~ProtectedMemoryLocker() {
michael@0 121 // First protect the memory
michael@0 122 if (allocator_) {
michael@0 123 allocator_->Protect();
michael@0 124 }
michael@0 125
michael@0 126 // Then unlock the mutex
michael@0 127 assert(pthread_mutex_unlock(mutex_) == 0);
michael@0 128 };
michael@0 129
michael@0 130 private:
michael@0 131 // Keep anybody from ever creating one of these things not on the stack.
michael@0 132 ProtectedMemoryLocker() { }
michael@0 133 ProtectedMemoryLocker(const ProtectedMemoryLocker&);
michael@0 134 ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
michael@0 135
michael@0 136 pthread_mutex_t *mutex_;
michael@0 137 ProtectedMemoryAllocator *allocator_;
michael@0 138 };
michael@0 139
michael@0 140 //=============================================================================
michael@0 141 class Breakpad {
michael@0 142 public:
michael@0 143 // factory method
michael@0 144 static Breakpad *Create(NSDictionary *parameters) {
michael@0 145 // Allocate from our special allocation pool
michael@0 146 Breakpad *breakpad =
michael@0 147 new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
michael@0 148 Breakpad();
michael@0 149
michael@0 150 if (!breakpad)
michael@0 151 return NULL;
michael@0 152
michael@0 153 if (!breakpad->Initialize(parameters)) {
michael@0 154 // Don't use operator delete() here since we allocated from special pool
michael@0 155 breakpad->~Breakpad();
michael@0 156 return NULL;
michael@0 157 }
michael@0 158
michael@0 159 return breakpad;
michael@0 160 }
michael@0 161
michael@0 162 ~Breakpad();
michael@0 163
michael@0 164 void SetKeyValue(NSString *key, NSString *value);
michael@0 165 NSString *KeyValue(NSString *key);
michael@0 166 void RemoveKeyValue(NSString *key);
michael@0 167 NSString *NextCrashReportToUpload();
michael@0 168 void UploadNextReport();
michael@0 169 void UploadData(NSData *data, NSString *name,
michael@0 170 NSDictionary *server_parameters);
michael@0 171 NSDictionary *GenerateReport(NSDictionary *server_parameters);
michael@0 172
michael@0 173 private:
michael@0 174 Breakpad()
michael@0 175 : handler_(NULL),
michael@0 176 config_params_(NULL) {}
michael@0 177
michael@0 178 bool Initialize(NSDictionary *parameters);
michael@0 179
michael@0 180 bool ExtractParameters(NSDictionary *parameters);
michael@0 181
michael@0 182 // Dispatches to HandleMinidump()
michael@0 183 static bool HandleMinidumpCallback(const char *dump_dir,
michael@0 184 const char *minidump_id,
michael@0 185 void *context, bool succeeded);
michael@0 186
michael@0 187 bool HandleMinidump(const char *dump_dir,
michael@0 188 const char *minidump_id);
michael@0 189
michael@0 190 // NSException handler
michael@0 191 static void UncaughtExceptionHandler(NSException *exception);
michael@0 192
michael@0 193 // Handle an uncaught NSException.
michael@0 194 void HandleUncaughtException(NSException *exception);
michael@0 195
michael@0 196 // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
michael@0 197 // MachineExceptions.h, we have to explicitly name the handler.
michael@0 198 google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
michael@0 199
michael@0 200 SimpleStringDictionary *config_params_; // Create parameters (STRONG)
michael@0 201
michael@0 202 ConfigFile config_file_;
michael@0 203
michael@0 204 // A static reference to the current Breakpad instance. Used for handling
michael@0 205 // NSException.
michael@0 206 static Breakpad *current_breakpad_;
michael@0 207 };
michael@0 208
michael@0 209 Breakpad *Breakpad::current_breakpad_ = NULL;
michael@0 210
michael@0 211 #pragma mark -
michael@0 212 #pragma mark Helper functions
michael@0 213
michael@0 214 //=============================================================================
michael@0 215 // Helper functions
michael@0 216
michael@0 217 //=============================================================================
michael@0 218 static BOOL IsDebuggerActive() {
michael@0 219 BOOL result = NO;
michael@0 220 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
michael@0 221
michael@0 222 // We check both defaults and the environment variable here
michael@0 223
michael@0 224 BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
michael@0 225
michael@0 226 if (!ignoreDebugger) {
michael@0 227 char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
michael@0 228 ignoreDebugger =
michael@0 229 (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
michael@0 230 }
michael@0 231
michael@0 232 if (!ignoreDebugger) {
michael@0 233 pid_t pid = getpid();
michael@0 234 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
michael@0 235 int mibSize = sizeof(mib) / sizeof(int);
michael@0 236 size_t actualSize;
michael@0 237
michael@0 238 if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
michael@0 239 struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
michael@0 240
michael@0 241 if (info) {
michael@0 242 // This comes from looking at the Darwin xnu Kernel
michael@0 243 if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
michael@0 244 result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
michael@0 245
michael@0 246 free(info);
michael@0 247 }
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 return result;
michael@0 252 }
michael@0 253
michael@0 254 //=============================================================================
michael@0 255 bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
michael@0 256 const char *minidump_id,
michael@0 257 void *context, bool succeeded) {
michael@0 258 Breakpad *breakpad = (Breakpad *)context;
michael@0 259
michael@0 260 // If our context is damaged or something, just return false to indicate that
michael@0 261 // the handler should continue without us.
michael@0 262 if (!breakpad || !succeeded)
michael@0 263 return false;
michael@0 264
michael@0 265 return breakpad->HandleMinidump(dump_dir, minidump_id);
michael@0 266 }
michael@0 267
michael@0 268 //=============================================================================
michael@0 269 void Breakpad::UncaughtExceptionHandler(NSException *exception) {
michael@0 270 NSSetUncaughtExceptionHandler(NULL);
michael@0 271 if (current_breakpad_) {
michael@0 272 current_breakpad_->HandleUncaughtException(exception);
michael@0 273 }
michael@0 274 }
michael@0 275
michael@0 276 //=============================================================================
michael@0 277 #pragma mark -
michael@0 278
michael@0 279 //=============================================================================
michael@0 280 bool Breakpad::Initialize(NSDictionary *parameters) {
michael@0 281 // Initialize
michael@0 282 current_breakpad_ = this;
michael@0 283 config_params_ = NULL;
michael@0 284 handler_ = NULL;
michael@0 285
michael@0 286 // Gather any user specified parameters
michael@0 287 if (!ExtractParameters(parameters)) {
michael@0 288 return false;
michael@0 289 }
michael@0 290
michael@0 291 // Check for debugger
michael@0 292 if (IsDebuggerActive()) {
michael@0 293 DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
michael@0 294 return true;
michael@0 295 }
michael@0 296
michael@0 297 // Create the handler (allocating it in our special protected pool)
michael@0 298 handler_ =
michael@0 299 new (gBreakpadAllocator->Allocate(
michael@0 300 sizeof(google_breakpad::ExceptionHandler)))
michael@0 301 google_breakpad::ExceptionHandler(
michael@0 302 config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
michael@0 303 0, &HandleMinidumpCallback, this, true, 0);
michael@0 304 NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
michael@0 305 return true;
michael@0 306 }
michael@0 307
michael@0 308 //=============================================================================
michael@0 309 Breakpad::~Breakpad() {
michael@0 310 NSSetUncaughtExceptionHandler(NULL);
michael@0 311 current_breakpad_ = NULL;
michael@0 312 // Note that we don't use operator delete() on these pointers,
michael@0 313 // since they were allocated by ProtectedMemoryAllocator objects.
michael@0 314 //
michael@0 315 if (config_params_) {
michael@0 316 config_params_->~SimpleStringDictionary();
michael@0 317 }
michael@0 318
michael@0 319 if (handler_)
michael@0 320 handler_->~ExceptionHandler();
michael@0 321 }
michael@0 322
michael@0 323 //=============================================================================
michael@0 324 bool Breakpad::ExtractParameters(NSDictionary *parameters) {
michael@0 325 NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
michael@0 326 NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
michael@0 327 NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
michael@0 328 NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
michael@0 329 NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
michael@0 330 NSString *vendor =
michael@0 331 [parameters objectForKey:@BREAKPAD_VENDOR];
michael@0 332 NSString *dumpSubdirectory =
michael@0 333 [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
michael@0 334
michael@0 335 NSDictionary *serverParameters =
michael@0 336 [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
michael@0 337
michael@0 338 if (!product)
michael@0 339 product = [parameters objectForKey:@"CFBundleName"];
michael@0 340
michael@0 341 if (!display) {
michael@0 342 display = [parameters objectForKey:@"CFBundleDisplayName"];
michael@0 343 if (!display) {
michael@0 344 display = product;
michael@0 345 }
michael@0 346 }
michael@0 347
michael@0 348 if (!version)
michael@0 349 version = [parameters objectForKey:@"CFBundleVersion"];
michael@0 350
michael@0 351 if (!vendor) {
michael@0 352 vendor = @"Vendor not specified";
michael@0 353 }
michael@0 354
michael@0 355 if (!dumpSubdirectory) {
michael@0 356 NSString *cachePath =
michael@0 357 [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
michael@0 358 NSUserDomainMask,
michael@0 359 YES)
michael@0 360 objectAtIndex:0];
michael@0 361 dumpSubdirectory =
michael@0 362 [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
michael@0 363
michael@0 364 EnsureDirectoryPathExists(dumpSubdirectory);
michael@0 365 }
michael@0 366
michael@0 367 // The product, version, and URL are required values.
michael@0 368 if (![product length]) {
michael@0 369 DEBUGLOG(stderr, "Missing required product key.\n");
michael@0 370 return false;
michael@0 371 }
michael@0 372
michael@0 373 if (![version length]) {
michael@0 374 DEBUGLOG(stderr, "Missing required version key.\n");
michael@0 375 return false;
michael@0 376 }
michael@0 377
michael@0 378 if (![urlStr length]) {
michael@0 379 DEBUGLOG(stderr, "Missing required URL key.\n");
michael@0 380 return false;
michael@0 381 }
michael@0 382
michael@0 383 config_params_ =
michael@0 384 new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
michael@0 385 SimpleStringDictionary();
michael@0 386
michael@0 387 SimpleStringDictionary &dictionary = *config_params_;
michael@0 388
michael@0 389 dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
michael@0 390 dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
michael@0 391 dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
michael@0 392 dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
michael@0 393 dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
michael@0 394 dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
michael@0 395 dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
michael@0 396 [dumpSubdirectory UTF8String]);
michael@0 397
michael@0 398 struct timeval tv;
michael@0 399 gettimeofday(&tv, NULL);
michael@0 400 char timeStartedString[32];
michael@0 401 sprintf(timeStartedString, "%zd", tv.tv_sec);
michael@0 402 dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
michael@0 403
michael@0 404 if (serverParameters) {
michael@0 405 // For each key-value pair, call BreakpadAddUploadParameter()
michael@0 406 NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
michael@0 407 NSString *aParameter;
michael@0 408 while ((aParameter = [keyEnumerator nextObject])) {
michael@0 409 BreakpadAddUploadParameter(this, aParameter,
michael@0 410 [serverParameters objectForKey:aParameter]);
michael@0 411 }
michael@0 412 }
michael@0 413 return true;
michael@0 414 }
michael@0 415
michael@0 416 //=============================================================================
michael@0 417 void Breakpad::SetKeyValue(NSString *key, NSString *value) {
michael@0 418 // We allow nil values. This is the same as removing the keyvalue.
michael@0 419 if (!config_params_ || !key)
michael@0 420 return;
michael@0 421
michael@0 422 config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
michael@0 423 }
michael@0 424
michael@0 425 //=============================================================================
michael@0 426 NSString *Breakpad::KeyValue(NSString *key) {
michael@0 427 if (!config_params_ || !key)
michael@0 428 return nil;
michael@0 429
michael@0 430 const char *value = config_params_->GetValueForKey([key UTF8String]);
michael@0 431 return value ? [NSString stringWithUTF8String:value] : nil;
michael@0 432 }
michael@0 433
michael@0 434 //=============================================================================
michael@0 435 void Breakpad::RemoveKeyValue(NSString *key) {
michael@0 436 if (!config_params_ || !key) return;
michael@0 437
michael@0 438 config_params_->RemoveKey([key UTF8String]);
michael@0 439 }
michael@0 440
michael@0 441 //=============================================================================
michael@0 442 NSString *Breakpad::NextCrashReportToUpload() {
michael@0 443 NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
michael@0 444 if (!directory)
michael@0 445 return nil;
michael@0 446 NSArray *dirContents = [[NSFileManager defaultManager]
michael@0 447 contentsOfDirectoryAtPath:directory error:nil];
michael@0 448 NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
michael@0 449 predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
michael@0 450 NSString *config = [configs lastObject];
michael@0 451 if (!config)
michael@0 452 return nil;
michael@0 453 return [NSString stringWithFormat:@"%@/%@", directory, config];
michael@0 454 }
michael@0 455
michael@0 456 //=============================================================================
michael@0 457 void Breakpad::UploadNextReport() {
michael@0 458 NSString *configFile = NextCrashReportToUpload();
michael@0 459 if (configFile) {
michael@0 460 Uploader *uploader = [[[Uploader alloc]
michael@0 461 initWithConfigFile:[configFile UTF8String]] autorelease];
michael@0 462 if (uploader)
michael@0 463 [uploader report];
michael@0 464 }
michael@0 465 }
michael@0 466
michael@0 467 //=============================================================================
michael@0 468 void Breakpad::UploadData(NSData *data, NSString *name,
michael@0 469 NSDictionary *server_parameters) {
michael@0 470 NSMutableDictionary *config = [NSMutableDictionary dictionary];
michael@0 471
michael@0 472 SimpleStringDictionaryIterator it(*config_params_);
michael@0 473 while (const KeyValueEntry *next = it.Next()) {
michael@0 474 [config setValue:[NSString stringWithUTF8String:next->GetValue()]
michael@0 475 forKey:[NSString stringWithUTF8String:next->GetKey()]];
michael@0 476 }
michael@0 477
michael@0 478 Uploader *uploader =
michael@0 479 [[[Uploader alloc] initWithConfig:config] autorelease];
michael@0 480 for (NSString *key in server_parameters) {
michael@0 481 [uploader addServerParameter:[server_parameters objectForKey:key]
michael@0 482 forKey:key];
michael@0 483 }
michael@0 484 [uploader uploadData:data name:name];
michael@0 485 }
michael@0 486
michael@0 487 //=============================================================================
michael@0 488 NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
michael@0 489 NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
michael@0 490 if (!dumpDirAsNSString)
michael@0 491 return nil;
michael@0 492 const char *dumpDir = [dumpDirAsNSString UTF8String];
michael@0 493
michael@0 494 google_breakpad::MinidumpGenerator generator(mach_task_self(),
michael@0 495 MACH_PORT_NULL);
michael@0 496 std::string dumpId;
michael@0 497 std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
michael@0 498 bool success = generator.Write(dumpFilename.c_str());
michael@0 499 if (!success)
michael@0 500 return nil;
michael@0 501
michael@0 502 SimpleStringDictionary params = *config_params_;
michael@0 503 for (NSString *key in server_parameters) {
michael@0 504 params.SetKeyValue([key UTF8String],
michael@0 505 [[server_parameters objectForKey:key] UTF8String]);
michael@0 506 }
michael@0 507 ConfigFile config_file;
michael@0 508 config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
michael@0 509
michael@0 510 // Handle results.
michael@0 511 NSMutableDictionary *result = [NSMutableDictionary dictionary];
michael@0 512 NSString *dumpFullPath = [dumpDirAsNSString stringByAppendingPathComponent:
michael@0 513 [NSString stringWithUTF8String:dumpFilename.c_str()]];
michael@0 514 [result setValue:dumpFullPath
michael@0 515 forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
michael@0 516 [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
michael@0 517 forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
michael@0 518 return result;
michael@0 519 }
michael@0 520
michael@0 521 //=============================================================================
michael@0 522 bool Breakpad::HandleMinidump(const char *dump_dir,
michael@0 523 const char *minidump_id) {
michael@0 524 DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n");
michael@0 525
michael@0 526 config_file_.WriteFile(dump_dir,
michael@0 527 config_params_,
michael@0 528 dump_dir,
michael@0 529 minidump_id);
michael@0 530
michael@0 531 // Return true here to indicate that we've processed things as much as we
michael@0 532 // want.
michael@0 533 return true;
michael@0 534 }
michael@0 535
michael@0 536 //=============================================================================
michael@0 537 void Breakpad::HandleUncaughtException(NSException *exception) {
michael@0 538 // Generate the minidump.
michael@0 539 google_breakpad::IosExceptionMinidumpGenerator generator(exception);
michael@0 540 const char *minidump_path =
michael@0 541 config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
michael@0 542 std::string minidump_id;
michael@0 543 std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
michael@0 544 &minidump_id);
michael@0 545 generator.Write(minidump_filename.c_str());
michael@0 546
michael@0 547 // Copy the config params and our custom parameter. This is necessary for 2
michael@0 548 // reasons:
michael@0 549 // 1- config_params_ is protected.
michael@0 550 // 2- If the application crash while trying to handle this exception, a usual
michael@0 551 // report will be generated. This report must not contain these special
michael@0 552 // keys.
michael@0 553 SimpleStringDictionary params = *config_params_;
michael@0 554 params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
michael@0 555 params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
michael@0 556 [[exception name] UTF8String]);
michael@0 557 params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
michael@0 558 [[exception reason] UTF8String]);
michael@0 559
michael@0 560 // And finally write the config file.
michael@0 561 ConfigFile config_file;
michael@0 562 config_file.WriteFile(minidump_path,
michael@0 563 &params,
michael@0 564 minidump_path,
michael@0 565 minidump_id.c_str());
michael@0 566 }
michael@0 567
michael@0 568 //=============================================================================
michael@0 569
michael@0 570 #pragma mark -
michael@0 571 #pragma mark Public API
michael@0 572
michael@0 573 //=============================================================================
michael@0 574 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
michael@0 575 try {
michael@0 576 // This is confusing. Our two main allocators for breakpad memory are:
michael@0 577 // - gKeyValueAllocator for the key/value memory
michael@0 578 // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
michael@0 579 // breakpad allocations which are accessed at exception handling time.
michael@0 580 //
michael@0 581 // But in order to avoid these two allocators themselves from being smashed,
michael@0 582 // we'll protect them as well by allocating them with gMasterAllocator.
michael@0 583 //
michael@0 584 // gMasterAllocator itself will NOT be protected, but this doesn't matter,
michael@0 585 // since once it does its allocations and locks the memory, smashes to
michael@0 586 // itself don't affect anything we care about.
michael@0 587 gMasterAllocator =
michael@0 588 new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
michael@0 589
michael@0 590 gKeyValueAllocator =
michael@0 591 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
michael@0 592 ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
michael@0 593
michael@0 594 // Create a mutex for use in accessing the SimpleStringDictionary
michael@0 595 int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
michael@0 596 if (mutexResult == 0) {
michael@0 597
michael@0 598 // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
michael@0 599 // Let's round up to the nearest page size.
michael@0 600 //
michael@0 601 int breakpad_pool_size = 4096;
michael@0 602
michael@0 603 /*
michael@0 604 sizeof(Breakpad)
michael@0 605 + sizeof(google_breakpad::ExceptionHandler)
michael@0 606 + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
michael@0 607 */
michael@0 608
michael@0 609 gBreakpadAllocator =
michael@0 610 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
michael@0 611 ProtectedMemoryAllocator(breakpad_pool_size);
michael@0 612
michael@0 613 // Stack-based autorelease pool for Breakpad::Create() obj-c code.
michael@0 614 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
michael@0 615 Breakpad *breakpad = Breakpad::Create(parameters);
michael@0 616
michael@0 617 if (breakpad) {
michael@0 618 // Make read-only to protect against memory smashers
michael@0 619 gMasterAllocator->Protect();
michael@0 620 gKeyValueAllocator->Protect();
michael@0 621 gBreakpadAllocator->Protect();
michael@0 622 // Can uncomment this line to figure out how much space was actually
michael@0 623 // allocated using this allocator
michael@0 624 // printf("gBreakpadAllocator allocated size = %d\n",
michael@0 625 // gBreakpadAllocator->GetAllocatedSize() );
michael@0 626 [pool release];
michael@0 627 return (BreakpadRef)breakpad;
michael@0 628 }
michael@0 629
michael@0 630 [pool release];
michael@0 631 }
michael@0 632 } catch(...) { // don't let exceptions leave this C API
michael@0 633 fprintf(stderr, "BreakpadCreate() : error\n");
michael@0 634 }
michael@0 635
michael@0 636 if (gKeyValueAllocator) {
michael@0 637 gKeyValueAllocator->~ProtectedMemoryAllocator();
michael@0 638 gKeyValueAllocator = NULL;
michael@0 639 }
michael@0 640
michael@0 641 if (gBreakpadAllocator) {
michael@0 642 gBreakpadAllocator->~ProtectedMemoryAllocator();
michael@0 643 gBreakpadAllocator = NULL;
michael@0 644 }
michael@0 645
michael@0 646 delete gMasterAllocator;
michael@0 647 gMasterAllocator = NULL;
michael@0 648
michael@0 649 return NULL;
michael@0 650 }
michael@0 651
michael@0 652 //=============================================================================
michael@0 653 void BreakpadRelease(BreakpadRef ref) {
michael@0 654 try {
michael@0 655 Breakpad *breakpad = (Breakpad *)ref;
michael@0 656
michael@0 657 if (gMasterAllocator) {
michael@0 658 gMasterAllocator->Unprotect();
michael@0 659 gKeyValueAllocator->Unprotect();
michael@0 660 gBreakpadAllocator->Unprotect();
michael@0 661
michael@0 662 breakpad->~Breakpad();
michael@0 663
michael@0 664 // Unfortunately, it's not possible to deallocate this stuff
michael@0 665 // because the exception handling thread is still finishing up
michael@0 666 // asynchronously at this point... OK, it could be done with
michael@0 667 // locks, etc. But since BreakpadRelease() should usually only
michael@0 668 // be called right before the process exits, it's not worth
michael@0 669 // deallocating this stuff.
michael@0 670 #if 0
michael@0 671 gKeyValueAllocator->~ProtectedMemoryAllocator();
michael@0 672 gBreakpadAllocator->~ProtectedMemoryAllocator();
michael@0 673 delete gMasterAllocator;
michael@0 674
michael@0 675 gMasterAllocator = NULL;
michael@0 676 gKeyValueAllocator = NULL;
michael@0 677 gBreakpadAllocator = NULL;
michael@0 678 #endif
michael@0 679
michael@0 680 pthread_mutex_destroy(&gDictionaryMutex);
michael@0 681 }
michael@0 682 } catch(...) { // don't let exceptions leave this C API
michael@0 683 fprintf(stderr, "BreakpadRelease() : error\n");
michael@0 684 }
michael@0 685 }
michael@0 686
michael@0 687 //=============================================================================
michael@0 688 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
michael@0 689 try {
michael@0 690 // Not called at exception time
michael@0 691 Breakpad *breakpad = (Breakpad *)ref;
michael@0 692
michael@0 693 if (breakpad && key && gKeyValueAllocator) {
michael@0 694 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
michael@0 695
michael@0 696 breakpad->SetKeyValue(key, value);
michael@0 697 }
michael@0 698 } catch(...) { // don't let exceptions leave this C API
michael@0 699 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
michael@0 700 }
michael@0 701 }
michael@0 702
michael@0 703 void BreakpadAddUploadParameter(BreakpadRef ref,
michael@0 704 NSString *key,
michael@0 705 NSString *value) {
michael@0 706 // The only difference, internally, between an upload parameter and
michael@0 707 // a key value one that is set with BreakpadSetKeyValue is that we
michael@0 708 // prepend the keyname with a special prefix. This informs the
michael@0 709 // crash sender that the parameter should be sent along with the
michael@0 710 // POST of the crash dump upload.
michael@0 711 try {
michael@0 712 Breakpad *breakpad = (Breakpad *)ref;
michael@0 713
michael@0 714 if (breakpad && key && gKeyValueAllocator) {
michael@0 715 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
michael@0 716
michael@0 717 NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
michael@0 718 stringByAppendingString:key];
michael@0 719 breakpad->SetKeyValue(prefixedKey, value);
michael@0 720 }
michael@0 721 } catch(...) { // don't let exceptions leave this C API
michael@0 722 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
michael@0 723 }
michael@0 724 }
michael@0 725
michael@0 726 void BreakpadRemoveUploadParameter(BreakpadRef ref,
michael@0 727 NSString *key) {
michael@0 728 try {
michael@0 729 // Not called at exception time
michael@0 730 Breakpad *breakpad = (Breakpad *)ref;
michael@0 731
michael@0 732 if (breakpad && key && gKeyValueAllocator) {
michael@0 733 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
michael@0 734
michael@0 735 NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
michael@0 736 @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
michael@0 737 breakpad->RemoveKeyValue(prefixedKey);
michael@0 738 }
michael@0 739 } catch(...) { // don't let exceptions leave this C API
michael@0 740 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
michael@0 741 }
michael@0 742 }
michael@0 743 //=============================================================================
michael@0 744 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
michael@0 745 NSString *value = nil;
michael@0 746
michael@0 747 try {
michael@0 748 // Not called at exception time
michael@0 749 Breakpad *breakpad = (Breakpad *)ref;
michael@0 750
michael@0 751 if (!breakpad || !key || !gKeyValueAllocator)
michael@0 752 return nil;
michael@0 753
michael@0 754 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
michael@0 755
michael@0 756 value = breakpad->KeyValue(key);
michael@0 757 } catch(...) { // don't let exceptions leave this C API
michael@0 758 fprintf(stderr, "BreakpadKeyValue() : error\n");
michael@0 759 }
michael@0 760
michael@0 761 return value;
michael@0 762 }
michael@0 763
michael@0 764 //=============================================================================
michael@0 765 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
michael@0 766 try {
michael@0 767 // Not called at exception time
michael@0 768 Breakpad *breakpad = (Breakpad *)ref;
michael@0 769
michael@0 770 if (breakpad && key && gKeyValueAllocator) {
michael@0 771 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
michael@0 772
michael@0 773 breakpad->RemoveKeyValue(key);
michael@0 774 }
michael@0 775 } catch(...) { // don't let exceptions leave this C API
michael@0 776 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
michael@0 777 }
michael@0 778 }
michael@0 779
michael@0 780 //=============================================================================
michael@0 781 bool BreakpadHasCrashReportToUpload(BreakpadRef ref) {
michael@0 782 try {
michael@0 783 // Not called at exception time
michael@0 784 Breakpad *breakpad = (Breakpad *)ref;
michael@0 785
michael@0 786 if (breakpad) {
michael@0 787 return breakpad->NextCrashReportToUpload() != 0;
michael@0 788 }
michael@0 789 } catch(...) { // don't let exceptions leave this C API
michael@0 790 fprintf(stderr, "BreakpadHasCrashReportToUpload() : error\n");
michael@0 791 }
michael@0 792 return false;
michael@0 793 }
michael@0 794
michael@0 795 //=============================================================================
michael@0 796 void BreakpadUploadNextReport(BreakpadRef ref) {
michael@0 797 try {
michael@0 798 // Not called at exception time
michael@0 799 Breakpad *breakpad = (Breakpad *)ref;
michael@0 800
michael@0 801 if (breakpad) {
michael@0 802 breakpad->UploadNextReport();
michael@0 803 }
michael@0 804 } catch(...) { // don't let exceptions leave this C API
michael@0 805 fprintf(stderr, "BreakpadUploadNextReport() : error\n");
michael@0 806 }
michael@0 807 }
michael@0 808
michael@0 809 //=============================================================================
michael@0 810 void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
michael@0 811 NSDictionary *server_parameters) {
michael@0 812 try {
michael@0 813 // Not called at exception time
michael@0 814 Breakpad *breakpad = (Breakpad *)ref;
michael@0 815
michael@0 816 if (breakpad) {
michael@0 817 breakpad->UploadData(data, name, server_parameters);
michael@0 818 }
michael@0 819 } catch(...) { // don't let exceptions leave this C API
michael@0 820 fprintf(stderr, "BreakpadUploadData() : error\n");
michael@0 821 }
michael@0 822 }
michael@0 823
michael@0 824 //=============================================================================
michael@0 825 NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
michael@0 826 NSDictionary *server_parameters) {
michael@0 827 try {
michael@0 828 // Not called at exception time
michael@0 829 Breakpad *breakpad = (Breakpad *)ref;
michael@0 830
michael@0 831 if (breakpad) {
michael@0 832 return breakpad->GenerateReport(server_parameters);
michael@0 833 } else {
michael@0 834 return nil;
michael@0 835 }
michael@0 836 } catch(...) { // don't let exceptions leave this C API
michael@0 837 fprintf(stderr, "BreakpadGenerateReport() : error\n");
michael@0 838 return nil;
michael@0 839 }
michael@0 840 }

mercurial