Skip to content

Commit b003768

Browse files
committed
Rewrite watchpoints to be much more stable
This mainly involves a separate abstraction layer for elements (e.g. $a->b) and watchpoints (on pointer of the Bucket for example). Also better comparison handling (value backup vs. page dumps). It is not yet finished (there are sometimes false positives announced and names not yet perfect), but the functionality is working and not crashing as far as I have tested. Future scope is also relative watchpoints, e.g. "w $this->val expression()" which does not have the symbol tables as basis, but the value (in this example: return value of expression()) as basis.
1 parent 1223f7f commit b003768

18 files changed

+1523
-962
lines changed

Zend/zend.c

+6
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) /* {{{ */
300300
ZEND_PUTS(")");
301301
break;
302302
}
303+
case IS_REFERENCE:
304+
zend_print_flat_zval_r(Z_REFVAL_P(expr));
305+
break;
303306
default:
304307
zend_print_variable(expr);
305308
break;
@@ -355,6 +358,9 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /*
355358
case IS_LONG:
356359
smart_str_append_long(buf, Z_LVAL_P(expr));
357360
break;
361+
case IS_REFERENCE:
362+
zend_print_zval_r_to_buf(buf, Z_REFVAL_P(expr), indent);
363+
break;
358364
default:
359365
{
360366
zend_string *str = zval_get_string(expr);

main/SAPI.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -479,10 +479,9 @@ SAPI_API void sapi_activate(void)
479479

480480
/* Cookies */
481481
SG(request_info).cookie_data = sapi_module.read_cookies();
482-
483-
if (sapi_module.activate) {
484-
sapi_module.activate();
485-
}
482+
}
483+
if (sapi_module.activate) {
484+
sapi_module.activate();
486485
}
487486
if (sapi_module.input_filter_init) {
488487
sapi_module.input_filter_init();

sapi/phpdbg/phpdbg.c

+97-78
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ PHP_INI_END()
7575

7676
static zend_bool phpdbg_booted = 0;
7777
static zend_bool phpdbg_fully_started = 0;
78+
zend_bool use_mm_wrappers = 0;
7879

7980
static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
8081
{
@@ -189,79 +190,27 @@ static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
189190
destroy_zend_function(function);
190191
} /* }}} */
191192

193+
static void php_phpdbg_destroy_file_source(zval *data) /* {{{ */
194+
{
195+
phpdbg_file_source *source = (phpdbg_file_source *) Z_PTR_P(data);
196+
destroy_op_array(&source->op_array);
197+
if (source->buf) {
198+
efree(source->buf);
199+
}
200+
efree(source);
201+
} /* }}} */
202+
192203

193204
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
194205
{
195-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
196-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
197-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
198-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
199-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
200-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
201-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
202-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
203-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
204-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
205-
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
206-
207-
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
208-
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
206+
/* deactivate symbol table caching to have these properly destroyed upon stack leaving (especially important for watchpoints) */
207+
EG(symtable_cache_limit) = EG(symtable_cache) - 1;
209208

210209
return SUCCESS;
211210
} /* }}} */
212211

213212
static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
214213
{
215-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
216-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
217-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
218-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
219-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
220-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
221-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
222-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
223-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
224-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
225-
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
226-
zend_hash_destroy(&PHPDBG_G(file_sources));
227-
zend_hash_destroy(&PHPDBG_G(seek));
228-
zend_hash_destroy(&PHPDBG_G(registered));
229-
zend_hash_destroy(&PHPDBG_G(watchpoints));
230-
zend_llist_destroy(&PHPDBG_G(watchlist_mem));
231-
232-
if (PHPDBG_G(buffer)) {
233-
free(PHPDBG_G(buffer));
234-
PHPDBG_G(buffer) = NULL;
235-
}
236-
237-
if (PHPDBG_G(exec)) {
238-
efree(PHPDBG_G(exec));
239-
PHPDBG_G(exec) = NULL;
240-
}
241-
242-
if (PHPDBG_G(oplog)) {
243-
fclose(PHPDBG_G(oplog));
244-
PHPDBG_G(oplog) = NULL;
245-
}
246-
247-
if (PHPDBG_G(ops)) {
248-
destroy_op_array(PHPDBG_G(ops));
249-
efree(PHPDBG_G(ops));
250-
PHPDBG_G(ops) = NULL;
251-
}
252-
253-
if (PHPDBG_G(oplog_list)) {
254-
phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
255-
do {
256-
phpdbg_oplog_list *prev = cur->prev;
257-
efree(cur);
258-
cur = prev;
259-
} while (cur != NULL);
260-
261-
zend_arena_destroy(PHPDBG_G(oplog_arena));
262-
PHPDBG_G(oplog_list) = NULL;
263-
}
264-
265214
return SUCCESS;
266215
} /* }}} */
267216

@@ -845,8 +794,86 @@ static void php_sapi_phpdbg_log_message(char *message, int syslog_type_int) /* {
845794
}
846795
/* }}} */
847796

797+
static int php_sapi_phpdbg_activate(void) /* {{{ */
798+
{
799+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
800+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
801+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
802+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
803+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
804+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
805+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, php_phpdbg_destroy_bp_opline, 0);
806+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
807+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
808+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
809+
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
810+
811+
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
812+
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
813+
814+
zend_hash_init(&PHPDBG_G(file_sources), 0, NULL, php_phpdbg_destroy_file_source, 0);
815+
phpdbg_setup_watchpoints();
816+
817+
return SUCCESS;
818+
}
819+
848820
static int php_sapi_phpdbg_deactivate(void) /* {{{ */
849821
{
822+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
823+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
824+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
825+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
826+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
827+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
828+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
829+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
830+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
831+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
832+
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
833+
zend_hash_destroy(&PHPDBG_G(file_sources));
834+
zend_hash_destroy(&PHPDBG_G(seek));
835+
zend_hash_destroy(&PHPDBG_G(registered));
836+
phpdbg_destroy_watchpoints();
837+
838+
/* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
839+
if (use_mm_wrappers) {
840+
/* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
841+
*(int *) zend_mm_get_heap() = 0;
842+
}
843+
844+
if (PHPDBG_G(buffer)) {
845+
free(PHPDBG_G(buffer));
846+
PHPDBG_G(buffer) = NULL;
847+
}
848+
849+
if (PHPDBG_G(exec)) {
850+
efree(PHPDBG_G(exec));
851+
PHPDBG_G(exec) = NULL;
852+
}
853+
854+
if (PHPDBG_G(oplog)) {
855+
fclose(PHPDBG_G(oplog));
856+
PHPDBG_G(oplog) = NULL;
857+
}
858+
859+
if (PHPDBG_G(ops)) {
860+
destroy_op_array(PHPDBG_G(ops));
861+
efree(PHPDBG_G(ops));
862+
PHPDBG_G(ops) = NULL;
863+
}
864+
865+
if (PHPDBG_G(oplog_list)) {
866+
phpdbg_oplog_list *cur = PHPDBG_G(oplog_list);
867+
do {
868+
phpdbg_oplog_list *prev = cur->prev;
869+
efree(cur);
870+
cur = prev;
871+
} while (cur != NULL);
872+
873+
zend_arena_destroy(PHPDBG_G(oplog_arena));
874+
PHPDBG_G(oplog_list) = NULL;
875+
}
876+
850877
fflush(stdout);
851878
if (SG(request_info).argv0) {
852879
free(SG(request_info).argv0);
@@ -994,7 +1021,7 @@ static sapi_module_struct phpdbg_sapi_module = {
9941021
php_sapi_phpdbg_module_startup, /* startup */
9951022
php_module_shutdown_wrapper, /* shutdown */
9961023

997-
NULL, /* activate */
1024+
php_sapi_phpdbg_activate, /* activate */
9981025
php_sapi_phpdbg_deactivate, /* deactivate */
9991026

10001027
php_sapi_phpdbg_ub_write, /* unbuffered write */
@@ -1236,11 +1263,11 @@ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
12361263
switch (sig) {
12371264
case SIGBUS:
12381265
case SIGSEGV:
1239-
if (PHPDBG_G(sigsegv_bailout)) {
1240-
LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1241-
}
12421266
is_handled = phpdbg_watchpoint_segfault_handler(info, context);
12431267
if (is_handled == FAILURE) {
1268+
if (PHPDBG_G(sigsegv_bailout)) {
1269+
LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
1270+
}
12441271
zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
12451272
}
12461273
break;
@@ -1308,7 +1335,6 @@ int main(int argc, char **argv) /* {{{ */
13081335
FILE* stream = NULL;
13091336
char *print_opline_func;
13101337
zend_bool ext_stmt = 0;
1311-
zend_bool use_mm_wrappers = 0;
13121338
zend_bool is_exit;
13131339
int exit_status;
13141340

@@ -1635,8 +1661,6 @@ int main(int argc, char **argv) /* {{{ */
16351661

16361662
use_mm_wrappers = !_malloc && !_realloc && !_free;
16371663

1638-
phpdbg_init_list();
1639-
16401664
PHPDBG_G(original_free_function) = _free;
16411665
_free = phpdbg_watch_efree;
16421666

@@ -1650,14 +1674,14 @@ int main(int argc, char **argv) /* {{{ */
16501674
zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
16511675
}
16521676

1653-
phpdbg_setup_watchpoints();
1654-
16551677
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
16561678
zend_try {
16571679
zend_signal_activate();
16581680
} zend_end_try();
16591681
#endif
16601682

1683+
phpdbg_init_list();
1684+
16611685
PHPDBG_G(sapi_name_ptr) = sapi_name;
16621686

16631687
if (exec) { /* set execution context */
@@ -1976,11 +2000,6 @@ int main(int argc, char **argv) /* {{{ */
19762000
}
19772001
}
19782002

