summaryrefslogtreecommitdiff
path: root/sapi/phpdbg/phpdbg_watch.c
diff options
context:
space:
mode:
authorRasmus Lerdorf <rasmus@php.net>2014-10-26 16:58:45 -0700
committerRasmus Lerdorf <rasmus@php.net>2014-10-26 16:58:45 -0700
commit8d84b1f67adb71bdb2394b62faad04b22f6226f3 (patch)
tree0c1c1ed2aaef8eee971575eb5e336631295f4951 /sapi/phpdbg/phpdbg_watch.c
parentfb85d0322d39d49f37e32df6f68c9769f2cce0e4 (diff)
parent09da8952d0434b53e740ffaca66a8051cd39cf93 (diff)
downloadphp-git-8d84b1f67adb71bdb2394b62faad04b22f6226f3.tar.gz
Merge branch 'master' of git.php.net:php-src
* 'master' of git.php.net:php-src: (240 commits) Do not execute anything after quit or clean command Fix last commit, and do not output unnecessary information Stabilize execution, always run destructors and extended file breakpoints Fix nullptr dereference in clean without exec context remove dodgy param parser, bring userland breakpoint api inline with PHP7 disable output buffering by default Add question to reset execution in run/exec/clean - Updated to version 2014.9 (2014i) actually remove this disable output buffering, better breakpoint api for userland, remove hand parsing of params Fix phpdbg output when outputting via php with active output handlers Fixed Closure::call() NEWS/UPGRADING Set engine back to initial state after fatal-ed ev command Fix eventual stack overflow after clean cmd Fix listing of files with no absolute path updated libmagic.patch in master updated libmagic.patch in 5.6 updated libmagic.patch in 5.5 NEWS NEWS ...
Diffstat (limited to 'sapi/phpdbg/phpdbg_watch.c')
-rw-r--r--sapi/phpdbg/phpdbg_watch.c639
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);
}
}