1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1008 @@ 1.4 +// Copyright (c) 2006, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 +// 1.33 + 1.34 +#define VERBOSE 0 1.35 + 1.36 +#if VERBOSE 1.37 + static bool gDebugLog = true; 1.38 +#else 1.39 + static bool gDebugLog = false; 1.40 +#endif 1.41 + 1.42 +#define DEBUGLOG if (gDebugLog) fprintf 1.43 +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" 1.44 + 1.45 +#import "client/mac/Framework/Breakpad.h" 1.46 + 1.47 +#import <Foundation/Foundation.h> 1.48 +#include <pthread.h> 1.49 +#include <sys/stat.h> 1.50 +#include <sys/sysctl.h> 1.51 + 1.52 +#import "client/mac/crash_generation/Inspector.h" 1.53 +#import "client/mac/handler/exception_handler.h" 1.54 +#import "client/mac/Framework/Breakpad.h" 1.55 +#import "client/mac/Framework/OnDemandServer.h" 1.56 +#import "client/mac/handler/protected_memory_allocator.h" 1.57 +#import "common/mac/MachIPC.h" 1.58 +#import "common/mac/SimpleStringDictionary.h" 1.59 + 1.60 +#ifndef __EXCEPTIONS 1.61 +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from 1.62 +// <c++/4.2.1/exception_defines.h> allowing this file to work properly with 1.63 +// exceptions disabled even when other C++ libraries are used. #undef the try 1.64 +// and catch macros first in case libstdc++ is in use and has already provided 1.65 +// its own definitions. 1.66 +#undef try 1.67 +#define try if (true) 1.68 +#undef catch 1.69 +#define catch(X) if (false) 1.70 +#endif // __EXCEPTIONS 1.71 + 1.72 +using google_breakpad::KeyValueEntry; 1.73 +using google_breakpad::MachPortSender; 1.74 +using google_breakpad::MachReceiveMessage; 1.75 +using google_breakpad::MachSendMessage; 1.76 +using google_breakpad::ReceivePort; 1.77 +using google_breakpad::SimpleStringDictionary; 1.78 +using google_breakpad::SimpleStringDictionaryIterator; 1.79 + 1.80 +//============================================================================= 1.81 +// We want any memory allocations which are used by breakpad during the 1.82 +// exception handling process (after a crash has happened) to be read-only 1.83 +// to prevent them from being smashed before a crash occurs. Unfortunately 1.84 +// we cannot protect against smashes to our exception handling thread's 1.85 +// stack. 1.86 +// 1.87 +// NOTE: Any memory allocations which are not used during the exception 1.88 +// handling process may be allocated in the normal ways. 1.89 +// 1.90 +// The ProtectedMemoryAllocator class provides an Allocate() method which 1.91 +// we'll using in conjunction with placement operator new() to control 1.92 +// allocation of C++ objects. Note that we don't use operator delete() 1.93 +// but instead call the objects destructor directly: object->~ClassName(); 1.94 +// 1.95 +ProtectedMemoryAllocator *gMasterAllocator = NULL; 1.96 +ProtectedMemoryAllocator *gKeyValueAllocator = NULL; 1.97 +ProtectedMemoryAllocator *gBreakpadAllocator = NULL; 1.98 + 1.99 +// Mutex for thread-safe access to the key/value dictionary used by breakpad. 1.100 +// It's a global instead of an instance variable of Breakpad 1.101 +// since it can't live in a protected memory area. 1.102 +pthread_mutex_t gDictionaryMutex; 1.103 + 1.104 +//============================================================================= 1.105 +// Stack-based object for thread-safe access to a memory-protected region. 1.106 +// It's assumed that normally the memory block (allocated by the allocator) 1.107 +// is protected (read-only). Creating a stack-based instance of 1.108 +// ProtectedMemoryLocker will unprotect this block after taking the lock. 1.109 +// Its destructor will first re-protect the memory then release the lock. 1.110 +class ProtectedMemoryLocker { 1.111 +public: 1.112 + // allocator may be NULL, in which case no Protect() or Unprotect() calls 1.113 + // will be made, but a lock will still be taken 1.114 + ProtectedMemoryLocker(pthread_mutex_t *mutex, 1.115 + ProtectedMemoryAllocator *allocator) 1.116 + : mutex_(mutex), allocator_(allocator) { 1.117 + // Lock the mutex 1.118 + assert(pthread_mutex_lock(mutex_) == 0); 1.119 + 1.120 + // Unprotect the memory 1.121 + if (allocator_ ) { 1.122 + allocator_->Unprotect(); 1.123 + } 1.124 + } 1.125 + 1.126 + ~ProtectedMemoryLocker() { 1.127 + // First protect the memory 1.128 + if (allocator_) { 1.129 + allocator_->Protect(); 1.130 + } 1.131 + 1.132 + // Then unlock the mutex 1.133 + assert(pthread_mutex_unlock(mutex_) == 0); 1.134 + }; 1.135 + 1.136 +private: 1.137 + // Keep anybody from ever creating one of these things not on the stack. 1.138 + ProtectedMemoryLocker() { } 1.139 + ProtectedMemoryLocker(const ProtectedMemoryLocker&); 1.140 + ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&); 1.141 + 1.142 + pthread_mutex_t *mutex_; 1.143 + ProtectedMemoryAllocator *allocator_; 1.144 +}; 1.145 + 1.146 +//============================================================================= 1.147 +class Breakpad { 1.148 + public: 1.149 + // factory method 1.150 + static Breakpad *Create(NSDictionary *parameters) { 1.151 + // Allocate from our special allocation pool 1.152 + Breakpad *breakpad = 1.153 + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) 1.154 + Breakpad(); 1.155 + 1.156 + if (!breakpad) 1.157 + return NULL; 1.158 + 1.159 + if (!breakpad->Initialize(parameters)) { 1.160 + // Don't use operator delete() here since we allocated from special pool 1.161 + breakpad->~Breakpad(); 1.162 + return NULL; 1.163 + } 1.164 + 1.165 + return breakpad; 1.166 + } 1.167 + 1.168 + ~Breakpad(); 1.169 + 1.170 + void SetKeyValue(NSString *key, NSString *value); 1.171 + NSString *KeyValue(NSString *key); 1.172 + void RemoveKeyValue(NSString *key); 1.173 + 1.174 + void GenerateAndSendReport(); 1.175 + 1.176 + void SetFilterCallback(BreakpadFilterCallback callback, void *context) { 1.177 + filter_callback_ = callback; 1.178 + filter_callback_context_ = context; 1.179 + } 1.180 + 1.181 + private: 1.182 + Breakpad() 1.183 + : handler_(NULL), 1.184 + config_params_(NULL), 1.185 + send_and_exit_(true), 1.186 + filter_callback_(NULL), 1.187 + filter_callback_context_(NULL) { 1.188 + inspector_path_[0] = 0; 1.189 + } 1.190 + 1.191 + bool Initialize(NSDictionary *parameters); 1.192 + 1.193 + bool ExtractParameters(NSDictionary *parameters); 1.194 + 1.195 + // Dispatches to HandleException() 1.196 + static bool ExceptionHandlerDirectCallback(void *context, 1.197 + int exception_type, 1.198 + int exception_code, 1.199 + int exception_subcode, 1.200 + mach_port_t crashing_thread); 1.201 + 1.202 + bool HandleException(int exception_type, 1.203 + int exception_code, 1.204 + int exception_subcode, 1.205 + mach_port_t crashing_thread); 1.206 + 1.207 + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's 1.208 + // MachineExceptions.h, we have to explicitly name the handler. 1.209 + google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) 1.210 + 1.211 + char inspector_path_[PATH_MAX]; // Path to inspector tool 1.212 + 1.213 + SimpleStringDictionary *config_params_; // Create parameters (STRONG) 1.214 + 1.215 + OnDemandServer inspector_; 1.216 + 1.217 + bool send_and_exit_; // Exit after sending, if true 1.218 + 1.219 + BreakpadFilterCallback filter_callback_; 1.220 + void *filter_callback_context_; 1.221 +}; 1.222 + 1.223 +#pragma mark - 1.224 +#pragma mark Helper functions 1.225 + 1.226 +//============================================================================= 1.227 +// Helper functions 1.228 + 1.229 +//============================================================================= 1.230 +static BOOL IsDebuggerActive() { 1.231 + BOOL result = NO; 1.232 + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; 1.233 + 1.234 + // We check both defaults and the environment variable here 1.235 + 1.236 + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; 1.237 + 1.238 + if (!ignoreDebugger) { 1.239 + char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); 1.240 + ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; 1.241 + } 1.242 + 1.243 + if (!ignoreDebugger) { 1.244 + pid_t pid = getpid(); 1.245 + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 1.246 + int mibSize = sizeof(mib) / sizeof(int); 1.247 + size_t actualSize; 1.248 + 1.249 + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { 1.250 + struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); 1.251 + 1.252 + if (info) { 1.253 + // This comes from looking at the Darwin xnu Kernel 1.254 + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) 1.255 + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; 1.256 + 1.257 + free(info); 1.258 + } 1.259 + } 1.260 + } 1.261 + 1.262 + return result; 1.263 +} 1.264 + 1.265 +//============================================================================= 1.266 +bool Breakpad::ExceptionHandlerDirectCallback(void *context, 1.267 + int exception_type, 1.268 + int exception_code, 1.269 + int exception_subcode, 1.270 + mach_port_t crashing_thread) { 1.271 + Breakpad *breakpad = (Breakpad *)context; 1.272 + 1.273 + // If our context is damaged or something, just return false to indicate that 1.274 + // the handler should continue without us. 1.275 + if (!breakpad) 1.276 + return false; 1.277 + 1.278 + return breakpad->HandleException( exception_type, 1.279 + exception_code, 1.280 + exception_subcode, 1.281 + crashing_thread); 1.282 +} 1.283 + 1.284 +//============================================================================= 1.285 +#pragma mark - 1.286 + 1.287 +#include <dlfcn.h> 1.288 + 1.289 +//============================================================================= 1.290 +// Returns the pathname to the Resources directory for this version of 1.291 +// Breakpad which we are now running. 1.292 +// 1.293 +// Don't make the function static, since _dyld_lookup_and_bind_fully needs a 1.294 +// simple non-static C name 1.295 +// 1.296 +extern "C" { 1.297 +NSString * GetResourcePath(); 1.298 +NSString * GetResourcePath() { 1.299 + NSString *resourcePath = nil; 1.300 + 1.301 + // If there are multiple breakpads installed then calling bundleWithIdentifier 1.302 + // will not work properly, so only use that as a backup plan. 1.303 + // We want to find the bundle containing the code where this function lives 1.304 + // and work from there 1.305 + // 1.306 + 1.307 + // Get the pathname to the code which contains this function 1.308 + Dl_info info; 1.309 + if (dladdr((const void*)GetResourcePath, &info) != 0) { 1.310 + NSFileManager *filemgr = [NSFileManager defaultManager]; 1.311 + NSString *filePath = 1.312 + [filemgr stringWithFileSystemRepresentation:info.dli_fname 1.313 + length:strlen(info.dli_fname)]; 1.314 + NSString *bundlePath = [filePath stringByDeletingLastPathComponent]; 1.315 + // The "Resources" directory should be in the same directory as the 1.316 + // executable code, since that's how the Breakpad framework is built. 1.317 + resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"]; 1.318 + } else { 1.319 + DEBUGLOG(stderr, "Could not find GetResourcePath\n"); 1.320 + // fallback plan 1.321 + NSBundle *bundle = 1.322 + [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; 1.323 + resourcePath = [bundle resourcePath]; 1.324 + } 1.325 + 1.326 + return resourcePath; 1.327 +} 1.328 +} // extern "C" 1.329 + 1.330 +//============================================================================= 1.331 +bool Breakpad::Initialize(NSDictionary *parameters) { 1.332 + // Initialize 1.333 + config_params_ = NULL; 1.334 + handler_ = NULL; 1.335 + 1.336 + // Check for debugger 1.337 + if (IsDebuggerActive()) { 1.338 + DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); 1.339 + return true; 1.340 + } 1.341 + 1.342 + // Gather any user specified parameters 1.343 + if (!ExtractParameters(parameters)) { 1.344 + return false; 1.345 + } 1.346 + 1.347 + // Get path to Inspector executable. 1.348 + NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); 1.349 + 1.350 + // Standardize path (resolve symlinkes, etc.) and escape spaces 1.351 + inspectorPathString = [inspectorPathString stringByStandardizingPath]; 1.352 + inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] 1.353 + componentsJoinedByString:@"\\ "]; 1.354 + 1.355 + // Create an on-demand server object representing the Inspector. 1.356 + // In case of a crash, we simply need to call the LaunchOnDemand() 1.357 + // method on it, then send a mach message to its service port. 1.358 + // It will then launch and perform a process inspection of our crashed state. 1.359 + // See the HandleException() method for the details. 1.360 +#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" 1.361 + 1.362 + name_t portName; 1.363 + snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); 1.364 + 1.365 + // Save the location of the Inspector 1.366 + strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], 1.367 + sizeof(inspector_path_)); 1.368 + 1.369 + // Append a single command-line argument to the Inspector path 1.370 + // representing the bootstrap name of the launch-on-demand receive port. 1.371 + // When the Inspector is launched, it can use this to lookup the port 1.372 + // by calling bootstrap_check_in(). 1.373 + strlcat(inspector_path_, " ", sizeof(inspector_path_)); 1.374 + strlcat(inspector_path_, portName, sizeof(inspector_path_)); 1.375 + 1.376 + kern_return_t kr = inspector_.Initialize(inspector_path_, 1.377 + portName, 1.378 + true); // shutdown on exit 1.379 + 1.380 + if (kr != KERN_SUCCESS) { 1.381 + return false; 1.382 + } 1.383 + 1.384 + // Create the handler (allocating it in our special protected pool) 1.385 + handler_ = 1.386 + new (gBreakpadAllocator->Allocate( 1.387 + sizeof(google_breakpad::ExceptionHandler))) 1.388 + google_breakpad::ExceptionHandler( 1.389 + Breakpad::ExceptionHandlerDirectCallback, this, true); 1.390 + return true; 1.391 +} 1.392 + 1.393 +//============================================================================= 1.394 +Breakpad::~Breakpad() { 1.395 + // Note that we don't use operator delete() on these pointers, 1.396 + // since they were allocated by ProtectedMemoryAllocator objects. 1.397 + // 1.398 + if (config_params_) { 1.399 + config_params_->~SimpleStringDictionary(); 1.400 + } 1.401 + 1.402 + if (handler_) 1.403 + handler_->~ExceptionHandler(); 1.404 +} 1.405 + 1.406 +//============================================================================= 1.407 +bool Breakpad::ExtractParameters(NSDictionary *parameters) { 1.408 + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; 1.409 + NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; 1.410 + NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; 1.411 + 1.412 + NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; 1.413 + NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; 1.414 + NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; 1.415 + NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; 1.416 + NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; 1.417 + NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; 1.418 + NSString *inspectorPathString = 1.419 + [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; 1.420 + NSString *reporterPathString = 1.421 + [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; 1.422 + NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; 1.423 + NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; 1.424 + NSString *logFileTailSize = 1.425 + [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; 1.426 + NSString *requestUserText = 1.427 + [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; 1.428 + NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; 1.429 + NSString *vendor = 1.430 + [parameters objectForKey:@BREAKPAD_VENDOR]; 1.431 + NSString *dumpSubdirectory = 1.432 + [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; 1.433 + 1.434 + NSDictionary *serverParameters = 1.435 + [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; 1.436 + 1.437 + // These may have been set above as user prefs, which take priority. 1.438 + if (!skipConfirm) { 1.439 + skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; 1.440 + } 1.441 + if (!sendAndExit) { 1.442 + sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; 1.443 + } 1.444 + 1.445 + if (!product) 1.446 + product = [parameters objectForKey:@"CFBundleName"]; 1.447 + 1.448 + if (!display) { 1.449 + display = [parameters objectForKey:@"CFBundleDisplayName"]; 1.450 + if (!display) { 1.451 + display = product; 1.452 + } 1.453 + } 1.454 + 1.455 + if (!version) 1.456 + version = [parameters objectForKey:@"CFBundleVersion"]; 1.457 + 1.458 + if (!interval) 1.459 + interval = @"3600"; 1.460 + 1.461 + if (!timeout) 1.462 + timeout = @"300"; 1.463 + 1.464 + if (!logFileTailSize) 1.465 + logFileTailSize = @"200000"; 1.466 + 1.467 + if (!vendor) { 1.468 + vendor = @"Vendor not specified"; 1.469 + } 1.470 + 1.471 + // Normalize the values. 1.472 + if (skipConfirm) { 1.473 + skipConfirm = [skipConfirm uppercaseString]; 1.474 + 1.475 + if ([skipConfirm isEqualToString:@"YES"] || 1.476 + [skipConfirm isEqualToString:@"TRUE"] || 1.477 + [skipConfirm isEqualToString:@"1"]) 1.478 + skipConfirm = @"YES"; 1.479 + else 1.480 + skipConfirm = @"NO"; 1.481 + } else { 1.482 + skipConfirm = @"NO"; 1.483 + } 1.484 + 1.485 + send_and_exit_ = true; 1.486 + if (sendAndExit) { 1.487 + sendAndExit = [sendAndExit uppercaseString]; 1.488 + 1.489 + if ([sendAndExit isEqualToString:@"NO"] || 1.490 + [sendAndExit isEqualToString:@"FALSE"] || 1.491 + [sendAndExit isEqualToString:@"0"]) 1.492 + send_and_exit_ = false; 1.493 + } 1.494 + 1.495 + if (requestUserText) { 1.496 + requestUserText = [requestUserText uppercaseString]; 1.497 + 1.498 + if ([requestUserText isEqualToString:@"YES"] || 1.499 + [requestUserText isEqualToString:@"TRUE"] || 1.500 + [requestUserText isEqualToString:@"1"]) 1.501 + requestUserText = @"YES"; 1.502 + else 1.503 + requestUserText = @"NO"; 1.504 + } else { 1.505 + requestUserText = @"NO"; 1.506 + } 1.507 + 1.508 + // Find the helper applications if not specified in user config. 1.509 + NSString *resourcePath = nil; 1.510 + if (!inspectorPathString || !reporterPathString) { 1.511 + resourcePath = GetResourcePath(); 1.512 + if (!resourcePath) { 1.513 + DEBUGLOG(stderr, "Could not get resource path\n"); 1.514 + return false; 1.515 + } 1.516 + } 1.517 + 1.518 + // Find Inspector. 1.519 + if (!inspectorPathString) { 1.520 + inspectorPathString = 1.521 + [resourcePath stringByAppendingPathComponent:@"Inspector"]; 1.522 + } 1.523 + 1.524 + // Verify that there is an Inspector tool. 1.525 + if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { 1.526 + DEBUGLOG(stderr, "Cannot find Inspector tool\n"); 1.527 + return false; 1.528 + } 1.529 + 1.530 + // Find Reporter. 1.531 + if (!reporterPathString) { 1.532 + reporterPathString = 1.533 + [resourcePath 1.534 + stringByAppendingPathComponent:@"crash_report_sender.app"]; 1.535 + reporterPathString = 1.536 + [[NSBundle bundleWithPath:reporterPathString] executablePath]; 1.537 + } 1.538 + 1.539 + // Verify that there is a Reporter application. 1.540 + if (![[NSFileManager defaultManager] 1.541 + fileExistsAtPath:reporterPathString]) { 1.542 + DEBUGLOG(stderr, "Cannot find Reporter tool\n"); 1.543 + return false; 1.544 + } 1.545 + 1.546 + if (!dumpSubdirectory) { 1.547 + dumpSubdirectory = @""; 1.548 + } 1.549 + 1.550 + // The product, version, and URL are required values. 1.551 + if (![product length]) { 1.552 + DEBUGLOG(stderr, "Missing required product key.\n"); 1.553 + return false; 1.554 + } 1.555 + 1.556 + if (![version length]) { 1.557 + DEBUGLOG(stderr, "Missing required version key.\n"); 1.558 + return false; 1.559 + } 1.560 + 1.561 + if (![urlStr length]) { 1.562 + DEBUGLOG(stderr, "Missing required URL key.\n"); 1.563 + return false; 1.564 + } 1.565 + 1.566 + config_params_ = 1.567 + new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) 1.568 + SimpleStringDictionary(); 1.569 + 1.570 + SimpleStringDictionary &dictionary = *config_params_; 1.571 + 1.572 + dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); 1.573 + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); 1.574 + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); 1.575 + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); 1.576 + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); 1.577 + dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); 1.578 + dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); 1.579 + dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); 1.580 + dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, 1.581 + [inspectorPathString fileSystemRepresentation]); 1.582 + dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, 1.583 + [reporterPathString fileSystemRepresentation]); 1.584 + dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, 1.585 + [logFileTailSize UTF8String]); 1.586 + dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, 1.587 + [requestUserText UTF8String]); 1.588 + dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]); 1.589 + dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); 1.590 + dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, 1.591 + [dumpSubdirectory UTF8String]); 1.592 + 1.593 + struct timeval tv; 1.594 + gettimeofday(&tv, NULL); 1.595 + char timeStartedString[32]; 1.596 + sprintf(timeStartedString, "%zd", tv.tv_sec); 1.597 + dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, 1.598 + timeStartedString); 1.599 + 1.600 + if (logFilePaths) { 1.601 + char logFileKey[255]; 1.602 + for(unsigned int i = 0; i < [logFilePaths count]; i++) { 1.603 + sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); 1.604 + dictionary.SetKeyValue(logFileKey, 1.605 + [[logFilePaths objectAtIndex:i] 1.606 + fileSystemRepresentation]); 1.607 + } 1.608 + } 1.609 + 1.610 + if (serverParameters) { 1.611 + // For each key-value pair, call BreakpadAddUploadParameter() 1.612 + NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; 1.613 + NSString *aParameter; 1.614 + while ((aParameter = [keyEnumerator nextObject])) { 1.615 + BreakpadAddUploadParameter(this, aParameter, 1.616 + [serverParameters objectForKey:aParameter]); 1.617 + } 1.618 + } 1.619 + return true; 1.620 +} 1.621 + 1.622 +//============================================================================= 1.623 +void Breakpad::SetKeyValue(NSString *key, NSString *value) { 1.624 + // We allow nil values. This is the same as removing the keyvalue. 1.625 + if (!config_params_ || !key) 1.626 + return; 1.627 + 1.628 + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); 1.629 +} 1.630 + 1.631 +//============================================================================= 1.632 +NSString *Breakpad::KeyValue(NSString *key) { 1.633 + if (!config_params_ || !key) 1.634 + return nil; 1.635 + 1.636 + const char *value = config_params_->GetValueForKey([key UTF8String]); 1.637 + return value ? [NSString stringWithUTF8String:value] : nil; 1.638 +} 1.639 + 1.640 +//============================================================================= 1.641 +void Breakpad::RemoveKeyValue(NSString *key) { 1.642 + if (!config_params_ || !key) return; 1.643 + 1.644 + config_params_->RemoveKey([key UTF8String]); 1.645 +} 1.646 + 1.647 +//============================================================================= 1.648 +void Breakpad::GenerateAndSendReport() { 1.649 + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); 1.650 + HandleException(0, 0, 0, mach_thread_self()); 1.651 + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); 1.652 +} 1.653 + 1.654 +//============================================================================= 1.655 +bool Breakpad::HandleException(int exception_type, 1.656 + int exception_code, 1.657 + int exception_subcode, 1.658 + mach_port_t crashing_thread) { 1.659 + DEBUGLOG(stderr, "Breakpad: an exception occurred\n"); 1.660 + 1.661 + if (filter_callback_) { 1.662 + bool should_handle = filter_callback_(exception_type, 1.663 + exception_code, 1.664 + crashing_thread, 1.665 + filter_callback_context_); 1.666 + if (!should_handle) return false; 1.667 + } 1.668 + 1.669 + // We need to reset the memory protections to be read/write, 1.670 + // since LaunchOnDemand() requires changing state. 1.671 + gBreakpadAllocator->Unprotect(); 1.672 + // Configure the server to launch when we message the service port. 1.673 + // The reason we do this here, rather than at startup, is that we 1.674 + // can leak a bootstrap service entry if this method is called and 1.675 + // there never ends up being a crash. 1.676 + inspector_.LaunchOnDemand(); 1.677 + gBreakpadAllocator->Protect(); 1.678 + 1.679 + // The Inspector should send a message to this port to verify it 1.680 + // received our information and has finished the inspection. 1.681 + ReceivePort acknowledge_port; 1.682 + 1.683 + // Send initial information to the Inspector. 1.684 + MachSendMessage message(kMsgType_InspectorInitialInfo); 1.685 + message.AddDescriptor(mach_task_self()); // our task 1.686 + message.AddDescriptor(crashing_thread); // crashing thread 1.687 + message.AddDescriptor(mach_thread_self()); // exception-handling thread 1.688 + message.AddDescriptor(acknowledge_port.GetPort());// message receive port 1.689 + 1.690 + InspectorInfo info; 1.691 + info.exception_type = exception_type; 1.692 + info.exception_code = exception_code; 1.693 + info.exception_subcode = exception_subcode; 1.694 + info.parameter_count = config_params_->GetCount(); 1.695 + message.SetData(&info, sizeof(info)); 1.696 + 1.697 + MachPortSender sender(inspector_.GetServicePort()); 1.698 + 1.699 + kern_return_t result = sender.SendMessage(message, 2000); 1.700 + 1.701 + if (result == KERN_SUCCESS) { 1.702 + // Now, send a series of key-value pairs to the Inspector. 1.703 + const KeyValueEntry *entry = NULL; 1.704 + SimpleStringDictionaryIterator iter(*config_params_); 1.705 + 1.706 + while ( (entry = iter.Next()) ) { 1.707 + KeyValueMessageData keyvalue_data(*entry); 1.708 + 1.709 + MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); 1.710 + keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); 1.711 + 1.712 + result = sender.SendMessage(keyvalue_message, 2000); 1.713 + 1.714 + if (result != KERN_SUCCESS) { 1.715 + break; 1.716 + } 1.717 + } 1.718 + 1.719 + if (result == KERN_SUCCESS) { 1.720 + // Wait for acknowledgement that the inspection has finished. 1.721 + MachReceiveMessage acknowledge_messsage; 1.722 + result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); 1.723 + } 1.724 + } 1.725 + 1.726 +#if VERBOSE 1.727 + PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); 1.728 + printf("Breakpad: Inspector service port = %#x\n", 1.729 + inspector_.GetServicePort()); 1.730 +#endif 1.731 + 1.732 + // If we don't want any forwarding, return true here to indicate that we've 1.733 + // processed things as much as we want. 1.734 + if (send_and_exit_) return true; 1.735 + 1.736 + return false; 1.737 +} 1.738 + 1.739 +//============================================================================= 1.740 +//============================================================================= 1.741 + 1.742 +#pragma mark - 1.743 +#pragma mark Public API 1.744 + 1.745 +//============================================================================= 1.746 +BreakpadRef BreakpadCreate(NSDictionary *parameters) { 1.747 + try { 1.748 + // This is confusing. Our two main allocators for breakpad memory are: 1.749 + // - gKeyValueAllocator for the key/value memory 1.750 + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other 1.751 + // breakpad allocations which are accessed at exception handling time. 1.752 + // 1.753 + // But in order to avoid these two allocators themselves from being smashed, 1.754 + // we'll protect them as well by allocating them with gMasterAllocator. 1.755 + // 1.756 + // gMasterAllocator itself will NOT be protected, but this doesn't matter, 1.757 + // since once it does its allocations and locks the memory, smashes to itself 1.758 + // don't affect anything we care about. 1.759 + gMasterAllocator = 1.760 + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); 1.761 + 1.762 + gKeyValueAllocator = 1.763 + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 1.764 + ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); 1.765 + 1.766 + // Create a mutex for use in accessing the SimpleStringDictionary 1.767 + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); 1.768 + if (mutexResult == 0) { 1.769 + 1.770 + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. 1.771 + // Let's round up to the nearest page size. 1.772 + // 1.773 + int breakpad_pool_size = 4096; 1.774 + 1.775 + /* 1.776 + sizeof(Breakpad) 1.777 + + sizeof(google_breakpad::ExceptionHandler) 1.778 + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) 1.779 + */ 1.780 + 1.781 + gBreakpadAllocator = 1.782 + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 1.783 + ProtectedMemoryAllocator(breakpad_pool_size); 1.784 + 1.785 + // Stack-based autorelease pool for Breakpad::Create() obj-c code. 1.786 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1.787 + Breakpad *breakpad = Breakpad::Create(parameters); 1.788 + 1.789 + if (breakpad) { 1.790 + // Make read-only to protect against memory smashers 1.791 + gMasterAllocator->Protect(); 1.792 + gKeyValueAllocator->Protect(); 1.793 + gBreakpadAllocator->Protect(); 1.794 + // Can uncomment this line to figure out how much space was actually 1.795 + // allocated using this allocator 1.796 + // printf("gBreakpadAllocator allocated size = %d\n", 1.797 + // gBreakpadAllocator->GetAllocatedSize() ); 1.798 + [pool release]; 1.799 + return (BreakpadRef)breakpad; 1.800 + } 1.801 + 1.802 + [pool release]; 1.803 + } 1.804 + } catch(...) { // don't let exceptions leave this C API 1.805 + fprintf(stderr, "BreakpadCreate() : error\n"); 1.806 + } 1.807 + 1.808 + if (gKeyValueAllocator) { 1.809 + gKeyValueAllocator->~ProtectedMemoryAllocator(); 1.810 + gKeyValueAllocator = NULL; 1.811 + } 1.812 + 1.813 + if (gBreakpadAllocator) { 1.814 + gBreakpadAllocator->~ProtectedMemoryAllocator(); 1.815 + gBreakpadAllocator = NULL; 1.816 + } 1.817 + 1.818 + delete gMasterAllocator; 1.819 + gMasterAllocator = NULL; 1.820 + 1.821 + return NULL; 1.822 +} 1.823 + 1.824 +//============================================================================= 1.825 +void BreakpadRelease(BreakpadRef ref) { 1.826 + try { 1.827 + Breakpad *breakpad = (Breakpad *)ref; 1.828 + 1.829 + if (gMasterAllocator) { 1.830 + gMasterAllocator->Unprotect(); 1.831 + gKeyValueAllocator->Unprotect(); 1.832 + gBreakpadAllocator->Unprotect(); 1.833 + 1.834 + breakpad->~Breakpad(); 1.835 + 1.836 + // Unfortunately, it's not possible to deallocate this stuff 1.837 + // because the exception handling thread is still finishing up 1.838 + // asynchronously at this point... OK, it could be done with 1.839 + // locks, etc. But since BreakpadRelease() should usually only 1.840 + // be called right before the process exits, it's not worth 1.841 + // deallocating this stuff. 1.842 +#if 0 1.843 + gKeyValueAllocator->~ProtectedMemoryAllocator(); 1.844 + gBreakpadAllocator->~ProtectedMemoryAllocator(); 1.845 + delete gMasterAllocator; 1.846 + 1.847 + gMasterAllocator = NULL; 1.848 + gKeyValueAllocator = NULL; 1.849 + gBreakpadAllocator = NULL; 1.850 +#endif 1.851 + 1.852 + pthread_mutex_destroy(&gDictionaryMutex); 1.853 + } 1.854 + } catch(...) { // don't let exceptions leave this C API 1.855 + fprintf(stderr, "BreakpadRelease() : error\n"); 1.856 + } 1.857 +} 1.858 + 1.859 +//============================================================================= 1.860 +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { 1.861 + try { 1.862 + // Not called at exception time 1.863 + Breakpad *breakpad = (Breakpad *)ref; 1.864 + 1.865 + if (breakpad && key && gKeyValueAllocator) { 1.866 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.867 + 1.868 + breakpad->SetKeyValue(key, value); 1.869 + } 1.870 + } catch(...) { // don't let exceptions leave this C API 1.871 + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 1.872 + } 1.873 +} 1.874 + 1.875 +void BreakpadAddUploadParameter(BreakpadRef ref, 1.876 + NSString *key, 1.877 + NSString *value) { 1.878 + // The only difference, internally, between an upload parameter and 1.879 + // a key value one that is set with BreakpadSetKeyValue is that we 1.880 + // prepend the keyname with a special prefix. This informs the 1.881 + // crash sender that the parameter should be sent along with the 1.882 + // POST of the crash dump upload. 1.883 + try { 1.884 + Breakpad *breakpad = (Breakpad *)ref; 1.885 + 1.886 + if (breakpad && key && gKeyValueAllocator) { 1.887 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.888 + 1.889 + NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX 1.890 + stringByAppendingString:key]; 1.891 + breakpad->SetKeyValue(prefixedKey, value); 1.892 + } 1.893 + } catch(...) { // don't let exceptions leave this C API 1.894 + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 1.895 + } 1.896 +} 1.897 + 1.898 +void BreakpadRemoveUploadParameter(BreakpadRef ref, 1.899 + NSString *key) { 1.900 + try { 1.901 + // Not called at exception time 1.902 + Breakpad *breakpad = (Breakpad *)ref; 1.903 + 1.904 + if (breakpad && key && gKeyValueAllocator) { 1.905 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.906 + 1.907 + NSString *prefixedKey = [NSString stringWithFormat:@"%@%@", 1.908 + @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; 1.909 + breakpad->RemoveKeyValue(prefixedKey); 1.910 + } 1.911 + } catch(...) { // don't let exceptions leave this C API 1.912 + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 1.913 + } 1.914 +} 1.915 +//============================================================================= 1.916 +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { 1.917 + NSString *value = nil; 1.918 + 1.919 + try { 1.920 + // Not called at exception time 1.921 + Breakpad *breakpad = (Breakpad *)ref; 1.922 + 1.923 + if (!breakpad || !key || !gKeyValueAllocator) 1.924 + return nil; 1.925 + 1.926 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.927 + 1.928 + value = breakpad->KeyValue(key); 1.929 + } catch(...) { // don't let exceptions leave this C API 1.930 + fprintf(stderr, "BreakpadKeyValue() : error\n"); 1.931 + } 1.932 + 1.933 + return value; 1.934 +} 1.935 + 1.936 +//============================================================================= 1.937 +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { 1.938 + try { 1.939 + // Not called at exception time 1.940 + Breakpad *breakpad = (Breakpad *)ref; 1.941 + 1.942 + if (breakpad && key && gKeyValueAllocator) { 1.943 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.944 + 1.945 + breakpad->RemoveKeyValue(key); 1.946 + } 1.947 + } catch(...) { // don't let exceptions leave this C API 1.948 + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 1.949 + } 1.950 +} 1.951 + 1.952 +//============================================================================= 1.953 +void BreakpadGenerateAndSendReport(BreakpadRef ref) { 1.954 + try { 1.955 + Breakpad *breakpad = (Breakpad *)ref; 1.956 + 1.957 + if (breakpad && gKeyValueAllocator) { 1.958 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.959 + 1.960 + gBreakpadAllocator->Unprotect(); 1.961 + breakpad->GenerateAndSendReport(); 1.962 + gBreakpadAllocator->Protect(); 1.963 + } 1.964 + } catch(...) { // don't let exceptions leave this C API 1.965 + fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); 1.966 + } 1.967 +} 1.968 + 1.969 +//============================================================================= 1.970 +void BreakpadSetFilterCallback(BreakpadRef ref, 1.971 + BreakpadFilterCallback callback, 1.972 + void *context) { 1.973 + 1.974 + try { 1.975 + Breakpad *breakpad = (Breakpad *)ref; 1.976 + 1.977 + if (breakpad && gBreakpadAllocator) { 1.978 + // share the dictionary mutex here (we really don't need a mutex) 1.979 + ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); 1.980 + 1.981 + breakpad->SetFilterCallback(callback, context); 1.982 + } 1.983 + } catch(...) { // don't let exceptions leave this C API 1.984 + fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); 1.985 + } 1.986 +} 1.987 + 1.988 +//============================================================================ 1.989 +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { 1.990 + int logFileCounter = 0; 1.991 + 1.992 + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", 1.993 + @BREAKPAD_LOGFILE_KEY_PREFIX, 1.994 + logFileCounter]; 1.995 + 1.996 + NSString *existingLogFilename = nil; 1.997 + existingLogFilename = BreakpadKeyValue(ref, logFileKey); 1.998 + // Find the first log file key that we can use by testing for existence 1.999 + while (existingLogFilename) { 1.1000 + if ([existingLogFilename isEqualToString:logPathname]) { 1.1001 + return; 1.1002 + } 1.1003 + logFileCounter++; 1.1004 + logFileKey = [NSString stringWithFormat:@"%@%d", 1.1005 + @BREAKPAD_LOGFILE_KEY_PREFIX, 1.1006 + logFileCounter]; 1.1007 + existingLogFilename = BreakpadKeyValue(ref, logFileKey); 1.1008 + } 1.1009 + 1.1010 + BreakpadSetKeyValue(ref, logFileKey, logPathname); 1.1011 +}