plcrashreporter/Source/PLCrashMachExceptionPort.m

250 строки
10 KiB
Objective-C

/*
* Author: Landon Fuller <landonf@bikemonkey.org>
*
* Copyright (c) 2012-2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import "PLCrashCompatConstants.h"
#import "PLCrashMachExceptionPort.h"
#import "PLCrashReporterNSError.h"
#if PLCRASH_FEATURE_MACH_EXCEPTIONS
/**
* @internal
*
* This class manages a reference to a Mach exception server port, and the associated
* mask, behavior, and thread state flavor expected by the given Mach exception server.
*/
@implementation PLCrashMachExceptionPort {
/** Exception mask. */
exception_mask_t _mask;
/** Exception server port. */
mach_port_t _port;
/** Exception behavior. */
exception_behavior_t _behavior;
/** Exception thread flavor. */
thread_state_flavor_t _flavor;
}
@synthesize server_port = _port;
@synthesize mask = _mask;
@synthesize behavior = _behavior;
@synthesize flavor = _flavor;
/**
* Return the current PLCrashMachExceptionPortState set registered for a @a task and @a mask.
*
* @param task The task for which exception port state will be retrieved.
* @param mask The exception mask for which exception port state will be retrieved.
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
* will contain an error object indicating why the Mach exception port state could not be fetched. If no error
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
* will be provided.
*
* @return Returns the set of registered port states on success, or nil on failure.
*/
+ (PLCrashMachExceptionPortSet *) exceptionPortsForTask: (task_t) task mask: (exception_mask_t) mask error: (NSError **) outError {
plcrash_mach_exception_port_set_t states;
kern_return_t kr;
/* Fetch the current ports */
kr = task_get_exception_ports(task,
mask,
states.masks,
&states.count,
states.ports,
states.behaviors,
states.flavors);
if (kr != KERN_SUCCESS) {
plcrash_populate_mach_error(outError, kr, @"Failed to fetch mach exception ports");
return nil;
}
return [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: states];
}
/**
* Return the current PLCrashMachExceptionPortState set registered for a @a thread and @a mask.
*
* @param thread The thread for which exception port state will be retrieved.
* @param mask The exception mask for which exception port state will be retrieved.
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
* will contain an error object indicating why the Mach exception port state could not be fetched. If no error
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
* will be provided.
*
* @return Returns the set of registered port states on success, or nil on failure.
*/
+ (PLCrashMachExceptionPortSet *) exceptionPortsForThread: (thread_t) thread mask: (exception_mask_t) mask error: (NSError **) outError {
plcrash_mach_exception_port_set_t states;
kern_return_t kr;
/* Fetch the current ports */
kr = thread_get_exception_ports(thread,
mask,
states.masks,
&states.count,
states.ports,
states.behaviors,
states.flavors);
if (kr != KERN_SUCCESS) {
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
return nil;
}
return [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: states];
}
/**
* Initialize a new instance.
*
* @param port The Mach exception server's port, for which a MACH_PORT_RIGHT_SEND will be retained (and released on dealloc). MACH_PORT_NULL may be specified.
* @param mask The exception masks for which @a port should be (or was) registered.
* @param behavior The exception behavior expected by the server on @a port.
* @param flavor The thread flavor expected by the server on @a port.
*/
- (instancetype) initWithServerPort: (mach_port_t) port
mask: (exception_mask_t) mask
behavior: (exception_behavior_t) behavior
flavor: (thread_state_flavor_t) flavor
{
kern_return_t kt;
if ((self = [super init]) == nil)
return nil;
_port = port;
_mask = mask;
_behavior = behavior;
_flavor = flavor;
/* Retain the port if it's not MACH_PORT_(NULL|INVALID) */
if (MACH_PORT_VALID(_port) && (kt = mach_port_mod_refs(mach_task_self(), _port, MACH_PORT_RIGHT_SEND, 1)) != KERN_SUCCESS) {
PLCR_LOG("Unexpected error incrementing mach port reference: %d", kt);
}
return self;
}
- (void) dealloc {
kern_return_t kt;
/* Release the port if it's not MACH_PORT_(NULL|INVALID) */
if (MACH_PORT_VALID(_port) && (kt = mach_port_mod_refs(mach_task_self(), _port, MACH_PORT_RIGHT_SEND, -1)) != KERN_SUCCESS) {
PLCR_LOG("Unexpected error incrementing mach port reference: %d", kt);
}
}
/**
* Atomically set the Mach exception server port managed by the receiver as the @a task's Mach exception server, returning
* the previously configured ports in @a portStates.
*
* @param task The task for which the Mach exception server should be set.
* @param ports On success, will contain a set of previously registered ports for the exception masks claimed
* by the receiver. If NULL, the previous ports will not be provided.
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
* will contain an error object indicating why the Mach exception port could not be registered. If no error
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
* will be provided.
* @return YES if the mach exception port state was successfully registered for @a task, NO on error.
*/
- (BOOL) registerForTask: (task_t) task previousPortSet: (__strong PLCrashMachExceptionPortSet **) ports error: (NSError **) outError {
plcrash_mach_exception_port_set_t prev;
kern_return_t kr;
kr = task_swap_exception_ports(task,
_mask,
_port,
_behavior,
_flavor,
prev.masks,
&prev.count,
prev.ports,
prev.behaviors,
prev.flavors);
if (kr != KERN_SUCCESS) {
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
return NO;
}
if (ports != NULL)
*ports = [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: prev];
return YES;
}
/**
* Atomically set the Mach exception server port managed by the receiver as the @a thread's Mach exception server, returning
* the previously configured ports in @a portStates.
*
* @param thread The thread for which the Mach exception server should be set.
* @param ports On success, will contain a set of previously registered ports for the exception masks claimed
* by the receiver.
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
* will contain an error object indicating why the Mach exception port could not be registered. If no error
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
* will be provided.
* @return YES if the mach exception port state was successfully registered for @a thread, NO on error.
*/
- (BOOL) registerForThread: (thread_t) thread previousPortSet: (__strong PLCrashMachExceptionPortSet **) ports error: (NSError **) outError {
plcrash_mach_exception_port_set_t prev;
kern_return_t kr;
kr = thread_swap_exception_ports(thread,
_mask,
_port,
_behavior,
_flavor,
prev.masks,
&prev.count,
prev.ports,
prev.behaviors,
prev.flavors);
if (kr != KERN_SUCCESS) {
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
return NO;
}
if (ports != NULL)
*ports = [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: prev];
return YES;
}
@end
#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */