root/sapi/phpdbg/phpdbg_watch.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. phpdbg_check_for_watchpoint
  2. phpdbg_change_watchpoint_access
  3. phpdbg_activate_watchpoint
  4. phpdbg_deactivate_watchpoint
  5. phpdbg_store_watchpoint
  6. phpdbg_remove_watchpoint
  7. phpdbg_create_addr_watchpoint
  8. phpdbg_create_zval_watchpoint
  9. phpdbg_create_ht_watchpoint
  10. phpdbg_create_watchpoint
  11. phpdbg_create_array_watchpoint
  12. phpdbg_get_property_key
  13. phpdbg_create_recursive_watchpoint
  14. phpdbg_delete_watchpoint_recursive
  15. phpdbg_delete_watchpoint
  16. phpdbg_watchpoint_parse_input
  17. phpdbg_watchpoint_parse_symtables
  18. PHPDBG_WATCH
  19. PHPDBG_WATCH
  20. PHPDBG_WATCH
  21. phpdbg_watch_HashTable_dtor
  22. phpdbg_create_var_watchpoint
  23. phpdbg_delete_var_watchpoint
  24. phpdbg_watchpoint_segfault_handler
  25. phpdbg_watchpoint_segfault_handler
  26. phpdbg_watchpoints_clean
  27. phpdbg_watch_dtor
  28. phpdbg_watch_mem_dtor
  29. phpdbg_setup_watchpoints
  30. phpdbg_print_changed_zval
  31. phpdbg_print_changed_zvals
  32. phpdbg_list_watchpoints
  33. phpdbg_watch_efree

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors: Felipe Pena <felipe@php.net>                                |
  16    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
  17    | Authors: Bob Weinand <bwoebi@php.net>                                |
  18    +----------------------------------------------------------------------+
  19 */
  20 
  21 #include "zend.h"
  22 #include "phpdbg.h"
  23 #include "phpdbg_btree.h"
  24 #include "phpdbg_watch.h"
  25 #include "phpdbg_utils.h"
  26 #ifndef _WIN32
  27 # include <unistd.h>
  28 # include <sys/mman.h>
  29 #endif
  30 
  31 ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
  32 
  33 
  34 typedef struct {
  35         void *page;
  36         size_t size;
  37         char reenable_writing;
  38         /* data must be last element */
  39         void *data;
  40 } phpdbg_watch_memdump;
  41 
  42 #define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
  43 
  44 
  45 static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) {
  46         phpdbg_watchpoint_t *watch;
  47         phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
  48 
  49         if (result == NULL) {
  50                 return NULL;
  51         }
  52 
  53         watch = result->ptr;
  54 
  55         /* check if that addr is in a mprotect()'ed memory area */
  56         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) {
  57                 /* failure */
  58                 return NULL;
  59         }
  60 
  61         return watch;
  62 }
  63 
  64 static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) {
  65         int m;
  66 
  67         /* pagesize is assumed to be in the range of 2^x */
  68         m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access);
  69 }
  70 
  71 static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
  72         phpdbg_change_watchpoint_access(watch, PROT_READ TSRMLS_CC);
  73 }
  74 
  75 static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
  76         phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE TSRMLS_CC);
  77 }
  78 
  79 static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
  80         phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch);
  81 }
  82 
  83 static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
  84         phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr);
  85 }
  86 
  87 void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
  88         watch->addr.ptr = addr;
  89         watch->size = size;
  90 }
  91 
  92 void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
  93         phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
  94         watch->type = WATCH_ON_ZVAL;
  95 }
  96 
  97 void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
  98         phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch);
  99         watch->type = WATCH_ON_HASHTABLE;
 100 }
 101 
 102 void phpdbg_watch_HashTable_dtor(zval **ptr);
 103 
 104 static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
 105         watch->flags |= PHPDBG_WATCH_SIMPLE;
 106 
 107         phpdbg_store_watchpoint(watch TSRMLS_CC);
 108         zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL);
 109 
 110         if (watch->type == WATCH_ON_ZVAL) {
 111                 phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor);
 112                 watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor;
 113         }
 114 
 115         phpdbg_activate_watchpoint(watch TSRMLS_CC);
 116 
 117         return SUCCESS;
 118 }
 119 
 120 static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
 121         HashTable *ht;
 122 
 123         switch (Z_TYPE_P(watch->addr.zv)) {
 124                 case IS_ARRAY:
 125                         ht = Z_ARRVAL_P(watch->addr.zv);
 126                         break;
 127                 case IS_OBJECT:
 128                         ht = Z_OBJPROP_P(watch->addr.zv);
 129                         break;
 130                 default:
 131                         return FAILURE;
 132         }
 133 
 134         phpdbg_create_ht_watchpoint(ht, watch);
 135 
 136         phpdbg_create_watchpoint(watch TSRMLS_CC);
 137 
 138         return SUCCESS;
 139 }
 140 
 141 static char *phpdbg_get_property_key(char *key) {
 142         if (*key != 0) {
 143                 return key;
 144         }
 145         return strchr(key + 1, 0) + 1;
 146 }
 147 
 148 static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
 149         HashTable *ht;
 150 
 151         if (watch->type != WATCH_ON_ZVAL) {
 152                 return FAILURE;
 153         }
 154 
 155         watch->flags |= PHPDBG_WATCH_RECURSIVE;
 156         phpdbg_create_watchpoint(watch TSRMLS_CC);
 157 
 158         switch (Z_TYPE_P(watch->addr.zv)) {
 159                 case IS_ARRAY:
 160                         ht = Z_ARRVAL_P(watch->addr.zv);
 161                         break;
 162                 case IS_OBJECT:
 163                         ht = Z_OBJPROP_P(watch->addr.zv);
 164                         break;
 165                 default:
 166                         return SUCCESS;
 167         }
 168 
 169         {
 170                 HashPosition position;
 171                 zval **zv;
 172                 zval key;
 173 
 174                 for (zend_hash_internal_pointer_reset_ex(ht, &position);
 175                      zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
 176                      zend_hash_move_forward_ex(ht, &position)) {
 177                         phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
 178 
 179                         new_watch->flags = PHPDBG_WATCH_RECURSIVE;
 180                         new_watch->parent = watch;
 181                         new_watch->parent_container = ht;
 182 
 183                         zend_hash_get_current_key_zval_ex(ht, &key, &position);
 184                         if (Z_TYPE(key) == IS_STRING) {
 185                                 new_watch->name_in_parent = zend_strndup(Z_STRVAL(key), Z_STRLEN(key));
 186                                 new_watch->name_in_parent_len = Z_STRLEN(key);
 187                         } else {
 188                                 new_watch->name_in_parent = NULL;
 189                                 new_watch->name_in_parent_len = asprintf(&new_watch->name_in_parent, "%ld", Z_LVAL(key));
 190                         }
 191 
 192                         new_watch->str = NULL;
 193                         new_watch->str_len = asprintf(&new_watch->str, "%.*s%s%s%s", (int)watch->str_len, watch->str, Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"]":"");
 194 
 195                         phpdbg_create_zval_watchpoint(*zv, new_watch);
 196                         phpdbg_create_recursive_watchpoint(new_watch TSRMLS_CC);
 197                 }
 198         }
 199 
 200         {
 201                 phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
 202 
 203                 new_watch->parent = watch;
 204                 new_watch->parent_container = watch->parent_container;
 205                 new_watch->name_in_parent = zend_strndup(watch->name_in_parent, watch->name_in_parent_len);
 206                 new_watch->name_in_parent_len = watch->name_in_parent_len;
 207                 new_watch->str = NULL;
 208                 new_watch->str_len = asprintf(&new_watch->str, "%.*s[]", (int)watch->str_len, watch->str);
 209                 new_watch->flags = PHPDBG_WATCH_RECURSIVE;
 210 
 211                 phpdbg_create_ht_watchpoint(ht, new_watch);
 212                 phpdbg_create_watchpoint(new_watch TSRMLS_CC);
 213         }
 214 
 215         return SUCCESS;
 216 }
 217 
 218 static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request TSRMLS_DC) {
 219         if (watch->type == WATCH_ON_HASHTABLE || (watch->type == WATCH_ON_ZVAL && (Z_TYPE_P(watch->addr.zv) == IS_ARRAY || Z_TYPE_P(watch->addr.zv) == IS_OBJECT))) {
 220                 HashTable *ht;
 221                 phpdbg_btree_result *result;
 222 
 223                 if (watch->type == WATCH_ON_HASHTABLE && user_request) {
 224                         HashPosition position;
 225                         zval **zv;
 226                         zval key;
 227                         char *str;
 228                         int str_len;
 229                         phpdbg_watchpoint_t **watchpoint;
 230 
 231                         ht = watch->addr.ht;
 232 
 233                         for (zend_hash_internal_pointer_reset_ex(ht, &position);
 234                              zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
 235                              zend_hash_move_forward_ex(ht, &position)) {
 236                                 zend_hash_get_current_key_zval_ex(ht, &key, &position);
 237                                 str = NULL;
 238                                 if (Z_TYPE(key) == IS_STRING) {
 239                                         str_len = asprintf(&str, "%.*s%s%s%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(Z_STRVAL(key)), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
 240                                 } else {
 241                                         str_len = asprintf(&str, "%.*s%s%li%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", Z_LVAL(key), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
 242                                 }
 243 
 244                                 if (zend_hash_find(&PHPDBG_G(watchpoints), str, str_len, (void **) &watchpoint) == SUCCESS) {
 245                                         phpdbg_delete_watchpoint_recursive(*watchpoint, 1 TSRMLS_CC);
 246                                 }
 247                         }
 248                 } else {
 249                         switch (Z_TYPE_P(watch->addr.zv)) {
 250                                 case IS_ARRAY:
 251                                         ht = Z_ARRVAL_P(watch->addr.zv);
 252                                         break;
 253                                 case IS_OBJECT:
 254                                         ht = Z_OBJPROP_P(watch->addr.zv);
 255                                         break;
 256                         }
 257 
 258                         if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) {
 259                                 phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request TSRMLS_CC);
 260                         }
 261                 }
 262         }
 263 
 264         return zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 265 }
 266 
 267 static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) {
 268         int ret;
 269         phpdbg_watchpoint_t *watch;
 270         phpdbg_btree_result *result;
 271 
 272         if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)tmp_watch->addr.ptr)) == NULL) {
 273                 return FAILURE;
 274         }
 275 
 276         watch = result->ptr;
 277 
 278         if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
 279                 ret = phpdbg_delete_watchpoint_recursive(watch, 1 TSRMLS_CC);
 280         } else {
 281                 ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 282         }
 283 
 284         free(tmp_watch->str);
 285         efree(tmp_watch);
 286 
 287         return ret;
 288 }
 289 
 290 static int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) {
 291         int ret = FAILURE;
 292         zend_bool new_index = 1;
 293         char *last_index;
 294         int index_len = 0;
 295         zval **zv;
 296 
 297         if (len < 2 || *input != '$') {
 298                 goto error;
 299         }
 300 
 301         while (i++ < len) {
 302                 if (i == len) {
 303                         new_index = 1;
 304                 } else {
 305                         switch (input[i]) {
 306                                 case '[':
 307                                         new_index = 1;
 308                                         break;
 309                                 case ']':
 310                                         break;
 311                                 case '>':
 312                                         if (last_index[index_len - 1] == '-') {
 313                                                 new_index = 1;
 314                                                 index_len--;
 315                                         }
 316                                         break;
 317 
 318                                 default:
 319                                         if (new_index) {
 320                                                 last_index = input + i;
 321                                                 new_index = 0;
 322                                         }
 323                                         if (input[i - 1] == ']') {
 324                                                 goto error;
 325                                         }
 326                                         index_len++;
 327                         }
 328                 }
 329 
 330                 if (new_index && index_len == 0) {
 331                         HashPosition position;
 332                         for (zend_hash_internal_pointer_reset_ex(parent, &position);
 333                              zend_hash_get_current_data_ex(parent, (void **)&zv, &position) == SUCCESS;
 334                              zend_hash_move_forward_ex(parent, &position)) {
 335                                 if (i == len || (i == len - 1 && input[len - 1] == ']')) {
 336                                         zval *key = emalloc(sizeof(zval));
 337                                         phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
 338                                         watch->flags = 0;
 339                                         zend_hash_get_current_key_zval_ex(parent, key, &position);
 340                                         convert_to_string(key);
 341                                         watch->str = malloc(i + Z_STRLEN_P(key) + 2);
 342                                         watch->str_len = sprintf(watch->str, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
 343                                         efree(key);
 344                                         watch->name_in_parent = zend_strndup(last_index, index_len);
 345                                         watch->name_in_parent_len = index_len;
 346                                         watch->parent_container = parent;
 347                                         phpdbg_create_zval_watchpoint(*zv, watch);
 348 
 349                                         ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
 350                                 } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
 351                                         phpdbg_watchpoint_parse_input(input, len, Z_OBJPROP_PP(zv), i, callback, silent TSRMLS_CC);
 352                                 } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
 353                                         phpdbg_watchpoint_parse_input(input, len, Z_ARRVAL_PP(zv), i, callback, silent TSRMLS_CC);
 354                                 } else {
 355                                         /* Ignore silently */
 356                                 }
 357                         }
 358                         return ret;
 359                 } else if (new_index) {
 360                         char last_chr = last_index[index_len];
 361                         last_index[index_len] = 0;
 362                         if (zend_symtable_find(parent, last_index, index_len + 1, (void **)&zv) == FAILURE) {
 363                                 if (!silent) {
 364                                         phpdbg_error("%.*s is undefined", (int)i, input);
 365                                 }
 366                                 return FAILURE;
 367                         }
 368                         last_index[index_len] = last_chr;
 369                         if (i == len) {
 370                                 phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
 371                                 watch->flags = 0;
 372                                 watch->str = zend_strndup(input, len);
 373                                 watch->str_len = len;
 374                                 watch->name_in_parent = zend_strndup(last_index, index_len);
 375                                 watch->name_in_parent_len = index_len;
 376                                 watch->parent_container = parent;
 377                                 phpdbg_create_zval_watchpoint(*zv, watch);
 378 
 379                                 ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
 380                         } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
 381                                 parent = Z_OBJPROP_PP(zv);
 382                         } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
 383                                 parent = Z_ARRVAL_PP(zv);
 384                         } else {
 385                                 phpdbg_error("%.*s is nor an array nor an object", (int)i, input);
 386                                 return FAILURE;
 387                         }
 388                         index_len = 0;
 389                 }
 390         }
 391 
 392         return ret;
 393         error:
 394                 phpdbg_error("Malformed input");
 395                 return FAILURE;
 396 }
 397 
 398 static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
 399         if (EG(This) && len >= 5 && !memcmp("$this", input, 5)) {
 400                 zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), NULL);
 401         }
 402 
 403         if (zend_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1 TSRMLS_CC) != FAILURE) {
 404                 return SUCCESS;
 405         }
 406 
 407         return phpdbg_watchpoint_parse_input(input, len, EG(active_symbol_table), 0, callback, 0 TSRMLS_CC);
 408 }
 409 
 410 PHPDBG_WATCH(delete) /* {{{ */
 411 {
 412         switch (param->type) {
 413                 case STR_PARAM:
 414                         if (phpdbg_delete_var_watchpoint(param->str, param->len TSRMLS_CC) == FAILURE) {
 415                                 phpdbg_error("Nothing was deleted, no corresponding watchpoint found");
 416                         } else {
 417                                 phpdbg_notice("Removed watchpoint %.*s", (int)param->len, param->str);
 418                         }
 419                         break;
 420 
 421                 phpdbg_default_switch_case();
 422         }
 423 
 424         return SUCCESS;
 425 } /* }}} */
 426 
 427 PHPDBG_WATCH(recursive) /* {{{ */
 428 {
 429         if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
 430                 return SUCCESS;
 431         }
 432 
 433         switch (param->type) {
 434                 case STR_PARAM:
 435                         if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint TSRMLS_CC) != FAILURE) {
 436                                 phpdbg_notice("Set recursive watchpoint on %.*s", (int)param->len, param->str);
 437                         }
 438                         break;
 439 
 440                 phpdbg_default_switch_case();
 441         }
 442 
 443         return SUCCESS;
 444 } /* }}} */
 445 
 446 PHPDBG_WATCH(array) /* {{{ */
 447 {
 448         if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
 449                 return SUCCESS;
 450         }
 451 
 452         switch (param->type) {
 453                 case STR_PARAM:
 454                         if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint TSRMLS_CC) != FAILURE) {
 455                                 phpdbg_notice("Set array watchpoint on %.*s", (int)param->len, param->str);
 456                         }
 457                         break;
 458 
 459                 phpdbg_default_switch_case();
 460         }
 461 
 462         return SUCCESS;
 463 } /* }}} */
 464 
 465 void phpdbg_watch_HashTable_dtor(zval **zv) {
 466         phpdbg_btree_result *result;
 467         TSRMLS_FETCH();
 468 
 469         zval_ptr_dtor_wrapper(zv);
 470 
 471         if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)*zv))) {
 472                 phpdbg_watchpoint_t *watch = result->ptr;
 473 
 474                 PHPDBG_G(watchpoint_hit) = 1;
 475 
 476                 phpdbg_notice("%.*s was removed, removing watchpoint%s", (int)watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE)?" recursively":"");
 477 
 478                 if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
 479                         phpdbg_delete_watchpoint_recursive(watch, 0 TSRMLS_CC);
 480                 } else {
 481                         zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 482                 }
 483         }
 484 }
 485 
 486 
 487 int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC) {
 488         if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
 489                 return FAILURE;
 490         }
 491 
 492         return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_watchpoint TSRMLS_CC);
 493 }
 494 
 495 int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC) {
 496         if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
 497                 return FAILURE;
 498         }
 499 
 500         return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_delete_watchpoint TSRMLS_CC);
 501 }
 502 
 503 #ifdef _WIN32
 504 int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC) {
 505 #else
 506 int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) {
 507 #endif
 508         void *page;
 509         phpdbg_watch_memdump *dump;
 510         phpdbg_watchpoint_t *watch;
 511         size_t size;
 512 
 513         watch = phpdbg_check_for_watchpoint(
 514 #ifdef _WIN32
 515                 addr
 516 #else
 517                 info->si_addr
 518 #endif
 519                 TSRMLS_CC);
 520 
 521         if (watch == NULL) {
 522                 return FAILURE;
 523         }
 524 
 525         page = phpdbg_get_page_boundary(watch->addr.ptr);
 526         size = phpdbg_get_total_page_size(watch->addr.ptr, watch->size);
 527 
 528         /* re-enable writing */
 529         mprotect(page, size, PROT_READ | PROT_WRITE);
 530 
 531         dump = malloc(MEMDUMP_SIZE(size));
 532         dump->page = page;
 533         dump->size = size;
 534 
 535         memcpy(&dump->data, page, size);
 536 
 537         zend_llist_add_element(&PHPDBG_G(watchlist_mem), &dump);
 538 
 539         return SUCCESS;
 540 }
 541 
 542 void phpdbg_watchpoints_clean(TSRMLS_D) {
 543         zend_hash_clean(&PHPDBG_G(watchpoints));
 544 }
 545 
 546 static void phpdbg_watch_dtor(void *pDest) {
 547         phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest;
 548         TSRMLS_FETCH();
 549 
 550         phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
 551         phpdbg_remove_watchpoint(watch TSRMLS_CC);
 552 
 553         free(watch->str);
 554         free(watch->name_in_parent);
 555         efree(watch);
 556 }
 557 
 558 static void phpdbg_watch_mem_dtor(void *llist_data) {
 559         phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data;
 560 
 561         /* Disble writing again */
 562         if (dump->reenable_writing) {
 563                 mprotect(dump->page, dump->size, PROT_READ);
 564         }
 565 
 566         free(*(void **)llist_data);
 567 }
 568 
 569 void phpdbg_setup_watchpoints(TSRMLS_D) {
 570 #if _SC_PAGE_SIZE
 571         phpdbg_pagesize = sysconf(_SC_PAGE_SIZE);
 572 #elif _SC_PAGESIZE
 573         phpdbg_pagesize = sysconf(_SC_PAGESIZE);
 574 #elif _SC_NUTC_OS_PAGESIZE
 575         phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE);
 576 #else
 577         phpdbg_pagesize = 4096; /* common pagesize */
 578 #endif
 579 
 580         zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1);
 581         phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8);
 582         phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8);
 583         zend_hash_init(&PHPDBG_G(watchpoints), 8, NULL, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC);
 584 }
 585 
 586 static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump TSRMLS_DC) {
 587         /* fetch all changes between dump->page and dump->page + dump->size */
 588         phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong)dump->page, (zend_ulong)dump->page + dump->size);
 589         phpdbg_btree_result *result, *htresult;
 590         int elementDiff;
 591         void *curTest;
 592 
 593         dump->reenable_writing = 0;
 594 
 595         while ((result = phpdbg_btree_next(&pos))) {
 596                 phpdbg_watchpoint_t *watch = result->ptr, *htwatch;
 597                 void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page);
 598                 char reenable = 1;
 599 
 600                 if ((size_t)watch->addr.ptr < (size_t)dump->page || (size_t)watch->addr.ptr + watch->size > (size_t)dump->page + dump->size) {
 601                         continue;
 602                 }
 603 
 604                 /* Test if the zval was separated and if necessary move the watchpoint */
 605                 if (zend_hash_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1, &curTest) == SUCCESS) {
 606                         if (watch->type == WATCH_ON_HASHTABLE) {
 607                                 switch (Z_TYPE_PP((zval **)curTest)) {
 608                                         case IS_ARRAY:
 609                                                 curTest = (void *)Z_ARRVAL_PP((zval **)curTest);
 610                                                 break;
 611                                         case IS_OBJECT:
 612                                                 curTest = (void *)Z_OBJPROP_PP((zval **)curTest);
 613                                                 break;
 614                                 }
 615                         } else {
 616                                 curTest = *(void **)curTest;
 617                         }
 618 
 619                         if (curTest != watch->addr.ptr) {
 620                                 phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
 621                                 phpdbg_remove_watchpoint(watch TSRMLS_CC);
 622                                 watch->addr.ptr = curTest;
 623                                 phpdbg_store_watchpoint(watch TSRMLS_CC);
 624                                 phpdbg_activate_watchpoint(watch TSRMLS_CC);
 625 
 626                                 reenable = 0;
 627                         }
 628                 }
 629 
 630                 /* Show to the user what changed and delete watchpoint upon removal */
 631                 if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) {
 632                         if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS || (watch->type == WATCH_ON_ZVAL && memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value))) || (watch->type == WATCH_ON_HASHTABLE
 633 #if ZEND_DEBUG
 634                             && !watch->addr.ht->inconsistent
 635 #endif
 636                             && zend_hash_num_elements((HashTable *)oldPtr) != zend_hash_num_elements(watch->addr.ht))) {
 637                                 PHPDBG_G(watchpoint_hit) = 1;
 638 
 639                                 phpdbg_notice("Breaking on watchpoint %s", watch->str);
 640                         }
 641 
 642                         switch (watch->type) {
 643                                 case WATCH_ON_ZVAL: {
 644                                         int removed = ((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);
 645                                         int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value));
 646                                         int show_ref = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc || ((zval *)oldPtr)->is_ref__gc != watch->addr.zv->is_ref__gc;
 647 
 648                                         if (removed || show_value) {
 649                                                 phpdbg_write("Old value: ");
 650                                                 if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) {
 651                                                         phpdbg_writeln("Value inaccessible, HashTable already destroyed");
 652                                                 } else {
 653                                                         zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC);
 654                                                         phpdbg_writeln("");
 655                                                 }
 656                                         }
 657                                         if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) {
 658                                                 phpdbg_writeln("Old refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc);
 659                                         }
 660 
 661                                         /* check if zval was removed */
 662                                         if (removed) {
 663                                                 phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
 664                                                 zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 665 
 666                                                 reenable = 0;
 667 
 668                                                 if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) {
 669                                                         goto remove_ht_watch;
 670                                                 }
 671 
 672                                                 break;
 673                                         }
 674 
 675                                         if (show_value) {
 676                                                 phpdbg_write("New value: ");
 677                                                 zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC);
 678                                                 phpdbg_writeln("");
 679                                         }
 680                                         if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) {
 681                                                 phpdbg_writeln("New refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc);
 682                                         }
 683 
 684                                         if ((Z_TYPE_P(watch->addr.zv) == IS_ARRAY && Z_ARRVAL_P(watch->addr.zv) != Z_ARRVAL_P((zval *)oldPtr)) || (Z_TYPE_P(watch->addr.zv) != IS_OBJECT && Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
 685                                                 /* add new watchpoints if necessary */
 686                                                 if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
 687                                                         phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
 688                                                 }
 689                                         }
 690 
 691                                         if ((Z_TYPE_P((zval *)oldPtr) != IS_ARRAY || Z_ARRVAL_P(watch->addr.zv) == Z_ARRVAL_P((zval *)oldPtr)) && (Z_TYPE_P((zval *)oldPtr) != IS_OBJECT || Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
 692                                                 break;
 693                                         }
 694 
 695 remove_ht_watch:
 696                                         if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) {
 697                                                 htwatch = htresult->ptr;
 698                                                 zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len);
 699                                         }
 700 
 701                                         break;
 702                                 }
 703                                 case WATCH_ON_HASHTABLE:
 704 
 705 #if ZEND_DEBUG
 706                                         if (watch->addr.ht->inconsistent) {
 707                                                 phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
 708                                                 zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 709 
 710                                                 reenable = 0;
 711 
 712                                                 break;
 713                                         }
 714 #endif
 715 
 716                                         elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht);
 717                                         if (elementDiff) {
 718                                                 if (elementDiff > 0) {
 719                                                         phpdbg_writeln("%d elements were removed from the array", elementDiff);
 720                                                 } else {
 721                                                         phpdbg_writeln("%d elements were added to the array", -elementDiff);
 722 
 723                                                         /* add new watchpoints if necessary */
 724                                                         if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
 725                                                                 phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
 726                                                         }
 727                                                 }
 728                                         }
 729                                         if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) {
 730                                                 phpdbg_writeln("Internal pointer of array was changed");
 731                                         }
 732                                         break;
 733                         }
 734                 }
 735 
 736                 dump->reenable_writing = dump->reenable_writing | reenable;
 737         }
 738 }
 739 
 740 int phpdbg_print_changed_zvals(TSRMLS_D) {
 741         zend_llist_position pos;
 742         phpdbg_watch_memdump **dump;
 743         int ret;
 744 
 745         if (zend_llist_count(&PHPDBG_G(watchlist_mem)) == 0) {
 746                 return FAILURE;
 747         }
 748 
 749         dump = (phpdbg_watch_memdump **)zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos);
 750 
 751         do {
 752                 phpdbg_print_changed_zval(*dump TSRMLS_CC);
 753         } while ((dump = (phpdbg_watch_memdump **)zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos)));
 754 
 755         zend_llist_clean(&PHPDBG_G(watchlist_mem));
 756 
 757         ret = PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE;
 758         PHPDBG_G(watchpoint_hit) = 0;
 759 
 760         return ret;
 761 }
 762 
 763 void phpdbg_list_watchpoints(TSRMLS_D) {
 764         HashPosition position;
 765         phpdbg_watchpoint_t **watch;
 766 
 767         for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(watchpoints), &position);
 768              zend_hash_get_current_data_ex(&PHPDBG_G(watchpoints), (void**) &watch, &position) == SUCCESS;
 769              zend_hash_move_forward_ex(&PHPDBG_G(watchpoints), &position)) {
 770                 phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str);
 771         }
 772 }
 773 
 774 void phpdbg_watch_efree(void *ptr) {
 775         phpdbg_btree_result *result;
 776         TSRMLS_FETCH();
 777 
 778         result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr);
 779 
 780         if (result) {
 781                 phpdbg_watchpoint_t *watch = result->ptr;
 782 
 783                 if ((size_t)watch->addr.ptr + watch->size > (size_t)ptr) {
 784                         zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
 785                 }
 786         }
 787 
 788         PHPDBG_G(original_free_function)(ptr);
 789 }

/* [<][>][^][v][top][bottom][index][help] */