phpdbg_watch.c 25 KB

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