1979-
/* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
1980-
if (use_mm_wrappers) {
1981-
/* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
1982-
*(int *) mm_heap = 0;
1983-
}
19842003
zend_try {
19852004
php_request_shutdown(NULL);
19862005
} zend_end_try();

sapi/phpdbg/phpdbg.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,15 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
253253
#endif
254254
phpdbg_btree watchpoint_tree; /* tree with watchpoints */
255255
phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */
256-
HashTable watchpoints; /* watchpoints */
256+
HashTable watch_elements; /* user defined watch elements */
257257
HashTable watch_collisions; /* collision table to check if multiple watches share the same recursive watchpoint */
258-
zend_llist watchlist_mem; /* triggered watchpoints */
258+
HashTable watch_recreation; /* watch elements pending recreation of their respective watchpoints */
259+
HashTable watch_free; /* pointers to watch for being freed */
260+
HashTable *watchlist_mem; /* triggered watchpoints */
261+
HashTable *watchlist_mem_backup; /* triggered watchpoints backup table while iterating over it */
259262
zend_bool watchpoint_hit; /* a watchpoint was hit */
260263
void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */
261-
phpdbg_watchpoint_t *watch_tmp; /* temporary pointer for a watchpoint */
264+
phpdbg_watch_element *watch_tmp; /* temporary pointer for a watch element */
262265

