mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Improve fiber backtraces
The start/resume/throw execute_data is now attached as the prev_execute_data to the bottom frame of the fiber stack when the fiber is running.
This commit is contained in:
54
Zend/tests/fibers/backtrace-deep-nesting.phpt
Normal file
54
Zend/tests/fibers/backtrace-deep-nesting.phpt
Normal file
@@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
Backtrace in deeply nested function call
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function suspend_fiber(int $level): void
|
||||
{
|
||||
if ($level >= 10) {
|
||||
$value = \Fiber::suspend($level);
|
||||
failing_function($value);
|
||||
}
|
||||
|
||||
suspend_fiber($level + 1);
|
||||
}
|
||||
|
||||
function failing_function(string $value): never
|
||||
{
|
||||
throw_exception();
|
||||
}
|
||||
|
||||
function throw_exception(): never
|
||||
{
|
||||
throw new Exception;
|
||||
}
|
||||
|
||||
$fiber = new Fiber(function (): void {
|
||||
suspend_fiber(0);
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
|
||||
$fiber->resume('test');
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception in %sbacktrace-deep-nesting.php:%d
|
||||
Stack trace:
|
||||
#0 %sbacktrace-deep-nesting.php(%d): throw_exception()
|
||||
#1 %sbacktrace-deep-nesting.php(%d): failing_function('test')
|
||||
#2 %sbacktrace-deep-nesting.php(%d): suspend_fiber(10)
|
||||
#3 %sbacktrace-deep-nesting.php(%d): suspend_fiber(9)
|
||||
#4 %sbacktrace-deep-nesting.php(%d): suspend_fiber(8)
|
||||
#5 %sbacktrace-deep-nesting.php(%d): suspend_fiber(7)
|
||||
#6 %sbacktrace-deep-nesting.php(%d): suspend_fiber(6)
|
||||
#7 %sbacktrace-deep-nesting.php(%d): suspend_fiber(5)
|
||||
#8 %sbacktrace-deep-nesting.php(%d): suspend_fiber(4)
|
||||
#9 %sbacktrace-deep-nesting.php(%d): suspend_fiber(3)
|
||||
#10 %sbacktrace-deep-nesting.php(%d): suspend_fiber(2)
|
||||
#11 %sbacktrace-deep-nesting.php(%d): suspend_fiber(1)
|
||||
#12 %sbacktrace-deep-nesting.php(%d): suspend_fiber(0)
|
||||
#13 [internal function]: {closure}()
|
||||
#14 %sbacktrace-deep-nesting.php(%d): Fiber->resume('test')
|
||||
#15 {main}
|
||||
thrown in %sbacktrace-deep-nesting.php on line %d
|
||||
28
Zend/tests/fibers/backtrace-nested.phpt
Normal file
28
Zend/tests/fibers/backtrace-nested.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
Backtrace in nested function call
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function suspend_fiber(): void
|
||||
{
|
||||
\Fiber::suspend();
|
||||
throw new Exception;
|
||||
}
|
||||
|
||||
$fiber = new Fiber(function (): void {
|
||||
suspend_fiber();
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
|
||||
$fiber->resume();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception in %sbacktrace-nested.php:%d
|
||||
Stack trace:
|
||||
#0 %sbacktrace-nested.php(%d): suspend_fiber()
|
||||
#1 [internal function]: {closure}()
|
||||
#2 %sbacktrace-nested.php(%d): Fiber->resume()
|
||||
#3 {main}
|
||||
thrown in %sbacktrace-nested.php on line %d
|
||||
28
Zend/tests/fibers/backtrace-object.phpt
Normal file
28
Zend/tests/fibers/backtrace-object.phpt
Normal file
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
Backtrace in with object as fiber callback
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Test
|
||||
{
|
||||
public function __invoke(string $arg): void
|
||||
{
|
||||
Fiber::suspend();
|
||||
throw new Exception($arg);
|
||||
}
|
||||
}
|
||||
|
||||
$fiber = new Fiber(new Test);
|
||||
|
||||
$fiber->start('test');
|
||||
|
||||
$fiber->resume();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception: test in %sbacktrace-object.php:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: Test->__invoke('test')
|
||||
#1 %sbacktrace-object.php(%d): Fiber->resume()
|
||||
#2 {main}
|
||||
thrown in %sbacktrace-object.php on line %d
|
||||
21
Zend/tests/fibers/debug-backtrace.phpt
Normal file
21
Zend/tests/fibers/debug-backtrace.phpt
Normal file
@@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Print backtrace in fiber
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function inner_function(): void
|
||||
{
|
||||
debug_print_backtrace();
|
||||
}
|
||||
|
||||
$fiber = new Fiber(function (): void {
|
||||
inner_function();
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
#0 inner_function() called at [%sdebug-backtrace.php:9]
|
||||
#1 {closure}()
|
||||
#2 Fiber->start() called at [%sdebug-backtrace.php:12]
|
||||
@@ -20,5 +20,6 @@ string(4) "test"
|
||||
Fatal error: Uncaught Exception: test in %sfailing-fiber.php:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: {closure}()
|
||||
#1 {main}
|
||||
#1 %sfailing-fiber.php(%d): Fiber->resume('test')
|
||||
#2 {main}
|
||||
thrown in %sfailing-fiber.php on line %d
|
||||
|
||||
30
Zend/tests/fibers/failing-nested-fiber.phpt
Normal file
30
Zend/tests/fibers/failing-nested-fiber.phpt
Normal file
@@ -0,0 +1,30 @@
|
||||
--TEST--
|
||||
Test throwing from fiber
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$fiber = new Fiber(function (): void {
|
||||
$fiber = new Fiber(function (int $x, int $y): void {
|
||||
Fiber::suspend($x + $y);
|
||||
throw new Exception('test');
|
||||
});
|
||||
|
||||
$value = $fiber->start(1, 2);
|
||||
var_dump($value);
|
||||
$fiber->resume($value);
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(3)
|
||||
|
||||
Fatal error: Uncaught Exception: test in %sfailing-nested-fiber.php:6
|
||||
Stack trace:
|
||||
#0 [internal function]: {closure}(1, 2)
|
||||
#1 %sfailing-nested-fiber.php(%d): Fiber->resume(3)
|
||||
#2 [internal function]: {closure}()
|
||||
#3 %sfailing-nested-fiber.php(%d): Fiber->start()
|
||||
#4 {main}
|
||||
thrown in %sfailing-nested-fiber.php on line %d
|
||||
@@ -23,5 +23,7 @@ int(1)
|
||||
Fatal error: Uncaught Exception: test in %sfiber-throw-in-destruct.php:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: class@anonymous::{closure}()
|
||||
#1 {main}
|
||||
#1 %sfiber-throw-in-destruct.php(%d): Fiber->resume()
|
||||
#2 [internal function]: class@anonymous->__destruct()
|
||||
#3 {main}
|
||||
thrown in %sfiber-throw-in-destruct.php on line %d
|
||||
|
||||
@@ -16,5 +16,6 @@ Fatal error: Uncaught FiberError: Cannot resume a fiber that is not suspended in
|
||||
Stack trace:
|
||||
#0 %sresume-running-fiber.php(%d): Fiber->resume()
|
||||
#1 [internal function]: {closure}()
|
||||
#2 {main}
|
||||
#2 %sresume-running-fiber.php(%d): Fiber->start()
|
||||
#3 {main}
|
||||
thrown in %sresume-running-fiber.php on line %d
|
||||
|
||||
@@ -24,5 +24,6 @@ int(1)
|
||||
Fatal error: Uncaught TypeError: {closure}(): Argument #1 ($x) must be of type int, string given in %sstart-arguments.php:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: {closure}('test')
|
||||
#1 {main}
|
||||
#1 %sstart-arguments.php(%d): Fiber->start('test')
|
||||
#2 {main}
|
||||
thrown in %sstart-arguments.php on line %d
|
||||
|
||||
@@ -44,6 +44,8 @@ static zend_class_entry *zend_ce_fiber_error;
|
||||
|
||||
static zend_object_handlers zend_fiber_handlers;
|
||||
|
||||
static zend_function zend_fiber_function = { ZEND_INTERNAL_FUNCTION };
|
||||
|
||||
typedef void *fcontext_t;
|
||||
|
||||
typedef struct _transfer_t {
|
||||
@@ -331,9 +333,13 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
|
||||
EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE;
|
||||
|
||||
fiber->execute_data = (zend_execute_data *) stack->top;
|
||||
fiber->stack_bottom = fiber->execute_data;
|
||||
|
||||
memset(fiber->execute_data, 0, sizeof(zend_execute_data));
|
||||
|
||||
fiber->execute_data->func = &zend_fiber_function;
|
||||
fiber->stack_bottom->prev_execute_data = EG(current_execute_data);
|
||||
|
||||
EG(current_execute_data) = fiber->execute_data;
|
||||
EG(jit_trace_num) = 0;
|
||||
EG(error_reporting) = error_reporting;
|
||||
@@ -360,6 +366,7 @@ static void ZEND_STACK_ALIGNED zend_fiber_execute(zend_fiber_context *context)
|
||||
|
||||
zend_vm_stack_destroy();
|
||||
fiber->execute_data = NULL;
|
||||
fiber->stack_bottom = NULL;
|
||||
}
|
||||
|
||||
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
|
||||
@@ -494,6 +501,7 @@ ZEND_METHOD(Fiber, suspend)
|
||||
|
||||
fiber->execute_data = execute_data;
|
||||
fiber->status = ZEND_FIBER_STATUS_SUSPENDED;
|
||||
fiber->stack_bottom->prev_execute_data = NULL;
|
||||
|
||||
zend_fiber_suspend(fiber);
|
||||
|
||||
@@ -547,6 +555,7 @@ ZEND_METHOD(Fiber, resume)
|
||||
}
|
||||
|
||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
||||
fiber->stack_bottom->prev_execute_data = execute_data;
|
||||
|
||||
zend_fiber_switch_to(fiber);
|
||||
|
||||
@@ -578,6 +587,7 @@ ZEND_METHOD(Fiber, throw)
|
||||
fiber->exception = exception;
|
||||
|
||||
fiber->status = ZEND_FIBER_STATUS_RUNNING;
|
||||
fiber->stack_bottom->prev_execute_data = execute_data;
|
||||
|
||||
zend_fiber_switch_to(fiber);
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ typedef struct _zend_fiber {
|
||||
/* Current Zend VM execute data being run by the fiber. */
|
||||
zend_execute_data *execute_data;
|
||||
|
||||
/* Frame on the bottom of the fiber vm stack. */
|
||||
zend_execute_data *stack_bottom;
|
||||
|
||||
/* Exception to be thrown from Fiber::suspend(). */
|
||||
zval *exception;
|
||||
|
||||
|
||||
@@ -6790,6 +6790,7 @@ ZEND_METHOD(ReflectionFiber, getTrace)
|
||||
{
|
||||
zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
|
||||
zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT;
|
||||
zend_execute_data *prev_execute_data;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||
Z_PARAM_OPTIONAL
|
||||
@@ -6798,6 +6799,9 @@ ZEND_METHOD(ReflectionFiber, getTrace)
|
||||
|
||||
REFLECTION_CHECK_VALID_FIBER(fiber);
|
||||
|
||||
prev_execute_data = fiber->stack_bottom->prev_execute_data;
|
||||
fiber->stack_bottom->prev_execute_data = NULL;
|
||||
|
||||
if (EG(current_fiber) != fiber) {
|
||||
// No need to replace current execute data if within the current fiber.
|
||||
EG(current_execute_data) = fiber->execute_data;
|
||||
@@ -6806,6 +6810,7 @@ ZEND_METHOD(ReflectionFiber, getTrace)
|
||||
zend_fetch_debug_backtrace(return_value, 0, options, 0);
|
||||
|
||||
EG(current_execute_data) = execute_data; // Restore original execute data.
|
||||
fiber->stack_bottom->prev_execute_data = prev_execute_data; // Restore prev execute data on fiber stack.
|
||||
}
|
||||
|
||||
ZEND_METHOD(ReflectionFiber, getExecutingLine)
|
||||
|
||||
74
ext/reflection/tests/ReflectionFiber_backtrace.phpt
Normal file
74
ext/reflection/tests/ReflectionFiber_backtrace.phpt
Normal file
@@ -0,0 +1,74 @@
|
||||
--TEST--
|
||||
ReflectionFiber backtrace test
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function suspend_fiber(): void {
|
||||
Fiber::suspend();
|
||||
}
|
||||
|
||||
class Test
|
||||
{
|
||||
public function __invoke(string $arg): void
|
||||
{
|
||||
suspend_fiber();
|
||||
}
|
||||
}
|
||||
|
||||
$fiber = new Fiber(new Test);
|
||||
|
||||
$fiber->start('test');
|
||||
|
||||
$reflection = new ReflectionFiber($fiber);
|
||||
|
||||
var_dump($reflection->getTrace(DEBUG_BACKTRACE_PROVIDE_OBJECT));
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(3) {
|
||||
[0]=>
|
||||
array(6) {
|
||||
["file"]=>
|
||||
string(%d) "%sReflectionFiber_backtrace.php"
|
||||
["line"]=>
|
||||
int(4)
|
||||
["function"]=>
|
||||
string(7) "suspend"
|
||||
["class"]=>
|
||||
string(5) "Fiber"
|
||||
["type"]=>
|
||||
string(2) "::"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
[1]=>
|
||||
array(4) {
|
||||
["file"]=>
|
||||
string(%d) "%sReflectionFiber_backtrace.php"
|
||||
["line"]=>
|
||||
int(11)
|
||||
["function"]=>
|
||||
string(13) "suspend_fiber"
|
||||
["args"]=>
|
||||
array(0) {
|
||||
}
|
||||
}
|
||||
[2]=>
|
||||
array(5) {
|
||||
["function"]=>
|
||||
string(8) "__invoke"
|
||||
["class"]=>
|
||||
string(4) "Test"
|
||||
["object"]=>
|
||||
object(Test)#2 (0) {
|
||||
}
|
||||
["type"]=>
|
||||
string(2) "->"
|
||||
["args"]=>
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(4) "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user