]> granicus.if.org Git - php/commitdiff
Added rudimentary support for watchpoints on an array itself
authorBob Weinand <bobwei9@hotmail.com>
Fri, 10 Jan 2014 18:54:07 +0000 (13:54 -0500)
committerBob Weinand <bobwei9@hotmail.com>
Fri, 10 Jan 2014 18:54:07 +0000 (13:54 -0500)
phpdbg.c
phpdbg.h
phpdbg_btree.c
phpdbg_watch.c
phpdbg_watch.h

index 4955d2e1bf3b5b153c60ca9f33ac483387e4a78f..54710fad7735ef33e1278c47ae464690861fe17e 100644 (file)
--- a/phpdbg.c
+++ b/phpdbg.c
 #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
index 63cdd352b7d96024bae51963b86246073518e01d..79f98fd2f7967c7f861687615af054b3e4ae4871 100644 (file)
--- 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 */
index 6b70d1453d750cd90040e017820323b5db755404..f3b86dd91fce663debc54ef7066204413d177c5f 100644 (file)
@@ -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) {
index f6d8646a1f06cd587f92ffd80039140deca0676f..a0b9d19dcc8aee5bbcebd5f1697f184d3bd9f92d 100644 (file)
@@ -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);
+}
index 411349816bed37ecc80b724cb12472b8515bc6ca..8e530875848fc2b6b24d1c295900b245709e4229 100644 (file)
@@ -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