void EventHook::onFunctionSuspendE(ActRec* suspending, const ActRec* resumableAR) { // When we're suspending an eagerly executing resumable, we've already // teleported the ActRec from suspending over to resumableAR, so we need to // make sure the unwinder knows not to touch the locals, $this, or // VarEnv/ExtraArgs. suspending->setThisOrClassAllowNull(nullptr); suspending->setLocalsDecRefd(); suspending->setVarEnv(nullptr); try { ssize_t flags = CheckSurprise(); onFunctionExit(resumableAR, nullptr, nullptr, flags); if ((flags & RequestInjectionData::AsyncEventHookFlag) && resumableAR->func()->isAsyncFunction()) { assert(resumableAR->resumed()); auto const afwh = frame_afwh(resumableAR); auto const session = AsioSession::Get(); if (session->hasOnResumableCreateCallback()) { session->onResumableCreate(afwh, afwh->getChild()); } } } catch (...) { auto const resumableObj = [&]() -> ObjectData* { if (resumableAR->func()->isAsyncFunction()) { return frame_afwh(resumableAR); } assert(resumableAR->func()->isGenerator()); return frame_base_generator(resumableAR); }(); decRefObj(resumableObj); throw; } }
void EventHook::onFunctionEnter(const ActRec* ar, int funcType) { CheckSurprise(); RunUserProfiler(ar, ProfileEnter); #ifdef HOTPROFILER Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { const char* name; switch (funcType) { case NormalFunc: name = ar->m_func->fullName()->data(); if (name[0] == '\0') { // We're evaling some code for internal purposes, most // likely getting the default value for a function parameter name = "{internal}"; } break; case PseudoMain: name = StringData::GetStaticString( std::string("run_init::") + ar->m_func->unit()->filepath()->data()) ->data(); break; case Eval: name = "_"; break; default: not_reached(); } begin_profiler_frame(profiler, name); } #endif }
void EventHook::onFunctionReturn(ActRec* ar, const TypedValue& retval) { // Null out $this for the exiting function, it has been decref'd so it's // garbage. ar->setThisOrClassAllowNull(nullptr); // The locals are already gone. Mark them as decref'd so that if this hook // fails and unwinder kicks in, it won't try to decref them again. ar->setLocalsDecRefd(); // TODO(#5758054): does this need setVarEnv(nullptr) ? ssize_t flags = CheckSurprise(); onFunctionExit(ar, &retval, nullptr, flags); // Async profiler if ((flags & RequestInjectionData::AsyncEventHookFlag) && ar->func()->isAsyncFunction() && ar->resumed()) { auto session = AsioSession::Get(); // Return @ resumed execution => AsyncFunctionWaitHandle succeeded. if (session->hasOnResumableSuccessCallback()) { auto afwh = frame_afwh(ar); session->onResumableSuccess(afwh, cellAsCVarRef(retval)); } } }
void EventHook::onFunctionReturn(ActRec* ar, TypedValue retval) { // The locals are already gone. Null out everything. ar->setThisOrClassAllowNull(nullptr); ar->setLocalsDecRefd(); ar->setVarEnv(nullptr); try { ssize_t flags = CheckSurprise(); onFunctionExit(ar, &retval, nullptr, flags); // Async profiler if ((flags & AsyncEventHookFlag) && ar->func()->isAsyncFunction() && ar->resumed()) { auto session = AsioSession::Get(); // Return @ resumed execution => AsyncFunctionWaitHandle succeeded. if (session->hasOnResumableSuccessCallback()) { auto afwh = frame_afwh(ar); session->onResumableSuccess(afwh, cellAsCVarRef(retval)); } } } catch (...) { /* * We're responsible for freeing the return value if we exit with an * exception. See irgen-ret. */ tvRefcountedDecRef(retval); throw; } }
bool EventHook::onFunctionCall(const ActRec* ar, int funcType) { ssize_t flags = CheckSurprise(); if (flags & RequestInjectionData::InterceptFlag && !RunInterceptHandler(const_cast<ActRec*>(ar))) { return false; } onFunctionEnter(ar, funcType, flags); return true; }
void EventHook::onFunctionResumeYield(const ActRec* ar) { ssize_t flags = CheckSurprise(); // Xenon if (flags & XenonSignalFlag) { Xenon::getInstance().log(Xenon::EnterSample); } if (flags & IntervalTimerFlag) { IntervalTimer::RunCallbacks(IntervalTimer::EnterSample); } onFunctionEnter(ar, EventHook::NormalFunc, flags); }
bool EventHook::onFunctionEnter(const ActRec* ar, int funcType) { ssize_t flags = CheckSurprise(); if (flags & RequestInjectionData::InterceptFlag && !RunInterceptHandler(const_cast<ActRec*>(ar))) { return false; } if (flags & RequestInjectionData::EventHookFlag) { RunUserProfiler(ar, ProfileEnter); #ifdef HOTPROFILER Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { begin_profiler_frame(profiler, GetFunctionNameForProfiler(ar, funcType)); } #endif } return true; }
// Child is the AFWH we're going to block on, nullptr iff this is a suspending // generator. void EventHook::onFunctionSuspendR(ActRec* suspending, ObjectData* child) { ssize_t flags = CheckSurprise(); onFunctionExit(suspending, nullptr, nullptr, flags); if ((flags & RequestInjectionData::AsyncEventHookFlag) && suspending->func()->isAsyncFunction()) { assert(child != nullptr); // This isn't a generator assert(child->instanceof(c_WaitableWaitHandle::classof())); assert(suspending->resumed()); auto const afwh = frame_afwh(suspending); auto const session = AsioSession::Get(); if (session->hasOnResumableAwaitCallback()) { session->onResumableAwait( afwh, static_cast<c_WaitableWaitHandle*>(child) ); } } }
bool EventHook::onFunctionCall(const ActRec* ar, int funcType) { ssize_t flags = CheckSurprise(); if (flags & InterceptFlag && !RunInterceptHandler(const_cast<ActRec*>(ar))) { return false; } // Xenon if (flags & XenonSignalFlag) { Xenon::getInstance().log(Xenon::EnterSample); } if (flags & IntervalTimerFlag) { IntervalTimer::RunCallbacks(IntervalTimer::EnterSample); } onFunctionEnter(ar, funcType, flags); return true; }
void EventHook::onFunctionSuspend(const ActRec* ar, bool suspendingResumed) { ssize_t flags = CheckSurprise(); onFunctionExit(ar, nullptr, nullptr, flags); // Async profiler if ((flags & RequestInjectionData::AsyncEventHookFlag) && ar->func()->isAsyncFunction()) { assert(ar->resumed()); auto afwh = frame_afwh(ar); auto session = AsioSession::Get(); // Blocking await @ eager execution => AsyncFunctionWaitHandle created. if (!suspendingResumed && session->hasOnAsyncFunctionCreateCallback()) { session->onAsyncFunctionCreate(afwh, afwh->getChild()); } // Blocking await @ resumed execution => AsyncFunctionWaitHandle awaiting. if (suspendingResumed && session->hasOnAsyncFunctionAwaitCallback()) { session->onAsyncFunctionAwait(afwh, afwh->getChild()); } } }
bool EventHook::onFunctionEnter(const ActRec* ar, int funcType) { ssize_t flags = CheckSurprise(); if (flags & RequestInjectionData::InterceptFlag && !RunInterceptHandler(const_cast<ActRec*>(ar))) { return false; } if (flags & RequestInjectionData::EventHookFlag) { RunUserProfiler(ar, ProfileEnter); #ifdef HOTPROFILER Profiler* profiler = ThreadInfo::s_threadInfo->m_profiler; if (profiler != nullptr) { const char* name; switch (funcType) { case NormalFunc: name = ar->m_func->fullName()->data(); if (name[0] == '\0') { // We're evaling some code for internal purposes, most // likely getting the default value for a function parameter name = "{internal}"; } break; case PseudoMain: name = StringData::GetStaticString( std::string("run_init::") + ar->m_func->unit()->filepath()->data()) ->data(); break; case Eval: name = "_"; break; default: not_reached(); } begin_profiler_frame(profiler, name); } #endif } return true; }
void EventHook::onFunctionResume(const ActRec* ar) { ssize_t flags = CheckSurprise(); onFunctionEnter(ar, EventHook::NormalFunc, flags); }