From 0a8c20e740d6680ecfe566e5e681a3da4c67970e Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 16 Dec 2013 10:29:31 -0500 Subject: [PATCH] Handle automatic removing of watchpoints and cleanup at the end --- phpdbg.c | 5 +- phpdbg_watch.c | 211 +++++++++++++++++++++++++++++-------------------- phpdbg_watch.h | 4 + 3 files changed, 131 insertions(+), 89 deletions(-) diff --git a/phpdbg.c b/phpdbg.c index 7c80b591e3..e3c998dda5 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -154,7 +154,7 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0); - zend_hash_init(&PHPDBG_G(watchpoints), 8, NULL, NULL, 0); /* TODO: dtor */ + phpdbg_setup_watchpoints(TSRMLS_C); zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); @@ -176,6 +176,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); zend_hash_destroy(&PHPDBG_G(seek)); zend_hash_destroy(&PHPDBG_G(registered)); + zend_hash_destroy(&PHPDBG_G(watchpoints)); if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); @@ -853,8 +854,6 @@ int main(int argc, char **argv) /* {{{ */ tsrm_ls = ts_resource(0); #endif - phpdbg_setup_watchpoints(TSRMLS_C); - phpdbg_main: if (!cleaning) { bp_tmp_file = malloc(L_tmpnam); diff --git a/phpdbg_watch.c b/phpdbg_watch.c index 0f3e1322cf..a87da5feca 100644 --- a/phpdbg_watch.c +++ b/phpdbg_watch.c @@ -38,31 +38,6 @@ typedef struct { #define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size)) -static void phpdbg_watch_mem_dtor(void *llist_data) { - void *page = (*(phpdbg_watch_memdump **)llist_data)->page; - size_t size = (*(phpdbg_watch_memdump **)llist_data)->size; - - efree(*(void **)llist_data); - - /* Disble writing again */ - mprotect(page, size, PROT_NONE | PROT_READ); -} - -void phpdbg_setup_watchpoints(TSRMLS_D) { -#ifdef _SC_PAGE_SIZE - phpdbg_pagesize = sysconf(_SC_PAGE_SIZE); -#endif -#ifdef _SC_PAGESIZE - phpdbg_pagesize = sysconf(_SC_PAGESIZE); -#endif -#ifdef _SC_NUTC_OS_PAGESIZE - phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE); -#endif - - zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 0); - phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8); -} - static inline void *phpdbg_get_page_boundary(void *addr) { return (void *)((size_t)addr & ~(phpdbg_pagesize - 1)); } @@ -83,6 +58,77 @@ static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) { return watch; } +static void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { + phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch); +} + +static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access) { + 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); + + if (m == FAILURE) { + phpdbg_error("Unable to (un)set watchpoint (mprotect() failed)"); + zend_bailout(); + } +} + +static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch) { + phpdbg_change_watchpoint_access(watch, PROT_READ); +} + +static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch) { + phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE); +} + +void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) { + watch->addr.ptr = addr; + watch->size = size; + watch->type = WATCH_ON_PTR; +} + +void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) { + phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch); + watch->type = WATCH_ON_ZVAL; +} + +int phpdbg_create_var_watchpoint(char *name, size_t len TSRMLS_DC) { + zval **zv; + phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); + + if (!EG(active_op_array)) { + phpdbg_error("No active op array!"); + return SUCCESS; + } + + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + + if (!EG(active_symbol_table)) { + phpdbg_error("No active symbol table!"); + return SUCCESS; + } + } + + watch->str = estrndup(name, len); + watch->parent_container = EG(current_execute_data)->symbol_table; + watch->name_in_parent = estrndup(name, len); + watch->name_in_parent_len = len + 1; + + /* Lookup current symbol table */ + if (zend_hash_find(EG(current_execute_data)->symbol_table, name, len + 1, (void **)&zv) == SUCCESS) { + phpdbg_create_zval_watchpoint(*zv, watch TSRMLS_CC); + zend_hash_add(&PHPDBG_G(watchpoints), name, len, &watch, sizeof(phpdbg_watchpoint_t *), NULL); + phpdbg_store_watchpoint(watch TSRMLS_CC); + + phpdbg_activate_watchpoint(watch); + return SUCCESS; + } + + return FAILURE; +} + int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) { void *addr; void *page; @@ -115,6 +161,50 @@ int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) return SUCCESS; } +void phpdbg_watchpoints_clean(TSRMLS_DC) { + zend_hash_clean(&PHPDBG_G(watchpoints)); +} + +static void phpdbg_watch_dtor(void *pDest) { + TSRMLS_FETCH(); + + phpdbg_watchpoint_t *watch = (phpdbg_watchpoint_t *)pDest; + + phpdbg_deactivate_watchpoint(watch); + phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr); + + efree(watch->str); + efree(watch->name_in_parent); + + efree(watch); +} + +static void phpdbg_watch_mem_dtor(void *llist_data) { + void *page = (*(phpdbg_watch_memdump **)llist_data)->page; + size_t size = (*(phpdbg_watch_memdump **)llist_data)->size; + + efree(*(void **)llist_data); + + /* Disble writing again */ + mprotect(page, size, PROT_NONE | PROT_READ); +} + +void phpdbg_setup_watchpoints(TSRMLS_D) { +#ifdef _SC_PAGE_SIZE + phpdbg_pagesize = sysconf(_SC_PAGE_SIZE); +#endif +#ifdef _SC_PAGESIZE + phpdbg_pagesize = sysconf(_SC_PAGESIZE); +#endif +#ifdef _SC_NUTC_OS_PAGESIZE + phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE); +#endif + + zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 0); + phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8); + _zend_hash_init(&PHPDBG_G(watchpoints), 8, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC); +} + static int phpdbg_print_changed_zval(void *llist_data) { TSRMLS_FETCH(); @@ -133,12 +223,21 @@ static int phpdbg_print_changed_zval(void *llist_data) { phpdbg_notice("Breaking on watchpoint %s", watch->str); switch (watch->type) { case WATCH_ON_ZVAL: + phpdbg_write("Old value: "); zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC); phpdbg_writeln("\nOld refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc); + + /* check if zval was removed */ + if (((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc && !zend_hash_exists(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len)) { + phpdbg_notice("Watchpoint %s was unset, removing watchpoint"); + phpdbg_deactivate_watchpoint(watch); + break; + } + phpdbg_write("New value: "); zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC); - phpdbg_writeln("\nNew refcount: %d; Old is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc); + phpdbg_writeln("\nNew refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc); break; } } @@ -157,63 +256,3 @@ int phpdbg_print_changed_zvals(TSRMLS_D) { return PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE; } - -static void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { - phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch); -} - -static void phpdbg_activate_watchpoint (phpdbg_watchpoint_t *watch) { - 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), PROT_NONE | PROT_READ); - - if (m == FAILURE) { - phpdbg_error("Unable to set watchpoint (mprotect() failed)"); - zend_bailout(); - } -} - -void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) { - watch->addr.ptr = addr; - watch->size = size; - watch->type = WATCH_ON_PTR; -} - -void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) { - phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch); - watch->type = WATCH_ON_ZVAL; -} - -int phpdbg_create_var_watchpoint(char *name, size_t len TSRMLS_DC) { - zval **zv; - phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t)); - - if (!EG(active_op_array)) { - phpdbg_error("No active op array!"); - return SUCCESS; - } - - if (!EG(active_symbol_table)) { - zend_rebuild_symbol_table(TSRMLS_C); - - if (!EG(active_symbol_table)) { - phpdbg_error("No active symbol table!"); - return SUCCESS; - } - } - - watch->str = estrndup(name, len); - - /* Lookup current symbol table */ - if (zend_hash_find(EG(current_execute_data)->symbol_table, name, len + 1, (void **)&zv) == SUCCESS) { - phpdbg_create_zval_watchpoint(*zv, watch TSRMLS_CC); - zend_hash_add(&PHPDBG_G(watchpoints), name, len, &watch, sizeof(phpdbg_watchpoint_t *), NULL); - phpdbg_store_watchpoint(watch TSRMLS_CC); - - phpdbg_activate_watchpoint(watch); - return SUCCESS; - } - - return FAILURE; -} diff --git a/phpdbg_watch.h b/phpdbg_watch.h index 272e613ad2..8c8336312b 100644 --- a/phpdbg_watch.h +++ b/phpdbg_watch.h @@ -44,10 +44,14 @@ typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t; struct _phpdbg_watchpoint_t { phpdbg_watchpoint_t *parent; + HashTable *parent_container; + char *name_in_parent; + size_t name_in_parent_len; char *str; union { zval *zv; HashTable *ht; + zend_llist *llist; void *ptr; } addr; size_t size; -- 2.50.1