From: Nikita Popov Date: Wed, 23 Sep 2020 15:06:28 +0000 (+0200) Subject: Skip unnecessary unknown() frames X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=450f2ff4062f1a53834f749e4f0f2a59cc2cc911;p=php Skip unnecessary unknown() frames Noticed this while working on attributes strict_types handling. We sometimes insert dummy frames internally, but I don't think these should show up in debug_backtrace output unless they're needed, either to display an include call or to preserve file/line information that would otherwise get lost. Closes GH-6195. --- diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index ac652b3b8a..5ec365c920 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1781,13 +1781,12 @@ ZEND_FUNCTION(debug_print_backtrace) } else { /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ zend_bool build_filename_arg = 1; + uint32_t include_kind = 0; + if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + include_kind = ptr->opline->extended_value; + } - if (!ptr->func || !ZEND_USER_CODE(ptr->func->common.type) || ptr->opline->opcode != ZEND_INCLUDE_OR_EVAL) { - /* can happen when calling eval from a custom sapi */ - function_name = "unknown"; - build_filename_arg = 0; - } else - switch (ptr->opline->extended_value) { + switch (include_kind) { case ZEND_EVAL: function_name = "eval"; build_filename_arg = 0; @@ -1805,8 +1804,11 @@ ZEND_FUNCTION(debug_print_backtrace) function_name = "require_once"; break; default: - /* this can actually happen if you use debug_backtrace() in your error_handler and - * you're in the top-scope */ + /* Skip dummy frame unless it is needed to preserve filename/lineno info. */ + if (!filename) { + goto skip_frame; + } + function_name = "unknown"; build_filename_arg = 0; break; @@ -1857,10 +1859,12 @@ ZEND_FUNCTION(debug_print_backtrace) ZEND_PUTS(")\n"); } } + ++indent; + +skip_frame: include_filename = filename; call = skip; ptr = skip->prev_execute_data; - ++indent; } } @@ -2009,13 +2013,12 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ zend_bool build_filename_arg = 1; zend_string *pseudo_function_name; + uint32_t include_kind = 0; + if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { + include_kind = ptr->opline->extended_value; + } - if (!ptr->func || !ZEND_USER_CODE(ptr->func->common.type) || ptr->opline->opcode != ZEND_INCLUDE_OR_EVAL) { - /* can happen when calling eval from a custom sapi */ - pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN); - build_filename_arg = 0; - } else - switch (ptr->opline->extended_value) { + switch (include_kind) { case ZEND_EVAL: pseudo_function_name = ZSTR_KNOWN(ZEND_STR_EVAL); build_filename_arg = 0; @@ -2033,8 +2036,12 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int pseudo_function_name = ZSTR_KNOWN(ZEND_STR_REQUIRE_ONCE); break; default: - /* this can actually happen if you use debug_backtrace() in your error_handler and - * you're in the top-scope */ + /* Skip dummy frame unless it is needed to preserve filename/lineno info. */ + if (!filename) { + zval_ptr_dtor(&stack_frame); + goto skip_frame; + } + pseudo_function_name = ZSTR_KNOWN(ZEND_STR_UNKNOWN); build_filename_arg = 0; break; @@ -2060,8 +2067,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &stack_frame); +skip_frame: include_filename = filename; - call = skip; ptr = skip->prev_execute_data; } diff --git a/ext/phar/tests/cache_list/frontcontroller29.phpt b/ext/phar/tests/cache_list/frontcontroller29.phpt index caaf34538e..a3a9f4e48e 100644 --- a/ext/phar/tests/cache_list/frontcontroller29.phpt +++ b/ext/phar/tests/cache_list/frontcontroller29.phpt @@ -16,7 +16,6 @@ Content-type: text/html; charset=UTF-8 --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function oopsie_daisy() in phar://%sfatalerror.phps:1 Stack trace: -#0 [internal function]: unknown() -#1 %s(%d): Phar::webPhar('whatever', 'index.php', '404.php', Array) -#2 {main} +#0 %s(%d): Phar::webPhar('whatever', 'index.php', '404.php', Array) +#1 {main} thrown in phar://%sfatalerror.phps on line 1 diff --git a/ext/phar/tests/frontcontroller29.phpt b/ext/phar/tests/frontcontroller29.phpt index 5e18357b6b..2a2ea8c665 100644 --- a/ext/phar/tests/frontcontroller29.phpt +++ b/ext/phar/tests/frontcontroller29.phpt @@ -15,7 +15,6 @@ Content-type: text/html; charset=UTF-8 --EXPECTF-- Fatal error: Uncaught Error: Call to undefined function oopsie_daisy() in phar://%sfatalerror.phps:1 Stack trace: -#0 [internal function]: unknown() -#1 %s(%d): Phar::webPhar('whatever', 'index.php', '404.php', Array) -#2 {main} +#0 %s(%d): Phar::webPhar('whatever', 'index.php', '404.php', Array) +#1 {main} thrown in phar://%sfatalerror.phps on line 1