263266
char *exec; /* file to execute */
264267
size_t exec_len; /* size of exec */

sapi/phpdbg/phpdbg_btree.c

+29-6
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@
2525
branch = branch->branches[!!(n)];
2626

2727
#ifdef _Win32
28-
# define emalloc malloc
29-
# define efree free
28+
# undef pemalloc
29+
# undef pefree
30+
# define pemalloc(size, persistent) malloc(size)
31+
# define pefree(ptr, persistent) free(ptr)
3032
#endif
3133

3234
/* depth in bits */
3335
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
3436
tree->depth = depth;
3537
tree->branch = NULL;
38+
tree->persistent = 0;
3639
tree->count = 0;
3740
}
3841

@@ -157,7 +160,7 @@ int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr,
157160
}
158161

159162
{
160-
phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
163+
phpdbg_btree_branch *memory = *branch = pemalloc((i + 2) * sizeof(phpdbg_btree_branch), tree->persistent);
161164
do {
162165
(*branch)->branches[!((idx >> i) % 2)] = NULL;
163166
branch = &(*branch)->branches[(idx >> i) % 2];
@@ -199,22 +202,22 @@ int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
199202
tree->count--;
200203

201204
if (i_last_dual_branch == -1) {
202-
efree(tree->branch);
205+
pefree(tree->branch, tree->persistent);
203206
tree->branch = NULL;
204207
} else {
205208
if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
206209
phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
207210

208211
memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
209-
efree(last_dual_branch->branches[!last_dual_branch_branch]);
212+
pefree(last_dual_branch->branches[!last_dual_branch_branch], tree->persistent);
210213
last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
211214

212215
branch = last_dual_branch->branches[!last_dual_branch_branch];
213216
for (i = i_last_dual_branch; i--;) {
214217
branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
215218
}
216219
} else {
217-
efree(last_dual_branch->branches[last_dual_branch_branch]);
220+
pefree(last_dual_branch->branches[last_dual_branch_branch], tree->persistent);
218221
}
219222

220223
last_dual_branch->branches[last_dual_branch_branch] = NULL;
@@ -223,6 +226,26 @@ int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
223226
return SUCCESS;
224227
}
225228

229+
void phpdbg_btree_clean_recursive(phpdbg_btree_branch *branch, zend_ulong depth, zend_bool persistent) {
230+
phpdbg_btree_branch *start = branch;
231+
while (depth--) {
232+
zend_bool use_branch = branch + 1 == branch->branches[0];
233+
if (branch->branches[use_branch]) {
234+
phpdbg_btree_clean_recursive(branch->branches[use_branch], depth, persistent);
235+
}
236+
}
237+
238+
pefree(start, persistent);
239+
}
240+
241+
void phpdbg_btree_clean(phpdbg_btree *tree) {
242+
if (tree->branch) {
243+
phpdbg_btree_clean_recursive(tree->branch, tree->depth, tree->persistent);
244+
tree->branch = NULL;
245+
tree->count = 0;
246+
}
247+
}
248+
226249
void phpdbg_btree_branch_dump(phpdbg_btree_branch *branch, zend_ulong depth) {
227250
if (branch) {
228251
if (depth--) {

0 commit comments

Comments
 (0)