diff --git a/phpdbg.c b/phpdbg.c index b484c83d4ec..c6000022595 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -26,6 +26,7 @@ void (*zend_execute_internal_old)(zend_execute_data *execute_data_ptr, zend_fcal static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) { pg->exec = NULL; pg->ops = NULL; + pg->stepping = 0; } static PHP_MINIT_FUNCTION(phpdbg) { diff --git a/phpdbg.h b/phpdbg.h index 8dbf89fe629..26fb118f4a4 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -36,14 +36,18 @@ # define PHPDBG_G(v) (phpdbg_globals.v) #endif +#define PHPDBG_NEXT 2 + ZEND_BEGIN_MODULE_GLOBALS(phpdbg) - HashTable break_files; - HashTable break_symbols; - char *exec; /* file to execute */ - size_t exec_len; /* size of exec */ - zend_op_array *ops; /* op_array */ - zval *retval; /* return value */ - zend_bool has_breakpoint; /* breakpoint has been set */ + HashTable break_files; + HashTable break_symbols; + char *exec; /* file to execute */ + size_t exec_len; /* size of exec */ + zend_op_array *ops; /* op_array */ + zval *retval; /* return value */ + zend_bool stepping; /* stepping */ + int vmret; /* return from last opcode handler execution */ + zend_bool has_file_bp; /* file-based breakpoint has been set */ ZEND_END_MODULE_GLOBALS(phpdbg) #include "phpdbg_prompt.h" diff --git a/phpdbg_help.c b/phpdbg_help.c index 38ae6c36f39..a72f53fd13c 100644 --- a/phpdbg_help.c +++ b/phpdbg_help.c @@ -30,6 +30,20 @@ PHPDBG_HELP(exec) /* {{{ */ return SUCCESS; } /* }}} */ +PHPDBG_HELP(step) { /* {{{ */ + printf("You can enable and disable stepping at any phpdbg prompt during execution\n"); + printf("For example:\n"); + printf("phpdbg> stepping 1\n"); + printf("Will enable stepping\n"); + printf("While stepping is enabled you are presented with a prompt after the execution of each opcode\n"); + return SUCCESS; +} /* }}} */ + +PHPDBG_HELP(next) { /* {{{ */ + printf("While stepping through execution, use the next command to step back into the vm and execute the next opcode"); + return SUCCESS; +} /* }}} */ + PHPDBG_HELP(compile) /* {{{ */ { printf("Pre-compilation of the execution context provides the opportunity to inspect the opcodes before they are executed\n"); @@ -43,7 +57,7 @@ PHPDBG_HELP(print) /* {{{ */ { printf("By default, print will show information about the current execution environment\n"); printf("To show specific information pass an expression to print, for example:\n"); - printf("\tprint opcodes[0]\n"); + printf("\tphpdbg> print opcodes[0]\n"); printf("Will show the opline @ 0\n"); printf("Available print commands:\n"); printf("\tNone\n"); @@ -51,6 +65,13 @@ PHPDBG_HELP(print) /* {{{ */ return SUCCESS; } /* }}} */ +PHPDBG_HELP(run) /* {{{ */ +{ + printf("Run the code inside the debug vm, you should have break points and variables set before running\n"); + printf("The execution context must not be set, but not necessarily compiled before execution occurs\n"); + return SUCCESS; +} /* }}} */ + PHPDBG_HELP(break) /* {{{ */ { printf("doing break help: %s\n", expr); diff --git a/phpdbg_help.h b/phpdbg_help.h index dba58d7dc48..69b710f782d 100644 --- a/phpdbg_help.h +++ b/phpdbg_help.h @@ -33,6 +33,9 @@ */ PHPDBG_HELP(exec); PHPDBG_HELP(compile); +PHPDBG_HELP(step); +PHPDBG_HELP(next); +PHPDBG_HELP(run); PHPDBG_HELP(print); PHPDBG_HELP(break); @@ -42,6 +45,9 @@ PHPDBG_HELP(break); static const phpdbg_command_t phpdbg_help_commands[] = { PHPDBG_HELP_D(exec, "the execution context should be a valid phpdbg path"), PHPDBG_HELP_D(compile, "pre-compilation allows inspection of code before execution"), + PHPDBG_HELP_D(step, "stepping through execution allows inspection of the opline after every opcode"), + PHPDBG_HELP_D(next, "execute the next opcode"), + PHPDBG_HELP_D(run, "execution inside the phpdbg vm allows detailed inspection and debugging"), PHPDBG_HELP_D(print, "printing allows inspection of the execution environment"), PHPDBG_HELP_D(break, "breakpoints allow execution interruption"), {NULL, 0, 0} diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 1af00badcf4..9ab38ae096a 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -85,6 +85,15 @@ static PHPDBG_COMMAND(compile) { /* {{{ */ } } /* }}} */ +static PHPDBG_COMMAND(step) { /* {{{ */ + PHPDBG_G(stepping) = atoi(expr); + return SUCCESS; +} /* }}} */ + +static PHPDBG_COMMAND(next) { /* {{{ */ + return PHPDBG_NEXT; +} /* }}} */ + static PHPDBG_COMMAND(run) { /* {{{ */ if (PHPDBG_G(ops) || PHPDBG_G(exec)) { if (!PHPDBG_G(ops)) { @@ -116,9 +125,12 @@ static PHPDBG_COMMAND(print) { /* {{{ */ printf("Showing Execution Context Information:\n"); printf("Exec\t\t%s\n", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); printf("Compiled\t%s\n", PHPDBG_G(ops) ? "yes" : "no"); + printf("Stepping\t%s\n", PHPDBG_G(stepping) ? "on" : "off"); if (PHPDBG_G(ops)) { - printf("Opcodes\t\t%d\n", PHPDBG_G(ops)->last-1); - printf("Variables\t%d\n", PHPDBG_G(ops)->last_var-1); + printf("Opcodes\t\t%d\n", PHPDBG_G(ops)->last); + if (PHPDBG_G(ops)->last_var) { + printf("Variables\t%d\n", PHPDBG_G(ops)->last_var-1); + } else printf("Variables\tNone\n"); } } else { printf( @@ -135,17 +147,24 @@ static PHPDBG_COMMAND(break) /* {{{ */ if (line_pos) { long line_num = strtol(line_pos+1, NULL, 0); phpdbg_breakfile_t new_break; - zend_llist break_files; + zend_llist *break_files_ptr; - new_break.filename = estrndup(expr, line_pos - expr); + size_t name_len = line_pos - expr; + + new_break.filename = estrndup(expr, name_len); new_break.line = line_num; - PHPDBG_G(has_breakpoint) = 1; + PHPDBG_G(has_file_bp) = 1; - if (zend_hash_find(&PHPDBG_G(break_files), new_break.filename, line_pos - expr, &break_files) == FAILURE) { + if (zend_hash_find(&PHPDBG_G(break_files), + new_break.filename, name_len, (void**)&break_files_ptr) == FAILURE) { + zend_llist break_files; zend_llist_init(&break_files, sizeof(phpdbg_breakfile_t), NULL, 0); + + zend_hash_update(&PHPDBG_G(break_files), + new_break.filename, name_len, &break_files, sizeof(zend_llist), (void**)&break_files_ptr); } - zend_llist_add_element(&break_files, &new_break); + zend_llist_add_element(break_files_ptr, &new_break); } return SUCCESS; @@ -194,6 +213,8 @@ static PHPDBG_COMMAND(help) /* {{{ */ static const phpdbg_command_t phpdbg_prompt_commands[] = { PHPDBG_COMMAND_D(exec, "set execution context"), PHPDBG_COMMAND_D(compile, "attempt to pre-compile execution context"), + PHPDBG_COMMAND_D(step, "step through execution"), + PHPDBG_COMMAND_D(next, "next opcode"), PHPDBG_COMMAND_D(run, "attempt execution"), PHPDBG_COMMAND_D(print, "print something"), PHPDBG_COMMAND_D(break, "set breakpoint"), @@ -219,12 +240,29 @@ int phpdbg_do_cmd(const phpdbg_command_t *command, char *cmd_line, size_t cmd_le return FAILURE; } /* }}} */ -void phpdbg_breakpoint(zend_op_array *op_array) /* {{{ */ +int phpdbg_breakpoint(zend_op_array *op_array TSRMLS_DC) /* {{{ */ { - printf(">> %s\n", op_array->filename); + size_t name_len = strlen(op_array->filename); + zend_llist *break_list; + + if (zend_hash_find(&PHPDBG_G(break_files), op_array->filename, name_len, + (void**)&break_list) == SUCCESS) { + zend_llist_element *le; + + for (le = break_list->head; le; le = le->next) { + phpdbg_breakfile_t *bp = (phpdbg_breakfile_t*) le->data; + + if (bp->line == (*EG(opline_ptr))->lineno) { + printf("breakpoint reached!\n"); + return SUCCESS; + } + } + } + + return FAILURE; } /* }}} */ -void phpdbg_interactive(int argc, char **argv TSRMLS_DC) /* {{{ */ +int phpdbg_interactive(int argc, char **argv TSRMLS_DC) /* {{{ */ { char cmd[PHPDBG_MAX_CMD]; @@ -238,13 +276,20 @@ void phpdbg_interactive(int argc, char **argv TSRMLS_DC) /* {{{ */ } if (cmd_len) { - if (phpdbg_do_cmd(phpdbg_prompt_commands, cmd, cmd_len TSRMLS_CC) == FAILURE) { - printf("error executing %s !\n", cmd); - } + switch (phpdbg_do_cmd(phpdbg_prompt_commands, cmd, cmd_len TSRMLS_CC)) { + case FAILURE: + printf("error executing %s !\n", cmd); + break; + + case PHPDBG_NEXT: + return PHPDBG_NEXT; + } } printf("phpdbg> "); } + + return SUCCESS; } /* }}} */ void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) @@ -260,7 +305,6 @@ zend_vm_enter: } while (1) { - int ret; #ifdef ZEND_WIN32 if (EG(timed_out)) { zend_timeout(0); @@ -269,12 +313,24 @@ zend_vm_enter: printf("[OPLINE: %p]\n", execute_data->opline); - if (PHPDBG_G(has_breakpoint)) { - phpdbg_breakpoint(execute_data->op_array); + if (PHPDBG_G(has_file_bp) + && phpdbg_breakpoint(execute_data->op_array TSRMLS_CC) == SUCCESS) { + while (phpdbg_interactive(0, NULL TSRMLS_CC) != PHPDBG_NEXT) { + continue; + } } - if ((ret = execute_data->opline->handler(execute_data TSRMLS_CC)) > 0) { - switch (ret) { + PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); + + if (PHPDBG_G(stepping)) { + while (phpdbg_interactive( + 0, NULL TSRMLS_CC) != PHPDBG_NEXT) { + continue; + } + } + + if (PHPDBG_G(vmret) > 0) { + switch (PHPDBG_G(vmret)) { case 1: EG(in_execution) = original_in_execution; return; diff --git a/phpdbg_prompt.h b/phpdbg_prompt.h index 01d3f26c0b2..8a4dfad675f 100644 --- a/phpdbg_prompt.h +++ b/phpdbg_prompt.h @@ -72,7 +72,7 @@ int phpdbg_do_cmd(const phpdbg_command_t *command, char *cmd_line, size_t cmd_le #define PHPDBG_COMMAND(name) \ int phpdbg_do_##name(const char *expr, size_t expr_len TSRMLS_DC) -void phpdbg_interactive(int argc, char **argv TSRMLS_DC); +int phpdbg_interactive(int argc, char **argv TSRMLS_DC); void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC); #endif /* PHPDBG_PROMPT_H */