1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Use common formatting for backtraces (#6977)

This makes debug_print_backtrace() use the same formatting as exception
backtraces. The only difference is that the final #{main} is omitted,
because it wouldn't make sense for limited backtraces, and wasn't there
previously either.
This commit is contained in:
Nikita Popov
2021-05-18 11:43:37 +02:00
committed by GitHub
parent 1c8bb6d681
commit de6e401e05
23 changed files with 129 additions and 171 deletions

View File

@@ -19,8 +19,8 @@ class Test {}
?>
--EXPECTF--
#0 MyAttribute->__construct() called at [%s031_backtrace.php:12]
#1 ReflectionAttribute->newInstance() called at [%s:%d]
#0 %s031_backtrace.php(12): MyAttribute->__construct()
#1 %s(%d): ReflectionAttribute->newInstance()
array(2) {
[0]=>
array(7) {

View File

@@ -22,6 +22,6 @@ function GenerateError2($A1)
GenerateError2("Test2");
?>
--EXPECTF--
#0 userErrorHandler(2, Undefined variable $b, %s, %d) called at [%s:%d]
#1 GenerateError1(Test1) called at [%sbug29896.php:16]
#2 GenerateError2(Test2) called at [%sbug29896.php:19]
#0 %s(%d): userErrorHandler(2, 'Undefined varia...', '%s', %d)
#1 %s(%d): GenerateError1('Test1')
#2 %s(%d): GenerateError2('Test2')

View File

@@ -47,15 +47,15 @@ $b->foo();
B::bar();
?>
--EXPECTF--
#0 A->__construct() called at [%sbug30828.php:30]
#1 B->__construct() called at [%sbug30828.php:42]
#0 %sbug30828.php(30): A->__construct()
#1 %sbug30828.php(42): B->__construct()
A->__construct
B->__construct
#0 A->foo() called at [%sbug30828.php:34]
#1 B->foo() called at [%sbug30828.php:43]
#0 %sbug30828.php(34): A->foo()
#1 %sbug30828.php(43): B->foo()
A->foo
B->foo
#0 A::bar() called at [%sbug30828.php:38]
#1 B::bar() called at [%sbug30828.php:44]
#0 %sbug30828.php(38): A::bar()
#1 %sbug30828.php(44): B::bar()
A::bar
B::bar

View File

@@ -27,7 +27,7 @@ $c->Bmethod();
$c->t2method();
?>
--EXPECTF--
#0 A->Bmethod() called at [%sbug64239_3.php:%d]
#0 A->t2method() called at [%sbug64239_3.php:%d]
#0 C->Bmethod() called at [%sbug64239_3.php:%d]
#0 A->t2method() called at [%sbug64239_3.php:%d]
#0 %s(%d): A->Bmethod()
#0 %s(%d): A->t2method()
#0 %s(%d): C->Bmethod()
#0 %s(%d): A->t2method()

View File

@@ -25,7 +25,7 @@ C::Bmethod();
C::t2method();
?>
--EXPECTF--
#0 A::Bmethod() called at [%sbug64239_4.php:%d]
#0 A::t2method() called at [%sbug64239_4.php:%d]
#0 C::Bmethod() called at [%sbug64239_4.php:%d]
#0 A::t2method() called at [%sbug64239_4.php:%d]
#0 %s(%d): A::Bmethod()
#0 %s(%d): A::t2method()
#0 %s(%d): C::Bmethod()
#0 %s(%d): A::t2method()

View File

@@ -32,6 +32,6 @@ class dummy {
new dummy();
?>
--EXPECTF--
#0 dummy->bar() called at [%sbug70156.php:%d]
#1 dummy->foo1() called at [%sbug70156.php:%d]
#2 dummy->__construct() called at [%sbug70156.php:%d]
#0 %s(%d): dummy->bar()
#1 %s(%d): dummy->foo1()
#2 %s(%d): dummy->__construct()

View File

@@ -13,4 +13,4 @@ function test() {
}
?>
--EXPECTF--
#0 test(Array ([0] => Array ([0] => a),[1] => b Object ())) called at [%sbug73916.php:%d]
#0 %s(%d): test(Array)

View File

@@ -15,4 +15,4 @@ test(new class {
?>
--EXPECTF--
#0 class@anonymous->__destruct() called at [%s:%d]
#0 %s(%d): class@anonymous->__destruct()

View File

@@ -19,8 +19,8 @@ eval("foo();");
echo "Done\n";
?>
--EXPECTF--
#0 boo() called at [%s:%d]
#1 bar() called at [%s:%d]
#2 foo() called at [%s(%d) : eval()'d code:1]
#3 eval() called at [%s:%d]
#0 %s(%d): boo()
#1 %s(%d): bar()
#2 %s(%d) : eval()'d code(1): foo()
#3 %s(%d): eval()
Done

View File

@@ -29,7 +29,7 @@ Array
)
)
#0 {closure}(23) called at [%s:%d]
#0 %s(%d): {closure}(23)
Array
(
[0] => Array
@@ -65,5 +65,5 @@ Array
)
)
#0 {closure}(23) called at [%s:%d]
#1 test(Closure Object ()) called at [%s:%d]
#0 %s(%d): {closure}(23)
#1 %s(%d): test(Object(Closure))

View File

@@ -45,29 +45,29 @@ foo::statCall("doit", "backtrace_print");
?>
--EXPECTF--
==default
#0 doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
==true
#0 doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
==false
#0 doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
==DEBUG_BACKTRACE_PROVIDE_OBJECT
#0 doit(a, b, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall(doit, debug_print_backtrace) called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit('a', 'b', 'debug_print_bac...')
#1 %sdebug_backtrace_options.php(%d): foo->doCall('doit', 'debug_print_bac...')
#2 %sdebug_backtrace_options.php(%d): foo::statCall('doit', 'debug_print_bac...')
==DEBUG_BACKTRACE_IGNORE_ARGS
#0 doit() called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall() called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall() called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit()
#1 %sdebug_backtrace_options.php(%d): foo->doCall()
#2 %sdebug_backtrace_options.php(%d): foo::statCall()
==both
#0 doit() called at [%sdebug_backtrace_options.php:%d]
#1 foo->doCall() called at [%sdebug_backtrace_options.php:%d]
#2 foo::statCall() called at [%sdebug_backtrace_options.php:%d]
#0 %sdebug_backtrace_options.php(%d): doit()
#1 %sdebug_backtrace_options.php(%d): foo->doCall()
#2 %sdebug_backtrace_options.php(%d): foo::statCall()
==default
Array
(

View File

@@ -5,3 +5,4 @@ Calling debug_print_backtrace() from main script
debug_print_backtrace();
?>
--EXPECT--

View File

@@ -12,20 +12,26 @@ function b() {
function c() {
debug_print_backtrace(0, 1);
echo "\n";
debug_print_backtrace(0, 2);
echo "\n";
debug_print_backtrace(0, 0);
echo "\n";
debug_print_backtrace(0, 4);
}
a();
?>
--EXPECTF--
#0 c() called at [%sdebug_print_backtrace_limit.php:7]
#0 c() called at [%sdebug_print_backtrace_limit.php:7]
#1 b() called at [%sdebug_print_backtrace_limit.php:3]
#0 c() called at [%sdebug_print_backtrace_limit.php:7]
#1 b() called at [%sdebug_print_backtrace_limit.php:3]
#2 a() called at [%sdebug_print_backtrace_limit.php:17]
#0 c() called at [%sdebug_print_backtrace_limit.php:7]
#1 b() called at [%sdebug_print_backtrace_limit.php:3]
#2 a() called at [%sdebug_print_backtrace_limit.php:17]
#0 %sdebug_print_backtrace_limit.php(7): c()
#0 %sdebug_print_backtrace_limit.php(7): c()
#1 %sdebug_print_backtrace_limit.php(3): b()
#0 %sdebug_print_backtrace_limit.php(7): c()
#1 %sdebug_print_backtrace_limit.php(3): b()
#2 %sdebug_print_backtrace_limit.php(20): a()
#0 %sdebug_print_backtrace_limit.php(7): c()
#1 %sdebug_print_backtrace_limit.php(3): b()
#2 %sdebug_print_backtrace_limit.php(20): a()

View File

@@ -16,6 +16,6 @@ $fiber->start();
?>
--EXPECTF--
#0 inner_function() called at [%sdebug-backtrace.php:9]
#1 {closure}()
#2 Fiber->start() called at [%sdebug-backtrace.php:12]
#0 %sdebug-backtrace.php(9): inner_function()
#1 [internal function]: {closure}()
#2 %sdebug-backtrace.php(12): Fiber->start()

View File

@@ -21,7 +21,7 @@ f3($gen);
?>
--EXPECTF--
#0 f1() called at [%s:%d]
#1 f2(foo, bar)
#2 Generator->rewind() called at [%s:%d]
#3 f3(Generator Object ()) called at [%s:%d]
#0 %s(%d): f1()
#1 [internal function]: f2('foo', 'bar')
#2 %s(%d): Generator->rewind()
#3 %s(%d): f3(Object(Generator))

View File

@@ -26,7 +26,7 @@ var_dump($gen2->current());
--EXPECTF--
int(1)
int(1)
#0 gen() called at [%s:10]
#1 from(Generator Object ())
#2 Generator->next() called at [%s:19]
#0 %s(10): gen()
#1 [internal function]: from(Object(Generator))
#2 %s(19): Generator->next()
int(2)

View File

@@ -28,21 +28,21 @@ for ($gen = gen(); $gen->valid(); $gen->next()) {
--EXPECTF--
Implicit foreach:
int(1)
#0 gen() called at [%s:%d]
#0 %s(%d): gen()
int(2)
#0 from(2) called at [%s:%d]
#1 gen() called at [%s:%d]
#0 %s(%d): from(2)
#1 %s(%d): gen()
int(3)
#0 gen() called at [%s:%d]
#0 %s(%d): gen()
Explicit iterator:
int(1)
#0 gen()
#1 Generator->next() called at [%s:%d]
#0 [internal function]: gen()
#1 %s(%d): Generator->next()
int(2)
#0 from(2) called at [%s:%d]
#1 gen()
#2 Generator->next() called at [%s:%d]
#0 %s(%d): from(2)
#1 [internal function]: gen()
#2 %s(%d): Generator->next()
int(3)
#0 gen()
#1 Generator->next() called at [%s:%d]
#0 [internal function]: gen()
#1 %s(%d): Generator->next()

View File

@@ -40,7 +40,7 @@ array(1) {
}
}
}
#0 test(1, 2, x: 3, y: 4) called at [%s:10]
#0 %s(10): test(1, 2, x: 3, y: 4)
array(1) {
[0]=>
array(4) {

View File

@@ -1637,33 +1637,12 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
}
/* }}} */
void debug_print_backtrace_args(smart_str *str, zval *arg_array) /* {{{ */
{
zend_string *name;
zval *tmp;
int i = 0;
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arg_array), name, tmp) {
if (i++) {
smart_str_appends(str, ", ");
}
if (name) {
smart_str_append(str, name);
smart_str_appends(str, ": ");
}
zend_print_flat_zval_r_to_buf(str, tmp);
} ZEND_HASH_FOREACH_END();
}
/* }}} */
/* {{{ */
ZEND_FUNCTION(debug_print_backtrace)
{
zend_long options = 0;
zend_long limit = 0;
zval backtrace, *frame;
zend_long frame_no;
smart_str str = {0};
zval backtrace;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &options, &limit) == FAILURE) {
RETURN_THROWS();
@@ -1671,48 +1650,11 @@ ZEND_FUNCTION(debug_print_backtrace)
zend_fetch_debug_backtrace(&backtrace, 1, options, limit);
ZEND_ASSERT(Z_TYPE(backtrace) == IS_ARRAY);
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARR(backtrace), frame_no, frame) {
ZEND_ASSERT(Z_TYPE_P(frame) == IS_ARRAY);
zval *function = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FUNCTION), 1);
zval *class = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_CLASS), 1);
zval *type = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_TYPE), 1);
zval *file = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_FILE), 1);
zval *line = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_LINE), 1);
zval *args = zend_hash_find_ex(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_ARGS), 1);
smart_str_append_printf(&str, "#%-2d ", (int) frame_no);
if (class) {
ZEND_ASSERT(Z_TYPE_P(class) == IS_STRING);
ZEND_ASSERT(type && Z_TYPE_P(type) == IS_STRING);
/* Cut off anonymous class names at null byte. */
smart_str_appends(&str, Z_STRVAL_P(class));
smart_str_append(&str, Z_STR_P(type));
}
smart_str_append(&str, Z_STR_P(function));
smart_str_appendc(&str, '(');
if (args) {
ZEND_ASSERT(Z_TYPE_P(args) == IS_ARRAY);
debug_print_backtrace_args(&str, args);
}
smart_str_appendc(&str, ')');
if (file) {
ZEND_ASSERT(Z_TYPE_P(file) == IS_STRING);
ZEND_ASSERT(line && Z_TYPE_P(line) == IS_LONG);
smart_str_appends(&str, " called at [");
smart_str_append(&str, Z_STR_P(file));
smart_str_appendc(&str, ':');
smart_str_append_long(&str, Z_LVAL_P(line));
smart_str_appendc(&str, ']');
}
smart_str_appendc(&str, '\n');
} ZEND_HASH_FOREACH_END();
zend_string *str = zend_trace_to_string(Z_ARRVAL(backtrace), /* include_main */ false);
ZEND_WRITE(ZSTR_VAL(str), ZSTR_LEN(str));
zend_string_release(str);
zval_ptr_dtor(&backtrace);
smart_str_0(&str);
if (str.s) {
ZEND_WRITE(ZSTR_VAL(str.s), ZSTR_LEN(str.s));
}
smart_str_free(&str);
}
/* }}} */

