diff --git a/hphp/runtime/debugger/break_point.cpp b/hphp/runtime/debugger/break_point.cpp index 261d55cee..b13a95ba3 100644 --- a/hphp/runtime/debugger/break_point.cpp +++ b/hphp/runtime/debugger/break_point.cpp @@ -182,6 +182,7 @@ BreakPointInfo::BreakPointInfo(bool regex, State state, m_line1(0), m_line2(0), m_regex(regex), m_check(false) { TRACE(2, "BreakPointInfo::BreakPointInfo..const std::string &file)\n"); + assert(m_interrupt != ExceptionHandler); // Server-side only. if (m_interrupt == ExceptionThrown) { parseExceptionThrown(exp); if (!m_file.empty() || m_line1 || m_line2 || !m_funcs.empty()) { diff --git a/hphp/runtime/debugger/break_point.h b/hphp/runtime/debugger/break_point.h index c524e837e..eb4c6adef 100644 --- a/hphp/runtime/debugger/break_point.h +++ b/hphp/runtime/debugger/break_point.h @@ -31,6 +31,12 @@ enum InterruptType { HardBreakPoint, // From f_hphpd_break(). BreakPointReached, // Break from the VM interpreter loop ExceptionThrown, + + // Interrupts for exception handler entry are, for now, server-side only and + // only for flow control. We could consider exposing this to clients, and + // allowing it as a break point much like ExceptionThrown is, but the value + // seems quite low at this time. We assert this stays server-side. + ExceptionHandler, }; // Represents a site in the code, at the source level. diff --git a/hphp/runtime/debugger/cmd/cmd_interrupt.cpp b/hphp/runtime/debugger/cmd/cmd_interrupt.cpp index 226ed77ad..95c7ee58b 100644 --- a/hphp/runtime/debugger/cmd/cmd_interrupt.cpp +++ b/hphp/runtime/debugger/cmd/cmd_interrupt.cpp @@ -25,6 +25,7 @@ TRACE_SET_MOD(debugger); void CmdInterrupt::sendImpl(DebuggerThriftBuffer &thrift) { DebuggerCommand::sendImpl(thrift); + assert(m_interrupt != ExceptionHandler); // Server-side only. thrift.write(m_interrupt); thrift.write(m_program); thrift.write(m_errorMsg); @@ -307,6 +308,8 @@ bool CmdInterrupt::shouldBreak(const BreakPointInfoPtrVec &bps, } } return !m_matched.empty(); + case ExceptionHandler: + return false; // For flow control only at this time. } assert(false); return false; diff --git a/hphp/runtime/debugger/cmd/cmd_next.cpp b/hphp/runtime/debugger/cmd/cmd_next.cpp index 2036b1f0e..e0830fb14 100644 --- a/hphp/runtime/debugger/cmd/cmd_next.cpp +++ b/hphp/runtime/debugger/cmd/cmd_next.cpp @@ -16,6 +16,8 @@ #include "hphp/runtime/debugger/cmd/cmd_next.h" #include "hphp/runtime/vm/debugger_hook.h" +#include "hphp/runtime/vm/runtime.h" +#include "hphp/runtime/ext/ext_continuation.h" namespace HPHP { namespace Eval { /////////////////////////////////////////////////////////////////////////////// @@ -54,14 +56,6 @@ void CmdNext::onSetup(DebuggerProxy& proxy, CmdInterrupt& interrupt) { void CmdNext::onBeginInterrupt(DebuggerProxy& proxy, CmdInterrupt& interrupt) { TRACE(2, "CmdNext::onBeginInterrupt\n"); assert(!m_complete); // Complete cmds should not be asked to do work. - if (interrupt.getInterruptType() == ExceptionThrown) { - // If an exception is thrown we turn interrupts on to ensure we stop when - // control reaches the first catch clause. - TRACE(2, "CmdNext: exception thrown\n"); - removeLocationFilter(); - m_needsVMInterrupt = true; - return; - } ActRec *fp = g_vmContext->getFP(); assert(fp); // All interrupts which reach a flow cmd have an AR. @@ -89,6 +83,8 @@ void CmdNext::onBeginInterrupt(DebuggerProxy& proxy, CmdInterrupt& interrupt) { deeper = true; } + m_needsVMInterrupt = false; // Will be set again below if still needed. + // First consider if we've got internal breakpoints setup. These are used when // we can make an accurate prediction of where execution should flow, // eventually, and when we want to let the program run normally until we get @@ -99,22 +95,55 @@ void CmdNext::onBeginInterrupt(DebuggerProxy& proxy, CmdInterrupt& interrupt) { if (deeper) return; // Recursion TRACE(2, "CmdNext: hit step-out\n"); } else if (atStepContOffset(unit, offset)) { - // For step-conts we want to hit the exact same frame, not a call to the - // same function higher or lower on the stack. - if (!originalDepth) return; + // For step-conts we want to hit the exact same frame, for the same + // continuation, not a call to the same function higher or lower on the + // stack. + if (!originalDepth || (m_stepContTag != getContinuationTag(fp))) return; TRACE(2, "CmdNext: hit step-cont\n"); + } else if (interrupt.getInterruptType() == ExceptionHandler) { + // Entering an exception handler may take us someplace we weren't + // expecting. Adjust internal breakpoints accordingly. First case is easy. + if (deeper) { + TRACE(2, "CmdNext: exception handler, deeper\n"); + return; + } + // For step-conts, we ignore handlers at the original level if we're not + // in the original continuation. We don't care about exception handlers + // in continuations being driven at the same level. + if (hasStepCont() && originalDepth && + (m_stepContTag != getContinuationTag(fp))) { + TRACE(2, "CmdNext: exception handler, original depth, wrong cont\n"); + return; + } + // Sometimes we have handlers in generated code, i.e., Continuation::next. + // These just help propagate exceptions so ignore those. + if (fp->m_func->line1() == 0) { + TRACE(2, "CmdNext: exception handler, ignoring func with no source\n"); + return; + } + TRACE(2, "CmdNext: exception handler altering expected flow\n"); } else { // We have internal breakpoints setup, but we haven't hit one yet. Keep // running until we reach one. TRACE(2, "CmdNext: waiting to hit internal breakpoint...\n"); return; } - // We've hit one internal breakpoint at a useful place, so we can remove - // them all now. + // We've hit one internal breakpoint at a useful place, or decided we don't, + // need them, so we can remove them all now. cleanupStepOuts(); cleanupStepCont(); } + if (interrupt.getInterruptType() == ExceptionHandler) { + // If we're about to enter an exception handler we turn interrupts on to + // ensure we stop when control reaches the handler. The normal logic below + // will decide if we're done at that point or not. + TRACE(2, "CmdNext: exception handler\n"); + removeLocationFilter(); + m_needsVMInterrupt = true; + return; + } + if (deeper) { TRACE(2, "CmdNext: deeper, setup step out to get back to original line\n"); setupStepOuts(); @@ -124,7 +153,6 @@ void CmdNext::onBeginInterrupt(DebuggerProxy& proxy, CmdInterrupt& interrupt) { // opcode executed while it's installed and that's bad if there's a lot of // code called from that line. removeLocationFilter(); - m_needsVMInterrupt = false; return; } @@ -162,7 +190,6 @@ void CmdNext::stepCurrentLine(CmdInterrupt& interrupt, ActRec* fp, PC pc) { setupStepCont(fp, pc); } removeLocationFilter(); - m_needsVMInterrupt = false; return; } @@ -192,6 +219,7 @@ void CmdNext::setupStepCont(ActRec* fp, PC pc) { Offset nextInst = fp->m_func->unit()->offsetOf(pc + 4); m_stepContUnit = fp->m_func->unit(); m_stepContOffset = nextInst; + m_stepContTag = getContinuationTag(fp); TRACE(2, "CmdNext: patch for cont step at '%s' offset %d\n", fp->m_func->fullName()->data(), nextInst); phpAddBreakPoint(m_stepContUnit, m_stepContOffset); @@ -203,9 +231,25 @@ void CmdNext::cleanupStepCont() { phpRemoveBreakPoint(m_stepContUnit, m_stepContOffset); m_stepContOffset = InvalidAbsoluteOffset; } + m_stepContTag = nullptr; m_stepContUnit = nullptr; } } +// Use the address of the c_Continuation object as a tag for this stepping +// operation, to ensure we only stop once we're back to the same continuation. +// Since we'll either stop when we get out of whatever is driving this +// continuation, or we'll stop when we get back into it, we know the object +// will remain alive. +void* CmdNext::getContinuationTag(ActRec* fp) { + TypedValue* tv = frame_local(fp, 0); + assert(tv->m_type == HPHP::KindOfObject); + assert(dynamic_cast(tv->m_data.pobj)); + c_Continuation* cont = static_cast(tv->m_data.pobj); + TRACE(2, "CmdNext: continuation tag %p for %s\n", cont, + cont->t_getorigfuncname()->data()); + return cont; +} + /////////////////////////////////////////////////////////////////////////////// }} diff --git a/hphp/runtime/debugger/cmd/cmd_next.h b/hphp/runtime/debugger/cmd/cmd_next.h index 3f3782638..3c3897d7e 100644 --- a/hphp/runtime/debugger/cmd/cmd_next.h +++ b/hphp/runtime/debugger/cmd/cmd_next.h @@ -27,7 +27,8 @@ class CmdNext : public CmdFlowControl { public: CmdNext() : CmdFlowControl(KindOfNext), m_stepContUnit(nullptr), - m_stepContOffset(InvalidAbsoluteOffset) {} + m_stepContOffset(InvalidAbsoluteOffset), + m_stepContTag(nullptr) {} virtual ~CmdNext(); virtual void help(DebuggerClient& client); @@ -40,9 +41,11 @@ private: bool atStepContOffset(Unit* unit, Offset o); void setupStepCont(ActRec* fp, PC pc); void cleanupStepCont(); + void* getContinuationTag(ActRec* fp); HPHP::Unit* m_stepContUnit; Offset m_stepContOffset; + void* m_stepContTag; // Unique identifier for the continuation we're stepping }; /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/debugger/cmd/cmd_out.cpp b/hphp/runtime/debugger/cmd/cmd_out.cpp index 17e94bd37..b09da1eb1 100644 --- a/hphp/runtime/debugger/cmd/cmd_out.cpp +++ b/hphp/runtime/debugger/cmd/cmd_out.cpp @@ -45,22 +45,27 @@ void CmdOut::onSetup(DebuggerProxy &proxy, CmdInterrupt &interrupt) { } void CmdOut::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) { - TRACE(2, "CmdNext::onBeginInterrupt\n"); + TRACE(2, "CmdOut::onBeginInterrupt\n"); assert(!m_complete); // Complete cmds should not be asked to do work. - if (interrupt.getInterruptType() == ExceptionThrown) { - // If an exception is thrown we turn interrupts on to ensure we stop when - // control reaches the first catch clause. - removeLocationFilter(); - m_needsVMInterrupt = true; - return; - } + m_needsVMInterrupt = false; int currentVMDepth = g_vmContext->m_nesting; int currentStackDepth = proxy.getStackDepth(); // Deeper or same depth? Keep running. if ((currentVMDepth > m_vmDepth) || ((currentVMDepth == m_vmDepth) && (currentStackDepth >= m_stackDepth))) { + TRACE(2, "CmdOut: deeper, keep running...\n"); + return; + } + + if (interrupt.getInterruptType() == ExceptionHandler) { + // If we're about to enter an exception handler we turn interrupts on to + // ensure we stop when control reaches the handler. The normal logic below + // will decide if we're done at that point or not. + TRACE(2, "CmdOut: exception thrown\n"); + removeLocationFilter(); + m_needsVMInterrupt = true; return; } @@ -71,7 +76,6 @@ void CmdOut::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) { TRACE(2, "CmdOut: not complete, step out again.\n"); onSetup(proxy, interrupt); } - m_needsVMInterrupt = false; } /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/debugger/cmd/cmd_step.cpp b/hphp/runtime/debugger/cmd/cmd_step.cpp index cfdf8ad75..ae39ee088 100644 --- a/hphp/runtime/debugger/cmd/cmd_step.cpp +++ b/hphp/runtime/debugger/cmd/cmd_step.cpp @@ -47,6 +47,9 @@ void CmdStep::onSetup(DebuggerProxy &proxy, CmdInterrupt &interrupt) { } void CmdStep::onBeginInterrupt(DebuggerProxy &proxy, CmdInterrupt &interrupt) { + // Step doesn't care about this interrupt... we just stay the course and + // keep stepping. + if (interrupt.getInterruptType() == ExceptionHandler) return; m_complete = (decCount() == 0); if (!m_complete) { installLocationFilterForLine(interrupt.getSite()); diff --git a/hphp/runtime/debugger/debugger.cpp b/hphp/runtime/debugger/debugger.cpp index 1d6b32267..e18df27be 100644 --- a/hphp/runtime/debugger/debugger.cpp +++ b/hphp/runtime/debugger/debugger.cpp @@ -283,6 +283,7 @@ void Debugger::Interrupt(int type, const char *program, // response to various events. While this function is quite general wrt. the // type of interrupt, practically the type will be one of the following: // - ExceptionThrown +// - ExceptionHandler // - BreakPointReached // - HardBreakPoint // @@ -618,6 +619,7 @@ const char *Debugger::InterruptTypeName(CmdInterrupt &cmd) { case HardBreakPoint: return "HardBreakPoint"; case BreakPointReached: return "BreakPointReached"; case ExceptionThrown: return "ExceptionThrown"; + case ExceptionHandler: return "ExceptionHandler"; default: return "unknown"; } diff --git a/hphp/runtime/debugger/debugger_proxy.cpp b/hphp/runtime/debugger/debugger_proxy.cpp index ebc99bed6..88c72688e 100644 --- a/hphp/runtime/debugger/debugger_proxy.cpp +++ b/hphp/runtime/debugger/debugger_proxy.cpp @@ -542,7 +542,7 @@ bool DebuggerProxy::checkFlowBreak(CmdInterrupt &cmd) { if ((cmd.getInterruptType() == BreakPointReached || cmd.getInterruptType() == HardBreakPoint || - cmd.getInterruptType() == ExceptionThrown) && m_flow) { + cmd.getInterruptType() == ExceptionHandler) && m_flow) { if (!m_flow->is(DebuggerCommand::KindOfContinue)) { m_flow->onBeginInterrupt(*this, cmd); if (m_flow->complete()) { diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 4d59b268c..5b38a7ad6 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -912,6 +912,7 @@ UnwindStatus Stack::unwindFrag(ActRec* fp, int offset, func->unit()->offsetOf(pc)); fault.m_savedRaiseOffset = func->unit()->offsetOf(pc); pc = (uchar*)(func->unit()->entry() + eh->m_fault); + DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindResumeVM; case EHEnt::EHType_Catch: // Note: we skip catch clauses if we have a pending C++ exception @@ -929,6 +930,7 @@ UnwindStatus Stack::unwindFrag(ActRec* fp, int offset, if (cls && obj->instanceof(cls)) { pc = handler; FTRACE(1, "unwindFrag: entering catch at {}\n", pc); + DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindResumeVM; } } @@ -4627,7 +4629,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopThrow(PC& pc) { Object obj(c1->m_data.pobj); m_stack.popC(); - DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHook(obj.get())); + DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(obj.get())); throw obj; } diff --git a/hphp/runtime/vm/debugger_hook.cpp b/hphp/runtime/vm/debugger_hook.cpp index 10416aa1b..35dbd8586 100644 --- a/hphp/runtime/vm/debugger_hook.cpp +++ b/hphp/runtime/vm/debugger_hook.cpp @@ -67,14 +67,26 @@ void phpDebuggerOpcodeHook(const uchar* pc) { // Hook called from iopThrow to signal that we are about to throw an exception. // NB: this does not hook any portion of exception unwind. -void phpDebuggerExceptionHook(ObjectData* e) { - TRACE(5, "in phpDebuggerExceptionHook()\n"); +void phpDebuggerExceptionThrownHook(ObjectData* e) { + TRACE(5, "in phpDebuggerExceptionThrownHook()\n"); if (UNLIKELY(g_vmContext->m_dbgNoBreak)) { TRACE(5, "NoBreak flag is on\n"); return; } Eval::Debugger::InterruptVMHook(Eval::ExceptionThrown, e); - TRACE(5, "out phpDebuggerExceptionHook()\n"); + TRACE(5, "out phpDebuggerExceptionThrownHook()\n"); +} + +// Hook called from exception unwind to signal that we are about to handle an +// exception. +void phpDebuggerExceptionHandlerHook() { + TRACE(5, "in phpDebuggerExceptionHandlerHook()\n"); + if (UNLIKELY(g_vmContext->m_dbgNoBreak)) { + TRACE(5, "NoBreak flag is on\n"); + return; + } + Eval::Debugger::InterruptVMHook(Eval::ExceptionHandler); + TRACE(5, "out phpDebuggerExceptionHandlerHook()\n"); } bool isDebuggerAttachedProcess() { diff --git a/hphp/runtime/vm/debugger_hook.h b/hphp/runtime/vm/debugger_hook.h index 6b093ab04..8b34c0a1a 100644 --- a/hphp/runtime/vm/debugger_hook.h +++ b/hphp/runtime/vm/debugger_hook.h @@ -39,7 +39,8 @@ namespace HPHP { // debugging to give the debugger a chance to act. The debugger may block // execution indefinitely within one of these hooks. void phpDebuggerOpcodeHook(const uchar* pc); -void phpDebuggerExceptionHook(ObjectData* e); +void phpDebuggerExceptionThrownHook(ObjectData* e); +void phpDebuggerExceptionHandlerHook(); void phpDebuggerEvalHook(const Func* f); void phpDebuggerFileLoadHook(Eval::PhpFile* efile); class Class; diff --git a/hphp/test/quick/debugger/flow_excep.php b/hphp/test/quick/debugger/flow_excep.php new file mode 100644 index 000000000..03cbadf8b --- /dev/null +++ b/hphp/test/quick/debugger/flow_excep.php @@ -0,0 +1,64 @@ +getMessage()); + $x++; + } + return $x; +} + +function throwNoCatch($a) { + $x = $a + 1; + $e = new Ex1('Thrown from throwNoCatch '.$x); + throw $e; +} + +function throwFromCallee($a) { + $x = $a + 1; + try { + throwNoCatch($x); + } catch (Ex1 $e) { + printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + $x++; + } + return $x; +} + +function foo($a) { + $x = $a + 1; + $x = throwAndCatch($x); + try { + $x = throwAndCatch($x); + throw new Ex1('Thrown from foo '.$x); + } catch (Exception $e) { + printf("Caught %s in foo()\n", $e->getMessage()); + $x = throwAndCatch($x); + $x++; + } + return $x; +} + +function main() { + $x = throwFromCallee(1); + var_dump($x); + + $x = foo(2); + var_dump($x); +} + +main(); + + diff --git a/hphp/test/quick/debugger/flow_excep.php.expectf b/hphp/test/quick/debugger/flow_excep.php.expectf new file mode 100644 index 000000000..09a5ad03a --- /dev/null +++ b/hphp/test/quick/debugger/flow_excep.php.expectf @@ -0,0 +1,469 @@ +Program %s/flow_excep.php loaded. Type '[r]un' or '[c]ontinue' to go. +break main() +Breakpoint 1 set upon entering main() +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +next +Caught Thrown from throwNoCatch 3 in throwFromCallee() +Break at main() on line 56 of %s/flow_excep.php + 55 $x = throwFromCallee(1); + 56 var_dump($x); + 57 + +next +int(3) +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +next +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +Break at main() on line 59 of %s/flow_excep.php + 58 $x = foo(2); + 59 var_dump($x); + 60 } + +continue +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +step +Break at throwFromCallee() on line 30 of %s/flow_excep.php + 29 function throwFromCallee($a) { + 30 $x = $a + 1; + 31 try { + +out +Caught Thrown from throwNoCatch 3 in throwFromCallee() +Break at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +next 2 +int(3) +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +step +Break at foo() on line 41 of %s/flow_excep.php + 40 function foo($a) { + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + +next +Break at foo() on line 42 of %s/flow_excep.php + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + 43 try { + +step +Break at throwAndCatch() on line 13 of %s/flow_excep.php + 12 function throwAndCatch($a) { + 13 $x = $a + 1; + 14 try { + +out +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Break at foo() on line 42 of %s/flow_excep.php + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + 43 try { + +out +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +continue +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +step +Break at throwFromCallee() on line 30 of %s/flow_excep.php + 29 function throwFromCallee($a) { + 30 $x = $a + 1; + 31 try { + +next +Break at throwFromCallee() on line 32 of %s/flow_excep.php + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + +next +Break at throwFromCallee() on line 31 of %s/flow_excep.php + 30 $x = $a + 1; + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + 36 } + 37 return $x; + +next +Break at throwFromCallee() on line 34 of %s/flow_excep.php + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + +next +Caught Thrown from throwNoCatch 3 in throwFromCallee() +Break at throwFromCallee() on line 35 of %s/flow_excep.php + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + 36 } + +continue +int(3) +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +step +Break at throwFromCallee() on line 30 of %s/flow_excep.php + 29 function throwFromCallee($a) { + 30 $x = $a + 1; + 31 try { + +next +Break at throwFromCallee() on line 32 of %s/flow_excep.php + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + +step +Break at throwNoCatch() on line 24 of %s/flow_excep.php + 23 function throwNoCatch($a) { + 24 $x = $a + 1; + 25 $e = new Ex1('Thrown from throwNoCatch '.$x); + +out +Break at throwFromCallee() on line 31 of %s/flow_excep.php + 30 $x = $a + 1; + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + 36 } + 37 return $x; + +next +Break at throwFromCallee() on line 34 of %s/flow_excep.php + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + +out +Caught Thrown from throwNoCatch 3 in throwFromCallee() +Break at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +continue +int(3) +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +step +Break at throwFromCallee() on line 30 of %s/flow_excep.php + 29 function throwFromCallee($a) { + 30 $x = $a + 1; + 31 try { + +next +Break at throwFromCallee() on line 32 of %s/flow_excep.php + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + +step +Break at throwNoCatch() on line 24 of %s/flow_excep.php + 23 function throwNoCatch($a) { + 24 $x = $a + 1; + 25 $e = new Ex1('Thrown from throwNoCatch '.$x); + +next +Break at throwNoCatch() on line 25 of %s/flow_excep.php + 24 $x = $a + 1; + 25 $e = new Ex1('Thrown from throwNoCatch '.$x); + 26 throw $e; + +next +Break at throwNoCatch() on line 26 of %s/flow_excep.php + 25 $e = new Ex1('Thrown from throwNoCatch '.$x); + 26 throw $e; + 27 } + +step +Break at throwFromCallee() on line 31 of %s/flow_excep.php + 30 $x = $a + 1; + 31 try { + 32 throwNoCatch($x); + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + 36 } + 37 return $x; + +step +Break at throwFromCallee() on line 34 of %s/flow_excep.php + 33 } catch (Ex1 $e) { + 34 printf("Caught %s in throwFromCallee()\n", $e->getMessage()); + 35 $x++; + +continue +Caught Thrown from throwNoCatch 3 in throwFromCallee() +int(3) +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +next 2 +Caught Thrown from throwNoCatch 3 in throwFromCallee() +int(3) +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +step +Break at foo() on line 41 of %s/flow_excep.php + 40 function foo($a) { + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + +next 2 +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Break at foo() on line 44 of %s/flow_excep.php + 43 try { + 44 $x = throwAndCatch($x); + 45 throw new Ex1('Thrown from foo '.$x); + +step +Break at throwAndCatch() on line 13 of %s/flow_excep.php + 12 function throwAndCatch($a) { + 13 $x = $a + 1; + 14 try { + +next +Break at throwAndCatch() on line 15 of %s/flow_excep.php + 14 try { + 15 throw new Ex2('Thrown from throwAndCatch '.$x); + 16 } catch (Ex2 $e) { + +next +Break at throwAndCatch() on line 14 of %s/flow_excep.php + 13 $x = $a + 1; + 14 try { + 15 throw new Ex2('Thrown from throwAndCatch '.$x); + 16 } catch (Ex2 $e) { + 17 printf("Caught %s in throwAndCatch()\n", $e->getMessage()); + 18 $x++; + 19 } + 20 return $x; + +next +Break at throwAndCatch() on line 17 of %s/flow_excep.php + 16 } catch (Ex2 $e) { + 17 printf("Caught %s in throwAndCatch()\n", $e->getMessage()); + 18 $x++; + +out +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Break at foo() on line 44 of %s/flow_excep.php + 43 try { + 44 $x = throwAndCatch($x); + 45 throw new Ex1('Thrown from foo '.$x); + +next +Break at foo() on line 45 of %s/flow_excep.php + 44 $x = throwAndCatch($x); + 45 throw new Ex1('Thrown from foo '.$x); + 46 } catch (Exception $e) { + +next +Break at foo() on line 43 of %s/flow_excep.php + 42 $x = throwAndCatch($x); + 43 try { + 44 $x = throwAndCatch($x); + 45 throw new Ex1('Thrown from foo '.$x); + 46 } catch (Exception $e) { + 47 printf("Caught %s in foo()\n", $e->getMessage()); + 48 $x = throwAndCatch($x); + 49 $x++; + 50 } + 51 return $x; + +next +Break at foo() on line 47 of %s/flow_excep.php + 46 } catch (Exception $e) { + 47 printf("Caught %s in foo()\n", $e->getMessage()); + 48 $x = throwAndCatch($x); + +out +Caught Thrown from foo 7 in foo() +Caught Thrown from throwAndCatch 8 in throwAndCatch() +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +continue +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +next 2 +Caught Thrown from throwNoCatch 3 in throwFromCallee() +int(3) +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +step +Break at foo() on line 41 of %s/flow_excep.php + 40 function foo($a) { + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + +next 6 +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Break at foo() on line 48 of %s/flow_excep.php + 47 printf("Caught %s in foo()\n", $e->getMessage()); + 48 $x = throwAndCatch($x); + 49 $x++; + +next +Caught Thrown from throwAndCatch 8 in throwAndCatch() +Break at foo() on line 49 of %s/flow_excep.php + 48 $x = throwAndCatch($x); + 49 $x++; + 50 } + +continue +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +next 2 +Caught Thrown from throwNoCatch 3 in throwFromCallee() +int(3) +Break at main() on line 58 of %s/flow_excep.php + 57 + 58 $x = foo(2); + 59 var_dump($x); + +step +Break at foo() on line 41 of %s/flow_excep.php + 40 function foo($a) { + 41 $x = $a + 1; + 42 $x = throwAndCatch($x); + +next 6 +Caught Thrown from throwAndCatch 4 in throwAndCatch() +Caught Thrown from throwAndCatch 6 in throwAndCatch() +Caught Thrown from foo 7 in foo() +Break at foo() on line 48 of %s/flow_excep.php + 47 printf("Caught %s in foo()\n", $e->getMessage()); + 48 $x = throwAndCatch($x); + 49 $x++; + +step +Break at throwAndCatch() on line 13 of %s/flow_excep.php + 12 function throwAndCatch($a) { + 13 $x = $a + 1; + 14 try { + +next +Break at throwAndCatch() on line 15 of %s/flow_excep.php + 14 try { + 15 throw new Ex2('Thrown from throwAndCatch '.$x); + 16 } catch (Ex2 $e) { + +out +Caught Thrown from throwAndCatch 8 in throwAndCatch() +Break at foo() on line 48 of %s/flow_excep.php + 47 printf("Caught %s in foo()\n", $e->getMessage()); + 48 $x = throwAndCatch($x); + 49 $x++; + +next +Break at foo() on line 49 of %s/flow_excep.php + 48 $x = throwAndCatch($x); + 49 $x++; + 50 } + +continue +int(10) +Program %s/flow_excep.php exited normally. +run +Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php + 54 function main() { + 55 $x = throwFromCallee(1); + 56 var_dump($x); + +quit diff --git a/hphp/test/quick/debugger/flow_excep.php.in b/hphp/test/quick/debugger/flow_excep.php.in new file mode 100644 index 000000000..a3f5a7063 --- /dev/null +++ b/hphp/test/quick/debugger/flow_excep.php.in @@ -0,0 +1,71 @@ +break main() +run +next +next +next +continue +run +step +out +next 2 +step +next +step +out +out +continue +run +step +next +next +next +next +continue +run +step +next +step +out +next +out +continue +run +step +next +step +next +next +step +step +continue +run +next 2 +step +next 2 +step +next +next +next +out +next +next +next +out +continue +run +next 2 +step +next 6 +next +continue +run +next 2 +step +next 6 +step +next +out +next +continue +run +quit diff --git a/hphp/test/quick/debugger/flow_excep.php.norepo b/hphp/test/quick/debugger/flow_excep.php.norepo new file mode 100644 index 000000000..e69de29bb diff --git a/hphp/test/quick/debugger/flow_excep.php.opts b/hphp/test/quick/debugger/flow_excep.php.opts new file mode 100644 index 000000000..cf014a32f --- /dev/null +++ b/hphp/test/quick/debugger/flow_excep.php.opts @@ -0,0 +1 @@ + -m debug \ No newline at end of file diff --git a/hphp/test/quick/debugger/flow_gen_excep.php b/hphp/test/quick/debugger/flow_gen_excep.php new file mode 100644 index 000000000..2c88a6eeb --- /dev/null +++ b/hphp/test/quick/debugger/flow_gen_excep.php @@ -0,0 +1,41 @@ +getMessage()); + } + yield $a+1; + error_log('Finished in genFoo'); +} + +function foo($a) { + $gen1 = genFoo($a); + $gen1->next(); + while ($gen1->valid()) { + $val = $gen1->current(); + var_dump($val); + try { + $gen1->raise(new Exception('Exception given to continuation '.$val)); + } catch (Exception $e) { + printf("Caught %s in foo()\n", $e->getMessage()); + } + } +} + +function main($a) { + foo($a); +} + +main(1); + diff --git a/hphp/test/quick/debugger/flow_gen_excep.php.expectf b/hphp/test/quick/debugger/flow_gen_excep.php.expectf new file mode 100644 index 000000000..a0ec209dd --- /dev/null +++ b/hphp/test/quick/debugger/flow_gen_excep.php.expectf @@ -0,0 +1,102 @@ +Program %s/flow_gen_excep.php loaded. Type '[r]un' or '[c]ontinue' to go. +run +int(8) +Caught Exception given to continuation 8 in genFoo() +int(4) +Caught Exception given to continuation 4 in foo() +Program %s/flow_gen_excep.php exited normally. +break foo() +Breakpoint 1 set upon entering foo() +next +Program %s/flow_gen_excep.php loaded. Type '[r]un' or '[c]ontinue' to go. +next +Breakpoint 1 reached at foo() on line 23 of %s/flow_gen_excep.php + 22 function foo($a) { + 23 $gen1 = genFoo($a); + 24 $gen1->next(); + +next +Break at foo() on line 24 of %s/flow_gen_excep.php + 23 $gen1 = genFoo($a); + 24 $gen1->next(); + 25 while ($gen1->valid()) { + +next +Break at foo() on line 25 of %s/flow_gen_excep.php + 24 $gen1->next(); + 25 while ($gen1->valid()) { + 26 $val = $gen1->current(); + +next +Break at foo() on line 26 of %s/flow_gen_excep.php + 25 while ($gen1->valid()) { + 26 $val = $gen1->current(); + 27 var_dump($val); + +next +Break at foo() on line 27 of %s/flow_gen_excep.php + 26 $val = $gen1->current(); + 27 var_dump($val); + 28 try { + +next +int(8) +Break at foo() on line 29 of %s/flow_gen_excep.php + 28 try { + 29 $gen1->raise(new Exception('Exception given to continuation '.$val)); + 30 } catch (Exception $e) { + +next +Caught Exception given to continuation 8 in genFoo() +Break at foo() on line 25 of %s/flow_gen_excep.php + 24 $gen1->next(); + 25 while ($gen1->valid()) { + 26 $val = $gen1->current(); + +next +Break at foo() on line 26 of %s/flow_gen_excep.php + 25 while ($gen1->valid()) { + 26 $val = $gen1->current(); + 27 var_dump($val); + +next +Break at foo() on line 27 of %s/flow_gen_excep.php + 26 $val = $gen1->current(); + 27 var_dump($val); + 28 try { + +next +int(4) +Break at foo() on line 29 of %s/flow_gen_excep.php + 28 try { + 29 $gen1->raise(new Exception('Exception given to continuation '.$val)); + 30 } catch (Exception $e) { + +next +Break at foo() on line 28 of %s/flow_gen_excep.php + 27 var_dump($val); + 28 try { + 29 $gen1->raise(new Exception('Exception given to continuation '.$val)); + 30 } catch (Exception $e) { + 31 printf("Caught %s in foo()\n", $e->getMessage()); + 32 } + 33 } + +next +Break at foo() on line 31 of %s/flow_gen_excep.php + 30 } catch (Exception $e) { + 31 printf("Caught %s in foo()\n", $e->getMessage()); + 32 } + +next +Caught Exception given to continuation 4 in foo() +Break at foo() on line 25 of %s/flow_gen_excep.php + 24 $gen1->next(); + 25 while ($gen1->valid()) { + 26 $val = $gen1->current(); + +break clear all +All breakpoints are cleared. +continue +Program %s/flow_gen_excep.php exited normally. +quit diff --git a/hphp/test/quick/debugger/flow_gen_excep.php.in b/hphp/test/quick/debugger/flow_gen_excep.php.in new file mode 100644 index 000000000..114b0e600 --- /dev/null +++ b/hphp/test/quick/debugger/flow_gen_excep.php.in @@ -0,0 +1,19 @@ +run +break foo() +next +next +next +next +next +next +next +next +next +next +next +next +next +next +break clear all +continue +quit diff --git a/hphp/test/quick/debugger/flow_gen_excep.php.norepo b/hphp/test/quick/debugger/flow_gen_excep.php.norepo new file mode 100644 index 000000000..e69de29bb diff --git a/hphp/test/quick/debugger/flow_gen_excep.php.opts b/hphp/test/quick/debugger/flow_gen_excep.php.opts new file mode 100644 index 000000000..cf014a32f --- /dev/null +++ b/hphp/test/quick/debugger/flow_gen_excep.php.opts @@ -0,0 +1 @@ + -m debug \ No newline at end of file