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)
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';
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
+#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
#ifdef ZTS
#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 */
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 */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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 <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#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;
+}
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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 <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#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
#include "zend.h"
#include "phpdbg.h"
+#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
#include <unistd.h>
#include <sys/mman.h>
#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;
#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) {
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;
}
}
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;
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;
}
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)");
}
}
+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;
/* 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;
}