/* If thrown_object is not null, allocate, initialize and throw a dependent exception. */ void __cxa_rethrow_primary_exception(void* thrown_object) { if ( thrown_object != NULL ) { // thrown_object guaranteed to be native because // __cxa_current_primary_exception returns NULL for foreign exceptions __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); __cxa_dependent_exception* dep_exception_header = static_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception()); dep_exception_header->primaryException = thrown_object; __cxa_increment_exception_refcount(thrown_object); dep_exception_header->exceptionType = exception_header->exceptionType; dep_exception_header->unexpectedHandler = std::get_unexpected(); dep_exception_header->terminateHandler = std::get_terminate(); setDependentExceptionClass(&dep_exception_header->unwindHeader); __cxa_get_globals()->uncaughtExceptions += 1; dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup; #if __arm__ _Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader); #else _Unwind_RaiseException(&dep_exception_header->unwindHeader); #endif // Some sort of unwinding error. Note that terminate is a handler. __cxa_begin_catch(&dep_exception_header->unwindHeader); } // If we return client will call terminate() }
/* After constructing the exception object with the throw argument value, the generated code calls the __cxa_throw runtime library routine. This routine never returns. The __cxa_throw routine will do the following: * Obtain the __cxa_exception header from the thrown exception object address, which can be computed as follows: __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); * Save the current unexpected_handler and terminate_handler in the __cxa_exception header. * Save the tinfo and dest arguments in the __cxa_exception header. * Set the exception_class field in the unwind header. This is a 64-bit value representing the ASCII string "XXXXC++\0", where "XXXX" is a vendor-dependent string. That is, for implementations conforming to this ABI, the low-order 4 bytes of this 64-bit value will be "C++\0". * Increment the uncaught_exception flag. * Call _Unwind_RaiseException in the system unwind library, Its argument is the pointer to the thrown exception, which __cxa_throw itself received as an argument. __Unwind_RaiseException begins the process of stack unwinding, described in Section 2.5. In special cases, such as an inability to find a handler, _Unwind_RaiseException may return. In that case, __cxa_throw will call terminate, assuming that there was no handler for the exception. */ LIBCXXABI_NORETURN void __cxa_throw(void* thrown_object, std::type_info* tinfo, void (*dest)(void*)) { __cxa_eh_globals *globals = __cxa_get_globals(); __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); exception_header->unexpectedHandler = std::get_unexpected(); exception_header->terminateHandler = std::get_terminate(); exception_header->exceptionType = tinfo; exception_header->exceptionDestructor = dest; setExceptionClass(&exception_header->unwindHeader); exception_header->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local exception_header->unwindHeader.exception_cleanup = exception_cleanup_func; #if __arm__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); #else _Unwind_RaiseException(&exception_header->unwindHeader); #endif // This only happens when there is no handler, or some unexpected unwinding // error happens. failed_throw(exception_header); }
/* The routine to be called after the cleanup has been performed. It will get the propagating __cxa_exception from __cxa_eh_globals, and continue the stack unwinding with _Unwind_Resume. According to ARM EHABI 8.4.1, __cxa_end_cleanup() should not clobber any register, thus we have to write this function in assembly so that we can save {r1, r2, r3}. We don't have to save r0 because it is the return value and the first argument to _Unwind_Resume(). In addition, we are saving r4 in order to align the stack to 16 bytes, even though it is a callee-save register. */ __attribute__((used)) static _Unwind_Exception * __cxa_end_cleanup_impl() { __cxa_eh_globals* globals = __cxa_get_globals(); __cxa_exception* exception_header = globals->propagatingExceptions; if (NULL == exception_header) { // It seems that __cxa_begin_cleanup() is not called properly. // We have no choice but terminate the program now. std::terminate(); } if (isOurExceptionClass(&exception_header->unwindHeader)) { --exception_header->propagationCount; if (0 == exception_header->propagationCount) { globals->propagatingExceptions = exception_header->nextPropagatingException; exception_header->nextPropagatingException = NULL; } } else { globals->propagatingExceptions = NULL; } return &exception_header->unwindHeader; }
/* The routine to be called before the cleanup. This will save __cxa_exception in __cxa_eh_globals, so that __cxa_end_cleanup() can recover later. */ bool __cxa_begin_cleanup(void* unwind_arg) throw () { _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); __cxa_eh_globals* globals = __cxa_get_globals(); __cxa_exception* exception_header = cxa_exception_from_exception_unwind_exception(unwind_exception); if (isOurExceptionClass(unwind_exception)) { if (0 == exception_header->propagationCount) { exception_header->nextPropagatingException = globals->propagatingExceptions; globals->propagatingExceptions = exception_header; } ++exception_header->propagationCount; } else { // If the propagatingExceptions stack is not empty, since we can't // chain the foreign exception, terminate it. if (NULL != globals->propagatingExceptions) std::terminate(); globals->propagatingExceptions = exception_header; } return true; }
void __cilkrts_save_exception_state(__cilkrts_worker *w, full_frame *ff) { save_exception_info(w, __cxa_get_globals(), 0, false, "undo-detach"); CILK_ASSERT(NULL == ff->pending_exception); ff->pending_exception = w->l->pending_exception; w->l->pending_exception = NULL; }
void __tdeh_init() { #if defined(__EXCEPTIONS) __cxa_get_globals(); #else return; #endif }
void __cilkrts_setup_for_execution_sysdep(__cilkrts_worker *w, full_frame *ff) { // ASSERT: We own w->lock and ff->lock || P == 1 __cxa_eh_globals *state = __cxa_get_globals (); struct pending_exception_info *info = w->l->pending_exception; if (info == NULL) return; w->l->pending_exception = 0; #if DEBUG_EXCEPTIONS _Unwind_Exception *exc = info->active; if (exc) { fflush(stdout); fprintf(stderr, "__cilkrts_resume_except W%u %p->%p [%u %p]\n", w->self, exc, to_cxx(exc)->nextException, info->runtime_state.uncaughtExceptions, info->runtime_state.caughtExceptions); /*CILK_ASSERT(info->runtime_state.uncaughtExceptions > 0);*/ } #endif if (state->uncaughtExceptions || state->caughtExceptions) __cilkrts_bug("W%u: resuming with non-empty prior exception state %u %p\n", state->uncaughtExceptions, state->caughtExceptions); *state = info->runtime_state; info->runtime_state.caughtExceptions = 0; info->runtime_state.uncaughtExceptions = 0; if (info->rethrow) { info->rethrow = false; /* Resuming function will rethrow. Runtime calls std::terminate if there is no caught exception. */ ff->call_stack->flags |= CILK_FRAME_EXCEPTING; } if (info->active) { ff->call_stack->flags |= CILK_FRAME_EXCEPTING; ff->call_stack->except_data = info->active; info->active = 0; } if (info->empty()) { info->destruct(); __cilkrts_frame_free(w, info, sizeof *info); w->l->pending_exception = NULL; } #if CILK_LIB_DEBUG if (ff->call_stack->except_data) CILK_ASSERT(std::uncaught_exception()); #endif }
/* This routine can catch foreign or native exceptions. If native, the exception can be a primary or dependent variety. This routine may remain blissfully ignorant of whether the native exception is primary or dependent. If the exception is native: * Increment's the exception's handler count. * Push the exception on the stack of currently-caught exceptions if it is not already there (from a rethrow). * Decrements the uncaught_exception count. * Returns the adjusted pointer to the exception object, which is stored in the __cxa_exception by the personality routine. If the exception is foreign, this means it did not originate from one of throw routines. The foreign exception does not necessarily have a __cxa_exception header. However we can catch it here with a catch (...), or with a call to terminate or unexpected during unwinding. * Do not try to increment the exception's handler count, we don't know where it is. * Push the exception on the stack of currently-caught exceptions only if the stack is empty. The foreign exception has no way to link to the current top of stack. If the stack is not empty, call terminate. Even with an empty stack, this is hacked in by pushing a pointer to an imaginary __cxa_exception block in front of the foreign exception. It would be better if the __cxa_eh_globals structure had a stack of _Unwind_Exception, but it doesn't. It has a stack of __cxa_exception (which has a next* in it). * Do not decrement the uncaught_exception count because we didn't increment it in __cxa_throw (or one of our rethrow functions). * If we haven't terminated, assume the exception object is just past the _Unwind_Exception and return a pointer to that. */ void* __cxa_begin_catch(void* unwind_arg) throw() { _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); bool native_exception = isOurExceptionClass(unwind_exception); __cxa_eh_globals* globals = __cxa_get_globals(); // exception_header is a hackish offset from a foreign exception, but it // works as long as we're careful not to try to access any __cxa_exception // parts. __cxa_exception* exception_header = cxa_exception_from_exception_unwind_exception ( static_cast<_Unwind_Exception*>(unwind_exception) ); if (native_exception) { // Increment the handler count, removing the flag about being rethrown exception_header->handlerCount = exception_header->handlerCount < 0 ? -exception_header->handlerCount + 1 : exception_header->handlerCount + 1; // place the exception on the top of the stack if it's not already // there by a previous rethrow if (exception_header != globals->caughtExceptions) { exception_header->nextException = globals->caughtExceptions; globals->caughtExceptions = exception_header; } globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local #if LIBCXXABI_ARM_EHABI return reinterpret_cast<void*>(exception_header->unwindHeader.barrier_cache.bitpattern[0]); #else return exception_header->adjustedPtr; #endif } // Else this is a foreign exception // If the caughtExceptions stack is not empty, terminate if (globals->caughtExceptions != 0) std::terminate(); // Push the foreign exception on to the stack globals->caughtExceptions = exception_header; return unwind_exception + 1; }
/* This routine can rethrow native or foreign exceptions. If the exception is native: * marks the exception object on top of the caughtExceptions stack (in an implementation-defined way) as being rethrown. * If the caughtExceptions stack is empty, it calls terminate() (see [C++FDIS] [except.throw], 15.1.8). * It then calls _Unwind_RaiseException which should not return (terminate if it does). Note: exception_header may be masquerading as a __cxa_dependent_exception and that's ok. */ LIBCXXABI_NORETURN void __cxa_rethrow() { __cxa_eh_globals* globals = __cxa_get_globals(); __cxa_exception* exception_header = globals->caughtExceptions; if (NULL == exception_header) std::terminate(); // throw; called outside of a exception handler bool native_exception = isOurExceptionClass(&exception_header->unwindHeader); if (native_exception) { // Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch) exception_header->handlerCount = -exception_header->handlerCount; globals->uncaughtExceptions += 1; // __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary } else // this is a foreign exception { // The only way to communicate to __cxa_end_catch that we've rethrown // a foreign exception, so don't delete us, is to pop the stack here // which must be empty afterwards. Then __cxa_end_catch will do // nothing globals->caughtExceptions = 0; } #if __arm__ _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); #else _Unwind_RaiseException(&exception_header->unwindHeader); #endif // If we get here, some kind of unwinding error has occurred. // There is some weird code generation bug happening with // Apple clang version 4.0 (tags/Apple/clang-418.0.2) (based on LLVM 3.1svn) // If we call failed_throw here. Turns up with -O2 or higher, and -Os. __cxa_begin_catch(&exception_header->unwindHeader); if (native_exception) std::__terminate(exception_header->terminateHandler); // Foreign exception: can't get exception_header->terminateHandler std::terminate(); }
NORETURN __cilkrts_c_sync_except (__cilkrts_worker *w, __cilkrts_stack_frame *sf) { __cxa_eh_globals *state = __cxa_get_globals(); _Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data; CILK_ASSERT((sf->flags & (CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING)) == (CILK_FRAME_UNSYNCHED|CILK_FRAME_EXCEPTING)); sf->flags &= ~CILK_FRAME_EXCEPTING; #if DEBUG_EXCEPTIONS fflush(stdout); char decoded[9]; decode_flags(sf->flags, decoded); if (exc) fprintf(stderr, "__cilkrts_sync_except W%u %p/%s %p->%p [%u %p]\n", w->self, sf, decoded, exc, to_cxx(exc)->nextException, state->uncaughtExceptions, state->caughtExceptions); else fprintf(stderr, "__cilkrts_sync_except W%d %p/%s none [%u %p]\n", w->self, sf, decoded, state->uncaughtExceptions, state->caughtExceptions); #endif /* Here the identity of an rethrown exception is always known. If exc is NULL this call is only to preserve parent state. */ save_exception_info(w, state, exc, false, "sync_except"); #if 0 { full_frame *ff = w->l->frame_ff; CILK_ASSERT(NULL == ff->pending_exception); ff->pending_exception = w->l->pending_exception; w->l->pending_exception = NULL; } #endif CILK_ASSERT(!std::uncaught_exception()); __cilkrts_c_sync(w, sf); }
std::vector<ExceptionInfo> getCurrentExceptions() { struct Once { Once() { // See if linked in with us (getExceptionStackTraceStack is weak) getExceptionStackTraceStackFn = getExceptionStackTraceStack; if (!getExceptionStackTraceStackFn) { // Nope, see if it's in a shared library getExceptionStackTraceStackFn = (GetExceptionStackTraceStackType)dlsym( RTLD_NEXT, "getExceptionStackTraceStack"); } } }; static Once once; std::vector<ExceptionInfo> exceptions; auto currentException = __cxa_get_globals()->caughtExceptions; if (!currentException) { return exceptions; } StackTraceStack* traceStack = nullptr; if (!getExceptionStackTraceStackFn) { static bool logged = false; if (!logged) { LOG(WARNING) << "Exception tracer library not linked, stack traces not available"; logged = true; } } else if ((traceStack = getExceptionStackTraceStackFn()) == nullptr) { static bool logged = false; if (!logged) { LOG(WARNING) << "Exception stack trace invalid, stack traces not available"; logged = true; } } StackTrace* trace = traceStack ? traceStack->top() : nullptr; while (currentException) { ExceptionInfo info; // Dependent exceptions (thrown via std::rethrow_exception) aren't // standard ABI __cxa_exception objects, and are correctly labeled as // such in the exception_class field. We could try to extract the // primary exception type in horribly hacky ways, but, for now, nullptr. info.type = isAbiCppException(currentException) ? currentException->exceptionType : nullptr; if (traceStack) { LOG_IF(DFATAL, !trace) << "Invalid trace stack for exception of type: " << (info.type ? folly::demangle(*info.type) : "null"); if (!trace) { return {}; } info.frames.assign( trace->addresses, trace->addresses + trace->frameCount); trace = traceStack->next(trace); } currentException = currentException->nextException; exceptions.push_back(std::move(info)); } LOG_IF(DFATAL, trace) << "Invalid trace stack!"; return exceptions; }
CILK_ABI_THROWS_VOID __cilkrts_return_exception(__cilkrts_stack_frame *sf) { __cilkrts_worker *w = sf->worker; _Unwind_Exception *exc = (_Unwind_Exception *)sf->except_data; CILK_ASSERT(sf->flags & CILK_FRAME_DETACHED); sf->flags &= ~CILK_FRAME_DETACHED; /* * If we are in replay mode, and a steal occurred during the recording * phase, stall till a steal actually occurs. */ replay_wait_for_steal_if_parent_was_stolen(w); /* If this is to be an abnormal return, save the active exception. */ if (!__cilkrts_pop_tail(w)) { /* Write a record to the replay log for an attempt to return to a stolen parent. This must be done before the exception handler invokes __cilkrts_leave_frame which will bump the pedigree so the replay_wait_for_steal_if_parent_was_stolen() above will match on replay */ replay_record_orphaned(w); /* Now that the record/replay stuff is done, update the pedigree */ update_pedigree_on_leave_frame(w, sf); /* Inline pop_frame; this may not be needed. */ w->current_stack_frame = sf->call_parent; sf->call_parent = 0; __cxa_eh_globals *state = __cxa_get_globals(); #if DEBUG_EXCEPTIONS fflush(stdout); char decoded[9]; decode_flags(sf->flags, decoded); fprintf(stderr, "__cilkrts_save_except W%u sf %p/%s exc %p [%u %p] suspend\n", w->self, sf, decoded, exc, state->uncaughtExceptions, state->caughtExceptions); #endif /* Like __cilkrts_save_exception_state except for setting the rethrow flag. */ save_exception_info(w, state, exc, exc == NULL, "save_except"); { full_frame *ff = w->l->frame_ff; CILK_ASSERT(NULL == ff->pending_exception); ff->pending_exception = w->l->pending_exception; w->l->pending_exception = NULL; } __cilkrts_exception_from_spawn(w, sf); /* does not return */ } /* This code path is taken when the parent is attached. It is on the same stack and part of the same full frame. The caller is cleaning up the Cilk frame during unwind and will reraise the exception */ /* Now that the record/replay stuff is done, update the pedigree */ update_pedigree_on_leave_frame(w, sf); #if DEBUG_EXCEPTIONS /* DEBUG ONLY */ { __cxa_eh_globals *state = __cxa_get_globals(); fflush(stdout); char decoded[9]; decode_flags(sf->flags, decoded); fprintf(stderr, "__cilkrts_save_except W%d %p/%s %p->%p [%u %p] escape\n", w->self, sf, decoded, exc, exc ? to_cxx(exc)->nextException : 0, state->uncaughtExceptions, state->caughtExceptions); /* XXX This is triggering in the user thread which gets an exception from somewhere but does not get the corresponding runtime exception state. XXX There might be two or more uncaught exceptions. Test could be (uncaught != 0) == (exc != 0). First, design tests to see if that case is otherwise handled correctly. And what if there's an uncaught exception that does not belong to this function? I.e. this is a return from spawn in a destructor. */ if (exc) CILK_ASSERT((int)state->uncaughtExceptions > 0); /*CILK_ASSERT(state->uncaughtExceptions == (exc != 0));*/ } #endif /* The parent is attached so this exception can be propagated normally. */ return; }