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 CmdBreak::setClientOutput(DebuggerClient *client) { // Output an array of current breakpoints including exceptions client->setOutputType(DebuggerClient::OTValues); Array values; m_breakpoints = client->getBreakPoints(); for (int i = 0; i < (int)m_breakpoints->size(); i++) { BreakPointInfoPtr bpi = m_breakpoints->at(i); Array breakpoint; breakpoint.set(s_id, bpi->index()); breakpoint.set(s_state, bpi->state(false)); if (bpi->m_interrupt == ExceptionThrown) { breakpoint.set(s_is_exception, true); breakpoint.set(s_exception_class, bpi->getExceptionClass()); } else { breakpoint.set(s_is_exception, false); breakpoint.set(s_file, bpi->m_file); breakpoint.set(s_line1, bpi->m_line1); breakpoint.set(s_line2, bpi->m_line2); breakpoint.set(s_namespace, bpi->getNamespace()); breakpoint.set(s_func, bpi->getFunction()); breakpoint.set(s_class, bpi->getClass()); } breakpoint.set(s_url, bpi->m_url); breakpoint.set(s_use_regex, bpi->m_regex); breakpoint.set(s_clause_type, bpi->m_check ? s_if : s_ampamp); breakpoint.set(s_clause, bpi->m_clause); breakpoint.set(s_desc, bpi->desc()); values.append(breakpoint); } client->setOTValues(values); }
// Hold the entire set of breakpoints, and sift breakpoints by function and // class name into separate containers for later. void DebuggerProxy::setBreakPoints(BreakPointInfoPtrVec &breakpoints) { TRACE(2, "DebuggerProxy::setBreakPoints\n"); // Hold the break mutex while we update the proxy's state. There's no need // to hold it over the longer operation to set breakpoints in each file later. { WriteLock lock(m_breakMutex); m_breakpoints = breakpoints; m_hasBreakPoints = !m_breakpoints.empty(); m_breaksEnterFunc.clear(); m_breaksEnterClsMethod.clear(); for (unsigned int i = 0; i < m_breakpoints.size(); i++) { BreakPointInfoPtr bp = m_breakpoints[i]; std::string funcFullName = bp->getFuncName(); if (funcFullName.empty()) { continue; } { StringDataMap::accessor acc; const StringData* sd = StringData::GetStaticString(funcFullName); m_breaksEnterFunc.insert(acc, sd); } std::string clsName = bp->getClass(); if (!clsName.empty()) { StringDataMap::accessor acc; const StringData* sd = StringData::GetStaticString(clsName); m_breaksEnterClsMethod.insert(acc, sd); } } } VM::phpSetBreakPointsInAllFiles(this); // Apply breakpoints to the code. }
Variant c_DebuggerClientCmdUser::t_getcurrentlocation() { TRACE(5, "c_DebuggerClientCmdUser::t_getcurrentlocation\n"); BreakPointInfoPtr bpi = m_client->getCurrentLocation(); if (!bpi) return Array::Create(); ArrayInit ret(6); ret.set(s_file, String(bpi->m_file)); ret.set(s_line, (int64_t)bpi->m_line1); ret.set(s_namespace, String(bpi->getNamespace())); ret.set(s_class, String(bpi->getClass())); ret.set(s_function, String(bpi->getFunction())); ret.set(s_text, String(bpi->site())); return ret.create(); }
Variant c_DebuggerClientCmdUser::t_getcurrentlocation() { INSTANCE_METHOD_INJECTION_BUILTIN(DebuggerClientCmdUser, DebuggerClientCmdUser::getcurrentlocation); BreakPointInfoPtr bpi = m_client->getCurrentLocation(); Array ret(Array::Create()); if (bpi) { ret.set("file", String(bpi->m_file)); ret.set("line", (int64)bpi->m_line1); ret.set("namespace", String(bpi->getNamespace())); ret.set("class", String(bpi->getClass())); ret.set("function", String(bpi->getFunction())); ret.set("text", String(bpi->site())); } return ret; }
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; } }