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