mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-21 21:25:52 +02:00
* Added breakpad support for Linux.
This commit is contained in:
316
thirdparty/breakpad/client/mac/Framework/Breakpad.h
vendored
Normal file
316
thirdparty/breakpad/client/mac/Framework/Breakpad.h
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
// 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.
|
||||
|
||||
typedef void *BreakpadRef;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
// Keys for configuration file
|
||||
#define kReporterMinidumpDirectoryKey "MinidumpDir"
|
||||
#define kReporterMinidumpIDKey "MinidumpID"
|
||||
|
||||
// Filename for recording uploaded IDs
|
||||
#define kReporterLogFilename "uploads.log"
|
||||
|
||||
// The default subdirectory of the Library to put crash dumps in
|
||||
// The subdirectory is
|
||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
||||
#define kDefaultLibrarySubdirectory "Breakpad"
|
||||
|
||||
// Specify some special keys to be used in the configuration file that is
|
||||
// generated by Breakpad and consumed by the crash_sender.
|
||||
#define BREAKPAD_PRODUCT "BreakpadProduct"
|
||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
|
||||
#define BREAKPAD_VERSION "BreakpadVersion"
|
||||
#define BREAKPAD_VENDOR "BreakpadVendor"
|
||||
#define BREAKPAD_URL "BreakpadURL"
|
||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
|
||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
|
||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
|
||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
|
||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
|
||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
|
||||
#define BREAKPAD_REPORTER_EXE_LOCATION \
|
||||
"BreakpadReporterExeLocation"
|
||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
|
||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
|
||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
|
||||
#define BREAKPAD_COMMENTS "BreakpadComments"
|
||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
|
||||
#define BREAKPAD_EMAIL "BreakpadEmail"
|
||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
|
||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
|
||||
|
||||
// The keys below are NOT user supplied, and are used internally.
|
||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
|
||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
|
||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
|
||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
|
||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
|
||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
|
||||
|
||||
// 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.
|
||||
//=============================================================================
|
||||
// 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 the process started.
|
||||
//
|
||||
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
|
||||
//
|
||||
// BREAKPAD_PROCESS_UP_TIME The total time 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
|
996
thirdparty/breakpad/client/mac/Framework/Breakpad.mm
vendored
Normal file
996
thirdparty/breakpad/client/mac/Framework/Breakpad.mm
vendored
Normal file
@@ -0,0 +1,996 @@
|
||||
// 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 VERBOSE 0
|
||||
|
||||
#if VERBOSE
|
||||
static bool gDebugLog = true;
|
||||
#else
|
||||
static bool gDebugLog = false;
|
||||
#endif
|
||||
|
||||
#define DEBUGLOG if (gDebugLog) fprintf
|
||||
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||
|
||||
#import "common/mac/MachIPC.h"
|
||||
#import "common/mac/SimpleStringDictionary.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"
|
||||
|
||||
#import <sys/stat.h>
|
||||
#import <sys/sysctl.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
using google_breakpad::KeyValueEntry;
|
||||
using google_breakpad::MachPortSender;
|
||||
using google_breakpad::MachReceiveMessage;
|
||||
using google_breakpad::MachSendMessage;
|
||||
using google_breakpad::ReceivePort;
|
||||
using google_breakpad::SimpleStringDictionary;
|
||||
using google_breakpad::SimpleStringDictionaryIterator;
|
||||
|
||||
//=============================================================================
|
||||
// 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:
|
||||
// allocator may be NULL, in which case no Protect() or Unprotect() calls
|
||||
// will be made, but a lock will still be taken
|
||||
ProtectedMemoryLocker(pthread_mutex_t *mutex,
|
||||
ProtectedMemoryAllocator *allocator)
|
||||
: mutex_(mutex), allocator_(allocator) {
|
||||
// Lock the mutex
|
||||
assert(pthread_mutex_lock(mutex_) == 0);
|
||||
|
||||
// Unprotect the memory
|
||||
if (allocator_ ) {
|
||||
allocator_->Unprotect();
|
||||
}
|
||||
}
|
||||
|
||||
~ProtectedMemoryLocker() {
|
||||
// First protect the memory
|
||||
if (allocator_) {
|
||||
allocator_->Protect();
|
||||
}
|
||||
|
||||
// Then unlock the mutex
|
||||
assert(pthread_mutex_unlock(mutex_) == 0);
|
||||
};
|
||||
|
||||
private:
|
||||
// Keep anybody from ever creating one of these things not on the stack.
|
||||
ProtectedMemoryLocker() { }
|
||||
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
|
||||
ProtectedMemoryLocker & operator=(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 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#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 {
|
||||
DEBUGLOG(stderr, "Could not find GetResourcePath\n");
|
||||
// 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()) {
|
||||
DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gather any user specified parameters
|
||||
if (!ExtractParameters(parameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
DEBUGLOG(stderr, "Could not get resource path\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Find Inspector.
|
||||
if (!inspectorPathString) {
|
||||
inspectorPathString =
|
||||
[resourcePath stringByAppendingPathComponent:@"Inspector"];
|
||||
}
|
||||
|
||||
// Verify that there is an Inspector tool.
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
|
||||
DEBUGLOG(stderr, "Cannot find Inspector tool\n");
|
||||
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]) {
|
||||
DEBUGLOG(stderr, "Cannot find Reporter tool\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dumpSubdirectory) {
|
||||
dumpSubdirectory = @"";
|
||||
}
|
||||
|
||||
// The product, version, and URL are required values.
|
||||
if (![product length]) {
|
||||
DEBUGLOG(stderr, "Missing required product key.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![version length]) {
|
||||
DEBUGLOG(stderr, "Missing required version key.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (![urlStr length]) {
|
||||
DEBUGLOG(stderr, "Missing required URL key.\n");
|
||||
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) {
|
||||
DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
|
||||
|
||||
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 KeyValueEntry *entry = NULL;
|
||||
SimpleStringDictionaryIterator 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;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
|
||||
#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);
|
||||
}
|
8
thirdparty/breakpad/client/mac/Framework/Breakpad_Prefix.pch
vendored
Normal file
8
thirdparty/breakpad/client/mac/Framework/Breakpad_Prefix.pch
vendored
Normal file
@@ -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
|
26
thirdparty/breakpad/client/mac/Framework/Info.plist
vendored
Normal file
26
thirdparty/breakpad/client/mac/Framework/Info.plist
vendored
Normal file
@@ -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>
|
146
thirdparty/breakpad/client/mac/Framework/OnDemandServer.h
vendored
Normal file
146
thirdparty/breakpad/client/mac/Framework/OnDemandServer.h
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// 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 <iostream>
|
||||
#import <mach/mach.h>
|
||||
#import <servers/bootstrap.h>
|
||||
#import <stdio.h>
|
||||
#import <stdlib.h>
|
||||
#import <sys/stat.h>
|
||||
#import <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_;
|
||||
};
|
145
thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm
vendored
Normal file
145
thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm
vendored
Normal file
@@ -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.
|
||||
|
||||
#import "OnDemandServer.h"
|
||||
|
||||
#if DEBUG
|
||||
#define PRINT_MACH_RESULT(result_, message_) \
|
||||
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
|
||||
#else
|
||||
#define PRINT_MACH_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;
|
||||
|
||||
kern_return_t kr =
|
||||
bootstrap_create_server(bootstrap_port,
|
||||
const_cast<char*>(server_command),
|
||||
geteuid(), // server uid
|
||||
true,
|
||||
&server_port_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "bootstrap_create_server() : ");
|
||||
return kr;
|
||||
}
|
||||
|
||||
strlcpy(service_name_, service_name, sizeof(service_name_));
|
||||
|
||||
// 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_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_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 != KERN_SUCCESS) {
|
||||
PRINT_MACH_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 = bootstrap_register(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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user