From: Bob Weinand Date: Fri, 10 Jan 2014 18:54:07 +0000 (-0500) Subject: Added rudimentary support for watchpoints on an array itself X-Git-Tag: php-5.6.0beta2~1^2~37^2~38 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=78e274af31e33078090ee29007960a9e169058e4;p=php Added rudimentary support for watchpoints on an array itself --- diff --git a/phpdbg.c b/phpdbg.c index 4955d2e1bf..54710fad77 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -28,6 +28,15 @@ #include "phpdbg_list.h" #include "phpdbg_utils.h" #include "phpdbg_set.h" +#include "zend_alloc.h" + +/* the beginning (= the important part) of the _zend_mm_heap struct defined in Zend/zend_alloc.c */ +struct _zend_mm_heap { + int use_zend_alloc; + void *(*_malloc)(size_t); + void (*_free)(void*); + void *(*_realloc)(void*, size_t); +}; /* {{{ remote console headers */ #ifndef _WIN32 @@ -1144,6 +1153,15 @@ phpdbg_main: if (phpdbg->startup(phpdbg) == SUCCESS) { + zend_mm_heap *mm_heap = zend_mm_set_heap(NULL TSRMLS_CC); + if (!mm_heap->use_zend_alloc) { + free(mm_heap); + mm_heap = zend_mm_startup(); + } + PHPDBG_G(original_free_function) = mm_heap->_free; + mm_heap->_free = phpdbg_watch_efree; + zend_mm_set_heap(mm_heap TSRMLS_CC); + zend_activate(TSRMLS_C); #ifdef ZEND_SIGNALS diff --git a/phpdbg.h b/phpdbg.h index 63cdd352b7..79f98fd2f7 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -175,6 +175,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable watchpoints; /* watchpoints */ zend_llist watchlist_mem; /* triggered watchpoints */ zend_bool watchpoint_hit; /* a watchpoint was hit */ + void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */ char *exec; /* file to execute */ size_t exec_len; /* size of exec */ diff --git a/phpdbg_btree.c b/phpdbg_btree.c index 6b70d1453d..f3b86dd91f 100644 --- a/phpdbg_btree.c +++ b/phpdbg_btree.c @@ -166,21 +166,24 @@ int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, } int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) { - int i = tree->depth - 1; + int i = tree->depth; phpdbg_btree_branch *branch = tree->branch; int i_last_dual_branch = -1, last_dual_branch_branch; phpdbg_btree_branch *last_dual_branch = NULL; + goto check_branch_existence; do { - if (branch == NULL) { - return FAILURE; - } if (branch->branches[0] && branch->branches[1]) { last_dual_branch = branch; i_last_dual_branch = i; last_dual_branch_branch = (idx >> i) % 2; } branch = branch->branches[(idx >> i) % 2]; + +check_branch_existence: + if (branch == NULL) { + return FAILURE; + } } while (i--); if (i_last_dual_branch == -1) { diff --git a/phpdbg_watch.c b/phpdbg_watch.c index f6d8646a1f..a0b9d19dcc 100644 --- a/phpdbg_watch.c +++ b/phpdbg_watch.c @@ -101,6 +101,11 @@ void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) { watch->type = WATCH_ON_ZVAL; } +void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) { + phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch); + watch->type = WATCH_ON_HASHTABLE; +} + static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { phpdbg_store_watchpoint(watch TSRMLS_CC); zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL); @@ -110,6 +115,27 @@ static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { return SUCCESS; } +static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { + HashTable *ht; + + switch (Z_TYPE_P(watch->addr.zv)) { + case IS_ARRAY: + ht = Z_ARRVAL_P(watch->addr.zv); + break; + case IS_OBJECT: + ht = Z_OBJPROP_P(watch->addr.zv); + break; + default: + return FAILURE; + } + + phpdbg_create_ht_watchpoint(Z_ARRVAL_P(watch->addr.zv), watch); + + phpdbg_create_watchpoint(watch TSRMLS_CC); + + return SUCCESS; +} + static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { HashTable *ht; @@ -134,6 +160,7 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ HashPosition position; zval **zv; zval key; + for (zend_hash_internal_pointer_reset_ex(ht, &position); zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS; zend_hash_move_forward_ex(ht, &position)) { @@ -159,6 +186,20 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ } } + { + phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t)); + + new_watch->parent = watch; + new_watch->parent_container = ht; + new_watch->name_in_parent = watch->name_in_parent; + new_watch->name_in_parent_len = watch->name_in_parent_len; + new_watch->str = watch->str; + new_watch->str_len = watch->str_len; + + phpdbg_create_ht_watchpoint(ht, new_watch); + phpdbg_create_watchpoint(new_watch TSRMLS_CC); + } + return SUCCESS; } @@ -306,6 +347,23 @@ PHPDBG_WATCH(recursive) /* {{{ */ return SUCCESS; } /* }}} */ +PHPDBG_WATCH(array) /* {{{ */ +{ + if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) { + return SUCCESS; + } + + switch (param->type) { + case STR_PARAM: + phpdbg_watchpoint_parse_input(param->str, param->len, EG(active_symbol_table), 0, phpdbg_create_array_watchpoint TSRMLS_CC); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC) { if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) { return SUCCESS; @@ -404,11 +462,11 @@ static int phpdbg_print_changed_zval(void *llist_data) { phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data; /* fetch all changes between dump->page and dump->page + dump->size */ phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong)dump->page, (zend_ulong)dump->page + dump->size); - phpdbg_btree_result *result; - + phpdbg_btree_result *result, *htresult; + int elementDiff; while ((result = phpdbg_btree_next(&pos))) { - phpdbg_watchpoint_t *watch = result->ptr; + phpdbg_watchpoint_t *watch = result->ptr, *htwatch; void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page); if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) { PHPDBG_G(watchpoint_hit) = 1; @@ -425,12 +483,40 @@ static int phpdbg_print_changed_zval(void *llist_data) { if (((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc && !zend_symtable_exists(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1)) { phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str); zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + + if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY) { + goto remove_ht_watch; + } + break; } phpdbg_write("New value: "); zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC); phpdbg_writeln("\nNew refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc); + + if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) { +remove_ht_watch: + htwatch = htresult->ptr; + zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len); + } + + break; + + case WATCH_ON_HASHTABLE: + + elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht); + if (elementDiff) { + if (elementDiff < 0) { + phpdbg_writeln("%d elements were removed from the array", -elementDiff); + } else { + phpdbg_writeln("%d elements were added to the array", elementDiff); + /* TODO: add, if recursive watchpoint, the new elements to the elements to watch */ + } + } + if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) { + phpdbg_writeln("Internal pointer of array was changed"); + } break; } } @@ -460,3 +546,18 @@ void phpdbg_list_watchpoints(TSRMLS_D) { phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str); } } + +void phpdbg_watch_efree(void *ptr) { + TSRMLS_FETCH(); + phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr); + + if (result) { + phpdbg_watchpoint_t *watch = result->ptr; + + if (watch->addr.ptr + watch->size > ptr) { + zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + } + } + + PHPDBG_G(original_free_function)(ptr); +} diff --git a/phpdbg_watch.h b/phpdbg_watch.h index 411349816b..8e53087584 100644 --- a/phpdbg_watch.h +++ b/phpdbg_watch.h @@ -29,6 +29,7 @@ /** * Printer Forward Declarations */ +PHPDBG_WATCH(array); PHPDBG_WATCH(delete); PHPDBG_WATCH(recursive); @@ -37,8 +38,9 @@ PHPDBG_WATCH(recursive); */ static const phpdbg_command_t phpdbg_watch_commands[] = { - PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, 1), - PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, 1), + PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, 1), + PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, 1), + PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, 1), }; /* Watchpoint functions/typedefs */ @@ -81,4 +83,6 @@ int phpdbg_print_changed_zvals(TSRMLS_D); void phpdbg_list_watchpoints(TSRMLS_D); +void phpdbg_watch_efree(void *ptr); + #endif