From cd37ab8a16ba70cc005a333fdfa4c0620f474fbb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 29 Nov 2021 11:00:58 +0100 Subject: [PATCH] Workaround for PHP bug 84130: https://bugs.php.net/bug.php?id=81430 --- tests/php_bug_84130.phpt | 61 ++++++++++++++++++++++++++++++++++++++++ tideways_xhprof.c | 21 ++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/php_bug_84130.phpt diff --git a/tests/php_bug_84130.phpt b/tests/php_bug_84130.phpt new file mode 100644 index 0000000..394349c --- /dev/null +++ b/tests/php_bug_84130.phpt @@ -0,0 +1,61 @@ +--TEST-- +Workaround for PHP Bug #81430 +--SKIPIF-- +foo = $foo; + } +} + +#[A("baz")] +class B { + public function getBar($input) { return $input * 2; } +} + +#[A("bar")] +function B() {} + +tideways_xhprof_enable(); + +$foo = new A("foo"); + +$b = new B(); +var_dump($b->getBar(4)); + +$r = new \ReflectionFunction("B"); +var_dump($r->getAttributes(A::class)[0]->newInstance()); +var_dump(call_user_func([$r->getAttributes(A::class)[0], 'newInstance'])); + +$output = tideways_xhprof_disable(); + +print_canonical($output); +--EXPECTF-- +int(8) +object(A)#5 (1) { + ["foo":"A":private]=> + string(3) "bar" +} +object(A)#4 (1) { + ["foo":"A":private]=> + string(3) "bar" +} +call_user_func==>ReflectionAttribute::newInstance: ct= 1; wt=*; +main() : ct= 1; wt=*; +main()==>B::getBar : ct= 1; wt=*; +main()==>ReflectionAttribute::newInstance: ct= 1; wt=*; +main()==>ReflectionFunction::__construct: ct= 1; wt=*; +main()==>ReflectionFunctionAbstract::getAttributes: ct= 2; wt=*; +main()==>call_user_func : ct= 1; wt=*; +main()==>tideways_xhprof_disable : ct= 1; wt=*; +main()==>var_dump : ct= 3; wt=*; diff --git a/tideways_xhprof.c b/tideways_xhprof.c index 66e55bd..8487a36 100644 --- a/tideways_xhprof.c +++ b/tideways_xhprof.c @@ -26,6 +26,7 @@ ZEND_DLEXPORT void tideways_xhprof_execute_internal(zend_execute_data *execute_d #if PHP_VERSION_ID >= 80000 #include "zend_observer.h" +#include "Zend/zend_attributes.h" static void tracer_observer_begin(zend_execute_data *ex) { if (!TXRG(enabled)) { @@ -49,6 +50,26 @@ static void tracer_observer_end(zend_execute_data *ex, zval *return_value) { static zend_observer_fcall_handlers tracer_observer(zend_execute_data *execute_data) { zend_function *func = execute_data->func; + if (func->common.scope != NULL && + func->common.scope->attributes != NULL && + zend_get_attribute_str(func->common.scope->attributes, "attribute", sizeof("attribute")-1) != NULL) { + /* + * This condition is a workaround for PHP bug #81430 + * (https://bugs.php.net/bug.php?id=81430) which was introduced in PHP + * 8.0.12. We created a pull request + * (https://github.com/php/php-src/pull/7665) which will hopefully be + * merged and fix this bug in PHP 8.0.14. + * + * In PHP 8.0.12 a new check was introduced to + * zend_observer_fcall_end() which expects the run_time_cache of + * functions to be properly initialized. However, there are dummy + * frames where the run_time_cache__ptr is simply NULL, so where the + * cache is not properly initialized. This condition ensures that + * attribute based dummy frames have no observers. + */ + return (zend_observer_fcall_handlers) {NULL, NULL}; + } + if (!func->common.function_name) { return (zend_observer_fcall_handlers){NULL, NULL}; }