From: Bob Weinand Date: Sun, 15 Dec 2013 14:05:50 +0000 (-0500) Subject: Moved btree to separate file; minor fixes and completitions X-Git-Tag: php-5.6.0beta2~1^2~37^2~50 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9ce9f48fb6db47219662d70504c386cbf7fea980;p=php Moved btree to separate file; minor fixes and completitions --- diff --git a/config.m4 b/config.m4 index 3016a2075d..9e2d210580 100644 --- a/config.m4 +++ b/config.m4 @@ -18,7 +18,7 @@ if test "$PHP_PHPDBG" != "no"; then fi PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE" - PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c" + PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_btree.c" PHP_SUBST(PHP_PHPDBG_CFLAGS) PHP_SUBST(PHP_PHPDBG_FILES) diff --git a/config.w32 b/config.w32 index d97873ec85..5813e7f156 100644 --- a/config.w32 +++ b/config.w32 @@ -1,7 +1,7 @@ ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes'); ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no'); -PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c'; +PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_btree.c'; PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll'; PHPDBG_EXE='phpdbg.exe'; diff --git a/phpdbg.h b/phpdbg.h index 5014976795..abbb5b8a7a 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -68,6 +68,7 @@ #include "phpdbg_cmd.h" #include "phpdbg_utils.h" +#include "phpdbg_btree.h" #include "phpdbg_watch.h" #ifdef ZTS @@ -161,12 +162,6 @@ #define PHPDBG_IO_FDS 3 /* }}} */ /* {{{ structs */ -typedef union _phpdbg_btree phpdbg_btree; -union _phpdbg_btree { - phpdbg_btree *branches[2]; - phpdbg_watchpoint_t *watchpoint; -}; - ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */ HashTable registered; /* registered */ @@ -175,7 +170,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) struct sigaction old_sigsegv_signal; /* segv signal handler */ - phpdbg_btree *watchpoint_tree; /* tree with watchpoints */ + phpdbg_btree watchpoint_tree; /* tree with watchpoints */ HashTable watchpoints; /* watchpoints */ zend_llist watchlist_mem; /* triggered watchpoints */ zend_bool watchpoint_hit; /* a watchpoint was hit */ diff --git a/phpdbg_btree.c b/phpdbg_btree.c new file mode 100644 index 0000000000..f47422f463 --- /dev/null +++ b/phpdbg_btree.c @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg_btree.h" +#include "phpdbg.h" + +#define CHOOSE_BRANCH(n) \ + branch = branch->branches[!!(n)]; + +/* depth in bits */ +void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) { + tree->depth = depth; + tree->branch = NULL; +} + +phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx) { + phpdbg_btree_branch *branch = tree->branch; + int i = tree->depth - 1; + + do { + if ((idx >> i) % 2 == 1) { + if (branch->branches[1]) { + CHOOSE_BRANCH(1); + } else { + return NULL; + } + } else { + if (branch->branches[0]) { + CHOOSE_BRANCH(0); + } else { + return NULL; + } + } + } while (i--); + + return &branch->result; +} + +phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx) { + phpdbg_btree_branch *branch = tree->branch; + int i = tree->depth - 1, last_superior_i = -1; + zend_bool had_alternative_branch = 0; + + if (branch == NULL) { + return NULL; + } + + /* find nearest watchpoint */ + do { + /* an impossible branch was found if: */ + if (!had_alternative_branch && (idx >> i) % 2 == 0 && !branch->branches[0]) { + /* there's no lower branch than idx */ + if (last_superior_i == -1) { + /* failure */ + return NULL; + } + /* reset state */ + branch = tree->branch; + i = sizeof(void *) * 8 - 1; + /* follow branch according to bits in idx until the last lower branch before the impossible branch */ + do { + CHOOSE_BRANCH((idx >> i) % 2 == 1 && branch->branches[1]); + } while (--i > last_superior_i); + /* use now the lower branch of which we can be sure that it contains only branches lower than idx */ + CHOOSE_BRANCH(0); + /* and choose the highest possible branch in the branch containing only branches lower than idx */ + while (i--) { + CHOOSE_BRANCH(branch->branches[1]); + } + break; + } + /* follow branch according to bits in idx until having found an impossible branch */ + if (had_alternative_branch || (idx >> i) % 2 == 1) { + if (branch->branches[1]) { + if (branch->branches[0]) { + last_superior_i = i; + } + CHOOSE_BRANCH(1); + } else { + CHOOSE_BRANCH(0); + had_alternative_branch = 1; + } + } else { + CHOOSE_BRANCH(0); + } + } while (i--); + + return &branch->result; +} + +phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx) { + phpdbg_btree_position pos; + + pos.tree = tree; + pos.end = lower_idx; + pos.cur = higher_idx; + + return pos; +} + +phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos) { + phpdbg_btree_result *result = phpdbg_btree_find_closest(pos->tree, pos->cur); + + if (result == NULL || result->idx < pos->end) { + return NULL; + } + + pos->cur = result->idx - 1; + + return result; +} + +int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags) { + int i = tree->depth - 1; + phpdbg_btree_branch **branch = &tree->branch; + + do { + if (*branch == NULL) { + break; + } + branch = &(*branch)->branches[(idx >> i) % 2]; + } while (i--); + + if (*branch == NULL) { + if (!(flags & PHPDBG_BTREE_INSERT)) { + return FAILURE; + } + + { + phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch)); + do { + (*branch)->branches[!((idx >> i) % 2)] = NULL; + branch = &(*branch)->branches[(idx >> i) % 2]; + *branch = ++memory; + } while (i--); + } + } else if (!(flags & PHPDBG_BTREE_UPDATE)) { + return FAILURE; + } + + (*branch)->result.idx = idx; + (*branch)->result.ptr = ptr; + + return SUCCESS; +} + +int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) { + int i = tree->depth - 1; + phpdbg_btree_branch *branch = tree->branch; + int i_last_dual_branch = -1, last_dual_branch_branch; + phpdbg_btree_branch *last_dual_branch = NULL; + + 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]; + } while (i--); + + if (i_last_dual_branch == -1) { + efree(tree); + tree = NULL; + } else { + if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) { + memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], i_last_dual_branch * sizeof(phpdbg_btree_branch)); + efree(last_dual_branch->branches[last_dual_branch_branch]); + last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1; + } else { + efree(last_dual_branch->branches[last_dual_branch_branch]); + } + + last_dual_branch->branches[i_last_dual_branch] = NULL; + } + + return SUCCESS; +} diff --git a/phpdbg_btree.h b/phpdbg_btree.h new file mode 100644 index 0000000000..7bb40aefca --- /dev/null +++ b/phpdbg_btree.h @@ -0,0 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena | + | Authors: Joe Watkins | + | Authors: Bob Weinand | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_BTREE_H +#define PHPDBG_BTREE_H + +#include "zend.h" + +typedef struct { + zend_ulong idx; + void *ptr; +} phpdbg_btree_result; + +typedef union _phpdbg_btree_branch phpdbg_btree_branch; +union _phpdbg_btree_branch { + phpdbg_btree_branch *branches[2]; + phpdbg_btree_result result; +}; + +typedef struct { + zend_ulong depth; + phpdbg_btree_branch *branch; +} phpdbg_btree; + +typedef struct { + phpdbg_btree *tree; + zend_ulong cur; + zend_ulong end; +} phpdbg_btree_position; + +void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth); +phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx); +phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx); +phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx); +phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos); +int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx); + +#define PHPDBG_BTREE_INSERT 1 +#define PHPDBG_BTREE_UPDATE 2 +#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE) + +int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags); +#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT) +#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE) +#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE) + +#endif diff --git a/phpdbg_watch.c b/phpdbg_watch.c index 00c83b5717..66fcb8eab7 100644 --- a/phpdbg_watch.c +++ b/phpdbg_watch.c @@ -20,6 +20,7 @@ #include "zend.h" #include "phpdbg.h" +#include "phpdbg_btree.h" #include "phpdbg_watch.h" #include #include @@ -37,10 +38,6 @@ typedef struct { #define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size)) -#define CHOOSE_BRANCH(n) \ - addr = (addr << 1) + !!(n); \ - branch = branch->branches[!!(n)]; - 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; @@ -63,6 +60,7 @@ void phpdbg_setup_watchpoints(TSRMLS_D) { #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); } void *phpdbg_get_page_boundary(void *addr) { @@ -73,53 +71,11 @@ size_t phpdbg_get_total_page_size(void *addr, size_t size) { return (size_t)phpdbg_get_page_boundary(addr + size - 1) - (size_t)phpdbg_get_page_boundary(addr) + phpdbg_pagesize; } -phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *watch_addr) { - phpdbg_watchpoint_t *watch; - phpdbg_btree *branch = PHPDBG_G(watchpoint_tree); - int i = sizeof(void *) * 8 - 1, last_superior_i = -1; - size_t addr = 0; - size_t opline = (size_t)phpdbg_get_page_boundary(watch_addr) + phpdbg_pagesize - 1; - - /* find nearest watchpoint */ - do { - /* an impossible branch was found if: */ - if ((opline >> i) % 2 == 0 && !branch->branches[0]) { - /* there's no lower branch than opline */ - if (last_superior_i == -1) { - /* failure */ - return NULL; - } - /* reset state */ - branch = PHPDBG_G(watchpoint_tree); - addr = 0; - i = sizeof(void *) * 8 - 1; - /* follow branch according to bits in opline until the last lower branch before the impossible branch */ - do { - CHOOSE_BRANCH((opline >> i) % 2 == 1 && branch->branches[1]); - } while (--i > last_superior_i); - /* use now the lower branch of which we can be sure that it contains only branches lower than opline */ - CHOOSE_BRANCH(0); - /* and choose the highest possible branch in the branch containing only branches lower than opline */ - while (i--) { - CHOOSE_BRANCH(branch->branches[1]); - } - break; - } - /* follow branch according to bits in opline until having found an impossible branch */ - if ((opline >> i) % 2 == 1 && branch->branches[1]) { - if (branch->branches[0]) { - last_superior_i = i; - } - CHOOSE_BRANCH(1); - } else { - CHOOSE_BRANCH(0); - } - } while (i--); - - watch = branch->watchpoint; +phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr) { + phpdbg_watchpoint_t *watch = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1)->ptr; /* check if that addr is in a mprotect()'ed memory area */ - if ((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; } @@ -160,60 +116,17 @@ int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) } int phpdbg_print_changed_zval(void *llist_data) { - phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data; - void *oldPtr; - size_t opline; - TSRMLS_FETCH(); + phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data; /* fetch all changes between dump->page and dump->page + dump->size */ - opline = (size_t)dump->page + dump->size - 1; - - while (1) { - phpdbg_btree *branch = PHPDBG_G(watchpoint_tree); - phpdbg_watchpoint_t *watch; - int i = sizeof(void *) * 8 - 1, last_superior_i = -1; - size_t addr = 0; - - do { - /* an impossible branch was found if: */ - if ((opline >> i) % 2 == 0 && !branch->branches[0]) { - /* there's no lower branch than opline */ - if (last_superior_i == -1) { - return 1; - } - /* reset state */ - branch = PHPDBG_G(watchpoint_tree); - addr = 0; - i = sizeof(void *) * 8 - 1; - /* follow branch according to bits in opline until the last lower branch before the impossible branch */ - do { - CHOOSE_BRANCH((opline >> i) % 2 == 1 && branch->branches[1]); - } while (--i > last_superior_i); - /* use now the lower branch of which we can be sure that it contains only branches lower than opline */ - CHOOSE_BRANCH(0); - /* and choose the highest possible branch in the branch containing only branches lower than opline */ - while (i--) { - CHOOSE_BRANCH(branch->branches[1]); - } - break; - } - /* follow branch according to bits in opline until having found an impossible branch */ - if ((opline >> i) % 2 == 1 && branch->branches[1]) { - if (branch->branches[0]) { - last_superior_i = i; - } - CHOOSE_BRANCH(1); - } else { - CHOOSE_BRANCH(0); - } - } while (i--); + 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; - if (watch == branch->watchpoint) - return 1; /* TODO: there's sometime wrong with the breaking condition ... */ - watch = branch->watchpoint; - oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page); + while ((result = phpdbg_btree_next(&pos))) { + phpdbg_watchpoint_t *watch = result->ptr; + 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; @@ -222,17 +135,13 @@ int phpdbg_print_changed_zval(void *llist_data) { case WATCH_ON_ZVAL: phpdbg_write("Old value: "); zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC); - phpdbg_writeln(""); + phpdbg_writeln("\nOld refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc); phpdbg_write("New value: "); zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC); - phpdbg_writeln(""); + phpdbg_writeln("\nNew refcount: %d; Old is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc); break; } - } else { - break; } - - opline = (size_t)watch->addr.ptr - 1; } return 1; @@ -250,37 +159,14 @@ int phpdbg_print_changed_zvals(TSRMLS_D) { } void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) { - phpdbg_btree **branch = &PHPDBG_G(watchpoint_tree); - int i = sizeof(void *) * 8 - 1; - - do { - if (*branch == NULL) { - break; - } - branch = &(*branch)->branches[((size_t)watch->addr.ptr >> i) % 2]; - } while (i--); - - if (*branch == NULL) { - phpdbg_btree *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree)); - do { - (*branch)->branches[!(((size_t)watch->addr.ptr >> i) % 2)] = NULL; - branch = &(*branch)->branches[((size_t)watch->addr.ptr >> i) % 2]; - *branch = ++memory; - } while (i--); - } - - (*branch)->watchpoint = watch; + phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch); } -void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) { +void phpdbg_activate_watchpoint (phpdbg_watchpoint_t *watch) { int m; - watch->addr.ptr = addr; - watch->size = size; - watch->type = WATCH_ON_PTR; - /* pagesize is assumed to be in the range of 2^x */ - m = mprotect(phpdbg_get_page_boundary(addr), phpdbg_get_total_page_size(addr, size), PROT_NONE | PROT_READ); + 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)"); @@ -288,6 +174,12 @@ void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t } } +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; @@ -315,9 +207,11 @@ int phpdbg_create_var_watchpoint(char *name, size_t len TSRMLS_DC) { /* 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); zend_hash_add(&PHPDBG_G(watchpoints), name, len, &watch, sizeof(phpdbg_watchpoint_t *), NULL); phpdbg_store_watchpoint(watch TSRMLS_CC); - phpdbg_create_zval_watchpoint(*zv, watch); + + phpdbg_activate_watchpoint(watch); return SUCCESS; }