summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/mac/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/mac/Framework')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h285
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm1043
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch8
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist26
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h145
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm189
6 files changed, 1696 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h
new file mode 100644
index 000000000..dc7e45d1c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h
@@ -0,0 +1,285 @@
+// 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.
+//
+// Framework to provide a simple C API to crash reporting for
+// applications. By default, if any machine-level exception (e.g.,
+// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
+// object as follows:
+//
+// 1. Create a minidump file (see Breakpad for details)
+// 2. Prompt the user (using CFUserNotification)
+// 3. Invoke a command line reporting tool to send the minidump to a
+// server
+//
+// By specifying parameters to the BreakpadCreate function, you can
+// modify the default behavior to suit your needs and wants and
+// desires.
+
+// A service name associated with the original bootstrap parent port, saved in
+// OnDemandServer and restored in Inspector.
+#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent"
+
+typedef void *BreakpadRef;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+
+#include "BreakpadDefines.h"
+
+// Optional user-defined function to dec to decide if we should handle
+// this crash or forward it along.
+// Return true if you want Breakpad to handle it.
+// Return false if you want Breakpad to skip it
+// The exception handler always returns false, as if SEND_AND_EXIT were false
+// (which means the next exception handler will take the exception)
+typedef bool (*BreakpadFilterCallback)(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread,
+ void *context);
+
+// Create a new BreakpadRef object and install it as an exception
+// handler. The |parameters| will typically be the contents of your
+// bundle's Info.plist.
+//
+// You can also specify these additional keys for customizable behavior:
+// Key: Value:
+// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
+// This one is used as the key to identify
+// the product when uploading. Falls back to
+// CFBundleName if not specified.
+// REQUIRED
+//
+// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
+// name for the product when the crash_sender
+// pops up UI for the user. Falls back first to
+// CFBundleDisplayName and then to
+// BREAKPAD_PRODUCT if not specified.
+//
+// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
+// as metadata for crash report. Falls back to
+// CFBundleVersion if not specified.
+// REQUIRED
+//
+// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
+// been created that you can send to <vendor>")
+//
+// BREAKPAD_URL URL destination for reporting
+// REQUIRED
+//
+// BREAKPAD_REPORT_INTERVAL # of seconds between sending
+// reports. If an additional report is
+// generated within this time, it will
+// be ignored. Default: 3600sec.
+// Specify 0 to send all reports.
+//
+// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report
+// without any user intervention.
+// Defaults to NO
+//
+// BREAKPAD_CONFIRM_TIMEOUT Number of seconds before the upload
+// confirmation dialog will be automatically
+// dismissed (cancelling the upload).
+// Default: 300 seconds (min of 60).
+// Specify 0 to prevent timeout.
+//
+// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending.
+// This will prevent any other handler (e.g.,
+// CrashReporter) from getting the crash.
+// Defaults TO YES
+//
+// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
+// in. By default, we use
+// ~/Library/Breakpad/<BREAKPAD_PRODUCT>
+// The path you specify here is tilde-expanded.
+//
+// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable.
+// Defaults to <Framework resources>/Inspector
+//
+// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender
+// executable.
+// Default:
+// <Framework Resources>/crash_report_sender.app
+//
+// BREAKPAD_LOGFILES Indicates an array of log file paths that
+// should be uploaded at crash time.
+//
+// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
+// text box for the user to enter comments.
+// Default: NO
+//
+// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also
+// true, the message dialog will have a text
+// box for the user to enter their email address.
+// Default: NO
+//
+// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
+// rewrite the upload parameters for a specific
+// server type. The currently valid values are
+// 'socorro' or 'google'. If you want to add
+// other types, see the function in
+// crash_report_sender.m that maps parameters to
+// URL parameters. Defaults to 'google'.
+//
+// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
+// parameters that are uploaded to the
+// server. The parameters are sent as
+// is to the crash server. Their
+// content isn't added to the minidump
+// but pass as URL parameters when
+// uploading theminidump to the crash
+// server.
+//
+// BREAKPAD_IN_PROCESS A boolean NSNumber value. If YES, Breakpad
+// will write the dump file in-process and then
+// launch the reporter executable as a child
+// process.
+//=============================================================================
+// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
+// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
+// will be the CFBundleName and the BREAKPAD_VERSION will be the
+// CFBundleVersion when these keys are present in the bundle's
+// Info.plist, which is usually passed in to BreakpadCreate() as an
+// NSDictionary (you could also pass in another dictionary that had
+// the same keys configured). If the BREAKPAD_PRODUCT or
+// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
+// fail. You have been warned.
+//
+// If you are running in a debugger, Breakpad will not install, unless the
+// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
+//
+// The BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default
+// values are NO and YES. However, they can be controlled by setting their
+// values in a user or global plist.
+//
+// It's easiest to use Breakpad via the Framework, but if you're compiling the
+// code in directly, BREAKPAD_INSPECTOR_LOCATION and
+// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths
+// to the helper executables.
+//
+//=============================================================================
+// The following are NOT user-supplied but are documented here for
+// completeness. They are calculated by Breakpad during initialization &
+// crash-dump generation, or entered in by the user.
+//
+// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
+// process started
+//
+// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
+// process crashed.
+//
+// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
+// has been running. This parameter is not
+// set until the crash-dump-generation phase.
+//
+// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
+// parameter dictionary correspond to log
+// file paths.
+//
+// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
+// internally, because Breakpad uses
+// the same dictionary internally to
+// track both its internal
+// configuration parameters and
+// parameters meant to be uploaded
+// to the server. This string is
+// used internally by Breakpad to
+// prefix user-supplied parameter
+// names so those can be sent to the
+// server without leaking Breakpad's
+// internal values.
+//
+// BREAKPAD_ON_DEMAND Used internally to indicate to the
+// Reporter that we're sending on-demand,
+// not as result of a crash.
+//
+// BREAKPAD_COMMENTS The text the user provided as comments.
+// Only used in crash_report_sender.
+
+// Returns a new BreakpadRef object on success, NULL otherwise.
+BreakpadRef BreakpadCreate(NSDictionary *parameters);
+
+// Uninstall and release the data associated with |ref|.
+void BreakpadRelease(BreakpadRef ref);
+
+// Clients may set an optional callback which gets called when a crash
+// occurs. The callback function should return |true| if we should
+// handle the crash, generate a crash report, etc. or |false| if we
+// should ignore it and forward the crash (normally to CrashReporter).
+// Context is a pointer to arbitrary data to make the callback with.
+void BreakpadSetFilterCallback(BreakpadRef ref,
+ BreakpadFilterCallback callback,
+ void *context);
+
+// User defined key and value string storage. Generally this is used
+// to configure Breakpad's internal operation, such as whether the
+// crash_sender should prompt the user, or the filesystem location for
+// the minidump file. See Breakpad.h for some parameters that can be
+// set. Anything longer than 255 bytes will be truncated. Note that
+// the string is converted to UTF8 before truncation, so any multibyte
+// character that straddles the 255(256 - 1 for terminator) byte limit
+// will be mangled.
+//
+// A maximum number of 64 key/value pairs are supported. An assert()
+// will fire if more than this number are set. Unfortunately, right
+// now, the same dictionary is used for both Breakpad's parameters AND
+// the Upload parameters.
+//
+// TODO (nealsid): Investigate how necessary this is if we don't
+// automatically upload parameters to the server anymore.
+// TODO (nealsid): separate server parameter dictionary from the
+// dictionary used to configure Breakpad, and document limits for each
+// independently.
+void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
+NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
+void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
+
+// You can use this method to specify parameters that will be uploaded
+// to the crash server. They will be automatically encoded as
+// necessary. Note that as mentioned above there are limits on both
+// the number of keys and their length.
+void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
+ NSString *value);
+
+// This method will remove a previously-added parameter from the
+// upload parameter set.
+void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
+
+// Add a log file for Breakpad to read and send upon crash dump
+void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname);
+
+// Generate a minidump and send
+void BreakpadGenerateAndSendReport(BreakpadRef ref);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
new file mode 100644
index 000000000..1d2e519bb
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm
@@ -0,0 +1,1043 @@
+// 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);
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch
new file mode 100644
index 000000000..729866635
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'Breakpad' target in the
+// 'Breakpad' project.
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist
new file mode 100644
index 000000000..e43baddc0
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.googlecode.google-breakpad</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h
new file mode 100644
index 000000000..b8aabbe47
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2007, 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.
+
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+//==============================================================================
+// class OnDemandServer :
+// A basic on-demand server launcher supporting a single named service port
+//
+// Example Usage :
+//
+// kern_return_t result;
+// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver",
+// "com.MyCompany.MyServiceName",
+// true,
+// &result);
+//
+// if (server) {
+// server->LaunchOnDemand();
+// mach_port_t service_port = GetServicePort();
+//
+// // Send a mach message to service_port and "myserver" will be launched
+// }
+//
+//
+// ---- Now in the server code ----
+//
+// // "myserver" should get the service port and read the message which
+// // launched it:
+// mach_port_t service_rcv_port_;
+// kern_return_t kr = bootstrap_check_in(bootstrap_port,
+// "com.MyCompany.MyServiceName",
+// &service_rcv_port_);
+// // mach_msg() read service_rcv_port_ ....
+//
+// ....
+//
+// // Later "myserver" may want to unregister the service if it doesn't
+// // want its bootstrap service to stick around after it exits.
+//
+// // DO NOT use mach_port_deallocate() here -- it will fail and the
+// // following bootstrap_register() will also fail leaving our service
+// // name hanging around forever (until reboot)
+// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
+//
+// kr = bootstrap_register(bootstrap_port,
+// "com.MyCompany.MyServiceName",
+// MACH_PORT_NULL);
+
+class OnDemandServer {
+ public:
+ // must call Initialize() to be useful
+ OnDemandServer()
+ : server_port_(MACH_PORT_NULL),
+ service_port_(MACH_PORT_NULL),
+ unregister_on_cleanup_(true) {
+ }
+
+ // Creates the bootstrap server and service
+ kern_return_t Initialize(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup);
+
+ // Returns an OnDemandServer object if successful, or NULL if there's
+ // an error. The error result will be returned in out_result.
+ //
+ // server_command : the full path name including optional command-line
+ // arguments to the executable representing the server
+ //
+ // service_name : represents service name
+ // something like "com.company.ServiceName"
+ //
+ // unregister_on_cleanup : if true, unregisters the service name
+ // when the OnDemandServer is deleted -- unregistering will
+ // ONLY be possible if LaunchOnDemand() has NOT been called.
+ // If false, then the service will continue to be registered
+ // even after the current process quits.
+ //
+ // out_result : if non-NULL, returns the result
+ // this value will be KERN_SUCCESS if Create() returns non-NULL
+ //
+ static OnDemandServer *Create(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup,
+ kern_return_t *out_result);
+
+ // Cleans up and if LaunchOnDemand() has not yet been called then
+ // the bootstrap service will be unregistered.
+ ~OnDemandServer();
+
+ // This must be called if we intend to commit to launching the server
+ // by sending a mach message to our service port. Do not call it otherwise
+ // or it will be difficult (impossible?) to unregister the service name.
+ void LaunchOnDemand();
+
+ // This is the port we need to send a mach message to after calling
+ // LaunchOnDemand(). Sending a message causing an immediate launch
+ // of the server
+ mach_port_t GetServicePort() { return service_port_; };
+
+ private:
+ // Disallow copy constructor
+ OnDemandServer(const OnDemandServer&);
+
+ // Cleans up and if LaunchOnDemand() has not yet been called then
+ // the bootstrap service will be unregistered.
+ void Unregister();
+
+ name_t service_name_;
+
+ mach_port_t server_port_;
+ mach_port_t service_port_;
+ bool unregister_on_cleanup_;
+};
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm
new file mode 100644
index 000000000..dbe601bb8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm
@@ -0,0 +1,189 @@
+// Copyright (c) 2007, 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.
+
+#import "OnDemandServer.h"
+
+#import "Breakpad.h"
+#include "common/mac/bootstrap_compat.h"
+
+#if DEBUG
+ #define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
+#if defined(MAC_OS_X_VERSION_10_5) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+ #define PRINT_BOOTSTRAP_RESULT(result_, message_) \
+ printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ );
+#else
+ #define PRINT_BOOTSTRAP_RESULT(result_, message_) \
+ PRINT_MACH_RESULT(result_, message_)
+#endif
+#else
+ #define PRINT_MACH_RESULT(result_, message_)
+ #define PRINT_BOOTSTRAP_RESULT(result_, message_)
+#endif
+
+//==============================================================================
+OnDemandServer *OnDemandServer::Create(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup,
+ kern_return_t *out_result) {
+ OnDemandServer *server = new OnDemandServer();
+
+ if (!server) return NULL;
+
+ kern_return_t result = server->Initialize(server_command,
+ service_name,
+ unregister_on_cleanup);
+
+ if (out_result) {
+ *out_result = result;
+ }
+
+ if (result == KERN_SUCCESS) {
+ return server;
+ }
+
+ delete server;
+ return NULL;
+};
+
+//==============================================================================
+kern_return_t OnDemandServer::Initialize(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup) {
+ unregister_on_cleanup_ = unregister_on_cleanup;
+
+ mach_port_t self_task = mach_task_self();
+
+ mach_port_t bootstrap_port;
+ kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port);
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): ");
+ return kr;
+ }
+
+ mach_port_t bootstrap_subset_port;
+ kr = bootstrap_subset(bootstrap_port, self_task, &bootstrap_subset_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): ");
+ return kr;
+ }
+
+ // The inspector will be invoked with its bootstrap port set to the subset,
+ // but the sender will need access to the original bootstrap port. Although
+ // the original port is the subset's parent, bootstrap_parent can't be used
+ // because it requires extra privileges. Stash the original bootstrap port
+ // in the subset by registering it under a known name. The inspector will
+ // recover this port and set it as its own bootstrap port in Inspector.mm
+ // Inspector::ResetBootstrapPort.
+ kr = breakpad::BootstrapRegister(
+ bootstrap_subset_port,
+ const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
+ bootstrap_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_register(): ");
+ return kr;
+ }
+
+ kr = bootstrap_create_server(bootstrap_subset_port,
+ const_cast<char*>(server_command),
+ geteuid(), // server uid
+ true,
+ &server_port_);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_server(): ");
+ return kr;
+ }
+
+ strlcpy(service_name_, service_name, sizeof(service_name_));
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ // Create a service called service_name, and return send rights to
+ // that port in service_port_.
+ kr = bootstrap_create_service(server_port_,
+ const_cast<char*>(service_name),
+ &service_port_);
+#pragma clang diagnostic pop
+ if (kr != BOOTSTRAP_SUCCESS) {
+ PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): ");
+
+ // perhaps the service has already been created - try to look it up
+ kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
+
+ if (kr != BOOTSTRAP_SUCCESS) {
+ PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): ");
+ Unregister(); // clean up server port
+ return kr;
+ }
+ }
+
+ return KERN_SUCCESS;
+}
+
+//==============================================================================
+OnDemandServer::~OnDemandServer() {
+ if (unregister_on_cleanup_) {
+ Unregister();
+ }
+}
+
+//==============================================================================
+void OnDemandServer::LaunchOnDemand() {
+ // We need to do this, since the launched server is another process
+ // and holding on to this port delays launching until the current process
+ // exits!
+ mach_port_deallocate(mach_task_self(), server_port_);
+ server_port_ = MACH_PORT_DEAD;
+
+ // Now, the service is still registered and all we need to do is send
+ // a mach message to the service port in order to launch the server.
+}
+
+//==============================================================================
+void OnDemandServer::Unregister() {
+ if (service_port_ != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), service_port_);
+ service_port_ = MACH_PORT_NULL;
+ }
+
+ if (server_port_ != MACH_PORT_NULL) {
+ // unregister the service
+ kern_return_t kr = breakpad::BootstrapRegister(server_port_,
+ service_name_,
+ MACH_PORT_NULL);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");
+ }
+
+ mach_port_deallocate(mach_task_self(), server_port_);
+ server_port_ = MACH_PORT_NULL;
+ }
+}