void EventHook::onFunctionExit(const ActRec* ar, const TypedValue* retval, const Fault* fault, ssize_t flags) { // Xenon if (flags & XenonSignalFlag) { Xenon::getInstance().log(Xenon::ExitSample); } // Run IntervalTimer callbacks only if it's safe to do so, i.e., not when // there's a pending exception or we're unwinding from a C++ exception. if (flags & IntervalTimerFlag && ThreadInfo::s_threadInfo->m_pendingException == nullptr && (!fault || fault->m_faultType == Fault::Type::UserException)) { IntervalTimer::RunCallbacks(IntervalTimer::ExitSample); } // Inlined calls normally skip the function enter and exit events. If we // side exit in an inlined callee, we short-circuit here in order to skip // exit events that could unbalance the call stack. if (RuntimeOption::EvalJit && ((jit::TCA) ar->m_savedRip == jit::mcg->tx().uniqueStubs.retInlHelper)) { return; } // User profiler if (flags & EventHookFlag) { auto profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr && !(profiler->shouldSkipBuiltins() && ar->func()->isBuiltin())) { // NB: we don't have a function type flag to match what we got in // onFunctionEnter. That's okay, though... we tolerate this in // TraceProfiler. end_profiler_frame(profiler, retval, GetFunctionNameForProfiler(ar->func(), NormalFunc)); } if (shouldRunUserProfiler(ar->func())) { if (ThreadInfo::s_threadInfo->m_pendingException != nullptr) { // Avoid running PHP code when exception from destructor is pending. // TODO(#2329497) will not happen once CheckSurprise is used } else if (!fault) { runUserProfilerOnFunctionExit(ar, retval, nullptr); } else if (fault->m_faultType == Fault::Type::UserException) { runUserProfilerOnFunctionExit(ar, retval, fault->m_userException); } else { // Avoid running PHP code when unwinding C++ exception. } } } // Debugger hook if (flags & DebuggerHookFlag) { DEBUGGER_ATTACHED_ONLY(phpDebuggerFuncExitHook(ar)); } }
void EventHook::onFunctionExit(const ActRec* ar, TypedValue* retval) { auto const inlinedRip = JIT::tx->uniqueStubs.retInlHelper; if ((JIT::TCA)ar->m_savedRip == inlinedRip) { // Inlined calls normally skip the function enter and exit events. If we // side exit in an inlined callee, we want to make sure to skip the exit // event to avoid unbalancing the call stack. return; } Xenon::getInstance().log(false); #ifdef HOTPROFILER Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { // NB: we don't have a function type flag to match what we got in // onFunctionEnter. That's okay, though... we tolerate this in // TraceProfiler. end_profiler_frame(profiler, GetFunctionNameForProfiler(ar->func(), NormalFunc)); } #endif // If we have a pending exception, then we're in the process of unwinding // for that exception. We avoid running more PHP code (the user profiler) and // also avoid raising more exceptions for surprises (including the pending // exception). if (ThreadInfo::s_threadInfo->m_pendingException == nullptr) { if (shouldRunUserProfiler(ar->func())) { runUserProfilerOnFunctionExit(ar, retval); } // XXX Disabled until t2329497 is fixed: // CheckSurprise(); } }
void EventHook::onFunctionExit(const ActRec* ar, const TypedValue* retval, const Fault* fault, ssize_t flags) { // Xenon if (flags & RequestInjectionData::XenonSignalFlag) { Xenon::getInstance().log(Xenon::ExitSample); } // Inlined calls normally skip the function enter and exit events. If we // side exit in an inlined callee, we short-circuit here in order to skip // exit events that could unbalance the call stack. if ((JIT::TCA) ar->m_savedRip == JIT::mcg->tx().uniqueStubs.retInlHelper) { return; } // User profiler if (flags & RequestInjectionData::EventHookFlag) { Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { // NB: we don't have a function type flag to match what we got in // onFunctionEnter. That's okay, though... we tolerate this in // TraceProfiler. end_profiler_frame(profiler, retval, GetFunctionNameForProfiler(ar->func(), NormalFunc)); } if (shouldRunUserProfiler(ar->func())) { if (ThreadInfo::s_threadInfo->m_pendingException != nullptr) { // Avoid running PHP code when exception from destructor is pending. // TODO(#2329497) will not happen once CheckSurprise is used } else if (!fault) { runUserProfilerOnFunctionExit(ar, retval, nullptr); } else if (fault->m_faultType == Fault::Type::UserException) { runUserProfilerOnFunctionExit(ar, retval, fault->m_userException); } else { // Avoid running PHP code when unwinding C++ exception. } } } // Debugger hook if (flags & RequestInjectionData::DebuggerHookFlag) { DEBUGGER_ATTACHED_ONLY(phpDebuggerFuncExitHook(ar)); } }
void EventHook::onFunctionExit(const ActRec* ar, const TypedValue* retval, const Fault* fault, ssize_t flags) { // Xenon if (flags & RequestInjectionData::XenonSignalFlag) { Xenon::getInstance().log(false); } // User profiler // // Inlined calls normally skip the function enter and exit events. If we // side exit in an inlined callee, we want to make sure to skip the exit // event to avoid unbalancing the call stack. if ((flags & RequestInjectionData::EventHookFlag) && (JIT::TCA)ar->m_savedRip != JIT::tx->uniqueStubs.retInlHelper) { #ifdef HOTPROFILER Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { // NB: we don't have a function type flag to match what we got in // onFunctionEnter. That's okay, though... we tolerate this in // TraceProfiler. end_profiler_frame(profiler, GetFunctionNameForProfiler(ar->func(), NormalFunc)); } #endif if (shouldRunUserProfiler(ar->func())) { if (ThreadInfo::s_threadInfo->m_pendingException != nullptr) { // Avoid running PHP code when exception from destructor is pending. // TODO(#2329497) will not happen once CheckSurprise is used } else if (!fault) { runUserProfilerOnFunctionExit(ar, retval, nullptr); } else if (fault->m_faultType == Fault::Type::UserException) { runUserProfilerOnFunctionExit(ar, retval, fault->m_userException); } else { // Avoid running PHP code when unwinding C++ exception. } } } }