]> granicus.if.org Git - php/commitdiff
Added frames and beautified backtrace
authorBob Weinand <bobwei9@hotmail.com>
Fri, 22 Nov 2013 17:27:55 +0000 (18:27 +0100)
committerBob Weinand <bobwei9@hotmail.com>
Fri, 22 Nov 2013 17:27:55 +0000 (18:27 +0100)
config.m4
config.w32
phpdbg.c
phpdbg.h
phpdbg_frame.c [new file with mode: 0644]
phpdbg_frame.h [new file with mode: 0644]
phpdbg_prompt.c
phpdbg_prompt.h
test.php

index ee80daa25c4863e7cb6f9c5db373d4975d32852b..d8bcce5a3159cea7060bbc943f45ecd600770acc 100644 (file)
--- 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)
index eee355da5e1c7ef293b954def03eef894496784a..1356ced609aa4457264b7c378d4e686b91d97e65 100644 (file)
@@ -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");
 }
 
index 6b219fa7561f7f413d8b53779106f4097c8260c2..4b9d39b6388df27b10c9c4605df8e2351f85e972 100644 (file)
--- 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) /* {{{ */
index 967cd8fb0beaa68842eda957069566a6c6d6fcf0..98c007036e936715988132c75604cb26378b768e 100644 (file)
--- 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 (file)
index 0000000..aeda9ed
--- /dev/null
@@ -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 <felipe@php.net>                                |
+   | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
+   | Authors: Bob Weinand <bwoebi@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+#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 (file)
index 0000000..39efbf1
--- /dev/null
@@ -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 <felipe@php.net>                                |
+   | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
+   | Authors: Bob Weinand <bwoebi@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+#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 */
index 4f557cdc9e55883d07cd47000b968598aa24d532..3a7ebacdb8abb975377299125b605ee541f148b6 100644 (file)
@@ -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;
 } /* }}} */
index a410cc4ec4a478f1d2c318e7fbd174006bcbc37a..0e52414d7c94207034c44d30108144712ed07f3b 100644 (file)
@@ -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);
index a2c4b48623cf8fddb835215c51b01d2633f4b0e7..949f7c9adaf365d71aa98acb39f2cd8afdde11c1 100644 (file)
--- 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";