void Debugger::Interrupt(int type, const char *program, InterruptSite *site /* = NULL */, const char *error /* = NULL */) { ASSERT(RuntimeOption::EnableDebugger); RequestInjectionData &rjdata = ThreadInfo::s_threadInfo->m_reqInjectionData; if (rjdata.debuggerIdle > 0 && type == BreakPointReached) { --rjdata.debuggerIdle; return; } DebuggerProxyPtr proxy = GetProxy(); if (proxy) { if (proxy->needInterrupt() || type != BreakPointReached) { // Interrupts may execute some PHP code, causing another interruption. std::stack<void *> &interrupts = rjdata.interrupts; CmdInterrupt cmd((InterruptType)type, program, site, error); interrupts.push(&cmd); proxy->interrupt(cmd); interrupts.pop(); } rjdata.debuggerIdle = proxy->needInterrupt() ? 0 : 1000; } else { // debugger clients are disconnected abnormally if (type == SessionStarted || type == SessionEnded) { // for command line programs, we need this exception to exit from // the infinite execution loop throw DebuggerClientExitException(); } } }
void DebuggerProxy::checkStop() { TRACE(2, "DebuggerProxy::checkStop\n"); if (m_stopped) { Debugger::RemoveProxy(shared_from_this()); m_thrift.close(); throw DebuggerClientExitException(); } }
void DebuggerProxy::processInterrupt(CmdInterrupt &cmd) { TRACE(2, "DebuggerProxy::processInterrupt\n"); // Do the server-side work for this cmd, which just notifies the client. if (!cmd.onServer(this)) { Debugger::RemoveProxy(shared_from_this()); // on socket error return; } // Wait for commands from the debugger client and process them. We'll stay // here until we get a command that should cause the thread to continue. while (true) { DebuggerCommandPtr res; while (!DebuggerCommand::Receive(m_thrift, res, "DebuggerProxy::processInterrupt()")) { // we will wait forever until DebuggerClient sends us something checkStop(); } checkStop(); if (res) { // Any control flow command gets installed here and we continue execution. m_flow = dynamic_pointer_cast<CmdFlowControl>(res); if (m_flow) { m_flow->onServer(this); processFlowControl(cmd); if (m_flow && m_threadMode == Normal) { switchThreadMode(Sticky); } return; } if (res->is(DebuggerCommand::KindOfQuit)) { Debugger::RemoveProxy(shared_from_this()); throw DebuggerClientExitException(); } } try { // Perform the server-side work for this command. if (!res || !res->onServer(this)) { Debugger::RemoveProxy(shared_from_this()); return; } } catch (const DebuggerException &e) { throw; } catch (...) { Logger::Error("onServer() throws non DebuggerException: %d", res->getType()); Debugger::RemoveProxy(shared_from_this()); return; } if (res->shouldExitInterrupt()) { return; } } }
// Primary entrypoint for the debugger from the VM. Called in response to a host // of VM events that the debugger is interested in. The debugger will execute // any logic needed to handle the event, and will block below this to wait for // and process more commands from the debugger client. This function will only // return when the debugger is letting the thread continue execution, e.g., for // flow control command like continue, next, etc. void Debugger::Interrupt(int type, const char *program, InterruptSite *site /* = NULL */, const char *error /* = NULL */) { assert(RuntimeOption::EnableDebugger); TRACE_RB(2, "Debugger::Interrupt type %d\n", type); DebuggerProxyPtr proxy = GetProxy(); if (proxy) { TRACE(3, "proxy != null\n"); RequestInjectionData &rjdata = ThreadInfo::s_threadInfo->m_reqInjectionData; // The proxy will only service an interrupt if we've previously setup some // form of flow control command (steps, breakpoints, etc.) or if it's // an interrupt related to something like the session or request. if (proxy->needInterrupt() || type != BreakPointReached) { // Interrupts may execute some PHP code, causing another interruption. std::stack<void *> &interrupts = rjdata.interrupts; CmdInterrupt cmd((InterruptType)type, program, site, error); interrupts.push(&cmd); proxy->interrupt(cmd); interrupts.pop(); } // Some cmds require us to interpret all instructions until the cmd // completes. Setting this will ensure we stay out of JIT code and in the // interpreter so phpDebuggerOpcodeHook has a chance to work. rjdata.setDebuggerIntr(proxy->needVMInterrupts()); } else { TRACE(3, "proxy == null\n"); // Debugger clients are disconnected abnormally, or this sandbox is not // being debugged. if (type == SessionStarted || type == SessionEnded) { // For command line programs, we need this exception to exit from // the infinite execution loop. throw DebuggerClientExitException(); } } }
void Debugger::Interrupt(int type, const char *program, InterruptSite *site /* = NULL */, const char *error /* = NULL */) { ASSERT(RuntimeOption::EnableDebugger); DebuggerProxyPtr proxy = GetProxy(); if (proxy) { // Interrupts may execute some PHP code, causing another interruption. void *&tint = ThreadInfo::s_threadInfo->m_reqInjectionData.interrupt; if (!tint) { CmdInterrupt cmd((InterruptType)type, program, site, error); tint = &cmd; proxy->interrupt(cmd); tint = NULL; } } else { // debugger clients are disconnected abnormally if (type == SessionStarted || type == SessionEnded) { // for command line programs, we need this exception to exit from // the infinite execution loop throw DebuggerClientExitException(); } } }
// Stop the proxy, and stop execution of the current request. void DebuggerProxy::stopAndThrow() { stop(); throw DebuggerClientExitException(); }