1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29// 30 31#define VERBOSE 0 32 33#if VERBOSE 34 static bool gDebugLog = true; 35#else 36 static bool gDebugLog = false; 37#endif 38 39#define DEBUGLOG if (gDebugLog) fprintf 40#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" 41 42#import "common/mac/MachIPC.h" 43#import "common/mac/SimpleStringDictionary.h" 44 45#import "client/mac/crash_generation/Inspector.h" 46#import "client/mac/handler/exception_handler.h" 47#import "client/mac/Framework/Breakpad.h" 48#import "client/mac/Framework/OnDemandServer.h" 49#import "client/mac/handler/protected_memory_allocator.h" 50 51#import <sys/stat.h> 52#import <sys/sysctl.h> 53 54#import <Foundation/Foundation.h> 55 56 57using google_breakpad::KeyValueEntry; 58using google_breakpad::MachPortSender; 59using google_breakpad::MachReceiveMessage; 60using google_breakpad::MachSendMessage; 61using google_breakpad::ReceivePort; 62using google_breakpad::SimpleStringDictionary; 63using google_breakpad::SimpleStringDictionaryIterator; 64 65//============================================================================= 66// We want any memory allocations which are used by breakpad during the 67// exception handling process (after a crash has happened) to be read-only 68// to prevent them from being smashed before a crash occurs. Unfortunately 69// we cannot protect against smashes to our exception handling thread's 70// stack. 71// 72// NOTE: Any memory allocations which are not used during the exception 73// handling process may be allocated in the normal ways. 74// 75// The ProtectedMemoryAllocator class provides an Allocate() method which 76// we'll using in conjunction with placement operator new() to control 77// allocation of C++ objects. Note that we don't use operator delete() 78// but instead call the objects destructor directly: object->~ClassName(); 79// 80ProtectedMemoryAllocator *gMasterAllocator = NULL; 81ProtectedMemoryAllocator *gKeyValueAllocator = NULL; 82ProtectedMemoryAllocator *gBreakpadAllocator = NULL; 83 84// Mutex for thread-safe access to the key/value dictionary used by breakpad. 85// It's a global instead of an instance variable of Breakpad 86// since it can't live in a protected memory area. 87pthread_mutex_t gDictionaryMutex; 88 89//============================================================================= 90// Stack-based object for thread-safe access to a memory-protected region. 91// It's assumed that normally the memory block (allocated by the allocator) 92// is protected (read-only). Creating a stack-based instance of 93// ProtectedMemoryLocker will unprotect this block after taking the lock. 94// Its destructor will first re-protect the memory then release the lock. 95class ProtectedMemoryLocker { 96public: 97 // allocator may be NULL, in which case no Protect() or Unprotect() calls 98 // will be made, but a lock will still be taken 99 ProtectedMemoryLocker(pthread_mutex_t *mutex, 100 ProtectedMemoryAllocator *allocator) 101 : mutex_(mutex), allocator_(allocator) { 102 // Lock the mutex 103 assert(pthread_mutex_lock(mutex_) == 0); 104 105 // Unprotect the memory 106 if (allocator_ ) { 107 allocator_->Unprotect(); 108 } 109 } 110 111 ~ProtectedMemoryLocker() { 112 // First protect the memory 113 if (allocator_) { 114 allocator_->Protect(); 115 } 116 117 // Then unlock the mutex 118 assert(pthread_mutex_unlock(mutex_) == 0); 119 }; 120 121private: 122 // Keep anybody from ever creating one of these things not on the stack. 123 ProtectedMemoryLocker() { } 124 ProtectedMemoryLocker(const ProtectedMemoryLocker&); 125 ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&); 126 127 pthread_mutex_t *mutex_; 128 ProtectedMemoryAllocator *allocator_; 129}; 130 131//============================================================================= 132class Breakpad { 133 public: 134 // factory method 135 static Breakpad *Create(NSDictionary *parameters) { 136 // Allocate from our special allocation pool 137 Breakpad *breakpad = 138 new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) 139 Breakpad(); 140 141 if (!breakpad) 142 return NULL; 143 144 if (!breakpad->Initialize(parameters)) { 145 // Don't use operator delete() here since we allocated from special pool 146 breakpad->~Breakpad(); 147 return NULL; 148 } 149 150 return breakpad; 151 } 152 153 ~Breakpad(); 154 155 void SetKeyValue(NSString *key, NSString *value); 156 NSString *KeyValue(NSString *key); 157 void RemoveKeyValue(NSString *key); 158 159 void GenerateAndSendReport(); 160 161 void SetFilterCallback(BreakpadFilterCallback callback, void *context) { 162 filter_callback_ = callback; 163 filter_callback_context_ = context; 164 } 165 166 private: 167 Breakpad() 168 : handler_(NULL), 169 config_params_(NULL), 170 send_and_exit_(true), 171 filter_callback_(NULL), 172 filter_callback_context_(NULL) { 173 inspector_path_[0] = 0; 174 } 175 176 bool Initialize(NSDictionary *parameters); 177 178 bool ExtractParameters(NSDictionary *parameters); 179 180 // Dispatches to HandleException() 181 static bool ExceptionHandlerDirectCallback(void *context, 182 int exception_type, 183 int exception_code, 184 int exception_subcode, 185 mach_port_t crashing_thread); 186 187 bool HandleException(int exception_type, 188 int exception_code, 189 int exception_subcode, 190 mach_port_t crashing_thread); 191 192 // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's 193 // MachineExceptions.h, we have to explicitly name the handler. 194 google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) 195 196 char inspector_path_[PATH_MAX]; // Path to inspector tool 197 198 SimpleStringDictionary *config_params_; // Create parameters (STRONG) 199 200 OnDemandServer inspector_; 201 202 bool send_and_exit_; // Exit after sending, if true 203 204 BreakpadFilterCallback filter_callback_; 205 void *filter_callback_context_; 206}; 207 208#pragma mark - 209#pragma mark Helper functions 210 211//============================================================================= 212// Helper functions 213 214//============================================================================= 215static BOOL IsDebuggerActive() { 216 BOOL result = NO; 217 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; 218 219 // We check both defaults and the environment variable here 220 221 BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; 222 223 if (!ignoreDebugger) { 224 char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); 225 ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; 226 } 227 228 if (!ignoreDebugger) { 229 pid_t pid = getpid(); 230 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 231 int mibSize = sizeof(mib) / sizeof(int); 232 size_t actualSize; 233 234 if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { 235 struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); 236 237 if (info) { 238 // This comes from looking at the Darwin xnu Kernel 239 if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) 240 result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; 241 242 free(info); 243 } 244 } 245 } 246 247 return result; 248} 249 250//============================================================================= 251bool Breakpad::ExceptionHandlerDirectCallback(void *context, 252 int exception_type, 253 int exception_code, 254 int exception_subcode, 255 mach_port_t crashing_thread) { 256 Breakpad *breakpad = (Breakpad *)context; 257 258 // If our context is damaged or something, just return false to indicate that 259 // the handler should continue without us. 260 if (!breakpad) 261 return false; 262 263 return breakpad->HandleException( exception_type, 264 exception_code, 265 exception_subcode, 266 crashing_thread); 267} 268 269//============================================================================= 270#pragma mark - 271 272#include <dlfcn.h> 273 274//============================================================================= 275// Returns the pathname to the Resources directory for this version of 276// Breakpad which we are now running. 277// 278// Don't make the function static, since _dyld_lookup_and_bind_fully needs a 279// simple non-static C name 280// 281extern "C" { 282NSString * GetResourcePath(); 283NSString * GetResourcePath() { 284 NSString *resourcePath = nil; 285 286 // If there are multiple breakpads installed then calling bundleWithIdentifier 287 // will not work properly, so only use that as a backup plan. 288 // We want to find the bundle containing the code where this function lives 289 // and work from there 290 // 291 292 // Get the pathname to the code which contains this function 293 Dl_info info; 294 if (dladdr((const void*)GetResourcePath, &info) != 0) { 295 NSFileManager *filemgr = [NSFileManager defaultManager]; 296 NSString *filePath = 297 [filemgr stringWithFileSystemRepresentation:info.dli_fname 298 length:strlen(info.dli_fname)]; 299 NSString *bundlePath = [filePath stringByDeletingLastPathComponent]; 300 // The "Resources" directory should be in the same directory as the 301 // executable code, since that's how the Breakpad framework is built. 302 resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"]; 303 } else { 304 DEBUGLOG(stderr, "Could not find GetResourcePath\n"); 305 // fallback plan 306 NSBundle *bundle = 307 [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; 308 resourcePath = [bundle resourcePath]; 309 } 310 311 return resourcePath; 312} 313} // extern "C" 314 315//============================================================================= 316bool Breakpad::Initialize(NSDictionary *parameters) { 317 // Initialize 318 config_params_ = NULL; 319 handler_ = NULL; 320 321 // Check for debugger 322 if (IsDebuggerActive()) { 323 DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); 324 return true; 325 } 326 327 // Gather any user specified parameters 328 if (!ExtractParameters(parameters)) { 329 return false; 330 } 331 332 // Get path to Inspector executable. 333 NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); 334 335 // Standardize path (resolve symlinkes, etc.) and escape spaces 336 inspectorPathString = [inspectorPathString stringByStandardizingPath]; 337 inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] 338 componentsJoinedByString:@"\\ "]; 339 340 // Create an on-demand server object representing the Inspector. 341 // In case of a crash, we simply need to call the LaunchOnDemand() 342 // method on it, then send a mach message to its service port. 343 // It will then launch and perform a process inspection of our crashed state. 344 // See the HandleException() method for the details. 345#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" 346 347 name_t portName; 348 snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); 349 350 // Save the location of the Inspector 351 strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], 352 sizeof(inspector_path_)); 353 354 // Append a single command-line argument to the Inspector path 355 // representing the bootstrap name of the launch-on-demand receive port. 356 // When the Inspector is launched, it can use this to lookup the port 357 // by calling bootstrap_check_in(). 358 strlcat(inspector_path_, " ", sizeof(inspector_path_)); 359 strlcat(inspector_path_, portName, sizeof(inspector_path_)); 360 361 kern_return_t kr = inspector_.Initialize(inspector_path_, 362 portName, 363 true); // shutdown on exit 364 365 if (kr != KERN_SUCCESS) { 366 return false; 367 } 368 369 // Create the handler (allocating it in our special protected pool) 370 handler_ = 371 new (gBreakpadAllocator->Allocate( 372 sizeof(google_breakpad::ExceptionHandler))) 373 google_breakpad::ExceptionHandler( 374 Breakpad::ExceptionHandlerDirectCallback, this, true); 375 return true; 376} 377 378//============================================================================= 379Breakpad::~Breakpad() { 380 // Note that we don't use operator delete() on these pointers, 381 // since they were allocated by ProtectedMemoryAllocator objects. 382 // 383 if (config_params_) { 384 config_params_->~SimpleStringDictionary(); 385 } 386 387 if (handler_) 388 handler_->~ExceptionHandler(); 389} 390 391//============================================================================= 392bool Breakpad::ExtractParameters(NSDictionary *parameters) { 393 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; 394 NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; 395 NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; 396 397 NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; 398 NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; 399 NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; 400 NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; 401 NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; 402 NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; 403 NSString *inspectorPathString = 404 [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; 405 NSString *reporterPathString = 406 [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; 407 NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; 408 NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; 409 NSString *logFileTailSize = 410 [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; 411 NSString *requestUserText = 412 [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; 413 NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; 414 NSString *vendor = 415 [parameters objectForKey:@BREAKPAD_VENDOR]; 416 NSString *dumpSubdirectory = 417 [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; 418 419 NSDictionary *serverParameters = 420 [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; 421 422 // These may have been set above as user prefs, which take priority. 423 if (!skipConfirm) { 424 skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; 425 } 426 if (!sendAndExit) { 427 sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; 428 } 429 430 if (!product) 431 product = [parameters objectForKey:@"CFBundleName"]; 432 433 if (!display) { 434 display = [parameters objectForKey:@"CFBundleDisplayName"]; 435 if (!display) { 436 display = product; 437 } 438 } 439 440 if (!version) 441 version = [parameters objectForKey:@"CFBundleVersion"]; 442 443 if (!interval) 444 interval = @"3600"; 445 446 if (!timeout) 447 timeout = @"300"; 448 449 if (!logFileTailSize) 450 logFileTailSize = @"200000"; 451 452 if (!vendor) { 453 vendor = @"Vendor not specified"; 454 } 455 456 // Normalize the values. 457 if (skipConfirm) { 458 skipConfirm = [skipConfirm uppercaseString]; 459 460 if ([skipConfirm isEqualToString:@"YES"] || 461 [skipConfirm isEqualToString:@"TRUE"] || 462 [skipConfirm isEqualToString:@"1"]) 463 skipConfirm = @"YES"; 464 else 465 skipConfirm = @"NO"; 466 } else { 467 skipConfirm = @"NO"; 468 } 469 470 send_and_exit_ = true; 471 if (sendAndExit) { 472 sendAndExit = [sendAndExit uppercaseString]; 473 474 if ([sendAndExit isEqualToString:@"NO"] || 475 [sendAndExit isEqualToString:@"FALSE"] || 476 [sendAndExit isEqualToString:@"0"]) 477 send_and_exit_ = false; 478 } 479 480 if (requestUserText) { 481 requestUserText = [requestUserText uppercaseString]; 482 483 if ([requestUserText isEqualToString:@"YES"] || 484 [requestUserText isEqualToString:@"TRUE"] || 485 [requestUserText isEqualToString:@"1"]) 486 requestUserText = @"YES"; 487 else 488 requestUserText = @"NO"; 489 } else { 490 requestUserText = @"NO"; 491 } 492 493 // Find the helper applications if not specified in user config. 494 NSString *resourcePath = nil; 495 if (!inspectorPathString || !reporterPathString) { 496 resourcePath = GetResourcePath(); 497 if (!resourcePath) { 498 DEBUGLOG(stderr, "Could not get resource path\n"); 499 return false; 500 } 501 } 502 503 // Find Inspector. 504 if (!inspectorPathString) { 505 inspectorPathString = 506 [resourcePath stringByAppendingPathComponent:@"Inspector"]; 507 } 508 509 // Verify that there is an Inspector tool. 510 if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { 511 DEBUGLOG(stderr, "Cannot find Inspector tool\n"); 512 return false; 513 } 514 515 // Find Reporter. 516 if (!reporterPathString) { 517 reporterPathString = 518 [resourcePath 519 stringByAppendingPathComponent:@"crash_report_sender.app"]; 520 reporterPathString = 521 [[NSBundle bundleWithPath:reporterPathString] executablePath]; 522 } 523 524 // Verify that there is a Reporter application. 525 if (![[NSFileManager defaultManager] 526 fileExistsAtPath:reporterPathString]) { 527 DEBUGLOG(stderr, "Cannot find Reporter tool\n"); 528 return false; 529 } 530 531 if (!dumpSubdirectory) { 532 dumpSubdirectory = @""; 533 } 534 535 // The product, version, and URL are required values. 536 if (![product length]) { 537 DEBUGLOG(stderr, "Missing required product key.\n"); 538 return false; 539 } 540 541 if (![version length]) { 542 DEBUGLOG(stderr, "Missing required version key.\n"); 543 return false; 544 } 545 546 if (![urlStr length]) { 547 DEBUGLOG(stderr, "Missing required URL key.\n"); 548 return false; 549 } 550 551 config_params_ = 552 new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) 553 SimpleStringDictionary(); 554 555 SimpleStringDictionary &dictionary = *config_params_; 556 557 dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); 558 dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); 559 dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); 560 dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); 561 dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); 562 dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); 563 dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); 564 dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); 565 dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, 566 [inspectorPathString fileSystemRepresentation]); 567 dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, 568 [reporterPathString fileSystemRepresentation]); 569 dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, 570 [logFileTailSize UTF8String]); 571 dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, 572 [requestUserText UTF8String]); 573 dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]); 574 dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); 575 dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, 576 [dumpSubdirectory UTF8String]); 577 578 struct timeval tv; 579 gettimeofday(&tv, NULL); 580 char timeStartedString[32]; 581 sprintf(timeStartedString, "%zd", tv.tv_sec); 582 dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, 583 timeStartedString); 584 585 if (logFilePaths) { 586 char logFileKey[255]; 587 for(unsigned int i = 0; i < [logFilePaths count]; i++) { 588 sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); 589 dictionary.SetKeyValue(logFileKey, 590 [[logFilePaths objectAtIndex:i] 591 fileSystemRepresentation]); 592 } 593 } 594 595 if (serverParameters) { 596 // For each key-value pair, call BreakpadAddUploadParameter() 597 NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; 598 NSString *aParameter; 599 while ((aParameter = [keyEnumerator nextObject])) { 600 BreakpadAddUploadParameter(this, aParameter, 601 [serverParameters objectForKey:aParameter]); 602 } 603 } 604 return true; 605} 606 607//============================================================================= 608void Breakpad::SetKeyValue(NSString *key, NSString *value) { 609 // We allow nil values. This is the same as removing the keyvalue. 610 if (!config_params_ || !key) 611 return; 612 613 config_params_->SetKeyValue([key UTF8String], [value UTF8String]); 614} 615 616//============================================================================= 617NSString *Breakpad::KeyValue(NSString *key) { 618 if (!config_params_ || !key) 619 return nil; 620 621 const char *value = config_params_->GetValueForKey([key UTF8String]); 622 return value ? [NSString stringWithUTF8String:value] : nil; 623} 624 625//============================================================================= 626void Breakpad::RemoveKeyValue(NSString *key) { 627 if (!config_params_ || !key) return; 628 629 config_params_->RemoveKey([key UTF8String]); 630} 631 632//============================================================================= 633void Breakpad::GenerateAndSendReport() { 634 config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); 635 HandleException(0, 0, 0, mach_thread_self()); 636 config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); 637} 638 639//============================================================================= 640bool Breakpad::HandleException(int exception_type, 641 int exception_code, 642 int exception_subcode, 643 mach_port_t crashing_thread) { 644 DEBUGLOG(stderr, "Breakpad: an exception occurred\n"); 645 646 if (filter_callback_) { 647 bool should_handle = filter_callback_(exception_type, 648 exception_code, 649 crashing_thread, 650 filter_callback_context_); 651 if (!should_handle) return false; 652 } 653 654 // We need to reset the memory protections to be read/write, 655 // since LaunchOnDemand() requires changing state. 656 gBreakpadAllocator->Unprotect(); 657 // Configure the server to launch when we message the service port. 658 // The reason we do this here, rather than at startup, is that we 659 // can leak a bootstrap service entry if this method is called and 660 // there never ends up being a crash. 661 inspector_.LaunchOnDemand(); 662 gBreakpadAllocator->Protect(); 663 664 // The Inspector should send a message to this port to verify it 665 // received our information and has finished the inspection. 666 ReceivePort acknowledge_port; 667 668 // Send initial information to the Inspector. 669 MachSendMessage message(kMsgType_InspectorInitialInfo); 670 message.AddDescriptor(mach_task_self()); // our task 671 message.AddDescriptor(crashing_thread); // crashing thread 672 message.AddDescriptor(mach_thread_self()); // exception-handling thread 673 message.AddDescriptor(acknowledge_port.GetPort());// message receive port 674 675 InspectorInfo info; 676 info.exception_type = exception_type; 677 info.exception_code = exception_code; 678 info.exception_subcode = exception_subcode; 679 info.parameter_count = config_params_->GetCount(); 680 message.SetData(&info, sizeof(info)); 681 682 MachPortSender sender(inspector_.GetServicePort()); 683 684 kern_return_t result = sender.SendMessage(message, 2000); 685 686 if (result == KERN_SUCCESS) { 687 // Now, send a series of key-value pairs to the Inspector. 688 const KeyValueEntry *entry = NULL; 689 SimpleStringDictionaryIterator iter(*config_params_); 690 691 while ( (entry = iter.Next()) ) { 692 KeyValueMessageData keyvalue_data(*entry); 693 694 MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); 695 keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); 696 697 result = sender.SendMessage(keyvalue_message, 2000); 698 699 if (result != KERN_SUCCESS) { 700 break; 701 } 702 } 703 704 if (result == KERN_SUCCESS) { 705 // Wait for acknowledgement that the inspection has finished. 706 MachReceiveMessage acknowledge_messsage; 707 result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); 708 } 709 } 710 711#if VERBOSE 712 PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); 713 printf("Breakpad: Inspector service port = %#x\n", 714 inspector_.GetServicePort()); 715#endif 716 717 // If we don't want any forwarding, return true here to indicate that we've 718 // processed things as much as we want. 719 if (send_and_exit_) return true; 720 721 return false; 722} 723 724//============================================================================= 725//============================================================================= 726 727#pragma mark - 728#pragma mark Public API 729 730//============================================================================= 731BreakpadRef BreakpadCreate(NSDictionary *parameters) { 732 try { 733 // This is confusing. Our two main allocators for breakpad memory are: 734 // - gKeyValueAllocator for the key/value memory 735 // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other 736 // breakpad allocations which are accessed at exception handling time. 737 // 738 // But in order to avoid these two allocators themselves from being smashed, 739 // we'll protect them as well by allocating them with gMasterAllocator. 740 // 741 // gMasterAllocator itself will NOT be protected, but this doesn't matter, 742 // since once it does its allocations and locks the memory, smashes to itself 743 // don't affect anything we care about. 744 gMasterAllocator = 745 new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); 746 747 gKeyValueAllocator = 748 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 749 ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); 750 751 // Create a mutex for use in accessing the SimpleStringDictionary 752 int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); 753 if (mutexResult == 0) { 754 755 // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. 756 // Let's round up to the nearest page size. 757 // 758 int breakpad_pool_size = 4096; 759 760 /* 761 sizeof(Breakpad) 762 + sizeof(google_breakpad::ExceptionHandler) 763 + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) 764 */ 765 766 gBreakpadAllocator = 767 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 768 ProtectedMemoryAllocator(breakpad_pool_size); 769 770 // Stack-based autorelease pool for Breakpad::Create() obj-c code. 771 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 772 Breakpad *breakpad = Breakpad::Create(parameters); 773 774 if (breakpad) { 775 // Make read-only to protect against memory smashers 776 gMasterAllocator->Protect(); 777 gKeyValueAllocator->Protect(); 778 gBreakpadAllocator->Protect(); 779 // Can uncomment this line to figure out how much space was actually 780 // allocated using this allocator 781 // printf("gBreakpadAllocator allocated size = %d\n", 782 // gBreakpadAllocator->GetAllocatedSize() ); 783 [pool release]; 784 return (BreakpadRef)breakpad; 785 } 786 787 [pool release]; 788 } 789 } catch(...) { // don't let exceptions leave this C API 790 fprintf(stderr, "BreakpadCreate() : error\n"); 791 } 792 793 if (gKeyValueAllocator) { 794 gKeyValueAllocator->~ProtectedMemoryAllocator(); 795 gKeyValueAllocator = NULL; 796 } 797 798 if (gBreakpadAllocator) { 799 gBreakpadAllocator->~ProtectedMemoryAllocator(); 800 gBreakpadAllocator = NULL; 801 } 802 803 delete gMasterAllocator; 804 gMasterAllocator = NULL; 805 806 return NULL; 807} 808 809//============================================================================= 810void BreakpadRelease(BreakpadRef ref) { 811 try { 812 Breakpad *breakpad = (Breakpad *)ref; 813 814 if (gMasterAllocator) { 815 gMasterAllocator->Unprotect(); 816 gKeyValueAllocator->Unprotect(); 817 gBreakpadAllocator->Unprotect(); 818 819 breakpad->~Breakpad(); 820 821 // Unfortunately, it's not possible to deallocate this stuff 822 // because the exception handling thread is still finishing up 823 // asynchronously at this point... OK, it could be done with 824 // locks, etc. But since BreakpadRelease() should usually only 825 // be called right before the process exits, it's not worth 826 // deallocating this stuff. 827#if 0 828 gKeyValueAllocator->~ProtectedMemoryAllocator(); 829 gBreakpadAllocator->~ProtectedMemoryAllocator(); 830 delete gMasterAllocator; 831 832 gMasterAllocator = NULL; 833 gKeyValueAllocator = NULL; 834 gBreakpadAllocator = NULL; 835#endif 836 837 pthread_mutex_destroy(&gDictionaryMutex); 838 } 839 } catch(...) { // don't let exceptions leave this C API 840 fprintf(stderr, "BreakpadRelease() : error\n"); 841 } 842} 843 844//============================================================================= 845void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { 846 try { 847 // Not called at exception time 848 Breakpad *breakpad = (Breakpad *)ref; 849 850 if (breakpad && key && gKeyValueAllocator) { 851 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 852 853 breakpad->SetKeyValue(key, value); 854 } 855 } catch(...) { // don't let exceptions leave this C API 856 fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 857 } 858} 859 860void BreakpadAddUploadParameter(BreakpadRef ref, 861 NSString *key, 862 NSString *value) { 863 // The only difference, internally, between an upload parameter and 864 // a key value one that is set with BreakpadSetKeyValue is that we 865 // prepend the keyname with a special prefix. This informs the 866 // crash sender that the parameter should be sent along with the 867 // POST of the crash dump upload. 868 try { 869 Breakpad *breakpad = (Breakpad *)ref; 870 871 if (breakpad && key && gKeyValueAllocator) { 872 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 873 874 NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX 875 stringByAppendingString:key]; 876 breakpad->SetKeyValue(prefixedKey, value); 877 } 878 } catch(...) { // don't let exceptions leave this C API 879 fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 880 } 881} 882 883void BreakpadRemoveUploadParameter(BreakpadRef ref, 884 NSString *key) { 885 try { 886 // Not called at exception time 887 Breakpad *breakpad = (Breakpad *)ref; 888 889 if (breakpad && key && gKeyValueAllocator) { 890 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 891 892 NSString *prefixedKey = [NSString stringWithFormat:@"%@%@", 893 @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; 894 breakpad->RemoveKeyValue(prefixedKey); 895 } 896 } catch(...) { // don't let exceptions leave this C API 897 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 898 } 899} 900//============================================================================= 901NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { 902 NSString *value = nil; 903 904 try { 905 // Not called at exception time 906 Breakpad *breakpad = (Breakpad *)ref; 907 908 if (!breakpad || !key || !gKeyValueAllocator) 909 return nil; 910 911 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 912 913 value = breakpad->KeyValue(key); 914 } catch(...) { // don't let exceptions leave this C API 915 fprintf(stderr, "BreakpadKeyValue() : error\n"); 916 } 917 918 return value; 919} 920 921//============================================================================= 922void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { 923 try { 924 // Not called at exception time 925 Breakpad *breakpad = (Breakpad *)ref; 926 927 if (breakpad && key && gKeyValueAllocator) { 928 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 929 930 breakpad->RemoveKeyValue(key); 931 } 932 } catch(...) { // don't let exceptions leave this C API 933 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 934 } 935} 936 937//============================================================================= 938void BreakpadGenerateAndSendReport(BreakpadRef ref) { 939 try { 940 Breakpad *breakpad = (Breakpad *)ref; 941 942 if (breakpad && gKeyValueAllocator) { 943 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 944 945 gBreakpadAllocator->Unprotect(); 946 breakpad->GenerateAndSendReport(); 947 gBreakpadAllocator->Protect(); 948 } 949 } catch(...) { // don't let exceptions leave this C API 950 fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); 951 } 952} 953 954//============================================================================= 955void BreakpadSetFilterCallback(BreakpadRef ref, 956 BreakpadFilterCallback callback, 957 void *context) { 958 959 try { 960 Breakpad *breakpad = (Breakpad *)ref; 961 962 if (breakpad && gBreakpadAllocator) { 963 // share the dictionary mutex here (we really don't need a mutex) 964 ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); 965 966 breakpad->SetFilterCallback(callback, context); 967 } 968 } catch(...) { // don't let exceptions leave this C API 969 fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); 970 } 971} 972 973//============================================================================ 974void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { 975 int logFileCounter = 0; 976 977 NSString *logFileKey = [NSString stringWithFormat:@"%@%d", 978 @BREAKPAD_LOGFILE_KEY_PREFIX, 979 logFileCounter]; 980 981 NSString *existingLogFilename = nil; 982 existingLogFilename = BreakpadKeyValue(ref, logFileKey); 983 // Find the first log file key that we can use by testing for existence 984 while (existingLogFilename) { 985 if ([existingLogFilename isEqualToString:logPathname]) { 986 return; 987 } 988 logFileCounter++; 989 logFileKey = [NSString stringWithFormat:@"%@%d", 990 @BREAKPAD_LOGFILE_KEY_PREFIX, 991 logFileCounter]; 992 existingLogFilename = BreakpadKeyValue(ref, logFileKey); 993 } 994 995 BreakpadSetKeyValue(ref, logFileKey, logPathname); 996} 997