View Issue Details

IDProjectCategoryView StatusLast Update
0001165XdebugStep Debuggingpublic2020-03-12 16:51
Reporterunknownbrackets Assigned Toderick  
Status closedResolutionfixed 
Product Version2.3.3 
Fixed in Version2.5.1 
Summary0001165: Step Out skips subsequent function calls

Xdebug performs the "step out" or "finish" operation based on stack level, rather than function unit.

What this means is that when you Step Out from a function that is an argument, or a method that is part of a chain, it behaves incorrectly with respect to standard debugging behavior found in other languages.


Steps To Reproduce
  1. Create a source file with the following content:

function foo($arg)
echo $arg;

function bar()
return 42;

echo 6 * 9;


  1. Set a breakpoint on the line containing "foo(bar());" and begin debugging.

  2. When the breakpoint trips, execute a step_into, moving you inside bar().

  3. Next, execute a step_out. Expect to leave the "bar()" function unit, and enter the "foo()" function unit.

  4. Currently, observe that execution is moved to the "echo 6 * 9;" line instead.

Additional Information

I'm thinking of attempting a patch for this. My thinking is that this would require examining op_array, and potentially storing more info to determine a change in active function unit.

Would you accept such a patch? Do you expect it necessary to include an option to retain the old, but unusual, stepping behavior?

TagsNo tags attached.
Operating SystemAny
PHP Version5.6.5-5.6.9


has duplicate 0000842 resolvedderick Can't debug conditional statements without a block 



2015-07-04 01:00

reporter   ~0003145

Last edited: 2015-07-04 01:05

I suppose the easiest way would be to check op_array->function_name and op_array->scope->name. So something like this:

if (XG(context).do_finish) { / could still check level to try to skip some? /
int match = 0;
zend_string stmt_func_name = op_array->function_name;
stmt_scope_name = op_array->scope ? op_array->scope->name : NULL;
zend_string leave_func_name = XG(context).next_leave_function_name;
leave_scope_name = XG(context).next_leave_scope_name;

if (!stmt_func_name || !leave_func_name) {
match = stmt_func_name == leave_func_name;
} else if (zend_string_equals(stmt_func_name, leave_func_name)) {
if (!stmt_scope_name || !leave_scope_name) {
match = stmt_scope_name == leave_scope_name;
} else if (zend_string_equals(stmt_scope_name, leave_scope_name)) {
match = 1;

if (!match) {
/ break /

In a common case, I suppose the pointers would be identical, so it might not perform badly to simply refcount it. I'm not super familiar with the PHP source, though.

Also would need to check if that handles namespaces right.



2015-07-06 01:47

reporter   ~0003146

Hmm, closures also - line_start might be necessary.



2016-12-11 15:43

administrator   ~0003993

Fixed in Git for 2.5.1 by comparing the function numbers.

Issue History

Date Modified Username Field Change
2015-06-20 18:30 unknownbrackets New Issue
2015-07-04 01:00 unknownbrackets Note Added: 0003145
2015-07-04 01:05 unknownbrackets Note Edited: 0003145
2015-07-04 01:05 unknownbrackets Note Edited: 0003145
2015-07-06 01:47 unknownbrackets Note Added: 0003146
2016-12-11 15:23 derick Relationship added has duplicate 0000842
2016-12-11 15:43 derick Note Added: 0003993
2016-12-11 15:43 derick Status new => closed
2016-12-11 15:43 derick Assigned To => derick
2016-12-11 15:43 derick Resolution open => fixed
2016-12-11 15:43 derick Fixed in Version => 2.5.1
2020-03-12 16:51 derick Category Feature/Change request => Step Debugging