View File

@@ -600,29 +600,13 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
}
/* }}} */
/* {{{ Obtain the backtrace for the exception as a string (instead of an array) */
ZEND_METHOD(Exception, getTraceAsString)
{
zval *trace, *frame, rv;
ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main) {
zend_ulong index;
zval *object;
zend_class_entry *base_ce;
smart_str str = {0};
zval *frame;
uint32_t num = 0;
smart_str str = {0};
ZEND_PARSE_PARAMETERS_NONE();
object = ZEND_THIS;
base_ce = i_get_exception_base(Z_OBJ_P(object));
trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
if (EG(exception)) {
RETURN_THROWS();
}
/* Type should be guaranteed by property type. */
ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) {
ZEND_HASH_FOREACH_NUM_KEY_VAL(trace, index, frame) {
if (Z_TYPE_P(frame) != IS_ARRAY) {
zend_error(E_WARNING, "Expected array for frame " ZEND_ULONG_FMT, index);
continue;
@@ -631,12 +615,33 @@ ZEND_METHOD(Exception, getTraceAsString)
_build_trace_string(&str, Z_ARRVAL_P(frame), num++);
} ZEND_HASH_FOREACH_END();
smart_str_appendc(&str, '#');
smart_str_append_long(&str, num);
smart_str_appends(&str, " {main}");
smart_str_0(&str);
if (include_main) {
smart_str_appendc(&str, '#');
smart_str_append_long(&str, num);
smart_str_appends(&str, " {main}");
}
RETURN_NEW_STR(str.s);
smart_str_0(&str);
return str.s ? str.s : ZSTR_EMPTY_ALLOC();
}
/* {{{ Obtain the backtrace for the exception as a string (instead of an array) */
ZEND_METHOD(Exception, getTraceAsString)
{
ZEND_PARSE_PARAMETERS_NONE();
zval *object = ZEND_THIS;
zend_class_entry *base_ce = i_get_exception_base(Z_OBJ_P(object));
zval rv;
zval *trace = zend_read_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_TRACE), 1, &rv);
if (EG(exception)) {
RETURN_THROWS();
}
/* Type should be guaranteed by property type. */
ZEND_ASSERT(Z_TYPE_P(trace) == IS_ARRAY);
RETURN_NEW_STR(zend_trace_to_string(Z_ARRVAL_P(trace), /* include_main */ true));
}
/* }}} */

View File

@@ -68,6 +68,7 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex);
/* show an exception using zend_error(severity,...), severity should be E_ERROR */
ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity);
ZEND_API zend_string *zend_trace_to_string(HashTable *trace, bool include_main);
ZEND_API ZEND_COLD void zend_throw_unwind_exit(void);
ZEND_API ZEND_COLD void zend_throw_graceful_exit(void);

View File

@@ -26,5 +26,5 @@ class c1
c1::go();
?>
--EXPECTF--
#0 require() called at [%s:19]
#1 c1::go() called at [%s:23]
#0 %s(19): require()
#1 %s(23): c1::go()

View File

@@ -6,5 +6,8 @@ class FooBar { static function error() { debug_print_backtrace(); } }
set_error_handler(array('FooBar', 'error'));
include('foobar.php');
?>
--EXPECTREGEX--
.*#1\s*include.*
--EXPECTF--
#0 %s(%d): FooBar::error(2, 'include(foobar....', '%s', 4)
#1 %s(%d): include()
#0 %s(%d): FooBar::error(2, 'include(): Fail...', '%s', 4)
#1 %s(%d): include()