// Helper that adds the given function breakpoint corresponding to the given // function and id as a breakpoint. If a duplicate breakpoint already exists, // it is overwritten. static void add_func_breakpoint(int id, XDebugBreakpoint& bp, const Func* func) { // Function id is added to the breakpoint once matched auto const func_id = func->getFuncId(); bp.funcId = func_id; // Add the appropriate function map switch (bp.type) { case BreakType::CALL: { auto iter = FUNC_ENTRY.find(func_id); if (iter != FUNC_ENTRY.end()) { XDEBUG_REMOVE_BREAKPOINT(iter->second); } FUNC_ENTRY[func->getFuncId()] = id; phpAddBreakPointFuncEntry(func); break; } case BreakType::RETURN: { auto iter = FUNC_EXIT.find(func_id); if (iter != FUNC_EXIT.end()) { XDEBUG_REMOVE_BREAKPOINT(iter->second); } FUNC_EXIT[func->getFuncId()] = id; phpAddBreakPointFuncExit(func); break; } default: throw Exception("Not passed a function breakpoint"); } }
void HphpdHook::onDefClass(const Class* cls) { // Make sure we have a proxy DebuggerProxy* proxy = Debugger::GetProxy().get(); if (proxy == nullptr) return; // If the proxy has enabled breakpoints that match entry into methods of // the given class, arrange for the VM to stop execution and notify the // debugger whenever execution enters one of these matched method. // This function is called once, when a class is first loaded, so it is not // performance critical. size_t numFuncs = cls->numMethods(); if (numFuncs == 0) return; auto clsName = cls->name(); std::vector<BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { BreakPointInfoPtr bp = bps[i]; if (bp->m_state == BreakPointInfo::Disabled) continue; // TODO: check name space separately if (bp->getClass() != clsName->data()) continue; bp->m_bindState = BreakPointInfo::KnownToBeInvalid; for (size_t i = 0; i < numFuncs; ++i) { auto f = cls->getMethod(i); if (!matchFunctionName(bp->getFunction(), f)) continue; bp->m_bindState = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); } } }
void HphpdHook::onDefFunc(const Func* f) { // Make sure we have a proxy DebuggerProxyPtr proxy = Debugger::GetProxy(); if (proxy == nullptr) 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. std::vector<BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { BreakPointInfoPtr bp = bps[i]; if (bp->m_state == BreakPointInfo::Disabled) continue; if (!matchFunctionName(bp->getFuncName(), f)) continue; bp->m_bindState = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); return; } }
void proxySetBreakPoints(DebuggerProxy* proxy) { std::vector<BreakPointInfoPtr> bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { BreakPointInfoPtr bp = bps[i]; bp->m_bindState = 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 = 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 = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); break; } // TODO(#2527229): what about superclass methods accessed via the derived // class? continue; } auto funcName = bp->getFuncName(); if (!funcName.empty()) { auto fName = makeStaticString(funcName); Func* f = Unit::lookupFunc(fName); if (f == nullptr) continue; bp->m_bindState = BreakPointInfo::KnownToBeValid; phpAddBreakPointFuncEntry(f); continue; } auto fileName = bp->m_file; if (!fileName.empty()) { for (auto& kv : g_context->m_evaledFiles) { auto const unit = kv.second; if (!BreakPointInfo::MatchFile(fileName, unit->filepath()->toCppString())) { continue; } addBreakPointInUnit(bp, unit); break; } continue; } auto exceptionClassName = bp->getExceptionClass(); if (exceptionClassName == "@") { bp->m_bindState = 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 = BreakPointInfo::KnownToBeValid; } else { bp->m_bindState = 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 = BreakPointInfo::KnownToBeValid; } }