]> granicus.if.org Git - php/commitdiff
Pass zend_execute_data instead of zend_function to fcall init
authorSammy Kaye Powers <sammyk@php.net>
Thu, 24 Sep 2020 19:57:43 +0000 (12:57 -0700)
committerSammy Kaye Powers <sammyk@php.net>
Fri, 25 Sep 2020 18:46:15 +0000 (11:46 -0700)
The motivation for this change is to prevent extensions from having to check executor globals for the current execute_data during function call init. A previous implementation of the observer API initialized the function call from runtime cache initialization before execute_data was allocated which is why zend_function was passed in.

But now that the observer API is implemented via opcode specialization, it makes sense to pass in the execute_data. This also keeps the API a bit more consistent for existing extensions that already hook zend_execute_ex.

Closes GH-6209

Zend/zend_observer.c
Zend/zend_observer.h
ext/zend_test/test.c
ext/zend_test/tests/observer_backtrace_01.phpt [new file with mode: 0644]

index 68cf43ce66248ed10b9e7fdf8384e4cfbeccd53d..9c2d1cdf51c4e15999671140d0fd3598f6df360f 100644 (file)
@@ -89,9 +89,10 @@ ZEND_API void zend_observer_shutdown(void) {
        zend_llist_destroy(&zend_observer_error_callbacks);
 }
 
