diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 123849eee..0ba167f96 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -2346,8 +2346,23 @@ Array VMExecutionContext::debugBacktrace(bool skip /* = false */, const char *filename = unit->filepath()->data(); assert(filename); frame.set(String(s_file), filename, true); + + // In the normal method case, the "saved pc" for line number printing is + // pointing at the cell conversion (Unbox/Pop) instruction, not the call + // itself. For multi-line calls, this instruction is associated with the + // subsequent line which results in an off-by-n. We're subtracting one + // in order to look up the line associated with the FCall/FCallArray + // instruction. Exception handling and the other opcodes (ex. BoxR) + // already do the right thing. The emitter associates object access with + // the subsequent expression and this would be difficult to modify. + Opcode* opAtSavedPc = (Opcode*)unit->at(pc); + assert(opAtSavedPc); + Offset pcAdjust = 0; + if (*opAtSavedPc == Op::OpPopR || *opAtSavedPc == Op::OpUnboxR) { + pcAdjust = 1; + } frame.set(String(s_line), - prevFp->m_func->unit()->getLineNumber(pc), true); + prevFp->m_func->unit()->getLineNumber(pc - pcAdjust), true); } // check for include String funcname = const_cast(fp->m_func->name()); diff --git a/hphp/runtime/vm/unit.cpp b/hphp/runtime/vm/unit.cpp index 5f8e5f242..225726370 100644 --- a/hphp/runtime/vm/unit.cpp +++ b/hphp/runtime/vm/unit.cpp @@ -1399,6 +1399,8 @@ Func* Unit::getMain(Class* cls /*= NULL*/) const { return f; } +// This uses range lookups so offsets in the middle of instructions are +// supported. int Unit::getLineNumber(Offset pc) const { LineEntry key = LineEntry(pc, -1); std::vector::const_iterator it = diff --git a/hphp/test/quick/debug_backtrace_multiline.php b/hphp/test/quick/debug_backtrace_multiline.php new file mode 100644 index 000000000..abcf7c65c --- /dev/null +++ b/hphp/test/quick/debug_backtrace_multiline.php @@ -0,0 +1,55 @@ +x($line, 1); +} + +// UnboxR +(new A())->x(__LINE__)-> + x(__LINE__)-> + x(__LINE__); + +// PopR +(new A())->x(__LINE__) + ; + +// BoxR +$var = &newInstanceOfA(__LINE__); +$var = + &newInstanceOfA(__LINE__) + ; +$var = + &newInstanceOfA(__LINE__); + +// handled exception +try { + throw new Exception(__LINE__); +} catch (Exception $e) { + compare_lines($e->getMessage(), $e->getLine()); +} + +// unhandled exception +throw new Exception(__LINE__); diff --git a/hphp/test/quick/debug_backtrace_multiline.php.expectf b/hphp/test/quick/debug_backtrace_multiline.php.expectf new file mode 100644 index 000000000..196d0df09 --- /dev/null +++ b/hphp/test/quick/debug_backtrace_multiline.php.expectf @@ -0,0 +1 @@ +HipHop Fatal error: Uncaught exception 'Exception' with message '55' in %s:55\nStack trace:\n#0 {main}