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