diff options
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm')
-rw-r--r-- | toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm | 1043 |
1 files changed, 0 insertions, 1043 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm deleted file mode 100644 index 1d2e519bb..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm +++ /dev/null @@ -1,1043 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - - -#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" - -#import "client/mac/Framework/Breakpad.h" - -#include <assert.h> -#import <Foundation/Foundation.h> -#include <pthread.h> -#include <sys/stat.h> -#include <sys/sysctl.h> - -#import "client/mac/crash_generation/Inspector.h" -#import "client/mac/handler/exception_handler.h" -#import "client/mac/Framework/Breakpad.h" -#import "client/mac/Framework/OnDemandServer.h" -#import "client/mac/handler/protected_memory_allocator.h" -#include "common/mac/launch_reporter.h" -#import "common/mac/MachIPC.h" -#import "common/simple_string_dictionary.h" - -#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions)) -// This file uses C++ try/catch (but shouldn't). Duplicate the macros from -// <c++/4.2.1/exception_defines.h> allowing this file to work properly with -// exceptions disabled even when other C++ libraries are used. #undef the try -// and catch macros first in case libstdc++ is in use and has already provided -// its own definitions. -#undef try -#define try if (true) -#undef catch -#define catch(X) if (false) -#endif // __EXCEPTIONS - -using google_breakpad::MachPortSender; -using google_breakpad::MachReceiveMessage; -using google_breakpad::MachSendMessage; -using google_breakpad::ReceivePort; -using google_breakpad::SimpleStringDictionary; - -//============================================================================= -// We want any memory allocations which are used by breakpad during the -// exception handling process (after a crash has happened) to be read-only -// to prevent them from being smashed before a crash occurs. Unfortunately -// we cannot protect against smashes to our exception handling thread's -// stack. -// -// NOTE: Any memory allocations which are not used during the exception -// handling process may be allocated in the normal ways. -// -// The ProtectedMemoryAllocator class provides an Allocate() method which -// we'll using in conjunction with placement operator new() to control -// allocation of C++ objects. Note that we don't use operator delete() -// but instead call the objects destructor directly: object->~ClassName(); -// -ProtectedMemoryAllocator *gMasterAllocator = NULL; -ProtectedMemoryAllocator *gKeyValueAllocator = NULL; -ProtectedMemoryAllocator *gBreakpadAllocator = NULL; - -// Mutex for thread-safe access to the key/value dictionary used by breakpad. -// It's a global instead of an instance variable of Breakpad -// since it can't live in a protected memory area. -pthread_mutex_t gDictionaryMutex; - -//============================================================================= -// Stack-based object for thread-safe access to a memory-protected region. -// It's assumed that normally the memory block (allocated by the allocator) -// is protected (read-only). Creating a stack-based instance of -// ProtectedMemoryLocker will unprotect this block after taking the lock. -// Its destructor will first re-protect the memory then release the lock. -class ProtectedMemoryLocker { - public: - ProtectedMemoryLocker(pthread_mutex_t *mutex, - ProtectedMemoryAllocator *allocator) - : mutex_(mutex), - allocator_(allocator) { - // Lock the mutex - __attribute__((unused)) int rv = pthread_mutex_lock(mutex_); - assert(rv == 0); - - // Unprotect the memory - allocator_->Unprotect(); - } - - ~ProtectedMemoryLocker() { - // First protect the memory - allocator_->Protect(); - - // Then unlock the mutex - __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_); - assert(rv == 0); - }; - - private: - ProtectedMemoryLocker(); - ProtectedMemoryLocker(const ProtectedMemoryLocker&); - ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&); - - pthread_mutex_t *mutex_; - ProtectedMemoryAllocator *allocator_; -}; - -//============================================================================= -class Breakpad { - public: - // factory method - static Breakpad *Create(NSDictionary *parameters) { - // Allocate from our special allocation pool - Breakpad *breakpad = - new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) - Breakpad(); - - if (!breakpad) - return NULL; - - if (!breakpad->Initialize(parameters)) { - // Don't use operator delete() here since we allocated from special pool - breakpad->~Breakpad(); - return NULL; - } - - return breakpad; - } - - ~Breakpad(); - - void SetKeyValue(NSString *key, NSString *value); - NSString *KeyValue(NSString *key); - void RemoveKeyValue(NSString *key); - - void GenerateAndSendReport(); - - void SetFilterCallback(BreakpadFilterCallback callback, void *context) { - filter_callback_ = callback; - filter_callback_context_ = context; - } - - private: - Breakpad() - : handler_(NULL), - config_params_(NULL), - send_and_exit_(true), - filter_callback_(NULL), - filter_callback_context_(NULL) { - inspector_path_[0] = 0; - } - - bool Initialize(NSDictionary *parameters); - bool InitializeInProcess(NSDictionary *parameters); - bool InitializeOutOfProcess(NSDictionary *parameters); - - bool ExtractParameters(NSDictionary *parameters); - - // Dispatches to HandleException() - static bool ExceptionHandlerDirectCallback(void *context, - int exception_type, - int exception_code, - int exception_subcode, - mach_port_t crashing_thread); - - bool HandleException(int exception_type, - int exception_code, - int exception_subcode, - mach_port_t crashing_thread); - - // Dispatches to HandleMinidump(). - // This gets called instead of ExceptionHandlerDirectCallback when running - // with the BREAKPAD_IN_PROCESS option. - static bool HandleMinidumpCallback(const char *dump_dir, - const char *minidump_id, - void *context, - bool succeeded); - - // This is only used when BREAKPAD_IN_PROCESS is YES. - bool HandleMinidump(const char *dump_dir, const char *minidump_id); - - // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's - // MachineExceptions.h, we have to explicitly name the handler. - google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) - - char inspector_path_[PATH_MAX]; // Path to inspector tool - - SimpleStringDictionary *config_params_; // Create parameters (STRONG) - - OnDemandServer inspector_; - - bool send_and_exit_; // Exit after sending, if true - - BreakpadFilterCallback filter_callback_; - void *filter_callback_context_; -}; - -#pragma mark - -#pragma mark Helper functions - -//============================================================================= -// Helper functions - -//============================================================================= -static BOOL IsDebuggerActive() { - BOOL result = NO; - NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; - - // We check both defaults and the environment variable here - - BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; - - if (!ignoreDebugger) { - char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); - ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; - } - - if (!ignoreDebugger) { - pid_t pid = getpid(); - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; - int mibSize = sizeof(mib) / sizeof(int); - size_t actualSize; - - if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { - struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); - - if (info) { - // This comes from looking at the Darwin xnu Kernel - if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) - result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; - - free(info); - } - } - } - - return result; -} - -//============================================================================= -bool Breakpad::ExceptionHandlerDirectCallback(void *context, - int exception_type, - int exception_code, - int exception_subcode, - mach_port_t crashing_thread) { - Breakpad *breakpad = (Breakpad *)context; - - // If our context is damaged or something, just return false to indicate that - // the handler should continue without us. - if (!breakpad) - return false; - - return breakpad->HandleException( exception_type, - exception_code, - exception_subcode, - crashing_thread); -} - -//============================================================================= -bool Breakpad::HandleMinidumpCallback(const char *dump_dir, - const char *minidump_id, - void *context, - bool succeeded) { - Breakpad *breakpad = (Breakpad *)context; - - // If our context is damaged or something, just return false to indicate that - // the handler should continue without us. - if (!breakpad || !succeeded) - return false; - - return breakpad->HandleMinidump(dump_dir, minidump_id); -} - -//============================================================================= -#pragma mark - - -#include <dlfcn.h> - -//============================================================================= -// Returns the pathname to the Resources directory for this version of -// Breakpad which we are now running. -// -// Don't make the function static, since _dyld_lookup_and_bind_fully needs a -// simple non-static C name -// -extern "C" { -NSString * GetResourcePath(); -NSString * GetResourcePath() { - NSString *resourcePath = nil; - - // If there are multiple breakpads installed then calling bundleWithIdentifier - // will not work properly, so only use that as a backup plan. - // We want to find the bundle containing the code where this function lives - // and work from there - // - - // Get the pathname to the code which contains this function - Dl_info info; - if (dladdr((const void*)GetResourcePath, &info) != 0) { - NSFileManager *filemgr = [NSFileManager defaultManager]; - NSString *filePath = - [filemgr stringWithFileSystemRepresentation:info.dli_fname - length:strlen(info.dli_fname)]; - NSString *bundlePath = [filePath stringByDeletingLastPathComponent]; - // The "Resources" directory should be in the same directory as the - // executable code, since that's how the Breakpad framework is built. - resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"]; - } else { - // fallback plan - NSBundle *bundle = - [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; - resourcePath = [bundle resourcePath]; - } - - return resourcePath; -} -} // extern "C" - -//============================================================================= -bool Breakpad::Initialize(NSDictionary *parameters) { - // Initialize - config_params_ = NULL; - handler_ = NULL; - - // Check for debugger - if (IsDebuggerActive()) { - return true; - } - - // Gather any user specified parameters - if (!ExtractParameters(parameters)) { - return false; - } - - if ([[parameters objectForKey:@BREAKPAD_IN_PROCESS] boolValue]) - return InitializeInProcess(parameters); - else - return InitializeOutOfProcess(parameters); -} - -//============================================================================= -bool Breakpad::InitializeInProcess(NSDictionary* parameters) { - handler_ = - new (gBreakpadAllocator->Allocate( - sizeof(google_breakpad::ExceptionHandler))) - google_breakpad::ExceptionHandler( - config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY), - 0, &HandleMinidumpCallback, this, true, 0); - return true; -} - -//============================================================================= -bool Breakpad::InitializeOutOfProcess(NSDictionary* parameters) { - // Get path to Inspector executable. - NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); - - // Standardize path (resolve symlinkes, etc.) and escape spaces - inspectorPathString = [inspectorPathString stringByStandardizingPath]; - inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] - componentsJoinedByString:@"\\ "]; - - // Create an on-demand server object representing the Inspector. - // In case of a crash, we simply need to call the LaunchOnDemand() - // method on it, then send a mach message to its service port. - // It will then launch and perform a process inspection of our crashed state. - // See the HandleException() method for the details. -#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" - - name_t portName; - snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); - - // Save the location of the Inspector - strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], - sizeof(inspector_path_)); - - // Append a single command-line argument to the Inspector path - // representing the bootstrap name of the launch-on-demand receive port. - // When the Inspector is launched, it can use this to lookup the port - // by calling bootstrap_check_in(). - strlcat(inspector_path_, " ", sizeof(inspector_path_)); - strlcat(inspector_path_, portName, sizeof(inspector_path_)); - - kern_return_t kr = inspector_.Initialize(inspector_path_, - portName, - true); // shutdown on exit - - if (kr != KERN_SUCCESS) { - return false; - } - - // Create the handler (allocating it in our special protected pool) - handler_ = - new (gBreakpadAllocator->Allocate( - sizeof(google_breakpad::ExceptionHandler))) - google_breakpad::ExceptionHandler( - Breakpad::ExceptionHandlerDirectCallback, this, true); - return true; -} - -//============================================================================= -Breakpad::~Breakpad() { - // Note that we don't use operator delete() on these pointers, - // since they were allocated by ProtectedMemoryAllocator objects. - // - if (config_params_) { - config_params_->~SimpleStringDictionary(); - } - - if (handler_) - handler_->~ExceptionHandler(); -} - -//============================================================================= -bool Breakpad::ExtractParameters(NSDictionary *parameters) { - NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; - NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; - NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; - - NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; - NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; - NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; - NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; - NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; - NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; - NSString *inspectorPathString = - [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; - NSString *reporterPathString = - [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; - NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; - NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; - NSString *logFileTailSize = - [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; - NSString *requestUserText = - [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; - NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; - NSString *vendor = - [parameters objectForKey:@BREAKPAD_VENDOR]; - NSString *dumpSubdirectory = - [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; - - NSDictionary *serverParameters = - [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; - - // These may have been set above as user prefs, which take priority. - if (!skipConfirm) { - skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; - } - if (!sendAndExit) { - sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; - } - - if (!product) - product = [parameters objectForKey:@"CFBundleName"]; - - if (!display) { - display = [parameters objectForKey:@"CFBundleDisplayName"]; - if (!display) { - display = product; - } - } - - if (!version) - version = [parameters objectForKey:@"CFBundleVersion"]; - - if (!interval) - interval = @"3600"; - - if (!timeout) - timeout = @"300"; - - if (!logFileTailSize) - logFileTailSize = @"200000"; - - if (!vendor) { - vendor = @"Vendor not specified"; - } - - // Normalize the values. - if (skipConfirm) { - skipConfirm = [skipConfirm uppercaseString]; - - if ([skipConfirm isEqualToString:@"YES"] || - [skipConfirm isEqualToString:@"TRUE"] || - [skipConfirm isEqualToString:@"1"]) - skipConfirm = @"YES"; - else - skipConfirm = @"NO"; - } else { - skipConfirm = @"NO"; - } - - send_and_exit_ = true; - if (sendAndExit) { - sendAndExit = [sendAndExit uppercaseString]; - - if ([sendAndExit isEqualToString:@"NO"] || - [sendAndExit isEqualToString:@"FALSE"] || - [sendAndExit isEqualToString:@"0"]) - send_and_exit_ = false; - } - - if (requestUserText) { - requestUserText = [requestUserText uppercaseString]; - - if ([requestUserText isEqualToString:@"YES"] || - [requestUserText isEqualToString:@"TRUE"] || - [requestUserText isEqualToString:@"1"]) - requestUserText = @"YES"; - else - requestUserText = @"NO"; - } else { - requestUserText = @"NO"; - } - - // Find the helper applications if not specified in user config. - NSString *resourcePath = nil; - if (!inspectorPathString || !reporterPathString) { - resourcePath = GetResourcePath(); - if (!resourcePath) { - return false; - } - } - - // Find Inspector. - if (!inspectorPathString) { - inspectorPathString = - [resourcePath stringByAppendingPathComponent:@"Inspector"]; - } - - // Verify that there is an Inspector tool. - if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { - return false; - } - - // Find Reporter. - if (!reporterPathString) { - reporterPathString = - [resourcePath - stringByAppendingPathComponent:@"crash_report_sender.app"]; - reporterPathString = - [[NSBundle bundleWithPath:reporterPathString] executablePath]; - } - - // Verify that there is a Reporter application. - if (![[NSFileManager defaultManager] - fileExistsAtPath:reporterPathString]) { - return false; - } - - if (!dumpSubdirectory) { - dumpSubdirectory = @""; - } - - // The product, version, and URL are required values. - if (![product length]) { - return false; - } - - if (![version length]) { - return false; - } - - if (![urlStr length]) { - return false; - } - - config_params_ = - new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) - SimpleStringDictionary(); - - SimpleStringDictionary &dictionary = *config_params_; - - dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); - dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); - dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); - dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); - dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); - dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); - dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); - dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); - dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, - [inspectorPathString fileSystemRepresentation]); - dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, - [reporterPathString fileSystemRepresentation]); - dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, - [logFileTailSize UTF8String]); - dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, - [requestUserText UTF8String]); - dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]); - dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); - dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, - [dumpSubdirectory UTF8String]); - - struct timeval tv; - gettimeofday(&tv, NULL); - char timeStartedString[32]; - sprintf(timeStartedString, "%zd", tv.tv_sec); - dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, - timeStartedString); - - if (logFilePaths) { - char logFileKey[255]; - for(unsigned int i = 0; i < [logFilePaths count]; i++) { - sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); - dictionary.SetKeyValue(logFileKey, - [[logFilePaths objectAtIndex:i] - fileSystemRepresentation]); - } - } - - if (serverParameters) { - // For each key-value pair, call BreakpadAddUploadParameter() - NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; - NSString *aParameter; - while ((aParameter = [keyEnumerator nextObject])) { - BreakpadAddUploadParameter(this, aParameter, - [serverParameters objectForKey:aParameter]); - } - } - return true; -} - -//============================================================================= -void Breakpad::SetKeyValue(NSString *key, NSString *value) { - // We allow nil values. This is the same as removing the keyvalue. - if (!config_params_ || !key) - return; - - config_params_->SetKeyValue([key UTF8String], [value UTF8String]); -} - -//============================================================================= -NSString *Breakpad::KeyValue(NSString *key) { - if (!config_params_ || !key) - return nil; - - const char *value = config_params_->GetValueForKey([key UTF8String]); - return value ? [NSString stringWithUTF8String:value] : nil; -} - -//============================================================================= -void Breakpad::RemoveKeyValue(NSString *key) { - if (!config_params_ || !key) return; - - config_params_->RemoveKey([key UTF8String]); -} - -//============================================================================= -void Breakpad::GenerateAndSendReport() { - config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); - HandleException(0, 0, 0, mach_thread_self()); - config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); -} - -//============================================================================= -bool Breakpad::HandleException(int exception_type, - int exception_code, - int exception_subcode, - mach_port_t crashing_thread) { - if (filter_callback_) { - bool should_handle = filter_callback_(exception_type, - exception_code, - crashing_thread, - filter_callback_context_); - if (!should_handle) return false; - } - - // We need to reset the memory protections to be read/write, - // since LaunchOnDemand() requires changing state. - gBreakpadAllocator->Unprotect(); - // Configure the server to launch when we message the service port. - // The reason we do this here, rather than at startup, is that we - // can leak a bootstrap service entry if this method is called and - // there never ends up being a crash. - inspector_.LaunchOnDemand(); - gBreakpadAllocator->Protect(); - - // The Inspector should send a message to this port to verify it - // received our information and has finished the inspection. - ReceivePort acknowledge_port; - - // Send initial information to the Inspector. - MachSendMessage message(kMsgType_InspectorInitialInfo); - message.AddDescriptor(mach_task_self()); // our task - message.AddDescriptor(crashing_thread); // crashing thread - message.AddDescriptor(mach_thread_self()); // exception-handling thread - message.AddDescriptor(acknowledge_port.GetPort());// message receive port - - InspectorInfo info; - info.exception_type = exception_type; - info.exception_code = exception_code; - info.exception_subcode = exception_subcode; - info.parameter_count = config_params_->GetCount(); - message.SetData(&info, sizeof(info)); - - MachPortSender sender(inspector_.GetServicePort()); - - kern_return_t result = sender.SendMessage(message, 2000); - - if (result == KERN_SUCCESS) { - // Now, send a series of key-value pairs to the Inspector. - const SimpleStringDictionary::Entry *entry = NULL; - SimpleStringDictionary::Iterator iter(*config_params_); - - while ( (entry = iter.Next()) ) { - KeyValueMessageData keyvalue_data(*entry); - - MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); - keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); - - result = sender.SendMessage(keyvalue_message, 2000); - - if (result != KERN_SUCCESS) { - break; - } - } - - if (result == KERN_SUCCESS) { - // Wait for acknowledgement that the inspection has finished. - MachReceiveMessage acknowledge_messsage; - result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); - } - } - -#if VERBOSE - PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); - printf("Breakpad: Inspector service port = %#x\n", - inspector_.GetServicePort()); -#endif - - // If we don't want any forwarding, return true here to indicate that we've - // processed things as much as we want. - if (send_and_exit_) return true; - - return false; -} - -//============================================================================= -bool Breakpad::HandleMinidump(const char *dump_dir, const char *minidump_id) { - google_breakpad::ConfigFile config_file; - config_file.WriteFile(dump_dir, config_params_, dump_dir, minidump_id); - google_breakpad::LaunchReporter( - config_params_->GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION), - config_file.GetFilePath()); - return true; -} - -//============================================================================= -//============================================================================= - -#pragma mark - -#pragma mark Public API - -//============================================================================= -BreakpadRef BreakpadCreate(NSDictionary *parameters) { - try { - // This is confusing. Our two main allocators for breakpad memory are: - // - gKeyValueAllocator for the key/value memory - // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other - // breakpad allocations which are accessed at exception handling time. - // - // But in order to avoid these two allocators themselves from being smashed, - // we'll protect them as well by allocating them with gMasterAllocator. - // - // gMasterAllocator itself will NOT be protected, but this doesn't matter, - // since once it does its allocations and locks the memory, smashes to itself - // don't affect anything we care about. - gMasterAllocator = - new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); - - gKeyValueAllocator = - new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) - ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); - - // Create a mutex for use in accessing the SimpleStringDictionary - int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); - if (mutexResult == 0) { - - // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. - // Let's round up to the nearest page size. - // - int breakpad_pool_size = 4096; - - /* - sizeof(Breakpad) - + sizeof(google_breakpad::ExceptionHandler) - + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) - */ - - gBreakpadAllocator = - new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) - ProtectedMemoryAllocator(breakpad_pool_size); - - // Stack-based autorelease pool for Breakpad::Create() obj-c code. - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - Breakpad *breakpad = Breakpad::Create(parameters); - - if (breakpad) { - // Make read-only to protect against memory smashers - gMasterAllocator->Protect(); - gKeyValueAllocator->Protect(); - gBreakpadAllocator->Protect(); - // Can uncomment this line to figure out how much space was actually - // allocated using this allocator - // printf("gBreakpadAllocator allocated size = %d\n", - // gBreakpadAllocator->GetAllocatedSize() ); - [pool release]; - return (BreakpadRef)breakpad; - } - - [pool release]; - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadCreate() : error\n"); - } - - if (gKeyValueAllocator) { - gKeyValueAllocator->~ProtectedMemoryAllocator(); - gKeyValueAllocator = NULL; - } - - if (gBreakpadAllocator) { - gBreakpadAllocator->~ProtectedMemoryAllocator(); - gBreakpadAllocator = NULL; - } - - delete gMasterAllocator; - gMasterAllocator = NULL; - - return NULL; -} - -//============================================================================= -void BreakpadRelease(BreakpadRef ref) { - try { - Breakpad *breakpad = (Breakpad *)ref; - - if (gMasterAllocator) { - gMasterAllocator->Unprotect(); - gKeyValueAllocator->Unprotect(); - gBreakpadAllocator->Unprotect(); - - breakpad->~Breakpad(); - - // Unfortunately, it's not possible to deallocate this stuff - // because the exception handling thread is still finishing up - // asynchronously at this point... OK, it could be done with - // locks, etc. But since BreakpadRelease() should usually only - // be called right before the process exits, it's not worth - // deallocating this stuff. -#if 0 - gKeyValueAllocator->~ProtectedMemoryAllocator(); - gBreakpadAllocator->~ProtectedMemoryAllocator(); - delete gMasterAllocator; - - gMasterAllocator = NULL; - gKeyValueAllocator = NULL; - gBreakpadAllocator = NULL; -#endif - - pthread_mutex_destroy(&gDictionaryMutex); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadRelease() : error\n"); - } -} - -//============================================================================= -void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { - try { - // Not called at exception time - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && key && gKeyValueAllocator) { - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - breakpad->SetKeyValue(key, value); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadSetKeyValue() : error\n"); - } -} - -void BreakpadAddUploadParameter(BreakpadRef ref, - NSString *key, - NSString *value) { - // The only difference, internally, between an upload parameter and - // a key value one that is set with BreakpadSetKeyValue is that we - // prepend the keyname with a special prefix. This informs the - // crash sender that the parameter should be sent along with the - // POST of the crash dump upload. - try { - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && key && gKeyValueAllocator) { - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX - stringByAppendingString:key]; - breakpad->SetKeyValue(prefixedKey, value); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadSetKeyValue() : error\n"); - } -} - -void BreakpadRemoveUploadParameter(BreakpadRef ref, - NSString *key) { - try { - // Not called at exception time - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && key && gKeyValueAllocator) { - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - NSString *prefixedKey = [NSString stringWithFormat:@"%@%@", - @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; - breakpad->RemoveKeyValue(prefixedKey); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); - } -} -//============================================================================= -NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { - NSString *value = nil; - - try { - // Not called at exception time - Breakpad *breakpad = (Breakpad *)ref; - - if (!breakpad || !key || !gKeyValueAllocator) - return nil; - - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - value = breakpad->KeyValue(key); - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadKeyValue() : error\n"); - } - - return value; -} - -//============================================================================= -void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { - try { - // Not called at exception time - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && key && gKeyValueAllocator) { - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - breakpad->RemoveKeyValue(key); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); - } -} - -//============================================================================= -void BreakpadGenerateAndSendReport(BreakpadRef ref) { - try { - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && gKeyValueAllocator) { - ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); - - gBreakpadAllocator->Unprotect(); - breakpad->GenerateAndSendReport(); - gBreakpadAllocator->Protect(); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); - } -} - -//============================================================================= -void BreakpadSetFilterCallback(BreakpadRef ref, - BreakpadFilterCallback callback, - void *context) { - - try { - Breakpad *breakpad = (Breakpad *)ref; - - if (breakpad && gBreakpadAllocator) { - // share the dictionary mutex here (we really don't need a mutex) - ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); - - breakpad->SetFilterCallback(callback, context); - } - } catch(...) { // don't let exceptions leave this C API - fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); - } -} - -//============================================================================ -void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { - int logFileCounter = 0; - - NSString *logFileKey = [NSString stringWithFormat:@"%@%d", - @BREAKPAD_LOGFILE_KEY_PREFIX, - logFileCounter]; - - NSString *existingLogFilename = nil; - existingLogFilename = BreakpadKeyValue(ref, logFileKey); - // Find the first log file key that we can use by testing for existence - while (existingLogFilename) { - if ([existingLogFilename isEqualToString:logPathname]) { - return; - } - logFileCounter++; - logFileKey = [NSString stringWithFormat:@"%@%d", - @BREAKPAD_LOGFILE_KEY_PREFIX, - logFileCounter]; - existingLogFilename = BreakpadKeyValue(ref, logFileKey); - } - - BreakpadSetKeyValue(ref, logFileKey, logPathname); -} |