static void addBreakPointsInFile(Eval::DebuggerProxy* proxy, Eval::PhpFile* efile) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for(unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; if (bp->m_line1 == 0 || bp->m_file.empty()) { // invalid breakpoint for file:line continue; } if (!Eval::BreakPointInfo::MatchFile(bp->m_file, efile->getFileName(), efile->getRelPath())) { continue; } Unit* unit = efile->unit(); OffsetRangeVec offsets; if (!unit->getOffsetRanges(bp->m_line1, offsets)) { continue; } TRACE(3, "Add to breakpoint filter for %s:%d, unit %p:\n", efile->getFileName().c_str(), bp->m_line1, unit); getBreakPointFilter()->addRanges(unit, offsets); if (RuntimeOption::EvalJit) { blacklistRangesInJit(unit, offsets); } } }
static void addBreakPointsInFile(Eval::DebuggerProxy* proxy, Eval::PhpFile* efile) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for (unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; if (Eval::BreakPointInfo::MatchFile(bp->m_file, efile->getFileName())) { addBreakPointInUnit(bp, efile->unit()); } } }
// 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 (!matchFunctionName(bp->getFuncName(), f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); 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. static void addBreakPointsClass(Eval::DebuggerProxy* proxy, const Class* cls) { size_t numFuncs = cls->numMethods(); if (numFuncs == 0) return; auto clsName = cls->name(); auto funcs = cls->methods(); 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; // TODO: check name space separately if (bp->getClass() != clsName->data()) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid; for (size_t i = 0; i < numFuncs; ++i) { auto f = funcs[i]; if (!matchFunctionName(bp->getFunction(), f)) continue; bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid; addBreakPointFuncEntry(f); } } }
static void addBreakPointsInFile(Eval::DebuggerProxy* proxy, Eval::PhpFile* efile) { Eval::BreakPointInfoPtrVec bps; proxy->getBreakPoints(bps); for(unsigned int i = 0; i < bps.size(); i++) { Eval::BreakPointInfoPtr bp = bps[i]; if (bp->m_line1 == 0 || bp->m_file.empty()) { // invalid breakpoint for file:line continue; } if (!Eval::BreakPointInfo::MatchFile(bp->m_file, efile->getFileName(), efile->getRelPath())) { continue; } Unit* unit = efile->unit(); OffsetRangeVec offsets; if (!unit->getOffsetRanges(bp->m_line1, offsets)) { continue; } if (!g_vmContext->m_breakPointFilter) { g_vmContext->m_breakPointFilter = new PCFilter(); } if (debug && Trace::moduleEnabled(Trace::bcinterp, 5)) { for (OffsetRangeVec::const_iterator it = offsets.begin(); it != offsets.end(); ++it) { Trace::trace("file:line break %s:%d : unit %p offset [%d, %d)\n", efile->getFileName().c_str(), bp->m_line1, unit, it->m_base, it->m_past); } } g_vmContext->m_breakPointFilter->addRanges(unit, offsets); if (RuntimeOption::EvalJit) { blacklistRangesInJit(unit, offsets); } } }
// 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; } }