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