-static void zend_observer_fcall_install(zend_function *function) {
+static void zend_observer_fcall_install(zend_execute_data *execute_data) {
        zend_llist_element *element;
        zend_llist *list = &zend_observers_fcall_list;
+       zend_function *function = execute_data->func;
        zend_op_array *op_array = &function->op_array;
 
        if (fcall_handlers_arena == NULL) {
@@ -105,7 +106,7 @@ static void zend_observer_fcall_install(zend_function *function) {
        for (element = list->head; element; element = element->next) {
                zend_observer_fcall_init init;
                memcpy(&init, element->data, sizeof init);
-               zend_observer_fcall_handlers handlers = init(function);
+               zend_observer_fcall_handlers handlers = init(execute_data);
                if (handlers.begin || handlers.end) {
                        zend_llist_add_element(&handlers_list, &handlers);
                }
@@ -150,7 +151,7 @@ static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_d
 
        fcall_data = ZEND_OBSERVER_DATA(op_array);
        if (!fcall_data) {
-               zend_observer_fcall_install((zend_function *)op_array);
+               zend_observer_fcall_install(execute_data);
                fcall_data = ZEND_OBSERVER_DATA(op_array);
        }
 
index 246a3a61be3794507fe33b242a3132e8efaf1433..1d20306a170186a4e6a6cbd3e86125206634d54e 100644 (file)
@@ -50,7 +50,7 @@ typedef struct _zend_observer_fcall_handlers {
 } zend_observer_fcall_handlers;
 
 /* If the fn should not be observed then return {NULL, NULL} */
-typedef zend_observer_fcall_handlers (*zend_observer_fcall_init)(zend_function *func);
+typedef zend_observer_fcall_handlers (*zend_observer_fcall_init)(zend_execute_data *execute_data);
 
 // Call during minit/startup ONLY
 ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init);
index 4ca98a3c0831b19eb0797f8b83b602bc3cde5f49..92b8e4cf6a8158b17348316b9871a527999b6644 100644 (file)
@@ -36,6 +36,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
        int observer_observe_functions;
        int observer_show_return_type;
        int observer_show_return_value;
+       int observer_show_init_backtrace;
        int observer_nesting_depth;
 ZEND_END_MODULE_GLOBALS(zend_test)
 
@@ -315,9 +316,10 @@ PHP_INI_BEGIN()
        STD_PHP_INI_BOOLEAN("zend_test.observer.observe_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_functions, zend_zend_test_globals, zend_test_globals)
        STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_type", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_type, zend_zend_test_globals, zend_test_globals)
        STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_value", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_value, zend_zend_test_globals, zend_test_globals)
+       STD_PHP_INI_BOOLEAN("zend_test.observer.show_init_backtrace", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_init_backtrace, zend_zend_test_globals, zend_test_globals)
 PHP_INI_END()
 
-static zend_observer_fcall_handlers observer_fcall_init(zend_function *fbc);
+static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data);
 
 PHP_MINIT_FUNCTION(zend_test)
 {
@@ -498,10 +500,34 @@ static void observer_show_init(zend_function *fbc)
        }
 }
 
-static zend_observer_fcall_handlers observer_fcall_init(zend_function *fbc)
+static void observer_show_init_backtrace(zend_execute_data *execute_data)
 {
+       zend_execute_data *ex = execute_data;
+       php_printf("%*s<!--\n", 2 * ZT_G(observer_nesting_depth), "");
+       do {
+               zend_function *fbc = ex->func;
+               int indent = 2 * ZT_G(observer_nesting_depth) + 4;
+               if (fbc->common.function_name) {
+                       if (fbc->common.scope) {
+                               php_printf("%*s%s::%s()\n", indent, "", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
+                       } else {
+                               php_printf("%*s%s()\n", indent, "", ZSTR_VAL(fbc->common.function_name));
+                       }
+               } else {
+                       php_printf("%*s{main} %s\n", indent, "", ZSTR_VAL(fbc->op_array.filename));
+               }
+       } while ((ex = ex->prev_execute_data) != NULL);
+       php_printf("%*s-->\n", 2 * ZT_G(observer_nesting_depth), "");
+}
+
+static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data)
+{
+       zend_function *fbc = execute_data->func;
        if (ZT_G(observer_show_output)) {
                observer_show_init(fbc);
+               if (ZT_G(observer_show_init_backtrace)) {
+                       observer_show_init_backtrace(execute_data);
+               }
        }
 
        if (ZT_G(observer_observe_all)) {
diff --git a/ext/zend_test/tests/observer_backtrace_01.phpt b/ext/zend_test/tests/observer_backtrace_01.phpt
new file mode 100644 (file)
index 0000000..7aa47c2
--- /dev/null
@@ -0,0 +1,106 @@
+--TEST--
+Observer: Show backtrace on init
+--SKIPIF--
+<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
+--INI--
+zend_test.observer.enabled=1
+zend_test.observer.observe_all=1
+zend_test.observer.show_init_backtrace=1
+--FILE--
+<?php
+class TestClass
+{
+    private function bar($number)
+    {
+        return $number + 2;
+    }
+
+    public function foo()
+    {
+        return array_map(function ($value) {
+            return $this->bar($value);
+        }, [40, 1335]);
+    }
+}
+
+function gen()
+{
+    $test = new TestClass();
+    yield $test->foo();
+}
+
+function foo()
+{
+    return gen()->current();
+}
+
+var_dump(foo());
+?>
+--EXPECTF--
+<!-- init '%s/observer_backtrace_%d.php' -->
+<!--
+    {main} %s/observer_backtrace_%d.php
+-->
+<file '%s/observer_backtrace_%d.php'>
+  <!-- init foo() -->
+  <!--
+      foo()
+      {main} %s/observer_backtrace_%d.php
+  -->
+  <foo>
+    <!-- init gen() -->
+    <!--
+        gen()
+        Generator::current()
+        foo()
+        {main} %s/observer_backtrace_%d.php
+    -->
+    <gen>
+      <!-- init TestClass::foo() -->
+      <!--
+          TestClass::foo()
+          gen()
+          Generator::current()
+          foo()
+          {main} %s/observer_backtrace_%d.php
+      -->
+      <TestClass::foo>
+        <!-- init TestClass::{closure}() -->
+        <!--
+            TestClass::{closure}()
+            array_map()
+            TestClass::foo()
+            gen()
+            Generator::current()
+            foo()
+            {main} %s/observer_backtrace_%d.php
+        -->
+        <TestClass::{closure}>
+          <!-- init TestClass::bar() -->
+          <!--
+              TestClass::bar()
+              TestClass::{closure}()
+              array_map()
+              TestClass::foo()
+              gen()
+              Generator::current()
+              foo()
+              {main} %s/observer_backtrace_%d.php
+          -->
+          <TestClass::bar>
+          </TestClass::bar>
+        </TestClass::{closure}>
+        <TestClass::{closure}>
+          <TestClass::bar>
+          </TestClass::bar>
+        </TestClass::{closure}>
+      </TestClass::foo>
+    </gen>
+  </foo>
+array(2) {
+  [0]=>
+  int(42)
+  [1]=>
+  int(1337)
+}
+</file '%s/observer_backtrace_%d.php'>