1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,840 @@ 1.4 +// Copyright (c) 2011, 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 +#define VERBOSE 0 1.34 + 1.35 +#if VERBOSE 1.36 + static bool gDebugLog = true; 1.37 +#else 1.38 + static bool gDebugLog = false; 1.39 +#endif 1.40 + 1.41 +#define DEBUGLOG if (gDebugLog) fprintf 1.42 +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" 1.43 + 1.44 +#import "client/ios/Breakpad.h" 1.45 + 1.46 +#import <Foundation/Foundation.h> 1.47 +#include <pthread.h> 1.48 +#include <sys/stat.h> 1.49 +#include <sys/sysctl.h> 1.50 + 1.51 +#import "client/mac/crash_generation/ConfigFile.h" 1.52 +#import "client/mac/handler/exception_handler.h" 1.53 +#import "client/mac/handler/minidump_generator.h" 1.54 +#import "client/mac/sender/uploader.h" 1.55 +#import "common/mac/SimpleStringDictionary.h" 1.56 +#import "client/ios/handler/ios_exception_minidump_generator.h" 1.57 +#import "client/mac/handler/protected_memory_allocator.h" 1.58 + 1.59 +#ifndef __EXCEPTIONS 1.60 +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from 1.61 +// <c++/4.2.1/exception_defines.h> allowing this file to work properly with 1.62 +// exceptions disabled even when other C++ libraries are used. #undef the try 1.63 +// and catch macros first in case libstdc++ is in use and has already provided 1.64 +// its own definitions. 1.65 +#undef try 1.66 +#define try if (true) 1.67 +#undef catch 1.68 +#define catch(X) if (false) 1.69 +#endif // __EXCEPTIONS 1.70 + 1.71 +using google_breakpad::ConfigFile; 1.72 +using google_breakpad::EnsureDirectoryPathExists; 1.73 +using google_breakpad::KeyValueEntry; 1.74 +using google_breakpad::SimpleStringDictionary; 1.75 +using google_breakpad::SimpleStringDictionaryIterator; 1.76 + 1.77 +//============================================================================= 1.78 +// We want any memory allocations which are used by breakpad during the 1.79 +// exception handling process (after a crash has happened) to be read-only 1.80 +// to prevent them from being smashed before a crash occurs. Unfortunately 1.81 +// we cannot protect against smashes to our exception handling thread's 1.82 +// stack. 1.83 +// 1.84 +// NOTE: Any memory allocations which are not used during the exception 1.85 +// handling process may be allocated in the normal ways. 1.86 +// 1.87 +// The ProtectedMemoryAllocator class provides an Allocate() method which 1.88 +// we'll using in conjunction with placement operator new() to control 1.89 +// allocation of C++ objects. Note that we don't use operator delete() 1.90 +// but instead call the objects destructor directly: object->~ClassName(); 1.91 +// 1.92 +ProtectedMemoryAllocator *gMasterAllocator = NULL; 1.93 +ProtectedMemoryAllocator *gKeyValueAllocator = NULL; 1.94 +ProtectedMemoryAllocator *gBreakpadAllocator = NULL; 1.95 + 1.96 +// Mutex for thread-safe access to the key/value dictionary used by breakpad. 1.97 +// It's a global instead of an instance variable of Breakpad 1.98 +// since it can't live in a protected memory area. 1.99 +pthread_mutex_t gDictionaryMutex; 1.100 + 1.101 +//============================================================================= 1.102 +// Stack-based object for thread-safe access to a memory-protected region. 1.103 +// It's assumed that normally the memory block (allocated by the allocator) 1.104 +// is protected (read-only). Creating a stack-based instance of 1.105 +// ProtectedMemoryLocker will unprotect this block after taking the lock. 1.106 +// Its destructor will first re-protect the memory then release the lock. 1.107 +class ProtectedMemoryLocker { 1.108 +public: 1.109 + // allocator may be NULL, in which case no Protect() or Unprotect() calls 1.110 + // will be made, but a lock will still be taken 1.111 + ProtectedMemoryLocker(pthread_mutex_t *mutex, 1.112 + ProtectedMemoryAllocator *allocator) 1.113 + : mutex_(mutex), allocator_(allocator) { 1.114 + // Lock the mutex 1.115 + assert(pthread_mutex_lock(mutex_) == 0); 1.116 + 1.117 + // Unprotect the memory 1.118 + if (allocator_ ) { 1.119 + allocator_->Unprotect(); 1.120 + } 1.121 + } 1.122 + 1.123 + ~ProtectedMemoryLocker() { 1.124 + // First protect the memory 1.125 + if (allocator_) { 1.126 + allocator_->Protect(); 1.127 + } 1.128 + 1.129 + // Then unlock the mutex 1.130 + assert(pthread_mutex_unlock(mutex_) == 0); 1.131 + }; 1.132 + 1.133 +private: 1.134 + // Keep anybody from ever creating one of these things not on the stack. 1.135 + ProtectedMemoryLocker() { } 1.136 + ProtectedMemoryLocker(const ProtectedMemoryLocker&); 1.137 + ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&); 1.138 + 1.139 + pthread_mutex_t *mutex_; 1.140 + ProtectedMemoryAllocator *allocator_; 1.141 +}; 1.142 + 1.143 +//============================================================================= 1.144 +class Breakpad { 1.145 + public: 1.146 + // factory method 1.147 + static Breakpad *Create(NSDictionary *parameters) { 1.148 + // Allocate from our special allocation pool 1.149 + Breakpad *breakpad = 1.150 + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) 1.151 + Breakpad(); 1.152 + 1.153 + if (!breakpad) 1.154 + return NULL; 1.155 + 1.156 + if (!breakpad->Initialize(parameters)) { 1.157 + // Don't use operator delete() here since we allocated from special pool 1.158 + breakpad->~Breakpad(); 1.159 + return NULL; 1.160 + } 1.161 + 1.162 + return breakpad; 1.163 + } 1.164 + 1.165 + ~Breakpad(); 1.166 + 1.167 + void SetKeyValue(NSString *key, NSString *value); 1.168 + NSString *KeyValue(NSString *key); 1.169 + void RemoveKeyValue(NSString *key); 1.170 + NSString *NextCrashReportToUpload(); 1.171 + void UploadNextReport(); 1.172 + void UploadData(NSData *data, NSString *name, 1.173 + NSDictionary *server_parameters); 1.174 + NSDictionary *GenerateReport(NSDictionary *server_parameters); 1.175 + 1.176 + private: 1.177 + Breakpad() 1.178 + : handler_(NULL), 1.179 + config_params_(NULL) {} 1.180 + 1.181 + bool Initialize(NSDictionary *parameters); 1.182 + 1.183 + bool ExtractParameters(NSDictionary *parameters); 1.184 + 1.185 + // Dispatches to HandleMinidump() 1.186 + static bool HandleMinidumpCallback(const char *dump_dir, 1.187 + const char *minidump_id, 1.188 + void *context, bool succeeded); 1.189 + 1.190 + bool HandleMinidump(const char *dump_dir, 1.191 + const char *minidump_id); 1.192 + 1.193 + // NSException handler 1.194 + static void UncaughtExceptionHandler(NSException *exception); 1.195 + 1.196 + // Handle an uncaught NSException. 1.197 + void HandleUncaughtException(NSException *exception); 1.198 + 1.199 + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's 1.200 + // MachineExceptions.h, we have to explicitly name the handler. 1.201 + google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) 1.202 + 1.203 + SimpleStringDictionary *config_params_; // Create parameters (STRONG) 1.204 + 1.205 + ConfigFile config_file_; 1.206 + 1.207 + // A static reference to the current Breakpad instance. Used for handling 1.208 + // NSException. 1.209 + static Breakpad *current_breakpad_; 1.210 +}; 1.211 + 1.212 +Breakpad *Breakpad::current_breakpad_ = NULL; 1.213 + 1.214 +#pragma mark - 1.215 +#pragma mark Helper functions 1.216 + 1.217 +//============================================================================= 1.218 +// Helper functions 1.219 + 1.220 +//============================================================================= 1.221 +static BOOL IsDebuggerActive() { 1.222 + BOOL result = NO; 1.223 + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; 1.224 + 1.225 + // We check both defaults and the environment variable here 1.226 + 1.227 + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; 1.228 + 1.229 + if (!ignoreDebugger) { 1.230 + char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); 1.231 + ignoreDebugger = 1.232 + (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; 1.233 + } 1.234 + 1.235 + if (!ignoreDebugger) { 1.236 + pid_t pid = getpid(); 1.237 + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 1.238 + int mibSize = sizeof(mib) / sizeof(int); 1.239 + size_t actualSize; 1.240 + 1.241 + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { 1.242 + struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); 1.243 + 1.244 + if (info) { 1.245 + // This comes from looking at the Darwin xnu Kernel 1.246 + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) 1.247 + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; 1.248 + 1.249 + free(info); 1.250 + } 1.251 + } 1.252 + } 1.253 + 1.254 + return result; 1.255 +} 1.256 + 1.257 +//============================================================================= 1.258 +bool Breakpad::HandleMinidumpCallback(const char *dump_dir, 1.259 + const char *minidump_id, 1.260 + void *context, bool succeeded) { 1.261 + Breakpad *breakpad = (Breakpad *)context; 1.262 + 1.263 + // If our context is damaged or something, just return false to indicate that 1.264 + // the handler should continue without us. 1.265 + if (!breakpad || !succeeded) 1.266 + return false; 1.267 + 1.268 + return breakpad->HandleMinidump(dump_dir, minidump_id); 1.269 +} 1.270 + 1.271 +//============================================================================= 1.272 +void Breakpad::UncaughtExceptionHandler(NSException *exception) { 1.273 + NSSetUncaughtExceptionHandler(NULL); 1.274 + if (current_breakpad_) { 1.275 + current_breakpad_->HandleUncaughtException(exception); 1.276 + } 1.277 +} 1.278 + 1.279 +//============================================================================= 1.280 +#pragma mark - 1.281 + 1.282 +//============================================================================= 1.283 +bool Breakpad::Initialize(NSDictionary *parameters) { 1.284 + // Initialize 1.285 + current_breakpad_ = this; 1.286 + config_params_ = NULL; 1.287 + handler_ = NULL; 1.288 + 1.289 + // Gather any user specified parameters 1.290 + if (!ExtractParameters(parameters)) { 1.291 + return false; 1.292 + } 1.293 + 1.294 + // Check for debugger 1.295 + if (IsDebuggerActive()) { 1.296 + DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); 1.297 + return true; 1.298 + } 1.299 + 1.300 + // Create the handler (allocating it in our special protected pool) 1.301 + handler_ = 1.302 + new (gBreakpadAllocator->Allocate( 1.303 + sizeof(google_breakpad::ExceptionHandler))) 1.304 + google_breakpad::ExceptionHandler( 1.305 + config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY), 1.306 + 0, &HandleMinidumpCallback, this, true, 0); 1.307 + NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler); 1.308 + return true; 1.309 +} 1.310 + 1.311 +//============================================================================= 1.312 +Breakpad::~Breakpad() { 1.313 + NSSetUncaughtExceptionHandler(NULL); 1.314 + current_breakpad_ = NULL; 1.315 + // Note that we don't use operator delete() on these pointers, 1.316 + // since they were allocated by ProtectedMemoryAllocator objects. 1.317 + // 1.318 + if (config_params_) { 1.319 + config_params_->~SimpleStringDictionary(); 1.320 + } 1.321 + 1.322 + if (handler_) 1.323 + handler_->~ExceptionHandler(); 1.324 +} 1.325 + 1.326 +//============================================================================= 1.327 +bool Breakpad::ExtractParameters(NSDictionary *parameters) { 1.328 + NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; 1.329 + NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; 1.330 + NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; 1.331 + NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; 1.332 + NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; 1.333 + NSString *vendor = 1.334 + [parameters objectForKey:@BREAKPAD_VENDOR]; 1.335 + NSString *dumpSubdirectory = 1.336 + [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; 1.337 + 1.338 + NSDictionary *serverParameters = 1.339 + [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; 1.340 + 1.341 + if (!product) 1.342 + product = [parameters objectForKey:@"CFBundleName"]; 1.343 + 1.344 + if (!display) { 1.345 + display = [parameters objectForKey:@"CFBundleDisplayName"]; 1.346 + if (!display) { 1.347 + display = product; 1.348 + } 1.349 + } 1.350 + 1.351 + if (!version) 1.352 + version = [parameters objectForKey:@"CFBundleVersion"]; 1.353 + 1.354 + if (!vendor) { 1.355 + vendor = @"Vendor not specified"; 1.356 + } 1.357 + 1.358 + if (!dumpSubdirectory) { 1.359 + NSString *cachePath = 1.360 + [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, 1.361 + NSUserDomainMask, 1.362 + YES) 1.363 + objectAtIndex:0]; 1.364 + dumpSubdirectory = 1.365 + [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory]; 1.366 + 1.367 + EnsureDirectoryPathExists(dumpSubdirectory); 1.368 + } 1.369 + 1.370 + // The product, version, and URL are required values. 1.371 + if (![product length]) { 1.372 + DEBUGLOG(stderr, "Missing required product key.\n"); 1.373 + return false; 1.374 + } 1.375 + 1.376 + if (![version length]) { 1.377 + DEBUGLOG(stderr, "Missing required version key.\n"); 1.378 + return false; 1.379 + } 1.380 + 1.381 + if (![urlStr length]) { 1.382 + DEBUGLOG(stderr, "Missing required URL key.\n"); 1.383 + return false; 1.384 + } 1.385 + 1.386 + config_params_ = 1.387 + new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) 1.388 + SimpleStringDictionary(); 1.389 + 1.390 + SimpleStringDictionary &dictionary = *config_params_; 1.391 + 1.392 + dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); 1.393 + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); 1.394 + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); 1.395 + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); 1.396 + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); 1.397 + dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); 1.398 + dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, 1.399 + [dumpSubdirectory UTF8String]); 1.400 + 1.401 + struct timeval tv; 1.402 + gettimeofday(&tv, NULL); 1.403 + char timeStartedString[32]; 1.404 + sprintf(timeStartedString, "%zd", tv.tv_sec); 1.405 + dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString); 1.406 + 1.407 + if (serverParameters) { 1.408 + // For each key-value pair, call BreakpadAddUploadParameter() 1.409 + NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; 1.410 + NSString *aParameter; 1.411 + while ((aParameter = [keyEnumerator nextObject])) { 1.412 + BreakpadAddUploadParameter(this, aParameter, 1.413 + [serverParameters objectForKey:aParameter]); 1.414 + } 1.415 + } 1.416 + return true; 1.417 +} 1.418 + 1.419 +//============================================================================= 1.420 +void Breakpad::SetKeyValue(NSString *key, NSString *value) { 1.421 + // We allow nil values. This is the same as removing the keyvalue. 1.422 + if (!config_params_ || !key) 1.423 + return; 1.424 + 1.425 + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); 1.426 +} 1.427 + 1.428 +//============================================================================= 1.429 +NSString *Breakpad::KeyValue(NSString *key) { 1.430 + if (!config_params_ || !key) 1.431 + return nil; 1.432 + 1.433 + const char *value = config_params_->GetValueForKey([key UTF8String]); 1.434 + return value ? [NSString stringWithUTF8String:value] : nil; 1.435 +} 1.436 + 1.437 +//============================================================================= 1.438 +void Breakpad::RemoveKeyValue(NSString *key) { 1.439 + if (!config_params_ || !key) return; 1.440 + 1.441 + config_params_->RemoveKey([key UTF8String]); 1.442 +} 1.443 + 1.444 +//============================================================================= 1.445 +NSString *Breakpad::NextCrashReportToUpload() { 1.446 + NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY); 1.447 + if (!directory) 1.448 + return nil; 1.449 + NSArray *dirContents = [[NSFileManager defaultManager] 1.450 + contentsOfDirectoryAtPath:directory error:nil]; 1.451 + NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate 1.452 + predicateWithFormat:@"self BEGINSWITH 'Config-'"]]; 1.453 + NSString *config = [configs lastObject]; 1.454 + if (!config) 1.455 + return nil; 1.456 + return [NSString stringWithFormat:@"%@/%@", directory, config]; 1.457 +} 1.458 + 1.459 +//============================================================================= 1.460 +void Breakpad::UploadNextReport() { 1.461 + NSString *configFile = NextCrashReportToUpload(); 1.462 + if (configFile) { 1.463 + Uploader *uploader = [[[Uploader alloc] 1.464 + initWithConfigFile:[configFile UTF8String]] autorelease]; 1.465 + if (uploader) 1.466 + [uploader report]; 1.467 + } 1.468 +} 1.469 + 1.470 +//============================================================================= 1.471 +void Breakpad::UploadData(NSData *data, NSString *name, 1.472 + NSDictionary *server_parameters) { 1.473 + NSMutableDictionary *config = [NSMutableDictionary dictionary]; 1.474 + 1.475 + SimpleStringDictionaryIterator it(*config_params_); 1.476 + while (const KeyValueEntry *next = it.Next()) { 1.477 + [config setValue:[NSString stringWithUTF8String:next->GetValue()] 1.478 + forKey:[NSString stringWithUTF8String:next->GetKey()]]; 1.479 + } 1.480 + 1.481 + Uploader *uploader = 1.482 + [[[Uploader alloc] initWithConfig:config] autorelease]; 1.483 + for (NSString *key in server_parameters) { 1.484 + [uploader addServerParameter:[server_parameters objectForKey:key] 1.485 + forKey:key]; 1.486 + } 1.487 + [uploader uploadData:data name:name]; 1.488 +} 1.489 + 1.490 +//============================================================================= 1.491 +NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) { 1.492 + NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY); 1.493 + if (!dumpDirAsNSString) 1.494 + return nil; 1.495 + const char *dumpDir = [dumpDirAsNSString UTF8String]; 1.496 + 1.497 + google_breakpad::MinidumpGenerator generator(mach_task_self(), 1.498 + MACH_PORT_NULL); 1.499 + std::string dumpId; 1.500 + std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId); 1.501 + bool success = generator.Write(dumpFilename.c_str()); 1.502 + if (!success) 1.503 + return nil; 1.504 + 1.505 + SimpleStringDictionary params = *config_params_; 1.506 + for (NSString *key in server_parameters) { 1.507 + params.SetKeyValue([key UTF8String], 1.508 + [[server_parameters objectForKey:key] UTF8String]); 1.509 + } 1.510 + ConfigFile config_file; 1.511 + config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str()); 1.512 + 1.513 + // Handle results. 1.514 + NSMutableDictionary *result = [NSMutableDictionary dictionary]; 1.515 + NSString *dumpFullPath = [dumpDirAsNSString stringByAppendingPathComponent: 1.516 + [NSString stringWithUTF8String:dumpFilename.c_str()]]; 1.517 + [result setValue:dumpFullPath 1.518 + forKey:@BREAKPAD_OUTPUT_DUMP_FILE]; 1.519 + [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()] 1.520 + forKey:@BREAKPAD_OUTPUT_CONFIG_FILE]; 1.521 + return result; 1.522 +} 1.523 + 1.524 +//============================================================================= 1.525 +bool Breakpad::HandleMinidump(const char *dump_dir, 1.526 + const char *minidump_id) { 1.527 + DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n"); 1.528 + 1.529 + config_file_.WriteFile(dump_dir, 1.530 + config_params_, 1.531 + dump_dir, 1.532 + minidump_id); 1.533 + 1.534 + // Return true here to indicate that we've processed things as much as we 1.535 + // want. 1.536 + return true; 1.537 +} 1.538 + 1.539 +//============================================================================= 1.540 +void Breakpad::HandleUncaughtException(NSException *exception) { 1.541 + // Generate the minidump. 1.542 + google_breakpad::IosExceptionMinidumpGenerator generator(exception); 1.543 + const char *minidump_path = 1.544 + config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY); 1.545 + std::string minidump_id; 1.546 + std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path, 1.547 + &minidump_id); 1.548 + generator.Write(minidump_filename.c_str()); 1.549 + 1.550 + // Copy the config params and our custom parameter. This is necessary for 2 1.551 + // reasons: 1.552 + // 1- config_params_ is protected. 1.553 + // 2- If the application crash while trying to handle this exception, a usual 1.554 + // report will be generated. This report must not contain these special 1.555 + // keys. 1.556 + SimpleStringDictionary params = *config_params_; 1.557 + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception"); 1.558 + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName", 1.559 + [[exception name] UTF8String]); 1.560 + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason", 1.561 + [[exception reason] UTF8String]); 1.562 + 1.563 + // And finally write the config file. 1.564 + ConfigFile config_file; 1.565 + config_file.WriteFile(minidump_path, 1.566 + ¶ms, 1.567 + minidump_path, 1.568 + minidump_id.c_str()); 1.569 +} 1.570 + 1.571 +//============================================================================= 1.572 + 1.573 +#pragma mark - 1.574 +#pragma mark Public API 1.575 + 1.576 +//============================================================================= 1.577 +BreakpadRef BreakpadCreate(NSDictionary *parameters) { 1.578 + try { 1.579 + // This is confusing. Our two main allocators for breakpad memory are: 1.580 + // - gKeyValueAllocator for the key/value memory 1.581 + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other 1.582 + // breakpad allocations which are accessed at exception handling time. 1.583 + // 1.584 + // But in order to avoid these two allocators themselves from being smashed, 1.585 + // we'll protect them as well by allocating them with gMasterAllocator. 1.586 + // 1.587 + // gMasterAllocator itself will NOT be protected, but this doesn't matter, 1.588 + // since once it does its allocations and locks the memory, smashes to 1.589 + // itself don't affect anything we care about. 1.590 + gMasterAllocator = 1.591 + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); 1.592 + 1.593 + gKeyValueAllocator = 1.594 + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 1.595 + ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); 1.596 + 1.597 + // Create a mutex for use in accessing the SimpleStringDictionary 1.598 + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); 1.599 + if (mutexResult == 0) { 1.600 + 1.601 + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. 1.602 + // Let's round up to the nearest page size. 1.603 + // 1.604 + int breakpad_pool_size = 4096; 1.605 + 1.606 + /* 1.607 + sizeof(Breakpad) 1.608 + + sizeof(google_breakpad::ExceptionHandler) 1.609 + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) 1.610 + */ 1.611 + 1.612 + gBreakpadAllocator = 1.613 + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) 1.614 + ProtectedMemoryAllocator(breakpad_pool_size); 1.615 + 1.616 + // Stack-based autorelease pool for Breakpad::Create() obj-c code. 1.617 + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1.618 + Breakpad *breakpad = Breakpad::Create(parameters); 1.619 + 1.620 + if (breakpad) { 1.621 + // Make read-only to protect against memory smashers 1.622 + gMasterAllocator->Protect(); 1.623 + gKeyValueAllocator->Protect(); 1.624 + gBreakpadAllocator->Protect(); 1.625 + // Can uncomment this line to figure out how much space was actually 1.626 + // allocated using this allocator 1.627 + // printf("gBreakpadAllocator allocated size = %d\n", 1.628 + // gBreakpadAllocator->GetAllocatedSize() ); 1.629 + [pool release]; 1.630 + return (BreakpadRef)breakpad; 1.631 + } 1.632 + 1.633 + [pool release]; 1.634 + } 1.635 + } catch(...) { // don't let exceptions leave this C API 1.636 + fprintf(stderr, "BreakpadCreate() : error\n"); 1.637 + } 1.638 + 1.639 + if (gKeyValueAllocator) { 1.640 + gKeyValueAllocator->~ProtectedMemoryAllocator(); 1.641 + gKeyValueAllocator = NULL; 1.642 + } 1.643 + 1.644 + if (gBreakpadAllocator) { 1.645 + gBreakpadAllocator->~ProtectedMemoryAllocator(); 1.646 + gBreakpadAllocator = NULL; 1.647 + } 1.648 + 1.649 + delete gMasterAllocator; 1.650 + gMasterAllocator = NULL; 1.651 + 1.652 + return NULL; 1.653 +} 1.654 + 1.655 +//============================================================================= 1.656 +void BreakpadRelease(BreakpadRef ref) { 1.657 + try { 1.658 + Breakpad *breakpad = (Breakpad *)ref; 1.659 + 1.660 + if (gMasterAllocator) { 1.661 + gMasterAllocator->Unprotect(); 1.662 + gKeyValueAllocator->Unprotect(); 1.663 + gBreakpadAllocator->Unprotect(); 1.664 + 1.665 + breakpad->~Breakpad(); 1.666 + 1.667 + // Unfortunately, it's not possible to deallocate this stuff 1.668 + // because the exception handling thread is still finishing up 1.669 + // asynchronously at this point... OK, it could be done with 1.670 + // locks, etc. But since BreakpadRelease() should usually only 1.671 + // be called right before the process exits, it's not worth 1.672 + // deallocating this stuff. 1.673 +#if 0 1.674 + gKeyValueAllocator->~ProtectedMemoryAllocator(); 1.675 + gBreakpadAllocator->~ProtectedMemoryAllocator(); 1.676 + delete gMasterAllocator; 1.677 + 1.678 + gMasterAllocator = NULL; 1.679 + gKeyValueAllocator = NULL; 1.680 + gBreakpadAllocator = NULL; 1.681 +#endif 1.682 + 1.683 + pthread_mutex_destroy(&gDictionaryMutex); 1.684 + } 1.685 + } catch(...) { // don't let exceptions leave this C API 1.686 + fprintf(stderr, "BreakpadRelease() : error\n"); 1.687 + } 1.688 +} 1.689 + 1.690 +//============================================================================= 1.691 +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { 1.692 + try { 1.693 + // Not called at exception time 1.694 + Breakpad *breakpad = (Breakpad *)ref; 1.695 + 1.696 + if (breakpad && key && gKeyValueAllocator) { 1.697 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.698 + 1.699 + breakpad->SetKeyValue(key, value); 1.700 + } 1.701 + } catch(...) { // don't let exceptions leave this C API 1.702 + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 1.703 + } 1.704 +} 1.705 + 1.706 +void BreakpadAddUploadParameter(BreakpadRef ref, 1.707 + NSString *key, 1.708 + NSString *value) { 1.709 + // The only difference, internally, between an upload parameter and 1.710 + // a key value one that is set with BreakpadSetKeyValue is that we 1.711 + // prepend the keyname with a special prefix. This informs the 1.712 + // crash sender that the parameter should be sent along with the 1.713 + // POST of the crash dump upload. 1.714 + try { 1.715 + Breakpad *breakpad = (Breakpad *)ref; 1.716 + 1.717 + if (breakpad && key && gKeyValueAllocator) { 1.718 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.719 + 1.720 + NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX 1.721 + stringByAppendingString:key]; 1.722 + breakpad->SetKeyValue(prefixedKey, value); 1.723 + } 1.724 + } catch(...) { // don't let exceptions leave this C API 1.725 + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); 1.726 + } 1.727 +} 1.728 + 1.729 +void BreakpadRemoveUploadParameter(BreakpadRef ref, 1.730 + NSString *key) { 1.731 + try { 1.732 + // Not called at exception time 1.733 + Breakpad *breakpad = (Breakpad *)ref; 1.734 + 1.735 + if (breakpad && key && gKeyValueAllocator) { 1.736 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.737 + 1.738 + NSString *prefixedKey = [NSString stringWithFormat:@"%@%@", 1.739 + @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; 1.740 + breakpad->RemoveKeyValue(prefixedKey); 1.741 + } 1.742 + } catch(...) { // don't let exceptions leave this C API 1.743 + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 1.744 + } 1.745 +} 1.746 +//============================================================================= 1.747 +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { 1.748 + NSString *value = nil; 1.749 + 1.750 + try { 1.751 + // Not called at exception time 1.752 + Breakpad *breakpad = (Breakpad *)ref; 1.753 + 1.754 + if (!breakpad || !key || !gKeyValueAllocator) 1.755 + return nil; 1.756 + 1.757 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.758 + 1.759 + value = breakpad->KeyValue(key); 1.760 + } catch(...) { // don't let exceptions leave this C API 1.761 + fprintf(stderr, "BreakpadKeyValue() : error\n"); 1.762 + } 1.763 + 1.764 + return value; 1.765 +} 1.766 + 1.767 +//============================================================================= 1.768 +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { 1.769 + try { 1.770 + // Not called at exception time 1.771 + Breakpad *breakpad = (Breakpad *)ref; 1.772 + 1.773 + if (breakpad && key && gKeyValueAllocator) { 1.774 + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); 1.775 + 1.776 + breakpad->RemoveKeyValue(key); 1.777 + } 1.778 + } catch(...) { // don't let exceptions leave this C API 1.779 + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); 1.780 + } 1.781 +} 1.782 + 1.783 +//============================================================================= 1.784 +bool BreakpadHasCrashReportToUpload(BreakpadRef ref) { 1.785 + try { 1.786 + // Not called at exception time 1.787 + Breakpad *breakpad = (Breakpad *)ref; 1.788 + 1.789 + if (breakpad) { 1.790 + return breakpad->NextCrashReportToUpload() != 0; 1.791 + } 1.792 + } catch(...) { // don't let exceptions leave this C API 1.793 + fprintf(stderr, "BreakpadHasCrashReportToUpload() : error\n"); 1.794 + } 1.795 + return false; 1.796 +} 1.797 + 1.798 +//============================================================================= 1.799 +void BreakpadUploadNextReport(BreakpadRef ref) { 1.800 + try { 1.801 + // Not called at exception time 1.802 + Breakpad *breakpad = (Breakpad *)ref; 1.803 + 1.804 + if (breakpad) { 1.805 + breakpad->UploadNextReport(); 1.806 + } 1.807 + } catch(...) { // don't let exceptions leave this C API 1.808 + fprintf(stderr, "BreakpadUploadNextReport() : error\n"); 1.809 + } 1.810 +} 1.811 + 1.812 +//============================================================================= 1.813 +void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name, 1.814 + NSDictionary *server_parameters) { 1.815 + try { 1.816 + // Not called at exception time 1.817 + Breakpad *breakpad = (Breakpad *)ref; 1.818 + 1.819 + if (breakpad) { 1.820 + breakpad->UploadData(data, name, server_parameters); 1.821 + } 1.822 + } catch(...) { // don't let exceptions leave this C API 1.823 + fprintf(stderr, "BreakpadUploadData() : error\n"); 1.824 + } 1.825 +} 1.826 + 1.827 +//============================================================================= 1.828 +NSDictionary *BreakpadGenerateReport(BreakpadRef ref, 1.829 + NSDictionary *server_parameters) { 1.830 + try { 1.831 + // Not called at exception time 1.832 + Breakpad *breakpad = (Breakpad *)ref; 1.833 + 1.834 + if (breakpad) { 1.835 + return breakpad->GenerateReport(server_parameters); 1.836 + } else { 1.837 + return nil; 1.838 + } 1.839 + } catch(...) { // don't let exceptions leave this C API 1.840 + fprintf(stderr, "BreakpadGenerateReport() : error\n"); 1.841 + return nil; 1.842 + } 1.843 +}