From: Bob Weinand Date: Fri, 22 Nov 2013 17:27:55 +0000 (+0100) Subject: Added frames and beautified backtrace X-Git-Tag: php-5.6.0alpha1~110^2~144 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5b51fb40e91f3dd73854c22701c644124c0a16f8;p=php Added frames and beautified backtrace --- diff --git a/config.m4 b/config.m4 index ee80daa25c..d8bcce5a31 100644 --- a/config.m4 +++ b/config.m4 @@ -18,7 +18,7 @@ if test "$PHP_PHPDBG" != "no"; then fi PHP_PHPDBG_CFLAGS="-I$abc_srcdir" - PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c" + PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_frame.c" PHP_SUBST(PHP_PHPDBG_CFLAGS) PHP_SUBST(PHP_PHPDBG_FILES) diff --git a/config.w32 b/config.w32 index eee355da5e..1356ced609 100644 --- a/config.w32 +++ b/config.w32 @@ -1,7 +1,7 @@ ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes'); if (PHP_PHPDBG == "yes") { - SAPI('phpdbg', 'phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c', 'phpdbg.exe'); + SAPI('phpdbg', 'phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_frame.c', 'phpdbg.exe'); ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib"); } diff --git a/phpdbg.c b/phpdbg.c index 6b219fa756..4b9d39b638 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -45,6 +45,7 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->flags = PHPDBG_DEFAULT_FLAGS; pg->oplog = NULL; memset(&pg->lparam, 0, sizeof(phpdbg_param_t)); + pg->frame.num = 0; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ diff --git a/phpdbg.h b/phpdbg.h index 967cd8fb0b..98c007036e 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -54,6 +54,7 @@ #endif #include "phpdbg_cmd.h" +#include "phpdbg_frame.h" #ifdef ZTS # define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v) @@ -126,6 +127,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable seek; /* seek oplines */ zend_ulong flags; /* phpdbg flags */ HashTable registered; /* registered */ + phpdbg_frame frame; /* frame */ ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */ #endif /* PHPDBG_H */ diff --git a/phpdbg_frame.c b/phpdbg_frame.c new file mode 100644 index 0000000000..aeda9ed303 --- /dev/null +++ b/phpdbg_frame.c @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg.h" +#include "phpdbg_frame.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +#define PHPDBG_FRAME(v) (PHPDBG_G(frame).v) +#define PHPDBG_EX(v) (EG(current_execute_data)->v) + +void switch_to_frame(int frame) { + zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data); + int i = 0; + + if (PHPDBG_FRAME(num) == frame) { + phpdbg_notice("Already in frame #%d", frame); + return; + } + + while (execute_data) { + if (i++ == frame) { + break; + } + + do { + execute_data = execute_data->prev_execute_data; + } while (execute_data && execute_data->opline == NULL); + } + + if (execute_data == NULL) { + phpdbg_error("No frame #%d", frame); + return; + } + + restore_frame(); + + if (frame > 0) { + PHPDBG_FRAME(num) = frame; + + /* backup things and jump back */ + PHPDBG_FRAME(execute_data) = EG(current_execute_data); + EG(current_execute_data) = execute_data; + + EG(opline_ptr) = &PHPDBG_EX(opline); + EG(active_op_array) = PHPDBG_EX(op_array); + EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value); + EG(active_symbol_table) = PHPDBG_EX(symbol_table); + EG(This) = PHPDBG_EX(current_this); + EG(scope) = PHPDBG_EX(current_scope); + EG(called_scope) = PHPDBG_EX(current_called_scope); + } + + phpdbg_notice("Switched to frame #%d", frame); + phpdbg_list_file( + zend_get_executed_filename(TSRMLS_C), + 3, + zend_get_executed_lineno(TSRMLS_C)-1, + zend_get_executed_lineno(TSRMLS_C) + TSRMLS_CC + ); +} + +void restore_frame() { + if (PHPDBG_FRAME(num) == 0) { + return; + } + + PHPDBG_FRAME(num) = 0; + + /* move things back */ + EG(current_execute_data) = PHPDBG_FRAME(execute_data); + + EG(opline_ptr) = &PHPDBG_EX(opline); + EG(active_op_array) = PHPDBG_EX(op_array); + EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value); + EG(active_symbol_table) = PHPDBG_EX(symbol_table); + EG(This) = PHPDBG_EX(current_this); + EG(scope) = PHPDBG_EX(current_scope); + EG(called_scope) = PHPDBG_EX(current_called_scope); +} diff --git a/phpdbg_frame.h b/phpdbg_frame.h new file mode 100644 index 0000000000..39efbf18f3 --- /dev/null +++ b/phpdbg_frame.h @@ -0,0 +1,36 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_FRAME_H +#define PHPDBG_FRAME_H + +#include "php.h" + +typedef struct { + long num; + + zend_execute_data *execute_data; +} phpdbg_frame; + +void switch_to_frame(int frame); + +void restore_frame(); + +#endif /* PHPDBG_FRAME_H */ diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 4f557cdc9e..3a7ebacdb8 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -32,6 +32,7 @@ #include "phpdbg_utils.h" #include "phpdbg_prompt.h" #include "phpdbg_cmd.h" +#include "phpdbg_frame.h" /* {{{ command declarations */ static const phpdbg_command_t phpdbg_prompt_commands[] = { @@ -42,8 +43,9 @@ static const phpdbg_command_t phpdbg_prompt_commands[] = { PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0), PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1), PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0), - PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'f', NULL, 0), + PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0), PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1), PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2), PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1), PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0), @@ -370,6 +372,21 @@ PHPDBG_COMMAND(leave) /* {{{ */ return PHPDBG_LEAVE; } /* }}} */ +PHPDBG_COMMAND(frame) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: + switch_to_frame(param->num); + break; + + case EMPTY_PARAM: + phpdbg_writeln("Currently at frame %d:", PHPDBG_G(frame).num); + break; + + phpdbg_default_switch_case(); + } +} /* }}} */ + static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */ { zend_fcall_info fci; @@ -529,21 +546,58 @@ PHPDBG_COMMAND(back) /* {{{ */ case EMPTY_PARAM: case NUMERIC_PARAM: { zval zbacktrace; - zval **tmp; + zval **tmp, **argstmp; HashPosition position; int i = 0, limit = (param->type == NUMERIC_PARAM) ? param->num : 0; + zval **file, **line, **funcname, **class, **type, **args; + char *func, is_class; + long funcsize; + zend_fetch_debug_backtrace( &zbacktrace, 0, 0, limit TSRMLS_CC); - for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position); - zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position)) { - if (i++) { - phpdbg_writeln(","); + zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position); + zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position); + while (1) { + zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **)&file); + zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **)&line); + zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position); + if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position) == FAILURE) { + phpdbg_write("frame #%d {main} at %s:%d", i, Z_STRVAL_PP(file), Z_LVAL_PP(line)); + break; } - zend_print_flat_zval_r(*tmp TSRMLS_CC); + zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), (void **)&funcname); + if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "object", sizeof("object"), (void **)&class)) == FAILURE) { + is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"), (void **)&class); + } else { + zend_get_object_classname(class, &Z_STRVAL_PP(class), &Z_STRLEN_PP(class) TSRMLS_CC); + } + if (is_class) { + zend_hash_find(Z_ARRVAL_PP(tmp), "type", sizeof("type"), (void **)&type); + } + + funcsize = Z_STRLEN_PP(funcname) + (is_class == FAILURE?0:Z_STRLEN_PP(type) + Z_STRLEN_PP(class)) + 1; + + func = emalloc(funcsize + 2); + phpdbg_write("frame #%d: %s%s%s(", i++, Z_STRVAL_PP(funcname), is_class == FAILURE?"":Z_STRVAL_PP(type), is_class == FAILURE?"":Z_STRVAL_PP(class)); + + if (zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"), (void **)&args) == SUCCESS) { + HashPosition iterator; + int j = 0; + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(args), &iterator); + while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args), (void **) &argstmp, &iterator) == SUCCESS) { + if (j++) { + phpdbg_write(", "); + } + zend_print_flat_zval_r(*argstmp TSRMLS_CC); + zend_hash_move_forward_ex(Z_ARRVAL_PP(args), &iterator); + } + } + + phpdbg_writeln(") at %s:%d", Z_STRVAL_PP(file), Z_LVAL_PP(line)); } phpdbg_writeln(EMPTY); @@ -1021,6 +1075,9 @@ last: out: phpdbg_destroy_input(&input TSRMLS_CC); + if (EG(in_execution)) { + restore_frame(); + } return ret; } /* }}} */ diff --git a/phpdbg_prompt.h b/phpdbg_prompt.h index a410cc4ec4..0e52414d7c 100644 --- a/phpdbg_prompt.h +++ b/phpdbg_prompt.h @@ -44,6 +44,7 @@ PHPDBG_COMMAND(eval); PHPDBG_COMMAND(until); PHPDBG_COMMAND(finish); PHPDBG_COMMAND(leave); +PHPDBG_COMMAND(frame); PHPDBG_COMMAND(print); PHPDBG_COMMAND(break); PHPDBG_COMMAND(back); diff --git a/test.php b/test.php index a2c4b48623..949f7c9ada 100644 --- a/test.php +++ b/test.php @@ -12,12 +12,14 @@ class phpdbg { } } -function test() { - $var = 1 + 1; +function test($x) { + $var = $x + 1; $var += 2; $var <<= 3; - $foo = function () {}; + $foo = function () { + echo "bar!\n"; + }; $foo(); @@ -29,7 +31,7 @@ $dbg = new phpdbg(); var_dump( $dbg->isGreat("PHP Rocks !!")); -foreach (test() as $gen) +foreach (test(1) as $gen) continue; echo "it works!\n";