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