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