diff options
Diffstat (limited to 'sapi/phpdbg/phpdbg_watch.c')
-rw-r--r-- | sapi/phpdbg/phpdbg_watch.c | 639 |
1 files changed, 340 insertions, 299 deletions
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c index 054bea6d00..7a39d3ee1a 100644 --- a/sapi/phpdbg/phpdbg_watch.c +++ b/sapi/phpdbg/phpdbg_watch.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 7 | + | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2014 The PHP Group | +----------------------------------------------------------------------+ @@ -30,6 +30,15 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg); +const phpdbg_command_t phpdbg_watch_commands[] = { + PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s", 0), + PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s", 0), + PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s", 0), + PHPDBG_END_COMMAND +}; + +//#define HT_FROM_WATCH(watch) (watch->type == WATCH_ON_OBJECT ? watch->addr.obj->handlers->get_properties(watch->parent_container.zv TSRMLS_CC) : watch->type == WATCH_ON_ARRAY ? &watch->addr.arr->ht : NULL) +#define HT_FROM_ZVP(zvp) (Z_TYPE_P(zvp) == IS_OBJECT ? Z_OBJPROP_P(zvp) : Z_TYPE_P(zvp) == IS_ARRAY ? Z_ARRVAL_P(zvp) : NULL) typedef struct { void *page; @@ -53,7 +62,7 @@ static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) { watch = result->ptr; /* check if that addr is in a mprotect()'ed memory area */ - if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) { + if ((char *) phpdbg_get_page_boundary(watch->addr.ptr) > (char *) addr || (char *) phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *) addr) { /* failure */ return NULL; } @@ -62,10 +71,8 @@ static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) { } static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) { - int m; - /* pagesize is assumed to be in the range of 2^x */ - m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access); + mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access); } static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { @@ -77,11 +84,11 @@ static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRML } static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { - phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch); + phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr, watch); } static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { - phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr); + phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr); } void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) { @@ -99,17 +106,95 @@ void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) { watch->type = WATCH_ON_HASHTABLE; } -void phpdbg_watch_HashTable_dtor(zval **ptr); +void phpdbg_watch_HashTable_dtor(zval *ptr); + +static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC); +static void phpdbg_delete_ht_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC); +static void phpdbg_delete_zval_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC); +static void phpdbg_delete_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC); + +/* TODO: Store all the possible watches the refcounted may refer to (for displaying & deleting by identifier) */ + +static phpdbg_watchpoint_t *phpdbg_create_refcounted_watchpoint(phpdbg_watchpoint_t *parent, zend_refcounted *ref) { + phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); + watch->flags = parent->flags; + watch->parent = parent; + phpdbg_create_addr_watchpoint(&ref->refcount, sizeof(uint32_t), watch); + watch->type = WATCH_ON_REFCOUNTED; + + return watch; +} + +static void phpdbg_add_watch_collision(phpdbg_watchpoint_t *watch TSRMLS_DC) { + phpdbg_watch_collision *cur; + if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref))) { + cur->num++; + if (watch->flags == PHPDBG_WATCH_RECURSIVE) { + cur->refs++; + } + } else { + phpdbg_watch_collision coll; + coll.num = 1; + coll.refs = watch->flags == PHPDBG_WATCH_RECURSIVE; + coll.watch = *watch; + zend_hash_init(&coll.watches, 8, NULL, NULL, 0); + cur = zend_hash_index_add_mem(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref, &coll, sizeof(phpdbg_watch_collision)); + phpdbg_store_watchpoint(&cur->watch TSRMLS_CC); + phpdbg_activate_watchpoint(&cur->watch TSRMLS_CC); + } + + zend_hash_str_add_ptr(&cur->watches, watch->parent->str, watch->parent->str_len, watch->parent); +} + +static void phpdbg_remove_watch_collision(zend_refcounted *ref TSRMLS_DC) { + phpdbg_watch_collision *cur; + if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) ref))) { + phpdbg_watchpoint_t *watch = cur->watch.parent; + + if (watch->flags == PHPDBG_WATCH_RECURSIVE && !--cur->refs) { + phpdbg_delete_watchpoints_recursive(watch TSRMLS_CC); + } + + zend_hash_str_del(&cur->watches, watch->str, watch->str_len); + + if (!--cur->num) { + phpdbg_deactivate_watchpoint(&cur->watch TSRMLS_CC); + phpdbg_remove_watchpoint(&cur->watch TSRMLS_CC); + + phpdbg_delete_watchpoint(watch TSRMLS_CC); + + zend_hash_index_del(&PHPDBG_G(watch_collisions), (zend_ulong) ref); + } + } +} static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { watch->flags |= PHPDBG_WATCH_SIMPLE; phpdbg_store_watchpoint(watch TSRMLS_CC); - zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL); + zend_hash_str_add_ptr(&PHPDBG_G(watchpoints), watch->str, watch->str_len, watch); + + if (watch->parent && watch->parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->parent->addr.zv)) { + phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(watch->parent->addr.zv)) TSRMLS_CC); + } if (watch->type == WATCH_ON_ZVAL) { - phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor); - watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor; + if (watch->parent_container) { + phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container, watch->parent_container->pDestructor); + watch->parent_container->pDestructor = (dtor_func_t) phpdbg_watch_HashTable_dtor; + } + + if (Z_ISREF_P(watch->addr.zv)) { + phpdbg_watchpoint_t *ref = emalloc(sizeof(phpdbg_watchpoint_t)); + ref->flags = watch->flags; + ref->str = watch->str; + ref->str_len = watch->str_len; + ref->parent = watch; + ref->parent_container = NULL; + phpdbg_create_zval_watchpoint(Z_REFVAL_P(watch->addr.zv), ref); + + phpdbg_create_watchpoint(ref TSRMLS_CC); + } } phpdbg_activate_watchpoint(watch TSRMLS_CC); @@ -117,36 +202,35 @@ 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; +static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *zv_watch TSRMLS_DC) { + zval *zv = zv_watch->addr.zv; + phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); + HashTable *ht = HT_FROM_ZVP(zv); - 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; + watch->parent = zv_watch; + + if (!ht) { + return FAILURE; } phpdbg_create_ht_watchpoint(ht, watch); phpdbg_create_watchpoint(watch TSRMLS_CC); - return SUCCESS; -} - -static char *phpdbg_get_property_key(char *key) { - if (*key != 0) { - return key; + if (Z_TYPE_P(zv) == IS_ARRAY) { + watch->flags |= PHPDBG_WATCH_ARRAY; + } else { + watch->flags |= PHPDBG_WATCH_OBJECT; } - return strchr(key + 1, 0) + 1; + + phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(zv)) TSRMLS_CC); + + return SUCCESS; } static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { HashTable *ht; + zval *zvp = watch->addr.zv; if (watch->type != WATCH_ON_ZVAL) { return FAILURE; @@ -155,25 +239,18 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ watch->flags |= PHPDBG_WATCH_RECURSIVE; phpdbg_create_watchpoint(watch TSRMLS_CC); - 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 SUCCESS; + ZVAL_DEREF(zvp); + + if (!(ht = HT_FROM_ZVP(zvp))) { + return SUCCESS; } { HashPosition position; - zval **zv; + 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)) { + ZEND_HASH_FOREACH_VAL(ht, zv) { phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t)); new_watch->flags = PHPDBG_WATCH_RECURSIVE; @@ -182,19 +259,21 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ zend_hash_get_current_key_zval_ex(ht, &key, &position); if (Z_TYPE(key) == IS_STRING) { - new_watch->name_in_parent = zend_strndup(Z_STRVAL(key), Z_STRLEN(key)); + new_watch->name_in_parent = estrndup(Z_STRVAL(key), Z_STRLEN(key)); new_watch->name_in_parent_len = Z_STRLEN(key); } else { - new_watch->name_in_parent = NULL; - new_watch->name_in_parent_len = asprintf(&new_watch->name_in_parent, "%ld", Z_LVAL(key)); + new_watch->name_in_parent_len = spprintf(&new_watch->name_in_parent, 0, "%lld", Z_LVAL(key)); } - new_watch->str = NULL; - new_watch->str_len = asprintf(&new_watch->str, "%.*s%s%s%s", (int)watch->str_len, watch->str, Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"]":""); + new_watch->str_len = spprintf(&new_watch->str, 0, "%.*s%s%s%s", (int) watch->str_len, watch->str, Z_TYPE_P(zvp) == IS_ARRAY ? "[" : "->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(zvp) == IS_ARRAY ? "]" : ""); + + while (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } - phpdbg_create_zval_watchpoint(*zv, new_watch); + phpdbg_create_zval_watchpoint(zv, new_watch); phpdbg_create_recursive_watchpoint(new_watch TSRMLS_CC); - } + } ZEND_HASH_FOREACH_END(); } { @@ -202,12 +281,18 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ new_watch->parent = watch; new_watch->parent_container = watch->parent_container; - new_watch->name_in_parent = zend_strndup(watch->name_in_parent, watch->name_in_parent_len); + new_watch->name_in_parent = estrndup(watch->name_in_parent, watch->name_in_parent_len); new_watch->name_in_parent_len = watch->name_in_parent_len; new_watch->str = NULL; - new_watch->str_len = asprintf(&new_watch->str, "%.*s[]", (int)watch->str_len, watch->str); + new_watch->str_len = spprintf(&new_watch->str, 0, "%.*s[]", (int) watch->str_len, watch->str); new_watch->flags = PHPDBG_WATCH_RECURSIVE; + if (Z_TYPE_P(zvp) == IS_ARRAY) { + new_watch->flags |= PHPDBG_WATCH_ARRAY; + } else { + new_watch->flags |= PHPDBG_WATCH_OBJECT; + } + phpdbg_create_ht_watchpoint(ht, new_watch); phpdbg_create_watchpoint(new_watch TSRMLS_CC); } @@ -216,52 +301,58 @@ static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_ } static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request TSRMLS_DC) { - if (watch->type == WATCH_ON_HASHTABLE || (watch->type == WATCH_ON_ZVAL && (Z_TYPE_P(watch->addr.zv) == IS_ARRAY || Z_TYPE_P(watch->addr.zv) == IS_OBJECT))) { + if (watch->type == WATCH_ON_HASHTABLE) { HashTable *ht; phpdbg_btree_result *result; if (watch->type == WATCH_ON_HASHTABLE && user_request) { - HashPosition position; - zval **zv; - zval key; - char *str; - int str_len; - phpdbg_watchpoint_t **watchpoint; - - ht = watch->addr.ht; - - 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)) { - zend_hash_get_current_key_zval_ex(ht, &key, &position); - str = NULL; - if (Z_TYPE(key) == IS_STRING) { - str_len = asprintf(&str, "%.*s%s%s%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(Z_STRVAL(key)), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":""); - } else { - str_len = asprintf(&str, "%.*s%s%li%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", Z_LVAL(key), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":""); - } - - if (zend_hash_find(&PHPDBG_G(watchpoints), str, str_len, (void **) &watchpoint) == SUCCESS) { - phpdbg_delete_watchpoint_recursive(*watchpoint, 1 TSRMLS_CC); - } - } + phpdbg_delete_ht_watchpoints_recursive(watch TSRMLS_CC); } else { - 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; - } + ht = HT_FROM_ZVP(watch->addr.zv); if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) { phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request TSRMLS_CC); } } + } else if (watch->type == WATCH_ON_ZVAL) { + phpdbg_delete_zval_watchpoints_recursive(watch TSRMLS_CC); + } + + return zend_hash_str_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); +} + +static void phpdbg_delete_ht_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC) { + zend_string *strkey; + zend_long numkey; + char *str; + int str_len; + phpdbg_watchpoint_t *watchpoint; + + ZEND_HASH_FOREACH_KEY(watch->addr.ht, numkey, strkey) { + if (strkey) { + str_len = asprintf(&str, "%.*s%s%s%s", (int) watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_ARRAY) ? "[" : "->", phpdbg_get_property_key(strkey->val), (watch->flags & PHPDBG_WATCH_ARRAY) ? "]" : ""); + } else { + str_len = asprintf(&str, "%.*s%s%lli%s", (int) watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_ARRAY) ? "[" : "->", numkey, (watch->flags & PHPDBG_WATCH_ARRAY) ? "]" : ""); + } + + if ((watchpoint = zend_hash_str_find_ptr(&PHPDBG_G(watchpoints), str, str_len))) { + phpdbg_delete_watchpoint_recursive(watchpoint, 1 TSRMLS_CC); + } + } ZEND_HASH_FOREACH_END(); +} + +static void phpdbg_delete_zval_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC) { + if (Z_REFCOUNTED_P(watch->addr.zv)) { + phpdbg_remove_watch_collision(Z_COUNTED_P(watch->addr.zv) TSRMLS_CC); } +} - return zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); +static void phpdbg_delete_watchpoints_recursive(phpdbg_watchpoint_t *watch TSRMLS_DC) { + if (watch->type == WATCH_ON_ZVAL) { + phpdbg_delete_zval_watchpoints_recursive(watch TSRMLS_CC); + } else if (watch->type == WATCH_ON_HASHTABLE) { + phpdbg_delete_ht_watchpoints_recursive(watch TSRMLS_CC); + } } static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) { @@ -269,7 +360,7 @@ static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) { phpdbg_watchpoint_t *watch; phpdbg_btree_result *result; - if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)tmp_watch->addr.ptr)) == NULL) { + if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) tmp_watch->addr.ptr)) == NULL) { return FAILURE; } @@ -278,133 +369,52 @@ static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) { if (watch->flags & PHPDBG_WATCH_RECURSIVE) { ret = phpdbg_delete_watchpoint_recursive(watch, 1 TSRMLS_CC); } else { - ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + ret = zend_hash_str_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); } - free(tmp_watch->str); + efree(tmp_watch->str); + efree(tmp_watch->name_in_parent); efree(tmp_watch); return ret; } -static int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) { - int ret = FAILURE; - zend_bool new_index = 1; - char *last_index; - int index_len = 0; - zval **zv; - - if (len < 2 || *input != '$') { - goto error; - } - - while (i++ < len) { - if (i == len) { - new_index = 1; - } else { - switch (input[i]) { - case '[': - new_index = 1; - break; - case ']': - break; - case '>': - if (last_index[index_len - 1] == '-') { - new_index = 1; - index_len--; - } - break; - - default: - if (new_index) { - last_index = input + i; - new_index = 0; - } - if (input[i - 1] == ']') { - goto error; - } - index_len++; - } - } - - if (new_index && index_len == 0) { - HashPosition position; - for (zend_hash_internal_pointer_reset_ex(parent, &position); - zend_hash_get_current_data_ex(parent, (void **)&zv, &position) == SUCCESS; - zend_hash_move_forward_ex(parent, &position)) { - if (i == len || (i == len - 1 && input[len - 1] == ']')) { - zval *key = emalloc(sizeof(zval)); - phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); - watch->flags = 0; - zend_hash_get_current_key_zval_ex(parent, key, &position); - convert_to_string(key); - watch->str = malloc(i + Z_STRLEN_P(key) + 2); - watch->str_len = sprintf(watch->str, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":""); - efree(key); - watch->name_in_parent = zend_strndup(last_index, index_len); - watch->name_in_parent_len = index_len; - watch->parent_container = parent; - phpdbg_create_zval_watchpoint(*zv, watch); - - ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE; - } else if (Z_TYPE_PP(zv) == IS_OBJECT) { - phpdbg_watchpoint_parse_input(input, len, Z_OBJPROP_PP(zv), i, callback, silent TSRMLS_CC); - } else if (Z_TYPE_PP(zv) == IS_ARRAY) { - phpdbg_watchpoint_parse_input(input, len, Z_ARRVAL_PP(zv), i, callback, silent TSRMLS_CC); - } else { - /* Ignore silently */ - } - } - return ret; - } else if (new_index) { - char last_chr = last_index[index_len]; - last_index[index_len] = 0; - if (zend_symtable_find(parent, last_index, index_len + 1, (void **)&zv) == FAILURE) { - if (!silent) { - phpdbg_error("%.*s is undefined", (int)i, input); - } - return FAILURE; - } - last_index[index_len] = last_chr; - if (i == len) { - phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); - watch->flags = 0; - watch->str = zend_strndup(input, len); - watch->str_len = len; - watch->name_in_parent = zend_strndup(last_index, index_len); - watch->name_in_parent_len = index_len; - watch->parent_container = parent; - phpdbg_create_zval_watchpoint(*zv, watch); - - ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE; - } else if (Z_TYPE_PP(zv) == IS_OBJECT) { - parent = Z_OBJPROP_PP(zv); - } else if (Z_TYPE_PP(zv) == IS_ARRAY) { - parent = Z_ARRVAL_PP(zv); - } else { - phpdbg_error("%.*s is nor an array nor an object", (int)i, input); - return FAILURE; - } - index_len = 0; - } +static int phpdbg_watchpoint_parse_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval *zv, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) { + int ret; + phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); + watch->flags = 0; + watch->str = name; + watch->str_len = len; + watch->name_in_parent = keyname; + watch->name_in_parent_len = keylen; + watch->parent_container = parent; + phpdbg_create_zval_watchpoint(zv, watch); + + ret = callback(watch TSRMLS_CC); + + if (ret != SUCCESS) { + efree(watch); + efree(name); + efree(keyname); } return ret; - error: - phpdbg_error("Malformed input"); - return FAILURE; +} + +PHPDBG_API int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) { + return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, 0, callback TSRMLS_CC); } static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) { - if (EG(This) && len >= 5 && !memcmp("$this", input, 5)) { - zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), NULL); + if (EG(scope) && len >= 5 && !memcmp("$this", input, 5)) { + zend_hash_str_add(&EG(current_execute_data)->symbol_table->ht, ZEND_STRL("this"), &EG(current_execute_data)->This); } - if (zend_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1 TSRMLS_CC) != FAILURE) { + if (phpdbg_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table).ht, 0, callback, 1 TSRMLS_CC) != FAILURE) { return SUCCESS; } - return phpdbg_watchpoint_parse_input(input, len, EG(active_symbol_table), 0, callback, 0 TSRMLS_CC); + return phpdbg_watchpoint_parse_input(input, len, &EG(current_execute_data)->symbol_table->ht, 0, callback, 0 TSRMLS_CC); } PHPDBG_WATCH(delete) /* {{{ */ @@ -412,9 +422,9 @@ PHPDBG_WATCH(delete) /* {{{ */ switch (param->type) { case STR_PARAM: if (phpdbg_delete_var_watchpoint(param->str, param->len TSRMLS_CC) == FAILURE) { - phpdbg_error("Nothing was deleted, no corresponding watchpoint found"); + phpdbg_error("watchdelete", "type=\"nowatch\"", "Nothing was deleted, no corresponding watchpoint found"); } else { - phpdbg_notice("Removed watchpoint %.*s", (int)param->len, param->str); + phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Removed watchpoint %.*s", (int) param->len, param->str); } break; @@ -433,7 +443,7 @@ PHPDBG_WATCH(recursive) /* {{{ */ switch (param->type) { case STR_PARAM: if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint TSRMLS_CC) != FAILURE) { - phpdbg_notice("Set recursive watchpoint on %.*s", (int)param->len, param->str); + phpdbg_notice("watchrecursive", "variable=\"%.*s\"", "Set recursive watchpoint on %.*s", (int)param->len, param->str); } break; @@ -452,7 +462,7 @@ PHPDBG_WATCH(array) /* {{{ */ switch (param->type) { case STR_PARAM: if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint TSRMLS_CC) != FAILURE) { - phpdbg_notice("Set array watchpoint on %.*s", (int)param->len, param->str); + phpdbg_notice("watcharray", "variable=\"%.*s\"", "Set array watchpoint on %.*s", (int)param->len, param->str); } break; @@ -462,23 +472,23 @@ PHPDBG_WATCH(array) /* {{{ */ return SUCCESS; } /* }}} */ -void phpdbg_watch_HashTable_dtor(zval **zv) { +void phpdbg_watch_HashTable_dtor(zval *zv) { phpdbg_btree_result *result; TSRMLS_FETCH(); zval_ptr_dtor_wrapper(zv); - if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)*zv))) { + if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) zv))) { phpdbg_watchpoint_t *watch = result->ptr; PHPDBG_G(watchpoint_hit) = 1; - phpdbg_notice("%.*s was removed, removing watchpoint%s", (int)watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE)?" recursively":""); + phpdbg_notice("watchdelete", "variable=\"%.*s\" recursive=\"%s\"", "%.*s was removed, removing watchpoint%s", (int) watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE) ? " recursively" : ""); if (watch->flags & PHPDBG_WATCH_RECURSIVE) { phpdbg_delete_watchpoint_recursive(watch, 0 TSRMLS_CC); } else { - zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + zend_hash_str_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); } } } @@ -543,27 +553,26 @@ void phpdbg_watchpoints_clean(TSRMLS_D) { zend_hash_clean(&PHPDBG_G(watchpoints)); } -static void phpdbg_watch_dtor(void *pDest) { - phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest; +static void phpdbg_watch_dtor(zval *pDest) { + phpdbg_watchpoint_t *watch = (phpdbg_watchpoint_t *) Z_PTR_P(pDest); TSRMLS_FETCH(); phpdbg_deactivate_watchpoint(watch TSRMLS_CC); phpdbg_remove_watchpoint(watch TSRMLS_CC); - free(watch->str); - free(watch->name_in_parent); - efree(watch); + efree(watch->str); + efree(watch->name_in_parent); } static void phpdbg_watch_mem_dtor(void *llist_data) { - phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data; + phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **) llist_data; /* Disble writing again */ if (dump->reenable_writing) { mprotect(dump->page, dump->size, PROT_READ); } - free(*(void **)llist_data); + free(*(void **) llist_data); } void phpdbg_setup_watchpoints(TSRMLS_D) { @@ -580,131 +589,145 @@ void phpdbg_setup_watchpoints(TSRMLS_D) { zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1); phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8); phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8); - zend_hash_init(&PHPDBG_G(watchpoints), 8, NULL, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC); + zend_hash_init(&PHPDBG_G(watchpoints), 8, NULL, phpdbg_watch_dtor, 0); + zend_hash_init(&PHPDBG_G(watch_collisions), 8, NULL, NULL, 0); } static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump TSRMLS_DC) { /* 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, *htresult; + 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; int elementDiff; void *curTest; dump->reenable_writing = 0; while ((result = phpdbg_btree_next(&pos))) { - phpdbg_watchpoint_t *watch = result->ptr, *htwatch; - void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page); + phpdbg_watchpoint_t *watch = result->ptr; + void *oldPtr = (char *) &dump->data + ((size_t) watch->addr.ptr - (size_t) dump->page); char reenable = 1; + int removed = 0; - if ((size_t)watch->addr.ptr < (size_t)dump->page || (size_t)watch->addr.ptr + watch->size > (size_t)dump->page + dump->size) { + if ((size_t) watch->addr.ptr < (size_t) dump->page || (size_t) watch->addr.ptr + watch->size > (size_t) dump->page + dump->size) { continue; } /* Test if the zval was separated and if necessary move the watchpoint */ - if (zend_hash_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1, &curTest) == SUCCESS) { - if (watch->type == WATCH_ON_HASHTABLE) { - switch (Z_TYPE_PP((zval **)curTest)) { - case IS_ARRAY: - curTest = (void *)Z_ARRVAL_PP((zval **)curTest); - break; - case IS_OBJECT: - curTest = (void *)Z_OBJPROP_PP((zval **)curTest); - break; + if ((watch->type == WATCH_ON_HASHTABLE || watch->type == WATCH_ON_ZVAL) && watch->parent_container) { + if ((curTest = zend_hash_str_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len))) { + while (Z_TYPE_P((zval *) curTest) == IS_INDIRECT) { + curTest = Z_INDIRECT_P((zval *) curTest); } - } else { - curTest = *(void **)curTest; - } - if (curTest != watch->addr.ptr) { - phpdbg_deactivate_watchpoint(watch TSRMLS_CC); - phpdbg_remove_watchpoint(watch TSRMLS_CC); - watch->addr.ptr = curTest; - phpdbg_store_watchpoint(watch TSRMLS_CC); - phpdbg_activate_watchpoint(watch TSRMLS_CC); + if (watch->type == WATCH_ON_HASHTABLE) { + switch (Z_TYPE_P((zval *) curTest)) { + case IS_ARRAY: + curTest = (void *) Z_ARRVAL_P((zval *) curTest); + break; + case IS_OBJECT: + curTest = (void *) Z_OBJPROP_P((zval *) curTest); + break; + } + } - reenable = 0; + if (curTest != watch->addr.ptr) { + phpdbg_deactivate_watchpoint(watch TSRMLS_CC); + phpdbg_remove_watchpoint(watch TSRMLS_CC); + watch->addr.ptr = curTest; + phpdbg_store_watchpoint(watch TSRMLS_CC); + phpdbg_activate_watchpoint(watch TSRMLS_CC); + + reenable = 0; + } + } else { + removed = 1; } } /* Show to the user what changed and delete watchpoint upon removal */ if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) { - if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS || (watch->type == WATCH_ON_ZVAL && memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value))) || (watch->type == WATCH_ON_HASHTABLE -#if ZEND_DEBUG - && !watch->addr.ht->inconsistent -#endif - && zend_hash_num_elements((HashTable *)oldPtr) != zend_hash_num_elements(watch->addr.ht))) { + zend_bool do_break = 0; + + switch (watch->type) { + case WATCH_ON_ZVAL: + do_break = memcmp(oldPtr, watch->addr.zv, sizeof(zend_value) + sizeof(uint32_t) /* value + typeinfo */); + break; + case WATCH_ON_HASHTABLE: + do_break = zend_hash_num_elements((HashTable *) oldPtr) != zend_hash_num_elements(watch->addr.ht); + break; + case WATCH_ON_REFCOUNTED: + if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS) { + do_break = memcmp(oldPtr, watch->addr.ref, sizeof(uint32_t) /* no zend_refcounted metadata info */); + } + break; + } + + + if (do_break) { PHPDBG_G(watchpoint_hit) = 1; - phpdbg_notice("Breaking on watchpoint %s", watch->str); + phpdbg_notice("watchhit", "variable=\"%s\"", "Breaking on watchpoint %.*s", (int) watch->str_len, watch->str); + phpdbg_xml("<watchdata %r>"); } switch (watch->type) { case WATCH_ON_ZVAL: { - int removed = ((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); - int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value)); - int show_ref = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc || ((zval *)oldPtr)->is_ref__gc != watch->addr.zv->is_ref__gc; + int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zval) - sizeof(uint32_t) /* no metadata info */); if (removed || show_value) { - phpdbg_write("Old value: "); - if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) { - phpdbg_writeln("Value inaccessible, HashTable already destroyed"); + if (removed && (Z_TYPE_P((zval *) oldPtr) == IS_ARRAY || Z_TYPE_P((zval *) oldPtr) == IS_OBJECT)) { + phpdbg_writeln("watchvalue", "type=\"old\" inaccessible=\"inaccessible\"", "Old value inaccessible, array or object (HashTable) already destroyed"); } else { - zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC); - phpdbg_writeln(""); + phpdbg_out("Old value: "); + phpdbg_xml("<watchvalue %r type=\"old\">"); + zend_print_flat_zval_r((zval *) oldPtr TSRMLS_CC); + phpdbg_xml("</watchvalue>"); + phpdbg_out("\n"); } } - if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) { - phpdbg_writeln("Old refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc); - } /* check if zval was removed */ if (removed) { - phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str); - zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Watchpoint %.*s was unset, removing watchpoint", (int) watch->str_len, watch->str); + zend_hash_str_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); reenable = 0; - if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) { - goto remove_ht_watch; + if (Z_REFCOUNTED_P((zval *) oldPtr)) { + phpdbg_remove_watch_collision(Z_COUNTED_P((zval *) oldPtr) TSRMLS_CC); } - break; } if (show_value) { - phpdbg_write("New value: "); + phpdbg_out("New value: "); + phpdbg_xml("<watchvalue %r type=\"new\">"); zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC); - phpdbg_writeln(""); - } - if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) { - phpdbg_writeln("New refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc); + phpdbg_xml("</watchvalue>"); + phpdbg_out("\n"); } - if ((Z_TYPE_P(watch->addr.zv) == IS_ARRAY && Z_ARRVAL_P(watch->addr.zv) != Z_ARRVAL_P((zval *)oldPtr)) || (Z_TYPE_P(watch->addr.zv) != IS_OBJECT && Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) { - /* add new watchpoints if necessary */ - if (watch->flags & PHPDBG_WATCH_RECURSIVE) { - phpdbg_create_recursive_watchpoint(watch TSRMLS_CC); + /* add new watchpoints if necessary */ + if (Z_PTR_P(watch->addr.zv) != Z_PTR_P((zval *) oldPtr)) { + if (Z_REFCOUNTED_P((zval *) oldPtr)) { + phpdbg_remove_watch_collision(Z_COUNTED_P((zval *) oldPtr) TSRMLS_CC); + } + if (Z_REFCOUNTED_P(watch->addr.zv)) { + if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS) { + phpdbg_writeln("watchrefcount", "type=\"new\" refcount=\"%d\"", "New refcount: %d", Z_COUNTED_P(watch->addr.zv)->refcount); + } + if (watch->flags & PHPDBG_WATCH_RECURSIVE) { + phpdbg_create_recursive_watchpoint(watch TSRMLS_CC); + } } - } - - if ((Z_TYPE_P((zval *)oldPtr) != IS_ARRAY || Z_ARRVAL_P(watch->addr.zv) == Z_ARRVAL_P((zval *)oldPtr)) && (Z_TYPE_P((zval *)oldPtr) != IS_OBJECT || Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) { - break; - } - -remove_ht_watch: - if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) { - htwatch = htresult->ptr; - zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len); } break; } case WATCH_ON_HASHTABLE: - -#if ZEND_DEBUG - if (watch->addr.ht->inconsistent) { - phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str); +#if 0 && ZEND_DEBUG + if (watch->addr.arr->ht->inconsistent) { + phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Watchpoint %.*s was unset, removing watchpoint", (int) watch->str_len, watch->str); zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); reenable = 0; @@ -712,13 +735,12 @@ remove_ht_watch: break; } #endif - - elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht); + 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); + phpdbg_writeln("watchsize", "removed=\"%d\"", "%d elements were removed from the array", elementDiff); } else { - phpdbg_writeln("%d elements were added to the array", -elementDiff); + phpdbg_writeln("watchsize", "added=\"%d\"", "%d elements were added to the array", -elementDiff); /* add new watchpoints if necessary */ if (watch->flags & PHPDBG_WATCH_RECURSIVE) { @@ -726,10 +748,25 @@ remove_ht_watch: } } } - if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) { - phpdbg_writeln("Internal pointer of array was changed"); + if (watch->addr.ht->nInternalPointer != ((HashTable *) oldPtr)->nInternalPointer) { + phpdbg_writeln("watcharrayptr", "", "Internal pointer of array was changed"); + } + break; + case WATCH_ON_REFCOUNTED: { + if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS) { + phpdbg_writeln("watchrefcount", "type=\"old\" refcount=\"%d\"", "Old refcount: %d", ((zend_refcounted *) oldPtr)->refcount); + + if (!removed) { + phpdbg_writeln("watchrefcount", "type=\"old\" refcount=\"%d\"", "Old refcount: %d", ((zend_refcounted *) oldPtr)->refcount); + } } + break; + } + } + + if (do_break) { + phpdbg_xml("</watchdata>"); } } @@ -746,42 +783,46 @@ int phpdbg_print_changed_zvals(TSRMLS_D) { return FAILURE; } - dump = (phpdbg_watch_memdump **)zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos); + dump = (phpdbg_watch_memdump **) zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos); do { phpdbg_print_changed_zval(*dump TSRMLS_CC); - } while ((dump = (phpdbg_watch_memdump **)zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos))); + } while ((dump = (phpdbg_watch_memdump **) zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos))); zend_llist_clean(&PHPDBG_G(watchlist_mem)); - ret = PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE; + ret = PHPDBG_G(watchpoint_hit) ? SUCCESS : FAILURE; PHPDBG_G(watchpoint_hit) = 0; return ret; } void phpdbg_list_watchpoints(TSRMLS_D) { - HashPosition position; - phpdbg_watchpoint_t **watch; + phpdbg_watchpoint_t *watch; - for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(watchpoints), &position); - zend_hash_get_current_data_ex(&PHPDBG_G(watchpoints), (void**) &watch, &position) == SUCCESS; - zend_hash_move_forward_ex(&PHPDBG_G(watchpoints), &position)) { - phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str); - } + phpdbg_xml("<watchlist %r>"); + + ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watchpoints), watch) { + phpdbg_writeln("watchvariable", "variable=\"%.*s\" on=\"%s\" type=\"%s\"", "%.*s (%s, %s)", (int) watch->str_len, watch->str, watch->type == WATCH_ON_HASHTABLE ? "array" : watch->type == WATCH_ON_REFCOUNTED ? "refcount" : "variable", watch->flags == PHPDBG_WATCH_RECURSIVE ? "recursive" : "simple"); + } ZEND_HASH_FOREACH_END(); + + phpdbg_xml("</watchlist>"); } void phpdbg_watch_efree(void *ptr) { phpdbg_btree_result *result; TSRMLS_FETCH(); - result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr); + result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong) ptr); if (result) { phpdbg_watchpoint_t *watch = result->ptr; - if ((size_t)watch->addr.ptr + watch->size > (size_t)ptr) { - zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); + if ((size_t) watch->addr.ptr + watch->size > (size_t) ptr) { + if (watch->type == WATCH_ON_ZVAL) { + phpdbg_remove_watch_collision(Z_COUNTED_P(watch->addr.zv) TSRMLS_CC); + } + zend_hash_str_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len); } } |