summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
diff options
context:
space:
mode:
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.mm1043
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);
-}