From: Bob Weinand Date: Sun, 1 Dec 2013 14:58:59 +0000 (+0100) Subject: Several fixes and cleanups on opline num support X-Git-Tag: php-5.6.0alpha1~110^2~35 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=25f2d4eca166260bf5e47cbb274669c5d43a4e3c;p=php Several fixes and cleanups on opline num support --- diff --git a/phpdbg.c b/phpdbg.c index 322d54431e..52efd4338b 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -135,6 +135,7 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ 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_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0); @@ -150,6 +151,9 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ { zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]); zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); @@ -241,6 +245,9 @@ static PHP_FUNCTION(phpdbg_clear) { 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_FUNCTION_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); 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]); diff --git a/phpdbg.h b/phpdbg.h index 628f38a0f3..cd71cfd512 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -88,37 +88,41 @@ #define PHPDBG_BREAK_OPCODE 5 #define PHPDBG_BREAK_FUNCTION_OPLINE 6 #define PHPDBG_BREAK_METHOD_OPLINE 7 -#define PHPDBG_BREAK_TABLES 8/* }}} */ +#define PHPDBG_BREAK_FILE_OPLINE 8 +#define PHPDBG_BREAK_TABLES 9 /* }}} */ /* {{{ flags */ -#define PHPDBG_HAS_FILE_BP (1<<1) -#define PHPDBG_HAS_SYM_BP (1<<2) -#define PHPDBG_HAS_OPLINE_BP (1<<3) -#define PHPDBG_HAS_METHOD_BP (1<<4) -#define PHPDBG_HAS_COND_BP (1<<5) -#define PHPDBG_HAS_OPCODE_BP (1<<6) -#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP) - -#define PHPDBG_IN_COND_BP (1<<7) -#define PHPDBG_IN_EVAL (1<<8) - -#define PHPDBG_IS_STEPPING (1<<9) -#define PHPDBG_IS_QUIET (1<<10) -#define PHPDBG_IS_QUITTING (1<<11) -#define PHPDBG_IS_COLOURED (1<<12) -#define PHPDBG_IS_CLEANING (1<<13) - -#define PHPDBG_IN_UNTIL (1<<14) -#define PHPDBG_IN_FINISH (1<<15) -#define PHPDBG_IN_LEAVE (1<<16) -#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) - -#define PHPDBG_IS_REGISTERED (1<<17) -#define PHPDBG_IS_STEPONEVAL (1<<18) -#define PHPDBG_IS_INITIALIZING (1<<19) -#define PHPDBG_IS_SIGNALED (1<<20) -#define PHPDBG_IS_INTERACTIVE (1<<21) -#define PHPDBG_IS_BP_ENABLED (1<<22) +#define PHPDBG_HAS_FILE_BP (1<<1) +#define PHPDBG_HAS_SYM_BP (1<<2) +#define PHPDBG_HAS_OPLINE_BP (1<<3) +#define PHPDBG_HAS_METHOD_BP (1<<4) +#define PHPDBG_HAS_COND_BP (1<<5) +#define PHPDBG_HAS_OPCODE_BP (1<<6) +#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7) +#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8) +#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) +#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) + +#define PHPDBG_IN_COND_BP (1<<8) +#define PHPDBG_IN_EVAL (1<<9) + +#define PHPDBG_IS_STEPPING (1<<10) +#define PHPDBG_IS_QUIET (1<<11) +#define PHPDBG_IS_QUITTING (1<<12) +#define PHPDBG_IS_COLOURED (1<<13) +#define PHPDBG_IS_CLEANING (1<<14) + +#define PHPDBG_IN_UNTIL (1<<15) +#define PHPDBG_IN_FINISH (1<<16) +#define PHPDBG_IN_LEAVE (1<<17) +#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) + +#define PHPDBG_IS_REGISTERED (1<<18) +#define PHPDBG_IS_STEPONEVAL (1<<19) +#define PHPDBG_IS_INITIALIZING (1<<20) +#define PHPDBG_IS_SIGNALED (1<<21) +#define PHPDBG_IS_INTERACTIVE (1<<22) +#define PHPDBG_IS_BP_ENABLED (1<<23) #ifndef _WIN32 # define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED) @@ -144,8 +148,14 @@ /* {{{ structs */ typedef union _phpdbg_btree phpdbg_btree; union _phpdbg_btree { - phpdbg_btree *branches[2]; - zend_op_array *op_array; + phpdbg_btree *branches[2]; + struct { + char *func_name; + zend_uint func_len; + char *class_name; + zend_uint class_len; + zend_uint last; + } info; }; ZEND_BEGIN_MODULE_GLOBALS(phpdbg) diff --git a/phpdbg_bp.c b/phpdbg_bp.c index d22233a93c..2e51abffec 100644 --- a/phpdbg_bp.c +++ b/phpdbg_bp.c @@ -53,8 +53,12 @@ static void phpdbg_opline_breaks_dtor(void *data) /* {{{ */ { phpdbg_breakopline_t *bp = (phpdbg_breakopline_t *) data; - efree((char*)bp->class_name); - efree((char*)bp->func_name); + if (bp->class_name) { + efree((char*)bp->class_name); + } + if (bp->func_name) { + efree((char*)bp->func_name); + } } /* }}} */ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ @@ -284,7 +288,21 @@ PHPDBG_API void phpdbg_save_oplines(zend_op_array *op_array TSRMLS_DC) { } while (i--); } - (*branch)->op_array = op_array; + if (((*branch)->info.func_name = op_array->function_name)) { + (*branch)->info.func_len = strlen(op_array->function_name); + if (op_array->scope) { + (*branch)->info.class_name = op_array->scope->name; + (*branch)->info.class_len = op_array->scope->name_length; + } else { + (*branch)->info.class_name = NULL; + (*branch)->info.class_len = 0; + } + } else { + (*branch)->info.func_len = 0; + (*branch)->info.class_name = op_array->filename; + (*branch)->info.class_len = strlen(op_array->filename); + } + (*branch)->info.last = op_array->last; } PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */ @@ -300,75 +318,102 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ int i = sizeof(void *) * 8 - 1, last_superior_i = -1; phpdbg_btree *branch = PHPDBG_G(opline_btree); HashTable *insert = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]; - HashTable func_breaks, *func_table; + HashTable func_breaks, *func_table = EG(function_table); + zend_ulong opcodes = 0; + + /* get highest opcodes root lower than or eqal to opline */ +#define CHOOSE_BRANCH(n) \ + opcodes = (opcodes << 1) + !!(n); \ + branch = branch->branches[!!(n)]; do { + /* an impossible branch was found if: */ if ((opline >> i) % 2 == 0 && !branch->branches[0]) { + /* there's no lower branch than opline */ if (last_superior_i == -1) { - goto error; + phpdbg_error("No opline could be found at 0x%lx", opline); + return; } + /* reset state */ branch = PHPDBG_G(opline_btree); + opcodes = 0; i = sizeof(void *) * 8 - 1; + /* follow branch according to bits in opline until the last lower branch before the impossible branch */ do { - branch = branch->branches[(opline >> i) % 2 == 1 && branch->branches[1]]; - } while (i-- > last_superior_i); - branch = branch->branches[0]; + CHOOSE_BRANCH((opline >> i) % 2 == 1 && branch->branches[1]); + } while (--i > last_superior_i); + /* use now the lower branch of which we can be sure that it contains only branches lower than opline */ + CHOOSE_BRANCH(0); + /* and choose the highest possible branch in the branch containing only branches lower than opline */ while (i--) { - branch = branch->branches[branch->branches[1] != NULL]; + CHOOSE_BRANCH(branch->branches[1]); } break; } + /* follow branch according to bits in opline until having found an impossible branch */ if ((opline >> i) % 2 == 1 && branch->branches[1]) { if (branch->branches[0]) { last_superior_i = i; } - branch = branch->branches[1]; + CHOOSE_BRANCH(1); } else { - branch = branch->branches[0]; + CHOOSE_BRANCH(0); } } while (i--); - if ((zend_ulong)(branch->op_array->opcodes + branch->op_array->last) <= opline || - (opline - (zend_ulong)branch->op_array->opcodes) % sizeof(zend_op) > 0) { -error: - phpdbg_error("No opline could be found at 0x%lx", opline); + /* make sure that opline is an opcode address in the closest zend_op array */ + if (opcodes + branch->info.last * sizeof(zend_op) <= opline || + (opline - opcodes) % sizeof(zend_op) > 0) { + phpdbg_error("No opline could be found at 0x%lx; the closest opline is at 0x%lx (%s %s%s%s) with size %ld", + opline, + opcodes, + branch->info.func_name?(branch->info.class_name?"method":"function"):"file", + branch->info.func_name?branch->info.func_name:"", + branch->info.func_name&&branch->info.class_name?"::":"", + branch->info.class_name?branch->info.class_name:"", + branch->info.last); return; } - opline_break.opline = (opline - (zend_ulong)branch->op_array) / sizeof(zend_op); + opline_break.opline = (opline - opcodes) / sizeof(zend_op); PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP; - new_break.name = NULL; - new_break.opline = opline; - new_break.id = PHPDBG_G(bp_count)++; - - zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline, - &new_break, sizeof(phpdbg_breakline_t), NULL); - - opline_break.func_len = branch->op_array->function_name?strlen(branch->op_array->function_name):0; - opline_break.func_name = opline_break.func_len?estrndup(branch->op_array->function_name, opline_break.func_len):""; - opline_break.id = new_break.id; - if (branch->op_array->scope) { + opline_break.func_len = branch->info.func_len; + opline_break.func_name = branch->info.func_name; + opline_break.id = PHPDBG_G(bp_count)++; + if (branch->info.func_name == NULL) { + opline_break.func_len = branch->info.class_len; + opline_break.func_name = branch->info.class_name; + + insert = &PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]; + PHPDBG_G(flags) |= PHPDBG_HAS_FILE_OPLINE_BP; + new_break.type = PHPDBG_BREAK_FILE_OPLINE; + } else if (branch->info.class_name) { HashTable class_breaks, *class_table; if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], - branch->op_array->scope->name, - branch->op_array->scope->name_length, + branch->info.class_name, + branch->info.class_len, (void **)&class_table ) == FAILURE) { zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0); zend_hash_update( &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], - branch->op_array->scope->name, - branch->op_array->scope->name_length, + branch->info.class_name, + branch->info.class_len, (void **)&class_breaks, sizeof(HashTable), (void **)&class_table); } - opline_break.class_len = branch->op_array->scope->name_length; - opline_break.class_name = estrndup(branch->op_array->scope->name, opline_break.class_len); + opline_break.class_len = branch->info.class_len; + opline_break.class_name = branch->info.class_name; insert = class_table; + PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_OPLINE_BP; + new_break.type = PHPDBG_BREAK_METHOD_OPLINE; + } else { + PHPDBG_G(flags) |= PHPDBG_HAS_FUNCTION_OPLINE_BP; + new_break.type = PHPDBG_BREAK_FUNCTION_OPLINE; } if (zend_hash_find(insert, opline_break.func_name, opline_break.func_len, (void **)&func_table) == FAILURE) { @@ -380,10 +425,24 @@ error: (void **)&func_breaks, sizeof(HashTable), (void **)&func_table); } + /* insert opline num breakpoint information */ zend_hash_index_update(func_table, opline_break.opline, &opline_break, sizeof(phpdbg_breakopline_t), NULL); - phpdbg_notice("Breakpoint #%d added at %#lx", - new_break.id, new_break.opline); + /* insert opline breakpoint */ + new_break.name = NULL; + new_break.opline = opline; + new_break.id = opline_break.id; /* use same id to identify */ + zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline, + &new_break, sizeof(phpdbg_breakline_t), NULL); + + phpdbg_notice("Breakpoint #%d added at %#lx (opcode #%ld in %s %s%s%s)", + new_break.id, + new_break.opline, + (opline - opcodes) / sizeof(zend_op), + branch->info.func_name?(branch->info.class_name?"method":"function"):"file", + branch->info.func_name?branch->info.func_name:"", + branch->info.func_name&&branch->info.class_name?"::":"", + branch->info.class_name?branch->info.class_name:""); } else { phpdbg_notice("Breakpoint exists at %#lx", opline); } @@ -528,6 +587,8 @@ PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const cha return; } + PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_OPLINE_BP; + zend_hash_index_update(method_table, new_break.opline, &new_break, sizeof(phpdbg_breakopline_t), NULL); } @@ -538,6 +599,8 @@ PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int new_break.func_len = strlen(function); new_break.func_name = estrndup(function, new_break.func_len); + new_break.class_len = 0; + new_break.class_name = NULL; new_break.opline = opline; new_break.id = PHPDBG_G(bp_count)++; @@ -564,12 +627,14 @@ PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int } if (zend_hash_index_exists(func_table, opline)) { - phpdbg_notice("Breakpoint already exists for %s:%d", new_break.func_len, opline); + phpdbg_notice("Breakpoint already exists for %s:%d", new_break.func_name, opline); efree(new_break.func_name); PHPDBG_G(bp_count)--; return; } + PHPDBG_G(flags) |= PHPDBG_HAS_FUNCTION_OPLINE_BP; + zend_hash_index_update(func_table, new_break.opline, &new_break, sizeof(phpdbg_breakopline_t), NULL); } diff --git a/phpdbg_bp.h b/phpdbg_bp.h index 4e151d0398..5f8ed6622a 100644 --- a/phpdbg_bp.h +++ b/phpdbg_bp.h @@ -69,6 +69,7 @@ typedef struct _phpdbg_breakopline_t { typedef struct _phpdbg_breakline_t { const char *name; zend_ulong opline; + zend_uchar type; int id; } phpdbg_breakline_t;