- Clean up the signal handling callback types to seperate the user
callbacks vs. the internal callbacks, each of which accept slightly different state (internal callbacks determine the next callback in the chain, and then pass that data to user callbacks). - Modify the unit tests to validate basic signal handler pass-through without actually triggering a signal. Issue: PLCR-468
This commit is contained in:
Родитель
4c4bb497b0
Коммит
217f8a10f8
|
@ -291,9 +291,6 @@
|
|||
05A7E78F173C130200ACA689 /* PLCrashFrameCompactUnwind.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F3CD5F16DD6A3B007911FB /* PLCrashFrameCompactUnwind.c */; };
|
||||
05A7E7AE174284E700ACA689 /* PLCrashFrameCompactUnwind.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F3CD5F16DD6A3B007911FB /* PLCrashFrameCompactUnwind.c */; };
|
||||
05A7E7AF174284EE00ACA689 /* PLCrashFrameCompactUnwind.c in Sources */ = {isa = PBXBuildFile; fileRef = 05F3CD5F16DD6A3B007911FB /* PLCrashFrameCompactUnwind.c */; };
|
||||
05B929E217C7F4A900B051E3 /* PLCrashSignalHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05CD339B0EE948EB000FDE88 /* PLCrashSignalHandler.mm */; };
|
||||
05B929E317C7F4B500B051E3 /* PLCrashSignalHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05CD339B0EE948EB000FDE88 /* PLCrashSignalHandler.mm */; };
|
||||
05B929E417C7F4B900B051E3 /* PLCrashSignalHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05CD339B0EE948EB000FDE88 /* PLCrashSignalHandler.mm */; };
|
||||
05BB83CD1364A77800D53B84 /* PLCrashReportProcessorInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 05BB83CB1364A77800D53B84 /* PLCrashReportProcessorInfo.h */; };
|
||||
05BB83CE1364A77800D53B84 /* PLCrashReportProcessorInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 05BB83CC1364A77800D53B84 /* PLCrashReportProcessorInfo.m */; };
|
||||
05BB83CF1364A77800D53B84 /* PLCrashReportProcessorInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 05BB83CB1364A77800D53B84 /* PLCrashReportProcessorInfo.h */; };
|
||||
|
@ -2997,7 +2994,6 @@
|
|||
05BEC43117BD4F540082CBFB /* PLCrashAsyncMachExceptionInfoTests.m in Sources */,
|
||||
05A5E28C17C04188008A75E5 /* PLCrashAsyncLinkedList.cpp in Sources */,
|
||||
05A5E29417C056EB008A75E5 /* PLCrashAsyncLinkedListTests.mm in Sources */,
|
||||
05B929E417C7F4B900B051E3 /* PLCrashSignalHandler.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -3100,7 +3096,6 @@
|
|||
05BEC43217BD4F540082CBFB /* PLCrashAsyncMachExceptionInfoTests.m in Sources */,
|
||||
05A5E28D17C04188008A75E5 /* PLCrashAsyncLinkedList.cpp in Sources */,
|
||||
05A5E29517C056EB008A75E5 /* PLCrashAsyncLinkedListTests.mm in Sources */,
|
||||
05B929E317C7F4B500B051E3 /* PLCrashSignalHandler.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -3203,7 +3198,6 @@
|
|||
05BEC43317BD4F540082CBFB /* PLCrashAsyncMachExceptionInfoTests.m in Sources */,
|
||||
05A5E28E17C04188008A75E5 /* PLCrashAsyncLinkedList.cpp in Sources */,
|
||||
05A5E29617C056EB008A75E5 /* PLCrashAsyncLinkedListTests.mm in Sources */,
|
||||
05B929E217C7F4A900B051E3 /* PLCrashSignalHandler.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -18,3 +18,9 @@ _OBJC_CLASS_$_*PL*
|
|||
# PLCrashReport
|
||||
_*PLCrashReportHostOperatingSystem
|
||||
_*PLCrashReportHostArchitecture
|
||||
|
||||
# PLCrashSignalHandler functions. These are currently exposed to simplify unit testing,
|
||||
# though the entirity of the current internal set of symbols will likely be exposed as unsupported
|
||||
# API in a future release.
|
||||
_plcrash_signal_handler
|
||||
_PLCrashSignalHandlerForward
|
|
@ -48,6 +48,8 @@ typedef struct PLCrashSignalHandlerCallback PLCrashSignalHandlerCallback;
|
|||
*/
|
||||
typedef bool (*PLCrashSignalHandlerCallbackFunc)(int signo, siginfo_t *info, ucontext_t *uap, void *context, PLCrashSignalHandlerCallback *next);
|
||||
|
||||
void plcrash_signal_handler (int signo, siginfo_t *info, void *uapVoid);
|
||||
|
||||
bool PLCrashSignalHandlerForward (PLCrashSignalHandlerCallback *next, int signal, siginfo_t *info, ucontext_t *uap);
|
||||
|
||||
@interface PLCrashSignalHandler : NSObject {
|
||||
|
|
|
@ -59,16 +59,33 @@ static int n_fatal_signals = (sizeof(fatal_signals) / sizeof(fatal_signals[0]));
|
|||
/**
|
||||
* @internal
|
||||
*
|
||||
* PLCrashSignalHandlerCallbackFunc and associated context.
|
||||
* Manages the internal state for a user-registered callback and context.
|
||||
*/
|
||||
struct PLCrashSignalHandlerCallback {
|
||||
/** Signal handler internal callback function. */
|
||||
struct plcrash_signal_user_callback {
|
||||
/** Signal handler callback function. */
|
||||
PLCrashSignalHandlerCallbackFunc callback;
|
||||
|
||||
/** Signal handler context. */
|
||||
void *context;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* A signal handler callback context.
|
||||
*/
|
||||
struct PLCrashSignalHandlerCallback {
|
||||
/**
|
||||
* Internal callback function. This function is responsible for determining the next
|
||||
* signal handler in the chain of handlers, and issueing the actual PLCrashSignalHandlerCallback()
|
||||
* invocation.
|
||||
*/
|
||||
bool (*callback)(int signo, siginfo_t *info, ucontext_t *uap, void *context);
|
||||
|
||||
/** Signal handler context. */
|
||||
void *context;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
|
@ -91,7 +108,7 @@ struct plcrash_signal_handler_action {
|
|||
static struct {
|
||||
/** @internal
|
||||
* Registered callbacks. */
|
||||
async_list<PLCrashSignalHandlerCallback> callbacks;
|
||||
async_list<plcrash_signal_user_callback> callbacks;
|
||||
|
||||
/** @internal
|
||||
* Originaly registered signal handlers */
|
||||
|
@ -99,13 +116,17 @@ static struct {
|
|||
} shared_handler_context;
|
||||
|
||||
/*
|
||||
* A signal handler callback used to execute the first matching signal handler in the shared previous_actions list; this is used
|
||||
* Finds and executes the first matching signal handler in the shared previous_actions list; this is used
|
||||
* to support executing process-wide POSIX signal handlers that were previously registered before being replaced by
|
||||
* PLCrashSignalHandler::registerHandlerForSignal:.
|
||||
*/
|
||||
static bool previous_action_callback (int signo, siginfo_t *info, ucontext_t *uap, void *context, PLCrashSignalHandlerCallback *next) {
|
||||
bool handled = false;
|
||||
|
||||
/* Let any additional handler execute */
|
||||
if (PLCrashSignalHandlerForward(next, signo, info, uap))
|
||||
return true;
|
||||
|
||||
shared_handler_context.previous_actions.set_reading(true); {
|
||||
/* Find the first matching handler */
|
||||
async_list<plcrash_signal_handler_action>::node *next = NULL;
|
||||
|
@ -149,16 +170,22 @@ static bool previous_action_callback (int signo, siginfo_t *info, ucontext_t *ua
|
|||
}
|
||||
|
||||
/*
|
||||
* A signal handler callback used to recursively iterate the actual callbacks registered in our shared_handler_context. To begin iteration,
|
||||
* Recursively iterates the actual callbacks registered in our shared_handler_context. To begin iteration,
|
||||
* provide a value of NULL for 'context'.
|
||||
*/
|
||||
static bool internal_callback_iterator (int signo, siginfo_t *info, ucontext_t *uap, void *context, PLCrashSignalHandlerCallback *next) {
|
||||
static bool internal_callback_iterator (int signo, siginfo_t *info, ucontext_t *uap, void *context) {
|
||||
/* Call the next handler in the chain. If this is the last handler in the chain, pass it the original signal
|
||||
* handlers. */
|
||||
bool handled = false;
|
||||
shared_handler_context.callbacks.set_reading(true); {
|
||||
async_list<PLCrashSignalHandlerCallback>::node *prev = (async_list<PLCrashSignalHandlerCallback>::node *) context;
|
||||
async_list<PLCrashSignalHandlerCallback>::node *current = shared_handler_context.callbacks.next(prev);
|
||||
async_list<plcrash_signal_user_callback>::node *prev = (async_list<plcrash_signal_user_callback>::node *) context;
|
||||
async_list<plcrash_signal_user_callback>::node *current = shared_handler_context.callbacks.next(prev);
|
||||
|
||||
/* Check for end-of-list */
|
||||
if (current == NULL) {
|
||||
shared_handler_context.callbacks.set_reading(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if any additional handlers are registered. If so, provide the next handler as the forwarding target. */
|
||||
if (shared_handler_context.callbacks.next(current) != NULL) {
|
||||
|
@ -176,9 +203,17 @@ static bool internal_callback_iterator (int signo, siginfo_t *info, ucontext_t *
|
|||
return handled;
|
||||
};
|
||||
|
||||
/** @internal
|
||||
* Root fatal signal handler */
|
||||
static void fatal_signal_handler (int signo, siginfo_t *info, void *uapVoid) {
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* The signal handler function used by PLCrashSignalHandler. This function should not be called or referenced directly,
|
||||
* but is exposed to allow simulating signal handling behavior from unit tests.
|
||||
*
|
||||
* @param signo The signal number.
|
||||
* @param info The signal information.
|
||||
* @param uapVoid A ucontext_t pointer argument.
|
||||
*/
|
||||
void plcrash_signal_handler (int signo, siginfo_t *info, void *uapVoid) {
|
||||
/* Remove all signal handlers -- if the dump code fails, the default terminate
|
||||
* action will occur.
|
||||
*
|
||||
|
@ -202,7 +237,7 @@ static void fatal_signal_handler (int signo, siginfo_t *info, void *uapVoid) {
|
|||
/* Start iteration; we currently re-raise the signal if not handled by callbacks; this should be revisited
|
||||
* in the future, as the signal may not be raised on the expected thread.
|
||||
*/
|
||||
if (!internal_callback_iterator(signo, info, (ucontext_t *) uapVoid, NULL, NULL))
|
||||
if (!internal_callback_iterator(signo, info, (ucontext_t *) uapVoid, NULL))
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
|
@ -224,8 +259,7 @@ bool PLCrashSignalHandlerForward (PLCrashSignalHandlerCallback *next, int sig, s
|
|||
if (next == NULL)
|
||||
return false;
|
||||
|
||||
/* The 'next' value will be populated by our internal handlers; we supply a NULL value here. */
|
||||
return next->callback(sig, info, uap, next->context, NULL);
|
||||
return next->callback(sig, info, uap, next->context);
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -306,7 +340,7 @@ static PLCrashSignalHandler *sharedHandler;
|
|||
}
|
||||
|
||||
/* Add the pass-through sigaction callback as the last element in the callback list. */
|
||||
PLCrashSignalHandlerCallback sa = {
|
||||
plcrash_signal_user_callback sa = {
|
||||
.callback = previous_action_callback,
|
||||
.context = NULL
|
||||
};
|
||||
|
@ -321,7 +355,7 @@ static PLCrashSignalHandler *sharedHandler;
|
|||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = &fatal_signal_handler;
|
||||
sa.sa_sigaction = &plcrash_signal_handler;
|
||||
|
||||
/* Set new sigaction */
|
||||
if (sigaction(fatal_signals[i], &sa, &sa_prev) != 0) {
|
||||
|
@ -338,7 +372,7 @@ static PLCrashSignalHandler *sharedHandler;
|
|||
.signo = fatal_signals[i],
|
||||
.action = sa_prev
|
||||
};
|
||||
shared_handler_context.previous_actions.nasync_prepend(act);
|
||||
shared_handler_context.previous_actions.nasync_append(act);
|
||||
}
|
||||
} pthread_mutex_unlock(®isterHandlers);
|
||||
|
||||
|
@ -365,7 +399,7 @@ static PLCrashSignalHandler *sharedHandler;
|
|||
return NO;
|
||||
|
||||
/* Add the new callback to the shared state list. */
|
||||
PLCrashSignalHandlerCallback reg = {
|
||||
plcrash_signal_user_callback reg = {
|
||||
.callback = crashCallback,
|
||||
.context = context
|
||||
};
|
||||
|
|
|
@ -80,6 +80,7 @@ static bool crash_callback (int signal, siginfo_t *siginfo, ucontext_t *uap, voi
|
|||
}
|
||||
|
||||
static void sa_action_cb (int signo, siginfo_t *info, void *uapVoid) {
|
||||
/* Note that we ran */
|
||||
crash_page[1] = 0xFB;
|
||||
}
|
||||
|
||||
|
@ -90,7 +91,8 @@ static void sa_handler_cb (int signo) {
|
|||
#endif
|
||||
|
||||
static bool noop_crash_cb (int signal, siginfo_t *siginfo, ucontext_t *uap, void *context, PLCrashSignalHandlerCallback *next) {
|
||||
mprotect(crash_page, sizeof(crash_page), PROT_READ|PROT_WRITE);
|
||||
/* Note that we ran */
|
||||
crash_page[0] = 0xFA;
|
||||
|
||||
// Let the original signal handler run
|
||||
return PLCrashSignalHandlerForward(next, signal, siginfo, uap);
|
||||
|
@ -113,18 +115,13 @@ static bool noop_crash_cb (int signal, siginfo_t *siginfo, ucontext_t *uap, void
|
|||
/* Register our callback */
|
||||
STAssertTrue([[PLCrashSignalHandler sharedHandler] registerHandlerWithCallback: &noop_crash_cb context: NULL error: &error], @"Could not register signal handler: %@", error);
|
||||
|
||||
/* Verify that the callback is dispatched; if the process doesn't lock up here, the signal handler is working. Unfortunately, this
|
||||
* test will halt when run under a debugger due to their catching of fatal signals, so we only perform the test if we're not
|
||||
* currently being traced */
|
||||
if (![[PLCrashProcessInfo currentProcessInfo] isTraced]) {
|
||||
mprotect(crash_page, sizeof(crash_page), PROT_NONE);
|
||||
crash_page[0] = 0xCA;
|
||||
|
||||
STAssertEquals(crash_page[0], (uint8_t)0xCA, @"Byte should have been set to test value");
|
||||
STAssertEquals(crash_page[1], (uint8_t)0xFB, @"Crash callback did not run");
|
||||
} else {
|
||||
NSLog(@"Running under debugger; skipping signal callback validation.");
|
||||
}
|
||||
/* Verify that the callbacks are dispatched */
|
||||
siginfo_t si;
|
||||
ucontext_t uc;
|
||||
plcrash_signal_handler(SIGBUS, &si, &uc);
|
||||
|
||||
STAssertEquals(crash_page[0], (uint8_t)0xFA, @"Crash callback did not run");
|
||||
STAssertEquals(crash_page[1], (uint8_t)0xFB, @"Signal handler did not run");
|
||||
}
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче