@@ -70,6 +70,7 @@ static ZEND_FUNCTION(extension_loaded);
70
70
static ZEND_FUNCTION (get_extension_funcs );
71
71
static ZEND_FUNCTION (get_defined_constants );
72
72
static ZEND_FUNCTION (debug_backtrace );
73
+ static ZEND_FUNCTION (debug_print_backtrace );
73
74
#if ZEND_DEBUG
74
75
static ZEND_FUNCTION (zend_test_func );
75
76
#ifdef ZTS
@@ -129,6 +130,7 @@ static zend_function_entry builtin_functions[] = {
129
130
ZEND_FE (get_extension_funcs , NULL )
130
131
ZEND_FE (get_defined_constants , NULL )
131
132
ZEND_FE (debug_backtrace , NULL )
133
+ ZEND_FE (debug_print_backtrace , NULL )
132
134
#if ZEND_DEBUG
133
135
ZEND_FE (zend_test_func , NULL )
134
136
#ifdef ZTS
@@ -1296,6 +1298,143 @@ static zval *debug_backtrace_get_args(void ***curpos TSRMLS_DC) {
1296
1298
return arg_array ;
1297
1299
}
1298
1300
1301
+ void debug_print_backtrace_args (zval * arg_array )
1302
+ {
1303
+ zval * * tmp ;
1304
+ HashPosition iterator ;
1305
+ int i = 0 ;
1306
+
1307
+ zend_hash_internal_pointer_reset_ex (arg_array -> value .ht , & iterator );
1308
+ while (zend_hash_get_current_data_ex (arg_array -> value .ht , (void * * ) & tmp , & iterator ) == SUCCESS ) {
1309
+ if (i ++ ) {
1310
+ ZEND_PUTS (", " );
1311
+ }
1312
+ zend_print_flat_zval_r (* tmp );
1313
+ zend_hash_move_forward_ex (arg_array -> value .ht , & iterator );
1314
+ }
1315
+ }
1316
+
1317
+ /* {{{ proto void debug_print_backtrace(void) */
1318
+ ZEND_FUNCTION (debug_print_backtrace )
1319
+ {
1320
+ zend_execute_data * ptr ;
1321
+ int lineno ;
1322
+ char * function_name ;
1323
+ char * filename ;
1324
+ char * class_name ;
1325
+ char * call_type ;
1326
+ char * include_filename = NULL ;
1327
+ zval * arg_array ;
1328
+ void * * cur_arg_pos = EG (argument_stack ).top_element ;
1329
+ void * * args = cur_arg_pos ;
1330
+ int arg_stack_consistent = 0 ;
1331
+ int frames_on_stack = 0 ;
1332
+ int indent ;
1333
+
1334
+ if (ZEND_NUM_ARGS ()) {
1335
+ ZEND_WRONG_PARAM_COUNT ();
1336
+ }
1337
+
1338
+ while (-- args >= EG (argument_stack ).elements ) {
1339
+ if (* args -- ) {
1340
+ break ;
1341
+ }
1342
+ args -= * (ulong * )args ;
1343
+ frames_on_stack ++ ;
1344
+
1345
+ if (args == EG (argument_stack ).elements ) {
1346
+ arg_stack_consistent = 1 ;
1347
+ break ;
1348
+ }
1349
+ }
1350
+
1351
+ ptr = EG (current_execute_data );
1352
+
1353
+ /* skip debug_backtrace() */
1354
+ ptr = ptr -> prev_execute_data ;
1355
+ cur_arg_pos -= 2 ;
1356
+ frames_on_stack -- ;
1357
+
1358
+ array_init (return_value );
1359
+
1360
+ while (ptr ) {
1361
+ if (ptr -> op_array ) {
1362
+ filename = ptr -> op_array -> filename ;
1363
+ lineno = ptr -> opline -> lineno ;
1364
+ } else {
1365
+ filename = NULL ;
1366
+ }
1367
+
1368
+ function_name = ptr -> function_state .function -> common .function_name ;
1369
+
1370
+ if (function_name ) {
1371
+ if (ptr -> object ) {
1372
+ class_name = Z_OBJCE (* ptr -> object )-> name ;
1373
+ call_type = "->" ;
1374
+ } else if (ptr -> function_state .function -> common .scope ) {
1375
+ class_name = ptr -> function_state .function -> common .scope -> name ;
1376
+ call_type = "::" ;
1377
+ } else {
1378
+ class_name = NULL ;
1379
+ call_type = NULL ;
1380
+ }
1381
+ if ((! ptr -> opline ) || ((ptr -> opline -> opcode == ZEND_DO_FCALL_BY_NAME ) || (ptr -> opline -> opcode == ZEND_DO_FCALL ))) {
1382
+ if (arg_stack_consistent && (frames_on_stack > 0 )) {
1383
+ arg_array = debug_backtrace_get_args (& cur_arg_pos TSRMLS_CC );
1384
+ frames_on_stack -- ;
1385
+ }
1386
+ }
1387
+ } else {
1388
+ /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */
1389
+ zend_bool build_filename_arg = 1 ;
1390
+
1391
+ switch (ptr -> opline -> op2 .u .constant .value .lval ) {
1392
+ case ZEND_EVAL :
1393
+ function_name = "eval" ;
1394
+ build_filename_arg = 0 ;
1395
+ break ;
1396
+ case ZEND_INCLUDE :
1397
+ function_name = "include" ;
1398
+ break ;
1399
+ case ZEND_REQUIRE :
1400
+ function_name = "require" ;
1401
+ break ;
1402
+ case ZEND_INCLUDE_ONCE :
1403
+ function_name = "include_once" ;
1404
+ break ;
1405
+ case ZEND_REQUIRE_ONCE :
1406
+ function_name = "require_once" ;
1407
+ break ;
1408
+ default :
1409
+ /* this can actually happen if you use debug_backtrace() in your error_handler and
1410
+ * you're in the top-scope */
1411
+ function_name = "unknown" ;
1412
+ build_filename_arg = 0 ;
1413
+ break ;
1414
+ }
1415
+
1416
+ if (build_filename_arg && include_filename ) {
1417
+ MAKE_STD_ZVAL (arg_array );
1418
+ array_init (arg_array );
1419
+ add_next_index_string (arg_array , include_filename , 1 );
1420
+ }
1421
+ }
1422
+ zend_printf ("#%-2d " , indent );
1423
+ if (class_name ) {
1424
+ ZEND_PUTS (class_name );
1425
+ ZEND_PUTS (call_type );
1426
+ }
1427
+ zend_printf ("%s(" , function_name ?function_name :"main" );
1428
+ debug_print_backtrace_args (arg_array );
1429
+ ZVAL_DELREF (arg_array );
1430
+ zend_printf (") called at [%s:%d]\n" , filename , lineno );
1431
+ include_filename = filename ;
1432
+ ptr = ptr -> prev_execute_data ;
1433
+ ++ indent ;
1434
+ }
1435
+ }
1436
+
1437
+ /* }}} */
1299
1438
/* {{{ proto void debug_backtrace(void)
1300
1439
Prints out a backtrace */
1301
1440
ZEND_FUNCTION (debug_backtrace )
0 commit comments