// If the proxy has an enabled breakpoint that matches entry into the given // function, arrange for the VM to stop execution and notify the debugger // whenever execution enters the given function. static void addBreakPointFuncEntry(Eval::DebuggerProxy* proxy, const Func* f) { std::vector<Eval::BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; if (bp->m_state == Eval::BreakPointInfo::Disabled) continue; if (!matchFunctionName(bp->getFuncName(), f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); return; } }
// If the proxy has an enabled breakpoint that matches entry into the given // function, arrange for the VM to stop execution and notify the debugger // whenever execution enters the given function. static void addBreakPointFuncEntry(Eval::DebuggerProxy* proxy, const Func* f) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; if (bp->m_state == Eval::BreakPointInfo::Disabled) continue; if (bp->getFuncName() != f->fullName()->data()) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); return; } }
// Called by the proxy whenever its breakpoint list is updated. // Since this intended to be called when user input is received, it is not // performance critical. Also, in typical scenarios, the list is short. void phpSetBreakPoints(Eval::DebuggerProxy* proxy) { std::vector<Eval::BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; bp->m_bindState = Eval::BreakPointInfo::Unknown; auto className = bp->getClass(); if (!className.empty()) { auto clsName = makeStaticString(className); auto cls = Unit::lookupClass(clsName); if (cls == nullptr) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; size_t numFuncs = cls->numMethods(); if (numFuncs == 0) continue; auto methodName = bp->getFunction(); for (size_t i = 0; i < numFuncs; ++i) { auto f = cls->getMethod(i); if (!matchFunctionName(methodName, f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); break; } //TODO: what about superclass methods accessed via the derived class? //Task 2527229. continue; } auto funcName = bp->getFuncName(); if (!funcName.empty()) { auto fName = makeStaticString(funcName); Func* f = Unit::lookupFunc(fName); if (f == nullptr) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); continue; } auto fileName = bp->m_file; if (!fileName.empty()) { for (auto it = g_context->m_evaledFiles.begin(); it != g_context->m_evaledFiles.end(); ++it) { auto efile = it->second; if (!Eval::BreakPointInfo::MatchFile(fileName, efile->getFileName())) { continue; } addBreakPointInUnit(bp, efile->unit()); break; } continue; } auto exceptionClassName = bp->getExceptionClass(); if (exceptionClassName == "@") { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; continue; } else if (!exceptionClassName.empty()) { auto expClsName = makeStaticString(exceptionClassName); auto cls = Unit::lookupClass(expClsName); if (cls != nullptr) { auto baseClsName = makeStaticString("Exception"); auto baseCls = Unit::lookupClass(baseClsName); if (baseCls != nullptr) { if (cls->classof(baseCls)) { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } else { bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; } } } continue; } else { continue; } // If we get here, the break point is of a type that does // not need to be explicitly enabled in the VM. For example // a break point that get's triggered when the server starts // to process a page request. bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } }
// Called by the proxy whenever its breakpoint list is updated. // Since this intended to be called when user input is received, it is not // performance critical. Also, in typical scenarios, the list is short. void phpSetBreakPoints(Eval::DebuggerProxy* proxy) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; bp->m_bindState = Eval::BreakPointInfo::Unknown; auto className = bp->getClass(); if (!className.empty()) { auto clsName = makeStaticString(className); auto cls = Unit::lookupClass(clsName); if (cls == nullptr) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; size_t numFuncs = cls->numMethods(); if (numFuncs == 0) continue; auto methodName = bp->getFunction(); Func* const* funcs = cls->methods(); for (size_t i = 0; i < numFuncs; ++i) { auto f = funcs[i]; if (!matchFunctionName(methodName, f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); break; } //TODO: what about superclass methods accessed via the derived class? //Task 2527229. continue; } auto funcName = bp->getFuncName(); if (!funcName.empty()) { auto fName = makeStaticString(funcName); Func* f = Unit::lookupFunc(fName); if (f == nullptr) continue; if (f->hasGeneratorAsBody() && !f->isAsync()) { // This function is a generator, and it's the original // function which has been turned into a stub which creates a // continuation. We want to set the breakpoint on the // continuation function instead. fName = makeStaticString(funcName + "$continuation"); f = Unit::lookupFunc(fName); if (f == nullptr) continue; } bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); continue; } auto fileName = bp->m_file; if (!fileName.empty()) { for (EvaledFilesMap::const_iterator it = g_vmContext->m_evaledFiles.begin(); it != g_vmContext->m_evaledFiles.end(); ++it) { auto efile = it->second; if (!Eval::BreakPointInfo::MatchFile(fileName, efile->getFileName())) { continue; } addBreakPointInUnit(bp, efile->unit()); break; } continue; } auto exceptionClassName = bp->getExceptionClass(); if (exceptionClassName == "@") { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; continue; } else if (!exceptionClassName.empty()) { auto expClsName = makeStaticString(exceptionClassName); auto cls = Unit::lookupClass(expClsName); if (cls != nullptr) { auto baseClsName = makeStaticString("Exception"); auto baseCls = Unit::lookupClass(baseClsName); if (baseCls != nullptr) { if (cls->classof(baseCls)) { bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } else { bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; } } } continue; } else { continue; } // If we get here, the break point is of a type that does // not need to be explicitly enabled in the VM. For example // a break point that get's triggered when the server starts // to process a page request. bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; } }