From: krakjoe Date: Tue, 12 Nov 2013 00:27:48 +0000 (+0000) Subject: break on class methods X-Git-Tag: php-5.6.0alpha1~110^2~468 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e0cbd92fa484dea3549e708ffc0d8402600271fb;p=php break on class methods don't dtor eval'd retval more reliable break on methods --- diff --git a/phpdbg.c b/phpdbg.c index d24016275c..a37781b245 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -41,6 +41,7 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->has_file_bp = 0; pg->has_sym_bp = 0; pg->has_opline_bp = 0; + pg->has_method_bp = 0; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ @@ -68,11 +69,17 @@ static void php_phpdbg_destroy_bp_opline(void *brake) /* {{{ */ free((char*)((phpdbg_breakline_t*)brake)->name); } /* }}} */ +static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ { zend_hash_init(&PHPDBG_G(bp_files), 8, NULL, php_phpdbg_destroy_bp_file, 0); zend_hash_init(&PHPDBG_G(bp_symbols), 8, NULL, php_phpdbg_destroy_bp_symbol, 0); zend_hash_init(&PHPDBG_G(bp_oplines), 8, NULL, php_phpdbg_destroy_bp_opline, 0); + zend_hash_init(&PHPDBG_G(bp_methods), 8, NULL, php_phpdbg_destroy_bp_methods, 0); return SUCCESS; } /* }}} */ @@ -82,6 +89,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ zend_hash_destroy(&PHPDBG_G(bp_files)); zend_hash_destroy(&PHPDBG_G(bp_symbols)); zend_hash_destroy(&PHPDBG_G(bp_oplines)); + zend_hash_destroy(&PHPDBG_G(bp_methods)); if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); @@ -113,6 +121,7 @@ static PHP_FUNCTION(phpdbg_clear) zend_hash_clean(&PHPDBG_G(bp_files)); zend_hash_clean(&PHPDBG_G(bp_symbols)); zend_hash_clean(&PHPDBG_G(bp_oplines)); + zend_hash_clean(&PHPDBG_G(bp_methods)); } /* }}} */ zend_function_entry phpdbg_user_functions[] = { diff --git a/phpdbg.h b/phpdbg.h index 8adc69cf4f..7477c40111 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -48,9 +48,10 @@ typedef struct _phpdbg_command_t phpdbg_command_t; ZEND_BEGIN_MODULE_GLOBALS(phpdbg) - HashTable bp_files; - HashTable bp_symbols; - HashTable bp_oplines; + HashTable bp_files; /* file breakpoints */ + HashTable bp_symbols; /* symbol breakpoints */ + HashTable bp_oplines; /* opline breakpoints */ + HashTable bp_methods; /* method breakpoints */ char *exec; /* file to execute */ size_t exec_len; /* size of exec */ zend_op_array *ops; /* op_array */ @@ -61,6 +62,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) zend_bool has_file_bp; /* file-based breakpoint has been set */ zend_bool has_sym_bp; /* symbol-based breakpoint has been set */ zend_bool has_opline_bp; /* opline-based breakpoint has been set */ + zend_bool has_method_bp; /* method-based breakpoint has been set */ zend_bool quitting; /* quitting flag */ int quiet; /* quiet */ phpdbg_command_t *last; /* last command */ diff --git a/phpdbg_bp.c b/phpdbg_bp.c index 2949f97189..c6ebef3f73 100644 --- a/phpdbg_bp.c +++ b/phpdbg_bp.c @@ -32,6 +32,15 @@ static void phpdbg_llist_breakfile_dtor(void *data) /* {{{ */ efree((char*)bp->filename); } /* }}} */ + +static void phpdbg_class_breaks_dtor(void *data) /* {{{ */ +{ + phpdbg_breakmethod_t *bp = (phpdbg_breakmethod_t*) data; + + efree((char*)bp->class_name); + efree((char*)bp->func_name); +} /* }}} */ + void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */ { phpdbg_breakfile_t new_break; @@ -83,6 +92,42 @@ void phpdbg_set_breakpoint_symbol(const char *name TSRMLS_DC) /* {{{ */ } } /* }}} */ +void phpdbg_set_breakpoint_method(const char* class_name, + size_t class_len, + const char* func_name, + size_t func_len TSRMLS_DC) /* {{{ */ +{ + HashTable class_breaks, *class_table; + + if (zend_hash_find(&PHPDBG_G(bp_methods), class_name, class_len, (void**)&class_table) != SUCCESS) { + zend_hash_init( + &class_breaks, 8, NULL, phpdbg_class_breaks_dtor, 0); + zend_hash_update( + &PHPDBG_G(bp_methods), + class_name, class_len, + (void**)&class_breaks, sizeof(HashTable), (void**)&class_table); + } + + if (!zend_hash_exists(class_table, func_name, func_len)) { + phpdbg_breakmethod_t new_break; + + PHPDBG_G(has_method_bp) = 1; + + new_break.class_name = class_name; + new_break.class_len = class_len; + new_break.func_name = func_name; + new_break.func_len = func_len; + new_break.id = PHPDBG_G(bp_count)++; + + zend_hash_update(class_table, func_name, func_len, &new_break, sizeof(phpdbg_breakmethod_t), NULL); + printf( + "[Breakpoint #%d added at %s::%s]\n", new_break.id, class_name, func_name); + } else { + printf( + "[Breakpoint exists at %s::%s]\n", class_name, func_name); + } +} /* }}} */ + void phpdbg_set_breakpoint_opline(const char *name TSRMLS_DC) /* {{{ */ { zend_ulong opline = strtoul(name, 0, 16); @@ -172,6 +217,40 @@ int phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */ return FAILURE; } /* }}} */ +int phpdbg_find_breakpoint_method(zend_function *fbc TSRMLS_DC) /* {{{ */ +{ + HashTable *class_table; + phpdbg_breakmethod_t *bp; + zend_op_array *ops = NULL; + + if (fbc->type != ZEND_USER_FUNCTION) { + return FAILURE; + } + + ops = ((zend_op_array*)fbc); + + if (!ops->scope) { + return FAILURE; + } + + if (zend_hash_find(&PHPDBG_G(bp_methods), ops->scope->name, ops->scope->name_length, + (void**)&class_table) == SUCCESS) { + if (zend_hash_find( + class_table, + ops->function_name, + strlen(ops->function_name), (void**)&bp) == SUCCESS) { + + printf( + "[Breakpoint #%d in %s::%s() at %s:%u]\n", bp->id, bp->class_name, bp->func_name, + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C)); + return SUCCESS; + } + } + + return FAILURE; +} /* }}} */ + int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */ { phpdbg_breakline_t *bp; @@ -193,10 +272,12 @@ void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */ zend_hash_clean(&PHPDBG_G(bp_files)); zend_hash_clean(&PHPDBG_G(bp_symbols)); zend_hash_clean(&PHPDBG_G(bp_oplines)); - + zend_hash_clean(&PHPDBG_G(bp_methods)); + PHPDBG_G(has_file_bp) = 0; PHPDBG_G(has_sym_bp) = 0; PHPDBG_G(has_opline_bp) = 0; + PHPDBG_G(has_method_bp) = 0; PHPDBG_G(bp_count) = 0; } /* }}} */ diff --git a/phpdbg_bp.h b/phpdbg_bp.h index 4f1a2e237f..77dfd4861e 100644 --- a/phpdbg_bp.h +++ b/phpdbg_bp.h @@ -40,6 +40,17 @@ typedef struct _phpdbg_breaksymbol_t { int id; } phpdbg_breaksymbol_t; +/** + * Breakpoint method based representation + */ +typedef struct _phpdbg_breakmethod_t { + const char *class_name; + size_t class_len; + const char *func_name; + size_t func_len; + int id; +} phpdbg_breakmethod_t; + /** * Breakpoint opline based representation */ @@ -51,11 +62,13 @@ typedef struct _phpdbg_breakline_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*, size_t, const char*, size_t TSRMLS_DC); void phpdbg_set_breakpoint_opline(const char* TSRMLS_DC); void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_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_function* TSRMLS_DC); int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC); void phpdbg_clear_breakpoints(TSRMLS_D); diff --git a/phpdbg_help.c b/phpdbg_help.c index ef8b6208b9..00404a446b 100644 --- a/phpdbg_help.c +++ b/phpdbg_help.c @@ -85,12 +85,15 @@ PHPDBG_HELP(break) /* {{{ */ printf("Setting a breakpoint stops execution at a specific stage, the syntax is:\n"); printf("\tfile:line\n"); printf("\tfunction\n"); + printf("\t\\my\\class::method\n"); printf("\t0x16\n"); printf("For example:\n"); printf("\tphpdbg> break test.php:1\n"); printf("Will break execution on line 1 of test.php\n"); printf("\tphpdbg> break my_function\n"); printf("Will break execution on entry to my_function\n"); + printf("\tphpdbg> break \\my\\class::method\n"); + printf("Will break execution on entry to \\my\\class::method\n"); printf("\tphpdbg> break 0x7ff68f570e08\n"); printf("Will break at the opline with the address provided (addresses are shown during execution)\n"); printf("It is important to note, an address is only valid for the current compiled representation of the script\n"); diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 306e929c5a..a29bc75c1c 100644 --- a/phpdbg_prompt.c +++ b/phpdbg_prompt.c @@ -149,9 +149,9 @@ static PHPDBG_COMMAND(eval) /* {{{ */ if (expr_len) { if (zend_eval_stringl((char*)expr, expr_len-1, &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) { - zend_print_zval_r(&retval, 0 TSRMLS_CC); + zend_print_zval_r( + &retval, 0 TSRMLS_CC); printf("\n"); - zval_dtor(&retval); } } else { printf("[No expression provided !]\n"); @@ -286,25 +286,57 @@ static PHPDBG_COMMAND(print) /* {{{ */ static PHPDBG_COMMAND(break) /* {{{ */ { - const char *line_pos = zend_memrchr(expr, ':', expr_len); - + char *line_pos = NULL; + char *func_pos = NULL; + + if (!expr_len) { + printf( + "[No expression found]\n"); + return FAILURE; + } + + line_pos = strchr(expr, ':'); + if (line_pos) { - char path[MAXPATHLEN], resolved_name[MAXPATHLEN]; - long line_num = strtol(line_pos+1, NULL, 0); - - if (line_num) { - memcpy(path, expr, line_pos - expr); - path[line_pos - expr] = 0; - - if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { - printf("[Failed to expand path %s]\n", path); - return FAILURE; + if (!(func_pos=strchr(line_pos+1, ':'))) { + char path[MAXPATHLEN], resolved_name[MAXPATHLEN]; + long line_num = strtol(line_pos+1, NULL, 0); + + if (line_num) { + memcpy(path, expr, line_pos - expr); + path[line_pos - expr] = 0; + + if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { + printf("[Failed to expand path %s]\n", path); + return FAILURE; + } + + phpdbg_set_breakpoint_file(resolved_name, line_num TSRMLS_CC); + } else { + printf("[No line specified in expression %s]\n", expr); + return FAILURE; + } + } else { + char *class; + char *func; + + size_t func_len = strlen(func_pos+1), + class_len = (line_pos - expr); + + if (func_len) { + class = emalloc(class_len+1); + func = emalloc(func_len+1); + + memcpy(class, expr, class_len); + class[class_len]='\0'; + memcpy(func, func_pos+1, func_len); + func[func_len]='\0'; + + phpdbg_set_breakpoint_method(class, class_len, func, func_len TSRMLS_CC); + } else { + printf("[No function found in method expression %s]\n", expr); + return FAILURE; } - - phpdbg_set_breakpoint_file(resolved_name, line_num TSRMLS_CC); - } else { - printf("[No line specified in expression %s]\n", expr); - return FAILURE; } } else { if (expr_len > 2 && expr[0] == '0' && expr[1] == 'x') { @@ -313,11 +345,16 @@ static PHPDBG_COMMAND(break) /* {{{ */ char name[200]; size_t name_len = strlen(expr); - name_len = MIN(name_len, 200); - memcpy(name, expr, name_len); - name[name_len] = 0; + if (name_len) { + name_len = MIN(name_len, 200); + memcpy(name, expr, name_len); + name[name_len] = 0; - phpdbg_set_breakpoint_symbol(name TSRMLS_CC); + phpdbg_set_breakpoint_symbol(name TSRMLS_CC); + } else { + printf("[Malformed break command found]\n"); + return FAILURE; + } } } @@ -350,22 +387,28 @@ static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC) static PHPDBG_COMMAND(clean) /* {{{ */ { - printf("[Cleaning Environment:]\n"); - printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table))); - printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table))); - printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants))); - printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files))); - - zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); - zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); - zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) clean_non_persistent_constant_full TSRMLS_CC); - zend_hash_clean(&EG(included_files)); - - printf("[Clean Environment:]\n"); - printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table))); - printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table))); - printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants))); - printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files))); + if (!EG(in_execution)) { + printf("[Cleaning Environment:]\n"); + printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table))); + printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table))); + printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants))); + printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files))); + + zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); + zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); + zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) clean_non_persistent_constant_full TSRMLS_CC); + zend_hash_clean(&EG(included_files)); + + printf("[Clean Environment:]\n"); + printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table))); + printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table))); + printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants))); + printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files))); + } else { + printf( + "[Cannot clean environment while executing]\n"); + return FAILURE; + } return SUCCESS; } /* }}} */ @@ -376,7 +419,8 @@ static PHPDBG_COMMAND(clear) /* {{{ */ printf("[\tFile\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_files))); printf("[\tSymbols\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_symbols))); printf("[\tOplines\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_oplines))); - + printf("[\tMethods\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_methods))); + phpdbg_clear_breakpoints(TSRMLS_C); return SUCCESS; @@ -540,23 +584,29 @@ zend_vm_enter: } } - if (PHPDBG_G(has_sym_bp) && execute_data->opline->opcode != ZEND_RETURN) { + if ((PHPDBG_G(has_sym_bp)||PHPDBG_G(has_method_bp))) { zend_execute_data *previous = execute_data->prev_execute_data; if (previous && previous != execute_data && previous->opline) { - if (previous->opline->opcode == ZEND_DO_FCALL - || previous->opline->opcode == ZEND_DO_FCALL_BY_NAME) { - if (phpdbg_find_breakpoint_symbol( - previous->function_state.function TSRMLS_CC) == SUCCESS) { - while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { - if (!PHPDBG_G(quitting)) { - continue; + /* 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 || + phpdbg_find_breakpoint_method(previous->function_state.function TSRMLS_CC) == SUCCESS) { + while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { + if (!PHPDBG_G(quitting)) { + continue; + } + } } - } + } break; } } } } - + if (PHPDBG_G(has_opline_bp) && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) { while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) { diff --git a/test.php b/test.php index 20fc66c7c5..457e487db7 100644 --- a/test.php +++ b/test.php @@ -1,22 +1,18 @@ method()); -test2(); return true; ?>