From 57ae58c9bf2751433e2f355e3bffaea7679cc01d Mon Sep 17 00:00:00 2001 From: krakjoe Date: Wed, 13 Nov 2013 14:22:01 +0000 Subject: [PATCH] support conditional breakpoints --- config.m4 | 2 +- phpdbg.c | 24 +++++++-- phpdbg.h | 23 +++++---- phpdbg_bp.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++- phpdbg_bp.h | 11 +++++ phpdbg_prompt.c | 116 +++++++++++++++++++++++++++++-------------- test.php | 2 + 7 files changed, 256 insertions(+), 51 deletions(-) diff --git a/config.m4 b/config.m4 index 92f626e865..7d6b8262bf 100644 --- a/config.m4 +++ b/config.m4 @@ -9,7 +9,7 @@ if test "$PHP_PHPDBG" != "no"; then AC_DEFINE(HAVE_PHPDBG, 1, [ ]) PHP_PHPDBG_CFLAGS="-I$abc_srcdir" - PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.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" PHP_SUBST(PHP_PHPDBG_CFLAGS) PHP_SUBST(PHP_PHPDBG_FILES) diff --git a/phpdbg.c b/phpdbg.c index 24c5c1078a..f8bdd6aa78 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -64,13 +64,30 @@ static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ zend_hash_destroy((HashTable*)brake); } /* }}} */ +static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +{ + phpdbg_breakcond_t *brake = *(phpdbg_breakcond_t**) data; + + if (brake) { + if (brake->ops) { + TSRMLS_FETCH(); + + destroy_op_array( + brake->ops TSRMLS_CC); + efree(brake->ops); + } + zval_dtor(&brake->code); + } +} /* }}} */ + static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ { zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0); - + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); + return SUCCESS; } /* }}} */ @@ -80,7 +97,8 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); - + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); + if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); } @@ -455,7 +473,7 @@ int main(int argc, char **argv) /* {{{ */ zend_try { phpdbg_interactive(TSRMLS_C); } zend_catch { - + } zend_end_try(); } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); diff --git a/phpdbg.h b/phpdbg.h index 063cce1f9a..7ebc35aa5e 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -65,19 +65,22 @@ #define PHPDBG_BREAK_SYM 1 #define PHPDBG_BREAK_OPLINE 2 #define PHPDBG_BREAK_METHOD 3 -#define PHPDBG_BREAK_TABLES 4 /* }}} */ +#define PHPDBG_BREAK_COND 4 +#define PHPDBG_BREAK_TABLES 5 /* }}} */ /* {{{ flags */ -#define PHPDBG_HAS_FILE_BP 0x00000001 -#define PHPDBG_HAS_SYM_BP 0x00000010 -#define PHPDBG_HAS_OPLINE_BP 0x00000100 -#define PHPDBG_HAS_METHOD_BP 0x00001000 -#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP) +#define PHPDBG_HAS_FILE_BP 0x000000000001 +#define PHPDBG_HAS_SYM_BP 0x000000000010 +#define PHPDBG_HAS_OPLINE_BP 0x000000000100 +#define PHPDBG_HAS_METHOD_BP 0x000000001000 +#define PHPDBG_HAS_COND_BP 0x000000010000 +#define PHPDBG_IN_COND_BP 0x000000100000 +#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP) -#define PHPDBG_IS_STEPPING 0x00010000 -#define PHPDBG_IS_QUIET 0x00100000 -#define PHPDBG_IS_QUITTING 0x01000000 -#define PHPDBG_IS_COLOURED 0x10000000 +#define PHPDBG_IS_STEPPING 0x000001000000 +#define PHPDBG_IS_QUIET 0x000010000000 +#define PHPDBG_IS_QUITTING 0x000100000000 +#define PHPDBG_IS_COLOURED 0x001000000000 #define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED) /* }}} */ diff --git a/phpdbg_bp.c b/phpdbg_bp.c index 988898f22e..fdf9df78e5 100644 --- a/phpdbg_bp.c +++ b/phpdbg_bp.c @@ -23,6 +23,7 @@ #include "phpdbg.h" #include "phpdbg_bp.h" #include "phpdbg_utils.h" +#include "zend_globals.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); @@ -167,6 +168,59 @@ void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{ } } /* }}} */ +void phpdbg_set_breakpoint_expression(const char* expr, size_t expr_len TSRMLS_DC) /* {{{ */ +{ + zend_ulong hash = zend_inline_hash_func(expr, expr_len); + + if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) { + phpdbg_breakcond_t new_break; + + zend_op_array *ops = NULL; + zend_uint cops = CG(compiler_options); + + ZVAL_STRINGL(&new_break.code, expr, expr_len, 1); + + new_break.id = PHPDBG_G(bp_count)++; + + cops = CG(compiler_options); + + CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL; + { + zval pv; + + Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1; + Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1); + memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1); + memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len); + Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';'; + Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0'; + Z_TYPE(pv) = IS_STRING; + + new_break.ops = zend_compile_string( + &pv, "Conditional Breakpoint Code" TSRMLS_CC); + + if (new_break.ops) { + phpdbg_breakcond_t *broken; + + zend_hash_index_update( + &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break, sizeof(phpdbg_breakcond_t), (void**)&broken); + phpdbg_notice( + "Conditional breakpoint #%d added %s/%p", broken->id, Z_STRVAL(broken->code), broken->ops); + PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP; + + } else { + phpdbg_error( + "Failed to compile code for expression %s", expr); + zval_dtor(&new_break.code); + PHPDBG_G(bp_count)--; + } + } + CG(compiler_options) = cops; + } else { + phpdbg_notice("Conditional break %s exists", expr); + } +} /* }}} */ + int phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */ { size_t name_len = strlen(op_array->filename); @@ -191,6 +245,8 @@ int phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */ return FAILURE; } /* }}} */ + + int phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */ { const char *fname; @@ -267,13 +323,84 @@ int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ * return FAILURE; } /* }}} */ +int phpdbg_find_conditional_breakpoint(TSRMLS_D) /* {{{ */ +{ + phpdbg_breakcond_t *bp; + HashPosition position; + int breakpoint = FAILURE; + + for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position); + zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS; + zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) { + + zval *retval = NULL; + int orig_interactive = CG(interactive); + zval **orig_retval = EG(return_value_ptr_ptr); + zend_op_array *orig_ops = EG(active_op_array); + zend_op **orig_opline = EG(opline_ptr); + + ALLOC_INIT_ZVAL(retval); + + EG(return_value_ptr_ptr) = &retval; + EG(active_op_array) = bp->ops; + EG(no_extensions) = 1; + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + + CG(interactive) = 0; + + zend_try { + PHPDBG_G(flags) |= PHPDBG_IN_COND_BP; + zend_execute( + EG(active_op_array) TSRMLS_CC); + if (i_zend_is_true(retval)) { + breakpoint = SUCCESS; + } + } zend_catch { + phpdbg_error( + "Error detected while evaluating expression %s", Z_STRVAL(bp->code)); + CG(interactive) = orig_interactive; + + EG(no_extensions)=1; + EG(return_value_ptr_ptr) = orig_retval; + EG(active_op_array) = orig_ops; + EG(opline_ptr) = orig_opline; + PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP; + } zend_end_try(); + + CG(interactive) = orig_interactive; + + EG(no_extensions)=1; + EG(return_value_ptr_ptr) = orig_retval; + EG(active_op_array) = orig_ops; + EG(opline_ptr) = orig_opline; + PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP; + + if (breakpoint == SUCCESS) { + break; + } + } + + if (breakpoint == SUCCESS) { + phpdbg_notice("Conditional breakpoint #%d: (%s) %s:%u", + bp->id, Z_STRVAL(bp->code), + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C)); + } + + return breakpoint; +} /* }}} */ + void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */ { zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); - + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); + PHPDBG_G(flags) &= ~PHPDBG_BP_MASK; PHPDBG_G(bp_count) = 0; diff --git a/phpdbg_bp.h b/phpdbg_bp.h index d6167597f4..42c02c3a2b 100644 --- a/phpdbg_bp.h +++ b/phpdbg_bp.h @@ -60,16 +60,27 @@ typedef struct _phpdbg_breakline_t { int id; } phpdbg_breakline_t; +/** + * Breakpoint opline based representation + */ +typedef struct _phpdbg_breakcond_t { + zval code; + zend_op_array *ops; + int id; +} phpdbg_breakcond_t; + void phpdbg_set_breakpoint_file(const char*, long TSRMLS_DC); void phpdbg_set_breakpoint_symbol(const char* TSRMLS_DC); void phpdbg_set_breakpoint_method(const char*, const char* TSRMLS_DC); void phpdbg_set_breakpoint_opline(zend_ulong TSRMLS_DC); void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t TSRMLS_DC); +void phpdbg_set_breakpoint_expression(const char*, size_t TSRMLS_DC); int phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC); int phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC); int phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC); int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC); +int phpdbg_find_conditional_breakpoint(TSRMLS_D); void phpdbg_clear_breakpoints(TSRMLS_D); void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC); diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 85c5345fcc..e9f220aaa9 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -24,6 +24,7 @@ #include "phpdbg.h" #include "phpdbg_help.h" #include "phpdbg_print.h" +#include "phpdbg_break.h" #include "phpdbg_bp.h" #include "phpdbg_opcode.h" #include "phpdbg_list.h" @@ -122,6 +123,10 @@ static PHPDBG_COMMAND(run) /* {{{ */ } if (PHPDBG_G(ops) || PHPDBG_G(exec)) { + zend_op **orig_opline = EG(opline_ptr); + zend_op_array *orig_op_array = EG(active_op_array); + zval **orig_retval_ptr = EG(return_value_ptr_ptr); + if (!PHPDBG_G(ops)) { if (phpdbg_compile(TSRMLS_C) == FAILURE) { phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec)); @@ -131,16 +136,28 @@ static PHPDBG_COMMAND(run) /* {{{ */ EG(active_op_array) = PHPDBG_G(ops); EG(return_value_ptr_ptr) = &PHPDBG_G(retval); - + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + zend_try { - zend_execute(EG(active_op_array) TSRMLS_CC); + zend_execute( + EG(active_op_array) TSRMLS_CC); } zend_catch { + EG(active_op_array) = orig_op_array; + EG(opline_ptr) = orig_opline; + EG(return_value_ptr_ptr) = orig_retval_ptr; + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { phpdbg_error("Caught excetion in VM"); return FAILURE; } else return SUCCESS; } zend_end_try(); + EG(active_op_array) = orig_op_array; + EG(opline_ptr) = orig_opline; + EG(return_value_ptr_ptr) = orig_retval_ptr; + return SUCCESS; } else { phpdbg_error("Nothing to execute!"); @@ -257,6 +274,13 @@ static PHPDBG_COMMAND(print) /* {{{ */ static PHPDBG_COMMAND(break) /* {{{ */ { char *line_pos; + + if (expr_len > 0L) { + /* allow advanced breakers to run */ + if (phpdbg_do_cmd(phpdbg_break_commands, (char*)expr, expr_len TSRMLS_CC) == SUCCESS) { + return SUCCESS; + } + } if (expr_len <= 0L) { phpdbg_error("No expression found"); @@ -668,52 +692,72 @@ zend_vm_enter: phpdbg_print_opline( execute_data, 0 TSRMLS_CC); - if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP) - && phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC) == SUCCESS) { - while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - continue; - } + /* allow conditional breakpoints to access the vm uninterrupted */ + if (!(PHPDBG_G(flags) & PHPDBG_IN_COND_BP)) { + + if ((PHPDBG_G(flags) & PHPDBG_HAS_COND_BP) + && phpdbg_find_conditional_breakpoint(TSRMLS_C) == SUCCESS) { + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_NEXT: + goto next; + } + } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + } + + if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP) + && phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC) == SUCCESS) { + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_NEXT: + goto next; + } + } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); } - } - if ((PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP))) { - zend_execute_data *previous = execute_data->prev_execute_data; - if (previous && previous != execute_data && previous->opline) { - /* check we are the beginning of a function entry */ - if (execute_data->opline == EG(active_op_array)->opcodes) { - switch (previous->opline->opcode) { - case ZEND_DO_FCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_INIT_STATIC_METHOD_CALL: { - if (phpdbg_find_breakpoint_symbol(previous->function_state.function TSRMLS_CC) == SUCCESS) { - while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - continue; - } + if ((PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP))) { + zend_execute_data *previous = execute_data->prev_execute_data; + if (previous && previous != execute_data && previous->opline) { + /* check we are the beginning of a function entry */ + if (execute_data->opline == EG(active_op_array)->opcodes) { + switch (previous->opline->opcode) { + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_INIT_STATIC_METHOD_CALL: { + if (phpdbg_find_breakpoint_symbol(previous->function_state.function TSRMLS_CC) == SUCCESS) { + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_NEXT: + goto next; + } + } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); } - } - } break; + } break; + } } } } - } - if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP) - && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) { - while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - continue; - } + if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP) + && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) { + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_NEXT: + goto next; + } + } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); } } +next: PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); - if ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)) { - while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - continue; + if (!(PHPDBG_G(flags) & PHPDBG_IN_COND_BP)) { + if ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)) { + while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + continue; + } } } } diff --git a/test.php b/test.php index a9743f4213..ef786ca047 100644 --- a/test.php +++ b/test.php @@ -9,6 +9,8 @@ class phpdbg { $dbg = new phpdbg(); +$test = 1; + var_dump( $dbg->isGreat("PHP Rocks !!")); ?> -- 2.40.0