// Handle a continue cmd, or setup stepping. void DebuggerProxy::processFlowControl(CmdInterrupt &cmd) { TRACE(2, "DebuggerProxy::processFlowControl\n"); switch (m_flow->getType()) { case DebuggerCommand::KindOfContinue: if (!m_flow->decCount()) { m_flow.reset(); } break; case DebuggerCommand::KindOfStep: { // allows the breakpoint to be hit again when returns // from function call BreakPointInfoPtr bp = getBreakPointAtCmd(cmd); if (bp) { bp->setBreakable(getRealStackDepth()); } break; } case DebuggerCommand::KindOfOut: case DebuggerCommand::KindOfNext: m_flow->setStackDepth(getStackDepth()); m_flow->setVMDepth(g_vmContext->m_nesting); m_flow->setFileLine(cmd.getFileLine()); break; default: assert(false); break; } }
// Do not allow further breaks on the site of cmd, except during // calls made from the current site. void DebuggerProxy::unsetBreakableForBreakpointsMatching(CmdInterrupt& cmd) { TRACE(2, "DebuggerProxy::unsetBreakableForBreakpointsMatching\n"); auto stackDepth = getRealStackDepth(); for (unsigned int i = 0; i < m_breakpoints.size(); ++i) { BreakPointInfoPtr bp = m_breakpoints[i]; if (bp != nullptr && bp->m_state != BreakPointInfo::Disabled && bp->match(*this, cmd.getInterruptType(), *cmd.getSite())) { bp->unsetBreakable(stackDepth); } } }
void DebuggerProxy::changeBreakPointDepth(CmdInterrupt& cmd) { TRACE(2, "DebuggerProxy::changeBreakPointDepth\n"); for (unsigned int i = 0; i < m_breakpoints.size(); ++i) { // if the site changes, then update the breakpoint depth BreakPointInfoPtr bp = m_breakpoints[i]; if (bp->m_state != BreakPointInfo::Disabled && !bp->match(cmd.getInterruptType(), *cmd.getSite())) { m_breakpoints[i]->changeBreakPointDepth(getRealStackDepth()); } } }
// Checks whether the cmd has any breakpoints that match the current Site. // Also returns true for cmds that should always break, like SessionStarted, // and returns true when we have special modes setup for, say, breaking on // RequestEnded, PSPEnded, etc. bool DebuggerProxy::checkBreakPoints(CmdInterrupt &cmd) { TRACE(2, "DebuggerProxy::checkBreakPoints\n"); ReadLock lock(m_breakMutex); return cmd.shouldBreak(*this, m_breakpoints, getRealStackDepth()); }
// Handle an interrupt from the VM. void DebuggerProxy::interrupt(CmdInterrupt &cmd) { TRACE(2, "DebuggerProxy::interrupt\n"); changeBreakPointDepth(cmd); if (cmd.getInterruptType() == BreakPointReached) { if (!needInterrupt()) return; // NB: stepping is represented as a BreakPointReached interrupt. // Modify m_lastLocFilter to save current location. This will short-circuit // the work done up in phpDebuggerOpcodeHook() and ensure we don't break on // this line until we're completely off of it. InterruptSite *site = cmd.getSite(); if (g_vmContext->m_lastLocFilter) { g_vmContext->m_lastLocFilter->clear(); } else { g_vmContext->m_lastLocFilter = new VM::PCFilter(); } if (debug && Trace::moduleEnabled(Trace::bcinterp, 5)) { Trace::trace("prepare source loc filter\n"); const VM::OffsetRangeVec& offsets = site->getCurOffsetRange(); for (VM::OffsetRangeVec::const_iterator it = offsets.begin(); it != offsets.end(); ++it) { Trace::trace("block source loc in %s:%d: unit %p offset [%d, %d)\n", site->getFile(), site->getLine0(), site->getUnit(), it->m_base, it->m_past); } } g_vmContext->m_lastLocFilter->addRanges(site->getUnit(), site->getCurOffsetRange()); // If the breakpoint is not to be processed, we should continue execution. BreakPointInfoPtr bp = getBreakPointAtCmd(cmd); if (bp) { if (!bp->breakable(getRealStackDepth())) { return; } else { bp->unsetBreakable(getRealStackDepth()); } } } // Wait until this thread is the thread this proxy wants to debug. // NB: breakpoints and control flow checks happen here, too, and return false // if we're not done with the flow, or not at a breakpoint, etc. if (!blockUntilOwn(cmd, true)) { return; } if (processFlowBreak(cmd)) { while (true) { try { Lock lock(m_signalMutex); m_signum = CmdSignal::SignalNone; processInterrupt(cmd); } catch (const DebuggerException &e) { switchThreadMode(Normal); throw; } catch (...) { assert(false); // no other exceptions should be seen here switchThreadMode(Normal); throw; } if (cmd.getInterruptType() == PSPEnded) { switchThreadMode(Normal); return; // we are done with this thread } if (!m_newThread) { break; // we're not switching threads } switchThreadMode(Normal, m_newThread->m_id); blockUntilOwn(cmd, false); } } if (m_threadMode == Normal) { Lock lock(this); m_thread = 0; notify(); } else if (cmd.getInterruptType() == PSPEnded) { switchThreadMode(Normal); // we are done with this thread } }