diff --git a/hphp/hhvm/process_init.cpp b/hphp/hhvm/process_init.cpp index 2f11cc9e7..c23f0763a 100644 --- a/hphp/hhvm/process_init.cpp +++ b/hphp/hhvm/process_init.cpp @@ -100,13 +100,6 @@ void ProcessInit() { // ensure that nextTx64 and tx64 are set (void)VM::Transl::Translator::Get(); - if (!RuntimeOption::RepoAuthoritative && - RuntimeOption::EvalJitEnableRenameFunction && - RuntimeOption::EvalJit) { - VM::Func::enableIntercept(); - VM::Transl::TranslatorX64* tx64 = VM::Transl::TranslatorX64::Get(); - tx64->enableIntercepts(); - } // Save the current options, and set things up so that // systemlib.php can be read from and stored in the // normal repo. diff --git a/hphp/runtime/base/builtin_functions.cpp b/hphp/runtime/base/builtin_functions.cpp index d2538f010..1a72e7e57 100644 --- a/hphp/runtime/base/builtin_functions.cpp +++ b/hphp/runtime/base/builtin_functions.cpp @@ -504,7 +504,7 @@ void pause_forever() { for (;;) sleep(300); } -void check_request_surprise(ThreadInfo *info) { +ssize_t check_request_surprise(ThreadInfo *info) { RequestInjectionData &p = info->m_reqInjectionData; bool do_timedout, do_memExceeded, do_signaled; @@ -528,6 +528,7 @@ void check_request_surprise(ThreadInfo *info) { if (pendingException) { pendingException->throwException(); } + return flags; } void throw_missing_arguments_nr(const char *fn, int expected, int got, diff --git a/hphp/runtime/base/execution_context.cpp b/hphp/runtime/base/execution_context.cpp index f260ad53d..2a18086d9 100644 --- a/hphp/runtime/base/execution_context.cpp +++ b/hphp/runtime/base/execution_context.cpp @@ -53,7 +53,7 @@ Mutex VMExecutionContext::s_threadIdxLock; hphp_hash_map VMExecutionContext::s_threadIdxMap; BaseExecutionContext::BaseExecutionContext() : - m_fp(nullptr), m_pc(nullptr), m_isValid(1), m_eventHook(nullptr), + m_fp(nullptr), m_pc(nullptr), m_transport(nullptr), m_maxTime(RuntimeOption::RequestTimeoutSeconds), m_cwd(Process::CurrentWorkingDirectory), @@ -86,10 +86,6 @@ VMExecutionContext::VMExecutionContext() : "m_fp offset too large"); static_assert(offsetof(ExecutionContext, m_pc) <= 0xff, "m_pc offset too large"); - static_assert(offsetof(ExecutionContext, m_isValid) <= 0xff, - "m_isValid offset too large"); - static_assert(offsetof(ExecutionContext, m_eventHook) <= 0xff, - "m_eventHook offset too large"); static_assert(offsetof(ExecutionContext, m_currentThreadIdx) <= 0xff, "m_currentThreadIdx offset too large"); @@ -101,7 +97,6 @@ VMExecutionContext::VMExecutionContext() : s_threadIdxMap[tid] = m_currentThreadIdx; } } - m_eventHook = new HPHP::VM::EventHook(); } BaseExecutionContext::~BaseExecutionContext() { @@ -137,7 +132,6 @@ VMExecutionContext::~VMExecutionContext() { delete *it; } - delete m_eventHook; delete m_injTables; delete m_breakPointFilter; delete m_lastLocFilter; diff --git a/hphp/runtime/base/execution_context.h b/hphp/runtime/base/execution_context.h index 0c42790b0..60ea5fe14 100644 --- a/hphp/runtime/base/execution_context.h +++ b/hphp/runtime/base/execution_context.h @@ -187,8 +187,6 @@ public: Stack m_stack; ActRec* m_fp; PC m_pc; - uint32_t m_isValid; /* Debug-only: non-zero iff m_fp/m_stack are trustworthy */ - HPHP::VM::EventHook* m_eventHook; int64_t m_currentThreadIdx; public: enum ShutdownType { @@ -570,6 +568,8 @@ public: const StringData* cns); ActRec* arGetSfp(const ActRec* ar); + // Get the next outermost VM frame, even accross re-entry + ActRec* getOuterVMFrame(const ActRec* ar); std::string prettyStack(const std::string& prefix) const; static void DumpStack(); @@ -666,7 +666,6 @@ public: bool m_interpreting; bool m_dbgNoBreak; int switchMode(bool unwindBuiltin); - template void doFCall(HPHP::ActRec* ar, PC& pc); bool doFCallArray(PC& pc); CVarRef getEvaledArg(const StringData* val); @@ -684,7 +683,7 @@ private: template void pushClsMethodImpl(VM::Class* cls, StringData* name, ObjectData* obj, int numArgs); - template + template bool prepareFuncEntry(ActRec* ar, PC& pc, ExtraArgs* extraArgs); diff --git a/hphp/runtime/base/intercept.cpp b/hphp/runtime/base/intercept.cpp index 996218edc..8ba5e3f8b 100644 --- a/hphp/runtime/base/intercept.cpp +++ b/hphp/runtime/base/intercept.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -96,6 +97,8 @@ bool register_intercept(CStrRef name, CVarRef callback, CVarRef data) { return true; } + VM::EventHook::EnableIntercept(); + Array handler = CREATE_VECTOR2(callback, data); if (name.empty()) { @@ -106,18 +109,6 @@ bool register_intercept(CStrRef name, CVarRef callback, CVarRef data) { } Lock lock(s_mutex); - VM::Func::enableIntercept(); - TranslatorX64* tx64 = TranslatorX64::Get(); - if (!tx64->interceptsEnabled()) { - tx64->acquireWriteLease(true); - if (!tx64->interceptsEnabled()) { - tx64->enableIntercepts(); - // redirect all existing generated prologues so that they first - // call the intercept helper - Eval::FileRepository::enableIntercepts(); - } - tx64->dropWriteLease(); - } if (name.empty()) { for (RegisteredFlagsMap::iterator iter = s_registered_flags.begin(); diff --git a/hphp/runtime/base/thread_info.cpp b/hphp/runtime/base/thread_info.cpp index ff743313e..350d0ba06 100644 --- a/hphp/runtime/base/thread_info.cpp +++ b/hphp/runtime/base/thread_info.cpp @@ -169,17 +169,20 @@ void RequestInjectionData::clearPendingExceptionFlag() { ~RequestInjectionData::PendingExceptionFlag); } +void RequestInjectionData::setInterceptFlag() { + __sync_fetch_and_or(getConditionFlags(), + RequestInjectionData::InterceptFlag); +} + +void RequestInjectionData::clearInterceptFlag() { + __sync_fetch_and_and(getConditionFlags(), + ~RequestInjectionData::InterceptFlag); +} + ssize_t RequestInjectionData::fetchAndClearFlags() { - ssize_t flags; - for (;;) { - flags = atomic_acquire_load(getConditionFlags()); - const ssize_t newFlags = - flags & RequestInjectionData::EventHookFlag; - if (__sync_bool_compare_and_swap(getConditionFlags(), flags, newFlags)) { - break; - } - } - return flags; + return __sync_fetch_and_and(getConditionFlags(), + (RequestInjectionData::EventHookFlag | + RequestInjectionData::InterceptFlag)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/base/types.h b/hphp/runtime/base/types.h index 8083a386f..e9e05d508 100644 --- a/hphp/runtime/base/types.h +++ b/hphp/runtime/base/types.h @@ -389,7 +389,8 @@ public: static const ssize_t SignaledFlag = 1 << 2; static const ssize_t EventHookFlag = 1 << 3; static const ssize_t PendingExceptionFlag = 1 << 4; - static const ssize_t LastFlag = PendingExceptionFlag; + static const ssize_t InterceptFlag = 1 << 5; + static const ssize_t LastFlag = InterceptFlag; RequestInjectionData() : cflagsPtr(nullptr), surprisePage(nullptr), started(0), timeoutSeconds(-1), @@ -426,6 +427,8 @@ public: void clearEventHookFlag(); void setPendingExceptionFlag(); void clearPendingExceptionFlag(); + void setInterceptFlag(); + void clearInterceptFlag(); ssize_t fetchAndClearFlags(); void onSessionInit(); @@ -535,7 +538,7 @@ inline void check_recursion(ThreadInfo *&info) { } // implemented in runtime/base/builtin_functions.cpp -extern void check_request_surprise(ThreadInfo *info) ATTRIBUTE_COLD; +extern ssize_t check_request_surprise(ThreadInfo *info) ATTRIBUTE_COLD; // implemented in runtime/ext/ext_hotprofiler.cpp extern void begin_profiler_frame(Profiler *p, const char *symbol); diff --git a/hphp/runtime/eval/runtime/file_repository.cpp b/hphp/runtime/eval/runtime/file_repository.cpp index e527468ef..a6de80b2d 100644 --- a/hphp/runtime/eval/runtime/file_repository.cpp +++ b/hphp/runtime/eval/runtime/file_repository.cpp @@ -41,8 +41,6 @@ namespace Eval { std::set FileRepository::s_names; -static volatile bool s_interceptsEnabled = false; - PhpFile::PhpFile(const string &fileName, const string &srcRoot, const string &relPath, const string &md5, HPHP::VM::Unit* unit) @@ -163,7 +161,6 @@ PhpFile *FileRepository::checkoutFile(StringData *rname, } TRACE(1, "FR fast path miss: %s\n", rname->data()); - bool interceptsEnabled = s_interceptsEnabled; const StringData *n = StringData::GetStaticString(name.get()); ParsedFilesMap::accessor acc; bool isNew = s_files.insert(acc, n); @@ -231,16 +228,6 @@ PhpFile *FileRepository::checkoutFile(StringData *rname, if (md5Enabled()) { WriteLock lock(s_md5Lock); - // make sure intercepts are enabled for the functions within the - // new units - // Since we have the write lock on s_md5lock, s_interceptsEnabled - // can't change, and we are serialized wrt enableIntercepts - // (i.e., this will execute either before or after - // enableIntercepts). - if (interceptsEnabled != s_interceptsEnabled) { - // intercepts were enabled since the time we created the unit - ret->unit()->enableIntercepts(); - } s_md5Files[ret->getMd5()] = ret; } ret->incRef(); @@ -445,16 +432,6 @@ bool FileRepository::fileStat(const string &name, struct stat *s) { return StatCache::stat(name, s) == 0; } -void FileRepository::enableIntercepts() { - ReadLock lock(s_md5Lock); - s_interceptsEnabled = true; // write protected by s_mutex in intercept - - for (hphp_hash_map::const_iterator it = - s_md5Files.begin(); it != s_md5Files.end(); it++) { - it->second->unit()->enableIntercepts(); - } -} - struct ResolveIncludeContext { String path; // translated path of the file struct stat* s; // stat for the file diff --git a/hphp/runtime/eval/runtime/file_repository.h b/hphp/runtime/eval/runtime/file_repository.h index b914fcdf0..fcc3efe77 100644 --- a/hphp/runtime/eval/runtime/file_repository.h +++ b/hphp/runtime/eval/runtime/file_repository.h @@ -156,7 +156,6 @@ public: static PhpFile *readHhbc(const std::string &name, const FileInfo &fileInfo); static PhpFile *parseFile(const std::string &name, const FileInfo &fileInfo); static String translateFileName(StringData *file); - static void enableIntercepts(); static void onDelete(PhpFile *f); static void forEachUnit(VM::UnitVisitor& uit); static size_t getLoadedFiles(); diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 717d5a5fa..1cbcf93bc 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -1191,6 +1191,17 @@ ActRec* VMExecutionContext::arGetSfp(const ActRec* ar) { return const_cast(ar); } +ActRec* VMExecutionContext::getOuterVMFrame(const ActRec* ar) { + ActRec* prevFrame = (ActRec*)ar->m_savedRbp; + if (LIKELY(((uintptr_t)prevFrame - Util::s_stackLimit) >= + Util::s_stackSize)) { + if (LIKELY(prevFrame != nullptr)) return prevFrame; + } + + if (LIKELY(!m_nestedVMs.empty())) return m_nestedVMs.back().m_savedState.fp; + return nullptr; +} + TypedValue* VMExecutionContext::lookupClsCns(const NamedEntity* ne, const StringData* cls, const StringData* cns) { @@ -1688,22 +1699,14 @@ static inline void checkStack(Stack& stk, const Func* f) { } } -template +template bool VMExecutionContext::prepareFuncEntry(ActRec *ar, PC& pc, ExtraArgs* extraArgs) { const Func* func = ar->m_func; if (!reenter) { - // For the reenter case, intercept and magic shuffling are handled - // in invokeFunc() before calling prepareFuncEntry(), so we only - // need to perform these checks for the non-reenter case. - if (UNLIKELY(func->maybeIntercepted())) { - Variant *h = get_intercept_handler(func->fullNameRef(), - &func->maybeIntercepted()); - if (h && !run_intercept_handler(ar, h)) { - return false; - } - } + // For the reenter case, magic shuffling are handled + // in invokeFunc() before calling prepareFuncEntry(). if (UNLIKELY(ar->hasInvName())) { shuffleMagicArgs(ar); } @@ -1846,7 +1849,7 @@ void VMExecutionContext::syncGdbState() { void VMExecutionContext::enterVMWork(ActRec* enterFnAr) { TCA start = nullptr; if (enterFnAr) { - EventHook::FunctionEnter(enterFnAr, EventHook::NormalFunc); + if (!EventHook::FunctionEnter(enterFnAr, EventHook::NormalFunc)) return; INST_HOOK_FENTRY(enterFnAr->m_func->fullName()); start = enterFnAr->m_func->getFuncBody(); } @@ -1948,7 +1951,7 @@ short_jump: try { switch (jumpCode) { case EXCEPTION_START: - if (prepareFuncEntry(ar, m_pc, extraArgs)) { + if (prepareFuncEntry(ar, m_pc, extraArgs)) { enterVMWork(ar); } break; @@ -2065,20 +2068,6 @@ void VMExecutionContext::invokeFunc(TypedValue* retval, VMRegAnchor _; - // Check if we need to run an intercept handler - if (UNLIKELY(f->maybeIntercepted())) { - Variant *h = get_intercept_handler(f->fullNameRef(), - &f->maybeIntercepted()); - if (h) { - if (!run_intercept_handler_for_invokefunc(retval, f, params, this_, - invName, h)) { - return; - } - // Discard the handler's return value - tvRefcountedDecRef(retval); - } - } - bool isMagicCall = (invName != nullptr); if (this_ != nullptr) { @@ -2816,8 +2805,9 @@ bool VMExecutionContext::evalUnit(Unit* unit, bool local, m_fp = ar; pc = func->getEntry(); SYNC(); - EventHook::FunctionEnter(m_fp, funcType); - return true; + bool ret = EventHook::FunctionEnter(m_fp, funcType); + pc = m_pc; + return ret; } CVarRef VMExecutionContext::getEvaledArg(const StringData* val) { @@ -5937,7 +5927,6 @@ void VMExecutionContext::iopFPassM(PC& pc) { } } -template void VMExecutionContext::doFCall(ActRec* ar, PC& pc) { assert(ar->m_savedRbp == (uint64_t)m_fp); ar->m_savedRip = (uintptr_t)tx64->getRetFromInterpretedFrame(); @@ -5948,21 +5937,22 @@ void VMExecutionContext::doFCall(ActRec* ar, PC& pc) { ar->m_soff = m_fp->m_func->unit()->offsetOf(pc) - (uintptr_t)m_fp->m_func->base(); assert(pcOff() > m_fp->m_func->base()); - prepareFuncEntry(ar, pc, 0); + prepareFuncEntry(ar, pc, 0); SYNC(); - EventHook::FunctionEnter(ar, EventHook::NormalFunc); - INST_HOOK_FENTRY(ar->m_func->fullName()); + if (EventHook::FunctionEnter(ar, EventHook::NormalFunc)) { + INST_HOOK_FENTRY(ar->m_func->fullName()); + } else { + pc = m_pc; + } } -template void VMExecutionContext::doFCall(ActRec *ar, PC& pc); - inline void OPTBLD_INLINE VMExecutionContext::iopFCall(PC& pc) { ActRec* ar = arFromInstr(m_stack.top(), (Opcode*)pc); NEXT(); DECODE_IVA(numArgs); assert(numArgs == ar->numArgs()); checkStack(m_stack, ar->m_func); - doFCall(ar, pc); + doFCall(ar, pc); } // Return a function pointer type for calling a builtin with a given @@ -6216,35 +6206,17 @@ bool VMExecutionContext::doFCallArray(PC& pc) { - (uintptr_t)m_fp->m_func->base(); assert(pcOff() > m_fp->m_func->base()); - StringData* invName = ar->hasInvName() ? ar->getInvName() : nullptr; if (UNLIKELY(!prepareArrayArgs(ar, args.get(), extraArgs))) return false; - if (UNLIKELY(func->maybeIntercepted())) { - Variant *h = get_intercept_handler(func->fullNameRef(), - &func->maybeIntercepted()); - if (h) { - try { - TypedValue retval; - if (!run_intercept_handler_for_invokefunc( - &retval, func, args, - ar->hasThis() ? ar->getThis() : nullptr, - invName, h)) { - cleanupParamsAndActRec(m_stack, ar, extraArgs); - *m_stack.allocTV() = retval; - return false; - } - } catch (...) { - cleanupParamsAndActRec(m_stack, ar, extraArgs); - m_stack.pushNull(); - SYNC(); - throw; - } - } - } } - prepareFuncEntry(ar, pc, extraArgs); + if (UNLIKELY(!(prepareFuncEntry(ar, pc, extraArgs)))) { + return false; + } SYNC(); - EventHook::FunctionEnter(ar, EventHook::NormalFunc); + if (UNLIKELY(!EventHook::FunctionEnter(ar, EventHook::NormalFunc))) { + pc = m_pc; + return false; + } INST_HOOK_FENTRY(func->fullName()); return true; } @@ -6937,8 +6909,11 @@ void VMExecutionContext::iopContEnter(PC& pc) { pc = contAR->m_func->getEntry(); SYNC(); - EventHook::FunctionEnter(contAR, EventHook::NormalFunc); - INST_HOOK_FENTRY(contAR->m_func->fullName()); + if (LIKELY(EventHook::FunctionEnter(contAR, EventHook::NormalFunc))) { + INST_HOOK_FENTRY(contAR->m_func->fullName()); + } else { + pc = m_pc; + } } void VMExecutionContext::iopContExit(PC& pc) { diff --git a/hphp/runtime/vm/event_hook.cpp b/hphp/runtime/vm/event_hook.cpp index 4f6fd5105..2dda380ad 100644 --- a/hphp/runtime/vm/event_hook.cpp +++ b/hphp/runtime/vm/event_hook.cpp @@ -14,13 +14,14 @@ +----------------------------------------------------------------------+ */ -#include "runtime/base/types.h" #include "runtime/vm/event_hook.h" +#include "runtime/base/types.h" #include "runtime/vm/func.h" #include "runtime/vm/translator/translator-inline.h" #include "runtime/base/builtin_functions.h" #include "runtime/base/complex_types.h" #include "runtime/ext/ext_function.h" +#include "runtime/vm/runtime.h" namespace HPHP { namespace VM { @@ -40,9 +41,17 @@ void EventHook::Disable() { ThreadInfo::s_threadInfo->m_reqInjectionData.clearEventHookFlag(); } -void EventHook::CheckSurprise() { +void EventHook::EnableIntercept() { + ThreadInfo::s_threadInfo->m_reqInjectionData.setInterceptFlag(); +} + +void EventHook::DisableIntercept() { + ThreadInfo::s_threadInfo->m_reqInjectionData.clearInterceptFlag(); +} + +ssize_t EventHook::CheckSurprise() { ThreadInfo* info = ThreadInfo::s_threadInfo.getNoCheck(); - check_request_surprise(info); + return check_request_surprise(info); } class ExecutingSetprofileCallbackGuard { @@ -98,36 +107,113 @@ void EventHook::RunUserProfiler(const ActRec* ar, int mode) { vm_call_user_func(g_vmContext->m_setprofileCallback, params); } -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(); +static Array get_frame_args_with_ref(const ActRec* ar) { + int numParams = ar->m_func->numParams(); + int numArgs = ar->numArgs(); + HphpArray* retval = NEW(HphpArray)(numArgs); + + TypedValue* local = (TypedValue*)(uintptr_t(ar) - sizeof(TypedValue)); + for (int i = 0; i < numArgs; ++i) { + if (i < numParams) { + // This corresponds to one of the function's formal parameters, so it's + // on the stack. + retval->appendWithRef(tvAsCVarRef(local), false); + --local; + } else { + // This is not a formal parameter, so it's in the ExtraArgs. + retval->appendWithRef(tvAsCVarRef(ar->getExtraArg(i - numParams)), false); } - begin_profiler_frame(profiler, name); } + + return Array(retval); +} + +bool EventHook::RunInterceptHandler(ActRec* ar) { + const Func* func = ar->m_func; + if (LIKELY(func->maybeIntercepted() == 0)) return true; + + Variant *h = get_intercept_handler(func->fullNameRef(), + &func->maybeIntercepted()); + if (!h) return true; + + Transl::VMRegAnchor _; + + PC savePc = g_vmContext->m_pc; + + Offset pcOff; + ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff); + assert(outer); + g_vmContext->m_fp = outer; + g_vmContext->m_pc = outer->m_func->unit()->at(pcOff); + + try { + Variant doneFlag = true; + Variant obj = ar->hasThis() ? + Variant(Object(ar->getThis())) : uninit_null(); + Array intArgs = + CREATE_VECTOR5(ar->m_func->fullNameRef(), + obj, + get_frame_args_with_ref(ar), + h->asCArrRef()[1], + ref(doneFlag)); + + Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); + if (doneFlag.toBoolean()) { + frame_free_locals_inl_no_hook(ar, ar->m_func->numLocals()); + Stack& stack = g_vmContext->getStack(); + stack.top() = (Cell*)(ar + 1); + tvDup(ret.asTypedValue(), stack.allocTV()); + return false; + } + g_vmContext->m_fp = ar; + g_vmContext->m_pc = savePc; + } catch (...) { + g_vmContext->m_fp = ar; + g_vmContext->m_pc = savePc; + g_vmContext->m_stack.top() = Stack::frameStackBase(ar); + throw; + } + + return true; +} + +bool EventHook::onFunctionEnter(const ActRec* ar, int funcType) { + ssize_t flags = CheckSurprise(); + if (flags & RequestInjectionData::InterceptFlag && + !RunInterceptHandler(const_cast(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::onFunctionExit(const ActRec* ar) { diff --git a/hphp/runtime/vm/event_hook.h b/hphp/runtime/vm/event_hook.h index 31d8e620c..c5c72aa77 100644 --- a/hphp/runtime/vm/event_hook.h +++ b/hphp/runtime/vm/event_hook.h @@ -23,14 +23,6 @@ namespace HPHP { namespace VM { -#define DECLARE_HOOK(name, declargs, useargs) \ - static inline void name declargs { \ - if (UNLIKELY(Transl::TargetCache::loadConditionFlags())) { \ - g_vmContext->m_eventHook->on ## name useargs; \ - } \ - } \ - void on ## name declargs; - class EventHook { public: enum { @@ -41,14 +33,21 @@ class EventHook { static void Enable(); static void Disable(); - static void CheckSurprise(); + static void EnableIntercept(); + static void DisableIntercept(); + static ssize_t CheckSurprise(); /* * Can throw from user-defined signal handlers, or OOM or timeout * exceptions. */ - DECLARE_HOOK(FunctionEnter, (const ActRec* ar, int funcType), - (ar, funcType)); + static bool onFunctionEnter(const ActRec* ar, int funcType); + static inline bool FunctionEnter(const ActRec* ar, int funcType) { + if (UNLIKELY(Transl::TargetCache::loadConditionFlags())) { + return onFunctionEnter(ar, funcType); + } + return true; + } /* * FunctionExit may throw. @@ -58,7 +57,12 @@ class EventHook { * unwinder itself will call the function exit hooks and swallow * exceptions. */ - DECLARE_HOOK(FunctionExit, (const ActRec* ar), (ar)); + static void onFunctionExit(const ActRec* ar); + static inline void FunctionExit(const ActRec* ar) { + if (UNLIKELY(Transl::TargetCache::loadConditionFlags())) { + onFunctionExit(ar); + } + } private: enum { @@ -67,6 +71,7 @@ private: }; static void RunUserProfiler(const ActRec* ar, int mode); + static bool RunInterceptHandler(ActRec* ar); }; #undef DECLARE_HOOK diff --git a/hphp/runtime/vm/func.cpp b/hphp/runtime/vm/func.cpp index 65b439a42..878ce786c 100644 --- a/hphp/runtime/vm/func.cpp +++ b/hphp/runtime/vm/func.cpp @@ -37,7 +37,6 @@ namespace HPHP { namespace VM { static const Trace::Module TRACEMOD = Trace::bcinterp; -bool Func::s_interceptsEnabled = false; const StringData* Func::s___call = StringData::GetStaticString("__call"); const StringData* Func::s___callStatic = StringData::GetStaticString("__callStatic"); @@ -129,7 +128,7 @@ void Func::initPrologues(int numParams, bool isGenerator) { void Func::init(int numParams, bool isGenerator) { // For methods, we defer setting the full name until m_cls is initialized - m_maybeIntercepted = s_interceptsEnabled ? -1 : 0; + m_maybeIntercepted = -1; if (!preClass()) { setNewFuncId(); setFullName(); @@ -223,7 +222,7 @@ Func::Func(Unit& unit, PreClass* preClass, int line1, int line2, Offset base, } Func::~Func() { - if (m_fullName != nullptr && s_interceptsEnabled && m_maybeIntercepted != -1) { + if (m_fullName != nullptr && m_maybeIntercepted != -1) { unregister_intercept_flag(fullNameRef(), &m_maybeIntercepted); } #ifdef DEBUG @@ -647,13 +646,6 @@ void Func::SharedData::atomicRelease() { delete this; } -void Func::enableIntercept() { - // we are protected by s_mutex in intercept.cpp - if (!s_interceptsEnabled) { - s_interceptsEnabled = true; - } -} - Func** Func::getCachedAddr() { assert(!isMethod()); return getCachedFuncAddr(m_cachedOffset); diff --git a/hphp/runtime/vm/func.h b/hphp/runtime/vm/func.h index 0556c6879..d34f79954 100644 --- a/hphp/runtime/vm/func.h +++ b/hphp/runtime/vm/func.h @@ -300,13 +300,7 @@ struct Func { static size_t sharedBaseOffset() { return offsetof(SharedData, m_base); } - static void enableIntercept(); char &maybeIntercepted() const { return m_maybeIntercepted; } - bool checkInterceptable() const { - if (!m_maybeIntercepted) return false; - return m_maybeIntercepted > 0 || - get_intercept_handler(fullNameRef(), &m_maybeIntercepted); - } int numParams() const { return m_numParams; } const ParamInfoVec& params() const { return shared()->m_params; } int numLocals() const { return shared()->m_numLocals; } @@ -496,9 +490,6 @@ private: SharedData* shared() { return m_shared.get(); } const Func* findCachedClone(Class* cls) const; -private: - static bool s_interceptsEnabled; - private: Unit* m_unit; Class* m_cls; // The Class that provided this method implementation diff --git a/hphp/runtime/vm/runtime.cpp b/hphp/runtime/vm/runtime.cpp index 4e33d79ed..77fe6fe1c 100644 --- a/hphp/runtime/vm/runtime.cpp +++ b/hphp/runtime/vm/runtime.cpp @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ +#include "runtime/vm/runtime.h" #include "runtime/base/execution_context.h" #include "runtime/base/complex_types.h" #include "runtime/base/zend/zend_string.h" @@ -25,7 +26,6 @@ #include "runtime/vm/bytecode.h" #include "runtime/vm/repo.h" #include "util/trace.h" -#include "runtime.h" #include "runtime/vm/translator/translator-inline.h" #include "runtime/vm/translator/translator-x64.h" @@ -98,7 +98,7 @@ NEW_COLLECTION_HELPER(Vector) NEW_COLLECTION_HELPER(Map) NEW_COLLECTION_HELPER(StableMap) NEW_COLLECTION_HELPER(Set) - + ObjectData* newPairHelper() { ObjectData *obj = NEWOBJ(c_Pair)(); obj->incRefCount(); @@ -360,32 +360,6 @@ HphpArray* pack_args_into_array(ActRec* ar, int nargs) { return magicArgs; } -bool run_intercept_handler_for_invokefunc(TypedValue* retval, - const Func* f, - CArrRef params, - ObjectData* this_, - StringData* invName, - Variant* ihandler) { - using namespace HPHP::VM::Transl; - assert(ihandler); - assert(retval); - Variant doneFlag = true; - Array args = params; - if (invName) { - // This is a magic call, so we need to shuffle the args - HphpArray* magicArgs = NEW(HphpArray)(2); - magicArgs->append(invName, false); - magicArgs->append(params, false); - args = magicArgs; - } - Array intArgs = - CREATE_VECTOR5(f->fullNameRef(), (this_ ? Variant(Object(this_)) : uninit_null()), - args, ihandler->asCArrRef()[1], ref(doneFlag)); - call_intercept_handler(retval, intArgs, nullptr, ihandler); - // $done is true, meaning don't enter the intercepted function. - return !doneFlag.toBoolean(); -} - HphpArray* get_static_locals(const ActRec* ar) { if (ar->m_func->isClosureBody()) { TypedValue* closureLoc = frame_local(ar, ar->m_func->numParams()); diff --git a/hphp/runtime/vm/runtime.h b/hphp/runtime/vm/runtime.h index 9538190c4..1b377267d 100644 --- a/hphp/runtime/vm/runtime.h +++ b/hphp/runtime/vm/runtime.h @@ -112,12 +112,17 @@ frame_free_locals_helper_inl(ActRec* fp, int numLocals) { } inline void ALWAYS_INLINE -frame_free_locals_inl(ActRec* fp, int numLocals) { +frame_free_locals_inl_no_hook(ActRec* fp, int numLocals) { if (fp->hasThis()) { ObjectData* this_ = fp->getThis(); decRefObj(this_); } frame_free_locals_helper_inl(fp, numLocals); +} + +inline void ALWAYS_INLINE +frame_free_locals_inl(ActRec* fp, int numLocals) { + frame_free_locals_inl_no_hook(fp, numLocals); VM::EventHook::FunctionExit(fp); } @@ -150,93 +155,6 @@ VM::Unit* build_native_class_unit(const HhbcExtClassInfo* builtinClasses, HphpArray* pack_args_into_array(ActRec* ar, int nargs); -template -void call_intercept_handler(TypedValue* retval, - CArrRef intArgs, - ActRec* ar, - Variant* ihandler) { - if (handle_throw) { assert(ar); } - ObjectData* intThis = nullptr; - VM::Class* intCls = nullptr; - StringData* intInvName = nullptr; - const VM::Func* handler = vm_decode_function(ihandler->asCArrRef()[0], - g_vmContext->getFP(), - false, intThis, intCls, - intInvName); - if (handle_throw) { - // It's possible for the intercept handler could throw an exception. - // If run_intercept_handler() was called from the translator and the - // handler throws, we need to do some cleanup here before allowing - // the exception to propagate. - try { - g_vmContext->invokeFunc(retval, handler, intArgs, - intThis, intCls, nullptr, intInvName); - } catch (...) { - Stack& stack = g_vmContext->getStack(); - assert((TypedValue*)ar - stack.top() == ar->numArgs()); - while (uintptr_t(stack.top()) < uintptr_t(ar)) { - stack.popTV(); - } - stack.popAR(); - throw; - } - } else { - g_vmContext->invokeFunc(retval, handler, intArgs, - intThis, intCls, nullptr, intInvName); - } -} - -/** - * run_intercept_handler is used for functions invoked via FCall, whereas - * run_intercept_handler_for_invokefunc is used for functions invoked by - * the runtime via invokeFunc(). The run_intercept_handler* functions will - * return true if and only if the original function should be executed. - * - * run_intercept_handler is called when the ActRec for the original function - * is in the "pre-live" state. run_intercept_handler_for_invokefunc is called - * before the ActRec for the original function has been materialized. - */ - -template -bool run_intercept_handler(ActRec* ar, Variant* ihandler) { - using namespace HPHP::VM::Transl; - assert(ihandler); - TypedValue retval; - tvWriteNull(&retval); - Variant doneFlag = true; - Array intArgs = - CREATE_VECTOR5(ar->m_func->fullNameRef(), - (ar->hasThis() ? Variant(Object(ar->getThis())) : uninit_null()), - Array(pack_args_into_array(ar, ar->numArgs())), - ihandler->asCArrRef()[1], - ref(doneFlag)); - call_intercept_handler(&retval, intArgs, ar, ihandler); - if (doneFlag.toBoolean()) { - // $done is true, meaning don't enter the intercepted function. Clean up - // the pre-live ActRec and the args, move the intercept handler's return - // value to the right place, and get out. - Stack& stack = g_vmContext->getStack(); - assert((TypedValue*)ar - stack.top() == ar->numArgs()); - while (uintptr_t(stack.top()) < uintptr_t(ar)) { - stack.popTV(); - } - stack.popAR(); - stack.allocTV(); - memcpy(stack.top(), &retval, sizeof(TypedValue)); - return false; - } - // Discard the handler's return value - tvRefcountedDecRef(&retval); - return true; -} - -bool run_intercept_handler_for_invokefunc(TypedValue* retval, - const VM::Func* f, - CArrRef params, - ObjectData* this_, - StringData* invName, - Variant* ihandler); - static inline VM::Instance* newInstance(VM::Class* cls) { assert(cls); diff --git a/hphp/runtime/vm/translator/collector.cpp b/hphp/runtime/vm/translator/collector.cpp index d86cad0f0..fa08e3cf3 100644 --- a/hphp/runtime/vm/translator/collector.cpp +++ b/hphp/runtime/vm/translator/collector.cpp @@ -127,8 +127,6 @@ bool TranslatorX64::replace() { severFuncReferences(tx64); TranslatorX64* n00b = new TranslatorX64(); n00b->initGdb(); - // m_interceptsEnabled should persist across TC spaces. - n00b->m_interceptsEnabled = m_interceptsEnabled; TRACE(0, "Tx64: replace %p a.code %p -> %p a.code %p complete\n", this, a.code.base, n00b, n00b->a.code.base); // Here is the changing of the guard. diff --git a/hphp/runtime/vm/translator/translator-x64-helpers.cpp b/hphp/runtime/vm/translator/translator-x64-helpers.cpp index a017b2be2..4ab69962b 100644 --- a/hphp/runtime/vm/translator/translator-x64-helpers.cpp +++ b/hphp/runtime/vm/translator/translator-x64-helpers.cpp @@ -118,7 +118,7 @@ static TCA callAndResume(ActRec *ar) { */ VMRegAnchor _(ar); uint64_t rip = ar->m_savedRip; - g_vmContext->doFCall(ar, g_vmContext->m_pc); + g_vmContext->doFCall(ar, g_vmContext->m_pc); ar->m_savedRip = rip; return Translator::Get()->getResumeHelperRet(); } @@ -255,4 +255,20 @@ void TranslatorX64::fCallArrayHelper(const Offset pcOff, const Offset pcNext) { framePtr->m_savedRbp = (uint64_t)ec->m_fp; } +void functionEnterHelper(const ActRec* ar) { + DECLARE_FRAME_POINTER(framePtr); + + uint64_t savedRip = ar->m_savedRip; + uint64_t savedRbp = ar->m_savedRbp; + if (LIKELY(EventHook::onFunctionEnter(ar, EventHook::NormalFunc))) return; + /* We need to skip the function. + FunctionEnter already cleaned up ar, and pushed the return value, + so all we need to do is return to where ar would have returned to, + with rbp set to ar's outer frame. + */ + framePtr->m_savedRip = savedRip; + framePtr->m_savedRbp = savedRbp; + sp = g_vmContext->m_stack.top(); +} + } } } // HPHP::VM::Transl diff --git a/hphp/runtime/vm/translator/translator-x64.cpp b/hphp/runtime/vm/translator/translator-x64.cpp index 73c488872..e1a64be8d 100644 --- a/hphp/runtime/vm/translator/translator-x64.cpp +++ b/hphp/runtime/vm/translator/translator-x64.cpp @@ -1579,13 +1579,10 @@ TranslatorX64::emitCheckSurpriseFlagsEnter(bool inTracelet, Fixup fixup) { UnlikelyIfBlock ifTracer(CC_NZ, a, astubs); if (false) { // typecheck const ActRec* ar = nullptr; - EventHook::FunctionEnter(ar, 0); + functionEnterHelper(ar); } astubs.mov_reg64_reg64(rVmFp, argNumToRegName[0]); - static_assert(EventHook::NormalFunc == 0, - "EventHook::NormalFunc should be the first enum element"); - astubs.xor_reg32_reg32(argNumToRegName[1], argNumToRegName[1]); - emitCall(astubs, (TCA)&EventHook::FunctionEnter); + emitCall(astubs, (TCA)&functionEnterHelper); if (inTracelet) { recordSyncPoint(astubs, fixup.m_pcOffset, fixup.m_spOffset); } else { @@ -1650,8 +1647,8 @@ TranslatorX64::shuffleArgsForMagicCall(ActRec* ar) { * HHBC instructions, and that source HHBC instructions are in turn * uniquely associated with SP->FP deltas. * - * run_intercept_helper/trimExtraArgs is called from the prologue of - * the callee. The prologue is 1) still in the caller frame for now, + * trimExtraArgs is called from the prologue of the callee. + * The prologue is 1) still in the caller frame for now, * and 2) shared across multiple call sites. 1 means that we have the * fp from the caller's frame, and 2 means that this fp is not enough * to figure out sp. @@ -1672,19 +1669,6 @@ static void sync_regstate_to_caller(ActRec* preLive) { tl_regState = REGSTATE_CLEAN; } -static uint64_t run_intercept_helper(ActRec* ar, Variant* ihandler) { - sync_regstate_to_caller(ar); - bool ret = run_intercept_handler(ar, ihandler); - /* - * Restore tl_regState manually in the no-exception case only. (The - * VM regs are clean here---we only need to set them dirty if we are - * stopping to execute in the TC again, which we won't be doing if - * an exception is propagating.) - */ - tl_regState = REGSTATE_DIRTY; - return ret; -} - void TranslatorX64::trimExtraArgs(ActRec* ar) { assert(!ar->hasInvName()); @@ -1719,68 +1703,6 @@ TranslatorX64::trimExtraArgs(ActRec* ar) { tl_regState = REGSTATE_DIRTY; } -TCA -TranslatorX64::getInterceptHelper() { - if (false) { // typecheck - Variant *h = get_intercept_handler(CStrRef((StringData*)nullptr), - (char*)nullptr); - bool c UNUSED = run_intercept_helper((ActRec*)nullptr, h); - } - if (!m_interceptHelper) { - m_interceptHelper = TCA(astubs.code.frontier); - astubs. loadq (rStashedAR[AROFF(m_func)], rax); - astubs. lea (rax[Func::fullNameOff()], argNumToRegName[0]); - astubs. lea (rax[Func::maybeInterceptedOff()], argNumToRegName[1]); - - astubs. sub_imm32_reg64(8, rsp); // Stack parity { - astubs. call(TCA(get_intercept_handler)); - astubs. add_imm32_reg64(8, rsp); // } - astubs. test_reg64_reg64(rax, rax); - { - JccBlock ifNotIntercepted(astubs); - astubs. ret(); - } - - // we might re-enter, so align the stack - astubs. sub_imm32_reg64(8, rsp); - // Copy the old rbp into the savedRbp pointer. - astubs. store_reg64_disp_reg64(rbp, 0, rStashedAR); - - PhysReg rSavedRip = r13; // XXX ideally don't hardcode r13 ... but - // we need callee-saved and don't have - // any scratch ones. - - // Fish out the saved rip. We may need to jump there, and the helper will - // have wiped out the ActRec. - astubs. load_reg64_disp_reg64(rStashedAR, AROFF(m_savedRip), - rSavedRip); - astubs. mov_reg64_reg64(rStashedAR, argNumToRegName[0]); - astubs. mov_reg64_reg64(rax, argNumToRegName[1]); - astubs. call(TCA(run_intercept_helper)); - - // Normally we'd like to recordReentrantCall here, but the vmreg sync'ing - // for run_intercept_handler is a special little snowflake. See - // run_intercept_handler for details. - astubs. test_reg64_reg64(rax, rax); - { - // If the helper returned false, don't execute this function. The helper - // will have cleaned up the interceptee's arguments and AR, and pushed - // the handler's return value; we now need to get out. - // - // We don't need to touch rVmFp; it's still pointing to the caller of - // the interceptee. We need to adjust rVmSp. Then we need to jump to the - // saved rip from the interceptee's ActRec. - JccBlock ifDontEnterFunction(astubs); - astubs. add_imm32_reg64(16, rsp); - astubs. lea_reg64_disp_reg64(rStashedAR, AROFF(m_r), rVmSp); - astubs. jmp(rSavedRip); - } - astubs. add_imm32_reg64(8, rsp); - astubs. ret(); - } - return m_interceptHelper; -} - TCA TranslatorX64::getCallArrayProlog(Func* func) { TCA tca = func->getFuncBody(); @@ -1915,17 +1837,6 @@ funcPrologHasGuard(TCA prolog, const Func* func) { return *funcPrologToGuardImm(prolog) == iptr; } -static void -disableFuncGuard(TCA prolog, Func* func) { - assert(funcPrologHasGuard(prolog, func)); - if (deltaFits((intptr_t)func, sz::dword)) { - *funcPrologToGuardImm(prolog) = 0; - } else { - *funcPrologToGuardImm(prolog) = 0; - } - assert(!funcPrologHasGuard(prolog, func)); -} - static TCA funcPrologToGuard(TCA prolog, const Func* func) { if (!prolog || prolog == (TCA)fcallHelperThunk) return prolog; @@ -2192,61 +2103,6 @@ TranslatorX64::funcPrologue(Func* func, int nPassed, ActRec* ar) { return start; } -TCA -TranslatorX64::emitInterceptPrologue(Func* func) { - TCA start = a.code.frontier; - emitImmReg(a, int64_t(func), rax); - a. cmpb (0, rax[Func::maybeInterceptedOff()]); - semiLikelyIfBlock(CC_NE, a, [&]{ - // Prologues are not really sites for function entry yet; we can get - // here via an optimistic bindCall. Check that the func is as expected. - a. cmpq (rax, rStashedAR[AROFF(m_func)]); - { - JccBlock skip(a); - a.call(getInterceptHelper()); - } - }); - return start; -} - -void -TranslatorX64::interceptPrologues(Func* func) { - if (!RuntimeOption::EvalJitEnableRenameFunction && - !(func->attrs() & AttrDynamicInvoke)) { - return; - } - if (func->maybeIntercepted() == -1) { - return; - } - func->maybeIntercepted() = -1; - assert(s_writeLease.amOwner()); - int maxNumPrologues = func->numPrologues(); - for (int i = 0; i < maxNumPrologues; i++) { - TCA prologue = func->getPrologue(i); - if (prologue == (unsigned char*)fcallHelperThunk) - continue; - assert(funcPrologHasGuard(prologue, func)); - // There might already be calls hard-coded to this via FCall. - // blow away immediate comparison, so that we always use the Func*'s - // prologue table. We use 0 (== NULL on our architecture) as the bit - // pattern for an impossible Func. - // - // Note that we're modifying reachable code. - disableFuncGuard(prologue, func); - - // There's a prologue already generated; redirect it to first - // call the intercept helper. First, reset it (leaking the old - // prologue), so funcPrologue will re-emit it. - func->setPrologue(i, (TCA)fcallHelperThunk); - TCA addr = funcPrologue(func, i); - assert(funcPrologHasGuard(addr, func)); - assert(addr); - func->setPrologue(i, addr); - TRACE(1, "interceptPrologues %s prologue[%d]=%p\n", - func->fullName()->data(), i, (void*)addr); - } -} - static void raiseMissingArgument(const char* name, int expected, int got) { if (expected == 1) { raise_warning(Strings::MISSING_ARGUMENT, name, got); @@ -2259,15 +2115,6 @@ SrcKey TranslatorX64::emitPrologue(Func* func, int nPassed) { int numParams = func->numParams(); const Func::ParamInfoVec& paramInfo = func->params(); - assert(IMPLIES(func->maybeIntercepted() == -1, - m_interceptsEnabled)); - if (m_interceptsEnabled && - !func->isPseudoMain() && - !func->isGenerator() && - (RuntimeOption::EvalJitEnableRenameFunction || - func->attrs() & AttrDynamicInvoke)) { - emitInterceptPrologue(func); - } Offset dvInitializer = InvalidAbsoluteOffset; @@ -6793,7 +6640,7 @@ asm_label(a, varEnvReturn); rVmSp, retvalSrcDisp, rVmFp, AROFF(m_this), r(s)); } astubs.mov_reg64_reg64(rVmFp, argNumToRegName[0]); - emitCall(astubs, (TCA)&EventHook::FunctionExit, true); + emitCall(astubs, (TCA)&EventHook::onFunctionExit, true); recordReentrantStubCall(*m_curNI); } @@ -7409,7 +7256,7 @@ void TranslatorX64::emitContExit() { { UnlikelyIfBlock ifTracer(CC_NZ, a, astubs); astubs.mov_reg64_reg64(rVmFp, argNumToRegName[0]); - emitCall(astubs, (TCA)&EventHook::FunctionExit, true); + emitCall(astubs, (TCA)&EventHook::onFunctionExit, true); recordReentrantStubCall(*m_curNI); } a. push (rVmFp[AROFF(m_savedRip)]); @@ -11549,14 +11396,12 @@ TranslatorX64::TranslatorX64() : m_numNativeTrampolines(0), m_trampolineSize(0), m_spillFillCode(&a), - m_interceptHelper(0), m_defClsHelper(0), m_funcPrologueRedispatch(0), m_irAUsage(0), m_irAstubsUsage(0), m_numHHIRTrans(0), m_regMap(kCallerSaved, kCalleeSaved, this), - m_interceptsEnabled(false), m_unwindRegMap(128), m_curTrace(0), m_curNI(0), diff --git a/hphp/runtime/vm/translator/translator-x64.h b/hphp/runtime/vm/translator/translator-x64.h index bcbb4384d..435f6309b 100644 --- a/hphp/runtime/vm/translator/translator-x64.h +++ b/hphp/runtime/vm/translator/translator-x64.h @@ -176,7 +176,6 @@ class TranslatorX64 : public Translator TCA m_dtorGenericStub; TCA m_dtorGenericStubRegs; TCA m_dtorStubs[kDestrTableSize]; - TCA m_interceptHelper; TCA m_defClsHelper; TCA m_funcPrologueRedispatch; @@ -214,7 +213,6 @@ class TranslatorX64 : public Translator RegAlloc m_regMap; std::stack m_savedRegMaps; - volatile bool m_interceptsEnabled; FixupMap m_fixupMap; UnwindRegMap m_unwindRegMap; UnwindInfoHandle m_unwindRegistrar; @@ -720,9 +718,6 @@ PSEUDOINSTRS return m_unwindRegMap.find(ip); } - void enableIntercepts() {m_interceptsEnabled = true;} - bool interceptsEnabled() {return m_interceptsEnabled;} - static void SEGVHandler(int signum, siginfo_t *info, void *ctx); // public for syncing gdb state @@ -796,7 +791,6 @@ public: void dropWriteLease() { s_writeLease.drop(); } - void interceptPrologues(Func* func); void emitGuardChecks(Asm& a, SrcKey, const ChangeMap&, const RefDeps&, SrcRec&); @@ -815,7 +809,6 @@ public: bool freeRequestStub(TCA stub); TCA getFreeStub(); private: - TCA getInterceptHelper(); void translateInstr(const Tracelet& t, const NormalizedInstruction& i); void translateInstrWork(const Tracelet& t, const NormalizedInstruction& i); void irInterpretInstr(const NormalizedInstruction& i); @@ -968,7 +961,6 @@ private: bool checkCachedPrologue(const Func* func, int param, TCA& plgOut) const; SrcKey emitPrologue(Func* func, int nArgs); int32_t emitNativeImpl(const Func*, bool emitSavedRIPReturn); - TCA emitInterceptPrologue(Func* func); void emitBindJ(Asm& a, ConditionCode cc, SrcKey dest, ServiceRequest req); void emitBindJmp(Asm& a, SrcKey dest, @@ -1139,6 +1131,7 @@ const size_t kMaxNumTrampolines = kTrampolinesBlockSize / void fcallHelperThunk() asm ("__fcallHelperThunk"); void funcBodyHelperThunk() asm ("__funcBodyHelperThunk"); +void functionEnterHelper(const ActRec* ar); // These could be static but are used in hopt/codegen.cpp void raiseUndefVariable(StringData* nm); diff --git a/hphp/runtime/vm/translator/translator.h b/hphp/runtime/vm/translator/translator.h index 1b734dcba..9c21ca8df 100644 --- a/hphp/runtime/vm/translator/translator.h +++ b/hphp/runtime/vm/translator/translator.h @@ -892,12 +892,6 @@ public: virtual void unprotectCode() = 0; virtual bool isValidCodeAddress(TCA) const = 0; - enum FuncPrologueFlags { - FuncPrologueNormal = 0, - FuncPrologueMagicCall = 1, - FuncPrologueIntercepted = 2, - }; - const TransDB& getTransDB() const { return m_transDB; } diff --git a/hphp/runtime/vm/unit.cpp b/hphp/runtime/vm/unit.cpp index 7aca95f17..eef19bcc5 100644 --- a/hphp/runtime/vm/unit.cpp +++ b/hphp/runtime/vm/unit.cpp @@ -1576,66 +1576,6 @@ std::string Unit::toString() const { return ss.str(); } -void Unit::enableIntercepts() { - TranslatorX64* tx64 = TranslatorX64::Get(); - // Its ok to set maybeIntercepted(), because - // we are protected by s_mutex in intercept.cpp - for (MutableFuncRange fr(nonMainFuncs()); !fr.empty(); ) { - Func *func = fr.popFront(); - if (func->isPseudoMain()) { - // pseudomain's can't be intercepted - continue; - } - tx64->interceptPrologues(func); - } - { - Lock lock(s_classesMutex); - for (int i = m_preClasses.size(); i--; ) { - PreClass* pcls = m_preClasses[i].get(); - Class* cls = pcls->namedEntity()->clsList(); - while (cls) { - /* - * verify that this class corresponds to the - * preclass we're looking at. This avoids - * redundantly iterating over the same class - * multiple times, but also avoids a hard to - * repro crash, if the unit owning cls is being - * destroyed at the time we pick up cls from the - * list (which is possible). Note that cls - * itself will be destroyed by treadmill, so - * it is safe to call preClass() - */ - if (cls->preClass() == pcls) { - size_t numFuncs = cls->numMethods(); - Func* const* funcs = cls->methods(); - for (unsigned i = 0; i < numFuncs; i++) { - if (funcs[i]->cls() != cls) { - /* - * This func is defined by a base - * class. We can skip it now, because - * we'll hit it when we process - * the base class. More importantly, - * the base class's unit may have been - * destroyed; in which case we have to - * skip it here, or we'll likely crash. - * - * Note that Classes are ref counted, - * so the the funcs[i]'s Class cant have - * been freed yet, so the comparison is - * safe; although we do seem to have a - * class leak here (sandbox mode only) - */ - continue; - } - tx64->interceptPrologues(funcs[i]); - } - } - cls = cls->m_nextClass; - } - } - } -} - Func* Unit::lookupFunc(const NamedEntity* ne, const StringData* name) { Func* func = ne->getCachedFunc(); return func; diff --git a/hphp/runtime/vm/unit.h b/hphp/runtime/vm/unit.h index ad686b527..9da243a2f 100644 --- a/hphp/runtime/vm/unit.h +++ b/hphp/runtime/vm/unit.h @@ -636,7 +636,6 @@ public: } const Func* getFunc(Offset pc) const; - void enableIntercepts(); void setCacheId(unsigned id) { m_cacheOffset = id >> 3; m_cacheMask = 1 << (id & 7);