root/sapi/phpdbg/phpdbg_bp.c

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

DEFINITIONS

This source file includes following definitions.
  1. ZEND_EXTERN_MODULE_GLOBALS
  2. phpdbg_file_breaks_dtor
  3. phpdbg_class_breaks_dtor
  4. phpdbg_opline_class_breaks_dtor
  5. phpdbg_opline_breaks_dtor
  6. phpdbg_reset_breakpoints
  7. phpdbg_export_breakpoints
  8. phpdbg_export_breakpoints_to_string
  9. phpdbg_set_breakpoint_file
  10. phpdbg_resolve_pending_file_break_ex
  11. phpdbg_resolve_pending_file_break
  12. phpdbg_set_breakpoint_symbol
  13. phpdbg_set_breakpoint_method
  14. phpdbg_set_breakpoint_opline
  15. phpdbg_resolve_op_array_break
  16. phpdbg_resolve_op_array_breaks
  17. phpdbg_resolve_opline_break
  18. phpdbg_set_breakpoint_method_opline
  19. phpdbg_set_breakpoint_function_opline
  20. phpdbg_set_breakpoint_file_opline
  21. phpdbg_set_breakpoint_opcode
  22. phpdbg_set_breakpoint_opline_ex
  23. phpdbg_create_conditional_break
  24. phpdbg_set_breakpoint_expression
  25. phpdbg_set_breakpoint_at
  26. phpdbg_find_breakpoint_file
  27. phpdbg_find_breakpoint_symbol
  28. phpdbg_find_breakpoint_method
  29. phpdbg_find_breakpoint_opline
  30. phpdbg_find_breakpoint_opcode
  31. phpdbg_find_breakpoint_param
  32. phpdbg_find_conditional_breakpoint
  33. phpdbg_find_breakpoint
  34. phpdbg_delete_breakpoint
  35. phpdbg_clear_breakpoints
  36. phpdbg_hit_breakpoint
  37. phpdbg_print_breakpoint
  38. phpdbg_enable_breakpoint
  39. phpdbg_disable_breakpoint
  40. phpdbg_enable_breakpoints
  41. phpdbg_disable_breakpoints
  42. phpdbg_find_breakbase
  43. phpdbg_find_breakbase_ex
  44. phpdbg_print_breakpoints

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 7                                                        |
   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 "zend_hash.h"
  23 #include "phpdbg.h"
  24 #include "phpdbg_bp.h"
  25 #include "phpdbg_utils.h"
  26 #include "phpdbg_opcode.h"
  27 #include "zend_globals.h"
  28 
  29 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
  30 
  31 /* {{{ private api functions */
  32 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array*);
  33 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function*);
  34 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array*);
  35 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t);
  36 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar);
  37 static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data); /* }}} */
  38 
  39 /*
  40 * Note:
  41 *       A break point must always set the correct id and type
  42 *       A set breakpoint function must always map new points
  43 */
  44 static inline void _phpdbg_break_mapping(int id, HashTable *table) /* {{{ */
  45 {
  46         zend_hash_index_update_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], id, table);
  47 }
  48 /* }}} */
  49 
  50 #define PHPDBG_BREAK_MAPPING(id, table) _phpdbg_break_mapping(id, table)
  51 #define PHPDBG_BREAK_UNMAPPING(id) \
  52         zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], (id))
  53 
  54 #define PHPDBG_BREAK_INIT(b, t) do {\
  55         b.id = PHPDBG_G(bp_count)++; \
  56         b.type = t; \
  57         b.disabled = 0;\
  58         b.hits = 0; \
  59 } while(0)
  60 
  61 static void phpdbg_file_breaks_dtor(zval *data) /* {{{ */
  62 {
  63         phpdbg_breakfile_t *bp = (phpdbg_breakfile_t*) Z_PTR_P(data);
  64 
  65         efree((char*)bp->filename);
  66         efree(bp);
  67 } /* }}} */
  68 
  69 static void phpdbg_class_breaks_dtor(zval *data) /* {{{ */
  70 {
  71         phpdbg_breakmethod_t *bp = (phpdbg_breakmethod_t *) Z_PTR_P(data);
  72 
  73         efree((char*)bp->class_name);
  74         efree((char*)bp->func_name);
  75         efree(bp);
  76 } /* }}} */
  77 
  78 static void phpdbg_opline_class_breaks_dtor(zval *data) /* {{{ */
  79 {
  80         zend_hash_destroy(Z_ARRVAL_P(data));
  81         efree(Z_ARRVAL_P(data));
  82 } /* }}} */
  83 
  84 static void phpdbg_opline_breaks_dtor(zval *data) /* {{{ */
  85 {
  86         phpdbg_breakopline_t *bp = (phpdbg_breakopline_t *) Z_PTR_P(data);
  87 
  88         if (bp->class_name) {
  89                 efree((char*)bp->class_name);
  90         }
  91         if (bp->func_name) {
  92                 efree((char*)bp->func_name);
  93         }
  94         efree(bp);
  95 } /* }}} */
  96 
  97 PHPDBG_API void phpdbg_reset_breakpoints(void) /* {{{ */
  98 {
  99         HashTable *table;
 100 
 101         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], table) {
 102                 phpdbg_breakbase_t *brake;
 103 
 104                 ZEND_HASH_FOREACH_PTR(table, brake) {
 105                         brake->hits = 0;
 106                 } ZEND_HASH_FOREACH_END();
 107         } ZEND_HASH_FOREACH_END();
 108 } /* }}} */
 109 
 110 PHPDBG_API void phpdbg_export_breakpoints(FILE *handle) /* {{{ */
 111 {
 112         char *string;
 113         phpdbg_export_breakpoints_to_string(&string);
 114         fputs(string, handle);
 115 }
 116 /* }}} */
 117 
 118 PHPDBG_API void phpdbg_export_breakpoints_to_string(char **str) /* {{{ */
 119 {
 120         HashTable *table;
 121         zend_ulong id = 0L;
 122 
 123         *str = "";
 124 
 125         if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP])) {
 126                 phpdbg_notice("exportbreakpoint", "count=\"%d\"", "Exporting %d breakpoints", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]));
 127 
 128                 /* this only looks like magic, it isn't */
 129                 ZEND_HASH_FOREACH_NUM_KEY_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], id, table) {
 130                         phpdbg_breakbase_t *brake;
 131 
 132                         ZEND_HASH_FOREACH_PTR(table, brake) {
 133                                 if (brake->id == id) {
 134                                         char *new_str = NULL;
 135 
 136                                         switch (brake->type) {
 137                                                 case PHPDBG_BREAK_FILE: {
 138                                                         phpdbg_asprintf(&new_str,
 139                                                                 "%sbreak %s:%lu\n", *str,
 140                                                                 ((phpdbg_breakfile_t*)brake)->filename,
 141                                                                 ((phpdbg_breakfile_t*)brake)->line);
 142                                                 } break;
 143 
 144                                                 case PHPDBG_BREAK_SYM: {
 145                                                         phpdbg_asprintf(&new_str,
 146                                                                 "%sbreak %s\n", *str,
 147                                                                 ((phpdbg_breaksymbol_t*)brake)->symbol);
 148                                                 } break;
 149 
 150                                                 case PHPDBG_BREAK_METHOD: {
 151                                                         phpdbg_asprintf(&new_str,
 152                                                                 "%sbreak %s::%s\n", *str,
 153                                                                 ((phpdbg_breakmethod_t*)brake)->class_name,
 154                                                                 ((phpdbg_breakmethod_t*)brake)->func_name);
 155                                                 } break;
 156 
 157                                                 case PHPDBG_BREAK_METHOD_OPLINE: {
 158                                                         phpdbg_asprintf(&new_str,
 159                                                                 "%sbreak %s::%s#%llu\n", *str,
 160                                                                 ((phpdbg_breakopline_t*)brake)->class_name,
 161                                                                 ((phpdbg_breakopline_t*)brake)->func_name,
 162                                                                 ((phpdbg_breakopline_t*)brake)->opline_num);
 163                                                 } break;
 164 
 165                                                 case PHPDBG_BREAK_FUNCTION_OPLINE: {
 166                                                         phpdbg_asprintf(&new_str,
 167                                                                 "%sbreak %s#%llu\n", *str,
 168                                                                 ((phpdbg_breakopline_t*)brake)->func_name,
 169                                                                 ((phpdbg_breakopline_t*)brake)->opline_num);
 170                                                 } break;
 171 
 172                                                 case PHPDBG_BREAK_FILE_OPLINE: {
 173                                                         phpdbg_asprintf(&new_str,
 174                                                                 "%sbreak %s:#%llu\n", *str,
 175                                                                 ((phpdbg_breakopline_t*)brake)->class_name,
 176                                                                 ((phpdbg_breakopline_t*)brake)->opline_num);
 177                                                 } break;
 178 
 179                                                 case PHPDBG_BREAK_OPCODE: {
 180                                                         phpdbg_asprintf(&new_str,
 181                                                                 "%sbreak %s\n", *str,
 182                                                                 ((phpdbg_breakop_t*)brake)->name);
 183                                                 } break;
 184 
 185                                                 case PHPDBG_BREAK_COND: {
 186                                                         phpdbg_breakcond_t *conditional = (phpdbg_breakcond_t*) brake;
 187 
 188                                                         if (conditional->paramed) {
 189                                                                 switch (conditional->param.type) {
 190                                                                         case STR_PARAM:
 191                                                                                 phpdbg_asprintf(&new_str,
 192                                                                                         "%sbreak at %s if %s\n", *str, conditional->param.str, conditional->code);
 193                                                                         break;
 194 
 195                                                                         case METHOD_PARAM:
 196                                                                                 phpdbg_asprintf(&new_str,
 197                                                                                         "%sbreak at %s::%s if %s\n", *str,
 198                                                                                         conditional->param.method.class, conditional->param.method.name,
 199                                                                                         conditional->code);
 200                                                                         break;
 201 
 202                                                                         case FILE_PARAM:
 203                                                                                 phpdbg_asprintf(&new_str,
 204                                                                                         "%sbreak at %s:%lu if %s\n", *str,
 205                                                                                         conditional->param.file.name, conditional->param.file.line,
 206                                                                                         conditional->code);
 207                                                                         break;
 208 
 209                                                                         default: { /* do nothing */ } break;
 210                                                                 }
 211                                                         } else {
 212                                                                 phpdbg_asprintf(&new_str, "%sbreak if %s\n", str, conditional->code);
 213                                                         }
 214                                                 } break;
 215 
 216                                                 default: continue;
 217                                         }
 218 
 219                                         if ((*str)[0]) {
 220                                                 efree(*str);
 221                                         }
 222                                         *str = new_str;
 223                                 }
 224                         } ZEND_HASH_FOREACH_END();
 225                 } ZEND_HASH_FOREACH_END();
 226         }
 227 
 228         if (!(*str)[0]) {
 229                 *str = NULL;
 230         }
 231 } /* }}} */
 232 
 233 PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num) /* {{{ */
 234 {
 235         php_stream_statbuf ssb;
 236         char realpath[MAXPATHLEN];
 237         const char *original_path = path;
 238         zend_bool pending = 0;
 239         zend_string *path_str;
 240 
 241         HashTable *broken, *file_breaks = &PHPDBG_G(bp)[PHPDBG_BREAK_FILE];
 242         phpdbg_breakfile_t new_break;
 243         size_t path_len = 0L;
 244 
 245         if (VCWD_REALPATH(path, realpath)) {
 246                 path = realpath;
 247         }
 248         path_len = strlen(path);
 249 
 250         phpdbg_debug("file path: %s, resolved path: %s, was compiled: %d\n", original_path, path, zend_hash_str_exists(&PHPDBG_G(file_sources), path, path_len));
 251 
 252         if (!zend_hash_str_exists(&PHPDBG_G(file_sources), path, path_len)) {
 253                 if (php_stream_stat_path(path, &ssb) == FAILURE) {
 254                         if (original_path[0] == '/') {
 255                                 phpdbg_error("breakpoint", "type=\"nofile\" add=\"fail\" file=\"%s\"", "Cannot stat %s, it does not exist", original_path);
 256                                 return;
 257                         }
 258 
 259                         file_breaks = &PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING];
 260                         path = original_path;
 261                         path_len = strlen(path);
 262                         pending = 1;
 263                 } else if (!(ssb.sb.st_mode & (S_IFREG|S_IFLNK))) {
 264                         phpdbg_error("breakpoint", "type=\"notregular\" add=\"fail\" file=\"%s\"", "Cannot set breakpoint in %s, it is not a regular file", path);
 265                         return;
 266                 } else {
 267                         phpdbg_debug("File exists, but not compiled\n");
 268                 }
 269         }
 270 
 271         path_str = zend_string_init(path, path_len, 0);
 272 
 273         if (!(broken = zend_hash_find_ptr(file_breaks, path_str))) {
 274                 HashTable breaks;
 275                 zend_hash_init(&breaks, 8, NULL, phpdbg_file_breaks_dtor, 0);
 276 
 277                 broken = zend_hash_add_mem(file_breaks, path_str, &breaks, sizeof(HashTable));
 278         }
 279 
 280         if (!zend_hash_index_exists(broken, line_num)) {
 281                 PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE);
 282                 new_break.filename = estrndup(path, path_len);
 283                 new_break.line = line_num;
 284 
 285                 zend_hash_index_update_mem(broken, line_num, &new_break, sizeof(phpdbg_breakfile_t));
 286 
 287                 PHPDBG_BREAK_MAPPING(new_break.id, broken);
 288 
 289                 if (pending) {
 290                         zend_string *file;
 291                         ZEND_HASH_FOREACH_STR_KEY(&PHPDBG_G(file_sources), file) {
 292                                 HashTable *fileht;
 293 
 294                                 phpdbg_debug("Compare against loaded %s\n", file);
 295 
 296                                 if (!(pending = ((fileht = phpdbg_resolve_pending_file_break_ex(ZSTR_VAL(file), ZSTR_LEN(file), path_str, broken)) == NULL))) {
 297                                         new_break = *(phpdbg_breakfile_t *) zend_hash_index_find_ptr(fileht, line_num);
 298                                         break;
 299                                 }
 300                         } ZEND_HASH_FOREACH_END();
 301                 }
 302 
 303                 if (pending) {
 304                         PHPDBG_G(flags) |= PHPDBG_HAS_PENDING_FILE_BP;
 305 
 306                         phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" file=\"%s\" line=\"%ld\" pending=\"pending\"", "Pending breakpoint #%d added at %s:%ld", new_break.id, new_break.filename, new_break.line);
 307                 } else {
 308                         PHPDBG_G(flags) |= PHPDBG_HAS_FILE_BP;
 309 
 310                         phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" file=\"%s\" line=\"%ld\"", "Breakpoint #%d added at %s:%ld", new_break.id, new_break.filename, new_break.line);
 311                 }
 312         } else {
 313                 phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" file=\"%s\" line=\"%ld\"", "Breakpoint at %s:%ld exists", path, line_num);
 314         }
 315 
 316         zend_string_release(path_str);
 317 } /* }}} */
 318 
 319 PHPDBG_API HashTable *phpdbg_resolve_pending_file_break_ex(const char *file, uint filelen, zend_string *cur, HashTable *fileht) /* {{{ */
 320 {
 321         phpdbg_debug("file: %s, filelen: %u, cur: %s, curlen %u, pos: %c, memcmp: %d\n", file, filelen, ZSTR_VAL(cur), ZSTR_LEN(cur), filelen > ZSTR_LEN(cur) ? file[filelen - ZSTR_LEN(cur) - 1] : '?', filelen > ZSTR_LEN(cur) ? memcmp(file + filelen - ZSTR_LEN(cur), ZSTR_VAL(cur), ZSTR_LEN(cur)) : 0);
 322 
 323 #ifdef _WIN32
 324 # define WIN32_PATH_CHECK file[filelen - ZSTR_LEN(cur) - 1] == '\\'
 325 #else
 326 # define WIN32_PATH_CHECK 0
 327 #endif
 328 
 329         if (((ZSTR_LEN(cur) < filelen && (file[filelen - ZSTR_LEN(cur) - 1] == '/' || WIN32_PATH_CHECK)) || filelen == ZSTR_LEN(cur)) && !memcmp(file + filelen - ZSTR_LEN(cur), ZSTR_VAL(cur), ZSTR_LEN(cur))) {
 330                 phpdbg_breakfile_t *brake, new_brake;
 331                 HashTable *master;
 332 
 333                 PHPDBG_G(flags) |= PHPDBG_HAS_FILE_BP;
 334 
 335                 if (!(master = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], file, filelen))) {
 336                         HashTable new_ht;
 337                         zend_hash_init(&new_ht, 8, NULL, phpdbg_file_breaks_dtor, 0);
 338                         master = zend_hash_str_add_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], file, filelen, &new_ht, sizeof(HashTable));
 339                 }
 340 
 341                 ZEND_HASH_FOREACH_PTR(fileht, brake) {
 342                         new_brake = *brake;
 343                         new_brake.filename = estrndup(file, filelen);
 344                         PHPDBG_BREAK_UNMAPPING(brake->id);
 345 
 346                         if (zend_hash_index_add_mem(master, brake->line, &new_brake, sizeof(phpdbg_breakfile_t))) {
 347                                 PHPDBG_BREAK_MAPPING(brake->id, master);
 348                         }
 349                 } ZEND_HASH_FOREACH_END();
 350 
 351                 zend_hash_del(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], cur);
 352 
 353                 if (!zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING])) {
 354                         PHPDBG_G(flags) &= ~PHPDBG_HAS_PENDING_FILE_BP;
 355                 }
 356 
 357                 phpdbg_debug("compiled file: %s, cur bp file: %s\n", file, cur);
 358 
 359                 return master;
 360         }
 361 
 362         return NULL;
 363 } /* }}} */
 364 
 365 PHPDBG_API void phpdbg_resolve_pending_file_break(const char *file) /* {{{ */
 366 {
 367         HashTable *fileht;
 368         uint filelen = strlen(file);
 369         zend_string *cur;
 370 
 371         phpdbg_debug("was compiled: %s\n", file);
 372 
 373         ZEND_HASH_FOREACH_STR_KEY_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], cur, fileht) {
 374                 phpdbg_debug("check bp: %s\n", cur);
 375 
 376                 phpdbg_resolve_pending_file_break_ex(file, filelen, cur, fileht);
 377         } ZEND_HASH_FOREACH_END();
 378 } /* }}} */
 379 
 380 PHPDBG_API void phpdbg_set_breakpoint_symbol(const char *name, size_t name_len) /* {{{ */
 381 {
 382         char *lcname;
 383 
 384         if (*name == '\\') {
 385                 name++;
 386                 name_len--;
 387         }
 388 
 389         lcname = zend_str_tolower_dup(name, name_len);
 390 
 391         if (!zend_hash_str_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], name, name_len)) {
 392                 phpdbg_breaksymbol_t new_break;
 393 
 394                 PHPDBG_G(flags) |= PHPDBG_HAS_SYM_BP;
 395 
 396                 PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_SYM);
 397                 new_break.symbol = estrndup(name, name_len);
 398 
 399                 zend_hash_str_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], lcname, name_len, &new_break, sizeof(phpdbg_breaksymbol_t));
 400 
 401                 phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" function=\"%s\"", "Breakpoint #%d added at %s", new_break.id, new_break.symbol);
 402 
 403                 PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
 404         } else {
 405                 phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" function=\"%s\"", "Breakpoint exists at %s", name);
 406         }
 407 
 408         efree(lcname);
 409 } /* }}} */
 410 
 411 PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char *func_name) /* {{{ */
 412 {
 413         HashTable class_breaks, *class_table;
 414         size_t class_len = strlen(class_name);
 415         size_t func_len = strlen(func_name);
 416         char *func_lcname, *class_lcname;
 417 
 418         if (*class_name == '\\') {
 419                 class_name++;
 420                 class_len--;
 421         }
 422 
 423         func_lcname = zend_str_tolower_dup(func_name, func_len);
 424         class_lcname = zend_str_tolower_dup(class_name, class_len);
 425 
 426         if (!(class_table = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname, class_len))) {
 427                 zend_hash_init(&class_breaks, 8, NULL, phpdbg_class_breaks_dtor, 0);
 428                 class_table = zend_hash_str_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname, class_len, &class_breaks, sizeof(HashTable));
 429         }
 430 
 431         if (!zend_hash_str_exists(class_table, func_lcname, func_len)) {
 432                 phpdbg_breakmethod_t new_break;
 433 
 434                 PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_BP;
 435 
 436                 PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD);
 437                 new_break.class_name = estrndup(class_name, class_len);
 438                 new_break.class_len = class_len;
 439                 new_break.func_name = estrndup(func_name, func_len);
 440                 new_break.func_len = func_len;
 441 
 442                 zend_hash_str_update_mem(class_table, func_lcname, func_len, &new_break, sizeof(phpdbg_breakmethod_t));
 443 
 444                 phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" method=\"%s::%s\"", "Breakpoint #%d added at %s::%s", new_break.id, class_name, func_name);
 445 
 446                 PHPDBG_BREAK_MAPPING(new_break.id, class_table);
 447         } else {
 448                 phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" method=\"%s::%s\"", "Breakpoint exists at %s::%s", class_name, func_name);
 449         }
 450 
 451         efree(func_lcname);
 452         efree(class_lcname);
 453 } /* }}} */
 454 
 455 PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline) /* {{{ */
 456 {
 457         if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline)) {
 458                 phpdbg_breakline_t new_break;
 459 
 460                 PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
 461 
 462                 PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
 463                 new_break.name = NULL;
 464                 new_break.opline = opline;
 465                 new_break.base = NULL;
 466 
 467                 zend_hash_index_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline, &new_break, sizeof(phpdbg_breakline_t));
 468 
 469                 phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" opline=\"%#lx\"", "Breakpoint #%d added at %#lx", new_break.id, new_break.opline);
 470                 PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
 471         } else {
 472                 phpdbg_error("breakpoint", "type=\"exists\" add=\"fail\" opline=\"%#lx\"", "Breakpoint exists at %#lx", opline);
 473         }
 474 } /* }}} */
 475 
 476 PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array) /* {{{ */
 477 {
 478         phpdbg_breakline_t opline_break;
 479         if (op_array->last <= brake->opline_num) {
 480                 if (brake->class_name == NULL) {
 481                         phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" function=\"%s\" usedoplinenum=\"%ld\"", "There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num);
 482                 } else if (brake->func_name == NULL) {
 483                         phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" file=\"%s\" usedoplinenum=\"%ld\"", "There are only %d oplines in file %s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->opline_num);
 484                 } else {
 485                         phpdbg_error("breakpoint", "type=\"maxoplines\" add=\"fail\" maxoplinenum=\"%d\" method=\"%s::%s\" usedoplinenum=\"%ld\"", "There are only %d oplines in method %s::%s (breaking at opline %ld impossible)", op_array->last, brake->class_name, brake->func_name, brake->opline_num);
 486                 }
 487 
 488                 return FAILURE;
 489         }
 490 
 491         opline_break.disabled = 0;
 492         opline_break.hits = 0;
 493         opline_break.id = brake->id;
 494         opline_break.opline = brake->opline = (zend_ulong)(op_array->opcodes + brake->opline_num);
 495         opline_break.name = NULL;
 496         opline_break.base = brake;
 497         if (op_array->scope) {
 498                 opline_break.type = PHPDBG_BREAK_METHOD_OPLINE;
 499         } else if (op_array->function_name) {
 500                 opline_break.type = PHPDBG_BREAK_FUNCTION_OPLINE;
 501         } else {
 502                 opline_break.type = PHPDBG_BREAK_FILE_OPLINE;
 503         }
 504 
 505         PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
 506 
 507         zend_hash_index_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline_break.opline, &opline_break, sizeof(phpdbg_breakline_t));
 508 
 509         return SUCCESS;
 510 } /* }}} */
 511 
 512 PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array) /* {{{ */
 513 {
 514         HashTable *func_table = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE];
 515         HashTable *oplines_table;
 516         phpdbg_breakopline_t *brake;
 517 
 518         if (op_array->scope != NULL && !(func_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], op_array->scope->name))) {
 519                 return;
 520         }
 521 
 522         if (op_array->function_name == NULL) {
 523                 if (!(oplines_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], op_array->filename))) {
 524                         return;
 525                 }
 526         } else if (!op_array->function_name || !(oplines_table = zend_hash_find_ptr(func_table, op_array->function_name))) {
 527                 return;
 528         }
 529 
 530         ZEND_HASH_FOREACH_PTR(oplines_table, brake) {
 531                 if (phpdbg_resolve_op_array_break(brake, op_array) == SUCCESS) {
 532                         phpdbg_breakline_t *opline_break;
 533 
 534                         zend_hash_internal_pointer_end(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
 535                         opline_break = zend_hash_get_current_data_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
 536 
 537                         phpdbg_notice("breakpoint", "add=\"success\" id=\"%d\" symbol=\"%s\" num=\"%ld\" opline=\"%#lx\"", "Breakpoint #%d resolved at %s%s%s#%ld (opline %#lx)",
 538                                 opline_break->id,
 539                                 brake->class_name ? brake->class_name : "",
 540                                 brake->class_name && brake->func_name ? "::" : "",
 541                                 brake->func_name ? brake->func_name : "",
 542                                 brake->opline_num,
 543                                 opline_break->opline);
 544                 }
 545         } ZEND_HASH_FOREACH_END();
 546 } /* }}} */
 547 
 548 PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break) /* {{{ */
 549 {
 550         HashTable *func_table = EG(function_table);
 551         zend_function *func;
 552 
 553         if (new_break->func_name == NULL) {
 554                 if (EG(current_execute_data) == NULL) {
 555                         if (PHPDBG_G(ops) != NULL && !memcmp(PHPDBG_G(ops)->filename, new_break->class_name, new_break->class_len)) {
 556                                 if (phpdbg_resolve_op_array_break(new_break, PHPDBG_G(ops)) == SUCCESS) {
 557                                         return SUCCESS;
 558                                 } else {
 559                                         return 2;
 560                                 }
 561                         }
 562                         return FAILURE;
 563                 } else {
 564                         zend_execute_data *execute_data = EG(current_execute_data);
 565                         do {
 566                                 if (ZEND_USER_CODE(execute_data->func->common.type)) {
 567                                         zend_op_array *op_array = &execute_data->func->op_array;
 568                                         if (op_array->function_name == NULL && op_array->scope == NULL && new_break->class_len == ZSTR_LEN(op_array->filename) && !memcmp(ZSTR_VAL(op_array->filename), new_break->class_name, new_break->class_len)) {
 569                                                 if (phpdbg_resolve_op_array_break(new_break, op_array) == SUCCESS) {
 570                                                         return SUCCESS;
 571                                                 } else {
 572                                                         return 2;
 573                                                 }
 574                                         }
 575                                 }
 576                         } while ((execute_data = execute_data->prev_execute_data) != NULL);
 577                         return FAILURE;
 578                 }
 579         }
 580 
 581         if (new_break->class_name != NULL) {
 582                 zend_class_entry *ce;
 583                 if (!(ce = zend_hash_str_find_ptr(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len))) {
 584                         return FAILURE;
 585                 }
 586                 func_table = &ce->function_table;
 587         }
 588 
 589         if (!(func = zend_hash_str_find_ptr(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len))) {
 590                 if (new_break->class_name != NULL && new_break->func_name != NULL) {
 591                         phpdbg_error("breakpoint", "type=\"nomethod\" method=\"%s::%s\"", "Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name);
 592                         return 2;
 593                 }
 594                 return FAILURE;
 595         }
 596 
 597         if (func->type != ZEND_USER_FUNCTION) {
 598                 if (new_break->class_name == NULL) {
 599                         phpdbg_error("breakpoint", "type=\"internalfunction\" function=\"%s\"", "%s is not a user defined function, no oplines exist", new_break->func_name);
 600                 } else {
 601                         phpdbg_error("breakpoint", "type=\"internalfunction\" method=\"%s::%s\"", "%s::%s is not a user defined method, no oplines exist", new_break->class_name, new_break->func_name);
 602                 }
 603                 return 2;
 604         }
 605 
 606         if (phpdbg_resolve_op_array_break(new_break, &func->op_array) == FAILURE) {
 607                 return 2;
 608         }
 609 
 610         return SUCCESS;
 611 } /* }}} */
 612 
 613 /* TODO ... method/function oplines need to be normalized (leading backslash, lowercase) and file oplines need to be resolved properly */
 614 
 615 PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline) /* {{{ */
 616 {
 617         phpdbg_breakopline_t new_break;
 618         HashTable class_breaks, *class_table;
 619         HashTable method_breaks, *method_table;
 620 
 621         PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_METHOD_OPLINE);
 622         new_break.func_len = strlen(method);
 623         new_break.func_name = estrndup(method, new_break.func_len);
 624         new_break.class_len = strlen(class);
 625         new_break.class_name = estrndup(class, new_break.class_len);
 626         new_break.opline_num = opline;
 627         new_break.opline = 0;
 628 
 629         switch (phpdbg_resolve_opline_break(&new_break)) {
 630                 case FAILURE:
 631                         phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" method=\"%::%s\" num=\"%ld\"", "Pending breakpoint #%d at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
 632                         break;
 633 
 634                 case SUCCESS:
 635                         phpdbg_notice("breakpoint", "id=\"%d\" method=\"%::%s\" num=\"%ld\"", "Breakpoint #%d added at %s::%s#%ld", new_break.id, new_break.class_name, new_break.func_name, opline);
 636                         break;
 637 
 638                 case 2:
 639                         return;
 640         }
 641 
 642         if (!(class_table = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], new_break.class_name, new_break.class_len))) {
 643                 zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0);
 644                 class_table = zend_hash_str_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], new_break.class_name, new_break.class_len, &class_breaks, sizeof(HashTable));
 645         }
 646 
 647         if (!(method_table = zend_hash_str_find_ptr(class_table, new_break.func_name, new_break.func_len))) {
 648                 zend_hash_init(&method_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
 649                 method_table = zend_hash_str_update_mem(class_table, new_break.func_name, new_break.func_len, &method_breaks, sizeof(HashTable));
 650         }
 651 
 652         if (zend_hash_index_exists(method_table, opline)) {
 653                 phpdbg_error("breakpoint", "type=\"exists\" method=\"%s\" num=\"%ld\"", "Breakpoint already exists for %s::%s#%ld", new_break.class_name, new_break.func_name, opline);
 654                 efree((char*)new_break.func_name);
 655                 efree((char*)new_break.class_name);
 656                 PHPDBG_G(bp_count)--;
 657                 return;
 658         }
 659 
 660         PHPDBG_G(flags) |= PHPDBG_HAS_METHOD_OPLINE_BP;
 661 
 662         PHPDBG_BREAK_MAPPING(new_break.id, method_table);
 663 
 664         zend_hash_index_update_mem(method_table, opline, &new_break, sizeof(phpdbg_breakopline_t));
 665 }
 666 /* }}} */
 667 
 668 PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline) /* {{{ */
 669 {
 670         phpdbg_breakopline_t new_break;
 671         HashTable func_breaks, *func_table;
 672 
 673         PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FUNCTION_OPLINE);
 674         new_break.func_len = strlen(function);
 675         new_break.func_name = estrndup(function, new_break.func_len);
 676         new_break.class_len = 0;
 677         new_break.class_name = NULL;
 678         new_break.opline_num = opline;
 679         new_break.opline = 0;
 680 
 681         switch (phpdbg_resolve_opline_break(&new_break)) {
 682                 case FAILURE:
 683                         phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" function=\"%s\" num=\"%ld\"", "Pending breakpoint #%d at %s#%ld", new_break.id, new_break.func_name, opline);
 684                         break;
 685 
 686                 case SUCCESS:
 687                         phpdbg_notice("breakpoint", "id=\"%d\" function=\"%s\" num=\"%ld\"", "Breakpoint #%d added at %s#%ld", new_break.id, new_break.func_name, opline);
 688                         break;
 689 
 690                 case 2:
 691                         return;
 692         }
 693 
 694         if (!(func_table = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], new_break.func_name, new_break.func_len))) {
 695                 zend_hash_init(&func_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
 696                 func_table = zend_hash_str_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], new_break.func_name, new_break.func_len, &func_breaks, sizeof(HashTable));
 697         }
 698 
 699         if (zend_hash_index_exists(func_table, opline)) {
 700                 phpdbg_error("breakpoint", "type=\"exists\" function=\"%s\" num=\"%ld\"", "Breakpoint already exists for %s#%ld", new_break.func_name, opline);
 701                 efree((char*)new_break.func_name);
 702                 PHPDBG_G(bp_count)--;
 703                 return;
 704         }
 705 
 706         PHPDBG_BREAK_MAPPING(new_break.id, func_table);
 707 
 708         PHPDBG_G(flags) |= PHPDBG_HAS_FUNCTION_OPLINE_BP;
 709 
 710         zend_hash_index_update_mem(func_table, opline, &new_break, sizeof(phpdbg_breakopline_t));
 711 }
 712 /* }}} */
 713 
 714 PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline) /* {{{ */
 715 {
 716         phpdbg_breakopline_t new_break;
 717         HashTable file_breaks, *file_table;
 718 
 719         PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_FILE_OPLINE);
 720         new_break.func_len = 0;
 721         new_break.func_name = NULL;
 722         new_break.class_len = strlen(file);
 723         new_break.class_name = estrndup(file, new_break.class_len);
 724         new_break.opline_num = opline;
 725         new_break.opline = 0;
 726 
 727         switch (phpdbg_resolve_opline_break(&new_break)) {
 728                 case FAILURE:
 729                         phpdbg_notice("breakpoint", "pending=\"pending\" id=\"%d\" file=\"%s\" num=\"%ld\"", "Pending breakpoint #%d at %s:%ld", new_break.id, new_break.class_name, opline);
 730                         break;
 731 
 732                 case SUCCESS:
 733                         phpdbg_notice("breakpoint", "id=\"%d\" file=\"%s\" num=\"%ld\"", "Breakpoint #%d added at %s:%ld", new_break.id, new_break.class_name, opline);
 734                         break;
 735 
 736                 case 2:
 737                         return;
 738         }
 739 
 740         if (!(file_table = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], new_break.class_name, new_break.class_len))) {
 741                 zend_hash_init(&file_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0);
 742                 file_table = zend_hash_str_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], new_break.class_name, new_break.class_len, &file_breaks, sizeof(HashTable));
 743         }
 744 
 745         if (zend_hash_index_exists(file_table, opline)) {
 746                 phpdbg_error("breakpoint", "type=\"exists\" file=\"%s\" num=\"%d\"", "Breakpoint already exists for %s:%ld", new_break.class_name, opline);
 747                 efree((char*)new_break.class_name);
 748                 PHPDBG_G(bp_count)--;
 749                 return;
 750         }
 751 
 752         PHPDBG_BREAK_MAPPING(new_break.id, file_table);
 753 
 754         PHPDBG_G(flags) |= PHPDBG_HAS_FILE_OPLINE_BP;
 755 
 756         zend_hash_index_update_mem(file_table, opline, &new_break, sizeof(phpdbg_breakopline_t));
 757 }
 758 /* }}} */
 759 
 760 PHPDBG_API void phpdbg_set_breakpoint_opcode(const char *name, size_t name_len) /* {{{ */
 761 {
 762         phpdbg_breakop_t new_break;
 763         zend_ulong hash = zend_hash_func(name, name_len);
 764 
 765         if (zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash)) {
 766                 phpdbg_error("breakpoint", "type=\"exists\" opcode=\"%s\"", "Breakpoint exists for %s", name);
 767                 return;
 768         }
 769 
 770         PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPCODE);
 771         new_break.hash = hash;
 772         new_break.name = estrndup(name, name_len);
 773 
 774         zend_hash_index_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], hash, &new_break, sizeof(phpdbg_breakop_t));
 775 
 776         PHPDBG_G(flags) |= PHPDBG_HAS_OPCODE_BP;
 777 
 778         phpdbg_notice("breakpoint", "id=\"%d\" opcode=\"%s\"", "Breakpoint #%d added at %s", new_break.id, name);
 779         PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
 780 } /* }}} */
 781 
 782 PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline) /* {{{ */
 783 {
 784         if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (zend_ulong) opline)) {
 785                 phpdbg_breakline_t new_break;
 786 
 787                 PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP;
 788 
 789                 PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_OPLINE);
 790                 new_break.opline = (zend_ulong) opline;
 791                 new_break.base = NULL;
 792 
 793                 zend_hash_index_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (zend_ulong) opline, &new_break, sizeof(phpdbg_breakline_t));
 794 
 795                 phpdbg_notice("breakpoint", "id=\"%d\" opline=\"%#lx\"", "Breakpoint #%d added at %#lx", new_break.id, new_break.opline);
 796                 PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
 797         } else {
 798                 phpdbg_error("breakpoint", "type=\"exists\" opline=\"%#lx\"", "Breakpoint exists for opline %#lx", (zend_ulong) opline);
 799         }
 800 } /* }}} */
 801 
 802 static inline void phpdbg_create_conditional_break(phpdbg_breakcond_t *brake, const phpdbg_param_t *param, const char *expr, size_t expr_len, zend_ulong hash) /* {{{ */
 803 {
 804         phpdbg_breakcond_t new_break;
 805         uint32_t cops = CG(compiler_options);
 806         zval pv;
 807 
 808         PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
 809         new_break.hash = hash;
 810 
 811         if (param) {
 812                 new_break.paramed = 1;
 813                 phpdbg_copy_param(
 814                         param, &new_break.param);
 815         } else {
 816                 new_break.paramed = 0;
 817         }
 818 
 819         cops = CG(compiler_options);
 820 
 821         CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
 822 
 823         new_break.code = estrndup(expr, expr_len);
 824         new_break.code_len = expr_len;
 825 
 826         Z_STR(pv) = zend_string_alloc(expr_len + sizeof("return ;") - 1, 0);
 827         memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
 828         memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
 829         Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
 830         Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
 831         Z_TYPE_INFO(pv) = IS_STRING;
 832 
 833         new_break.ops = zend_compile_string(&pv, "Conditional Breakpoint Code");
 834 
 835         zval_dtor(&pv);
 836 
 837         if (new_break.ops) {
 838                 brake = zend_hash_index_update_mem(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break, sizeof(phpdbg_breakcond_t));
 839 
 840                 phpdbg_notice("breakpoint", "id=\"%d\" expression=\"%s\" ptr=\"%p\"", "Conditional breakpoint #%d added %s/%p", brake->id, brake->code, brake->ops);
 841 
 842                 PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
 843                 PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
 844         } else {
 845                  phpdbg_error("compile", "expression=\"%s\"", "Failed to compile code for expression %s", expr);
 846                  efree((char*)new_break.code);
 847                  PHPDBG_G(bp_count)--;
 848         }
 849 
 850         CG(compiler_options) = cops;
 851 } /* }}} */
 852 
 853 PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_len) /* {{{ */
 854 {
 855         zend_ulong expr_hash = zend_inline_hash_func(expr, expr_len);
 856         phpdbg_breakcond_t new_break;
 857 
 858         if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
 859                 phpdbg_create_conditional_break(
 860                         &new_break, NULL, expr, expr_len, expr_hash);
 861         } else {
 862                 phpdbg_error("breakpoint", "type=\"exists\" expression=\"%s\"", "Conditional break %s exists", expr);
 863         }
 864 } /* }}} */
 865 
 866 PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param) /* {{{ */
 867 {
 868         phpdbg_breakcond_t new_break;
 869         phpdbg_param_t *condition;
 870         zend_ulong hash = 0L;
 871 
 872         if (param->next) {
 873                 condition = param->next;
 874                 hash = zend_inline_hash_func(condition->str, condition->len);
 875 
 876                 if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
 877                         phpdbg_create_conditional_break(&new_break, param, condition->str, condition->len, hash);
 878                 } else {
 879                         phpdbg_notice("breakpoint", "type=\"exists\" arg=\"%s\"", "Conditional break %s exists at the specified location", condition->str);
 880                 }
 881         }
 882 
 883 } /* }}} */
 884 
 885 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array) /* {{{ */
 886 {
 887         HashTable *breaks;
 888         phpdbg_breakbase_t *brake;
 889         size_t path_len;
 890         char realpath[MAXPATHLEN];
 891         const char *path = ZSTR_VAL(op_array->filename);
 892 
 893         if (VCWD_REALPATH(path, realpath)) {
 894                 path = realpath;
 895         }
 896 
 897         path_len = strlen(path);
 898 
 899 #if 0
 900         phpdbg_debug("Op at: %.*s %d\n", path_len, path, (*EG(opline_ptr))->lineno);
 901 #endif
 902 
 903         if (!(breaks = zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], path, path_len))) {
 904                 return NULL;
 905         }
 906 
 907         if (EG(current_execute_data) && (brake = zend_hash_index_find_ptr(breaks, EG(current_execute_data)->opline->lineno))) {
 908                 return brake;
 909         }
 910 
 911         return NULL;
 912 } /* }}} */
 913 
 914 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *fbc) /* {{{ */
 915 {
 916         zend_op_array *ops;
 917 
 918         if (fbc->type != ZEND_USER_FUNCTION) {
 919                 return NULL;
 920         }
 921 
 922         ops = (zend_op_array *) fbc;
 923 
 924         if (ops->scope) {
 925                 /* find method breaks here */
 926                 return phpdbg_find_breakpoint_method(ops);
 927         }
 928 
 929         if (ops->function_name) {
 930                 phpdbg_breakbase_t *brake;
 931                 zend_string *fname = zend_string_tolower(ops->function_name);
 932 
 933                 brake = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname);
 934 
 935                 zend_string_release(fname);
 936                 return brake;
 937         } else {
 938                 return zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ZEND_STRL("main"));
 939         }
 940 } /* }}} */
 941 
 942 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *ops) /* {{{ */
 943 {
 944         HashTable *class_table;
 945         phpdbg_breakbase_t *brake = NULL;
 946         zend_string *class_lcname = zend_string_tolower(ops->scope->name);
 947 
 948         if ((class_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname))) {
 949                 zend_string *lcname = zend_string_tolower(ops->function_name);
 950 
 951                 brake = zend_hash_find_ptr(class_table, lcname);
 952 
 953                 zend_string_release(lcname);
 954         }
 955 
 956         zend_string_release(class_lcname);
 957         return brake;
 958 } /* }}} */
 959 
 960 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline) /* {{{ */
 961 {
 962         phpdbg_breakline_t *brake;
 963 
 964         if ((brake = zend_hash_index_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], (zend_ulong) opline)) && brake->base) {
 965                 return (phpdbg_breakbase_t *)brake->base;
 966         }
 967 
 968         return (phpdbg_breakbase_t *) brake;
 969 } /* }}} */
 970 
 971 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar opcode) /* {{{ */
 972 {
 973         const char *opname = zend_get_opcode_name(opcode);
 974 
 975         if (!opname) {
 976                 return NULL;
 977         }
 978 
 979         return zend_hash_index_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], zend_hash_func(opname, strlen(opname)));
 980 } /* }}} */
 981 
 982 static inline zend_bool phpdbg_find_breakpoint_param(phpdbg_param_t *param, zend_execute_data *execute_data) /* {{{ */
 983 {
 984         zend_function *function = execute_data->func;
 985 
 986         switch (param->type) {
 987                 case NUMERIC_FUNCTION_PARAM:
 988                 case STR_PARAM: {
 989                         /* function breakpoint */
 990 
 991                         if (function->type != ZEND_USER_FUNCTION) {
 992                                 return 0;
 993                         }
 994 
 995                         {
 996                                 const char *str = NULL;
 997                                 size_t len = 0L;
 998                                 zend_op_array *ops = (zend_op_array*)function;
 999                                 str = ops->function_name ? ZSTR_VAL(ops->function_name) : "main";
1000                                 len = ops->function_name ? ZSTR_LEN(ops->function_name) : strlen(str);
1001 
1002                                 if (len == param->len && memcmp(param->str, str, len) == SUCCESS) {
1003                                         return param->type == STR_PARAM || execute_data->opline - ops->opcodes == param->num;
1004                                 }
1005                         }
1006                 } break;
1007 
1008                 case FILE_PARAM: {
1009                         if (param->file.line == zend_get_executed_lineno()) {
1010                                 const char *str = zend_get_executed_filename();
1011                                 size_t lengths[2] = {strlen(param->file.name), strlen(str)};
1012 
1013                                 if (lengths[0] == lengths[1]) {
1014                                         return (memcmp(
1015                                                 param->file.name, str, lengths[0]) == SUCCESS);
1016                                 }
1017                         }
1018                 } break;
1019 
1020                 case NUMERIC_METHOD_PARAM:
1021                 case METHOD_PARAM: {
1022                         if (function->type != ZEND_USER_FUNCTION) {
1023                                 return 0;
1024                         }
1025 
1026                         {
1027                                 zend_op_array *ops = (zend_op_array*) function;
1028 
1029                                 if (ops->scope) {
1030                                         size_t lengths[2] = { strlen(param->method.class), ZSTR_LEN(ops->scope->name) };
1031                                         if (lengths[0] == lengths[1] && memcmp(param->method.class, ops->scope->name, lengths[0]) == SUCCESS) {
1032                                                 lengths[0] = strlen(param->method.name);
1033                                                 lengths[1] = ZSTR_LEN(ops->function_name);
1034 
1035                                                 if (lengths[0] == lengths[1] && memcmp(param->method.name, ops->function_name, lengths[0]) == SUCCESS) {
1036                                                         return param->type == METHOD_PARAM || (execute_data->opline - ops->opcodes) == param->num;
1037                                                 }
1038                                         }
1039                                 }
1040                         }
1041                 } break;
1042 
1043                 case ADDR_PARAM: {
1044                         return ((zend_ulong)(phpdbg_opline_ptr_t)execute_data->opline == param->addr);
1045                 } break;
1046 
1047                 default: {
1048                         /* do nothing */
1049                 } break;
1050         }
1051         return 0;
1052 } /* }}} */
1053 
1054 static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data) /* {{{ */
1055 {
1056         phpdbg_breakcond_t *bp;
1057         int breakpoint = FAILURE;
1058 
1059         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], bp) {
1060                 zval retval;
1061                 const zend_op *orig_opline = EG(current_execute_data)->opline;
1062                 zend_function *orig_func = EG(current_execute_data)->func;
1063                 zval *orig_retval = EG(current_execute_data)->return_value;
1064 
1065                 if (((phpdbg_breakbase_t*)bp)->disabled) {
1066                         continue;
1067                 }
1068 
1069                 if (bp->paramed) {
1070                         if (!phpdbg_find_breakpoint_param(&bp->param, execute_data)) {
1071                                 continue;
1072                         }
1073                 }
1074 
1075                 EG(no_extensions) = 1;
1076 
1077                 zend_rebuild_symbol_table();
1078 
1079                 zend_try {
1080                         PHPDBG_G(flags) |= PHPDBG_IN_COND_BP;
1081                         zend_execute(bp->ops, &retval);
1082                         if (zend_is_true(&retval)) {
1083                                 breakpoint = SUCCESS;
1084                         }
1085                 } zend_end_try();
1086 
1087                 EG(no_extensions) = 1;
1088                 EG(current_execute_data)->opline = orig_opline;
1089                 EG(current_execute_data)->func = orig_func;
1090                 EG(current_execute_data)->return_value = orig_retval;
1091                 PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
1092 
1093                 if (breakpoint == SUCCESS) {
1094                         break;
1095                 }
1096         } ZEND_HASH_FOREACH_END();
1097 
1098         return (breakpoint == SUCCESS) ? ((phpdbg_breakbase_t *) bp) : NULL;
1099 } /* }}} */
1100 
1101 PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakpoint(zend_execute_data *execute_data) /* {{{ */
1102 {
1103         phpdbg_breakbase_t *base = NULL;
1104 
1105         if (!(PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED)) {
1106                 return NULL;
1107         }
1108 
1109         /* conditions cannot be executed by eval()'d code */
1110         if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL) &&
1111                 (PHPDBG_G(flags) & PHPDBG_HAS_COND_BP) &&
1112                 (base = phpdbg_find_conditional_breakpoint(execute_data))) {
1113                 goto result;
1114         }
1115 
1116         if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP) && (base = phpdbg_find_breakpoint_file(&execute_data->func->op_array))) {
1117                 goto result;
1118         }
1119 
1120         if (PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP)) {
1121                 zend_op_array *op_array = &execute_data->func->op_array;
1122                 /* check we are at the beginning of the stack, but after argument RECV */
1123                 if (execute_data->opline == op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
1124                         if ((base = phpdbg_find_breakpoint_symbol(execute_data->func))) {
1125                                 goto result;
1126                         }
1127                 }
1128         }
1129 
1130         if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP) && (base = phpdbg_find_breakpoint_opline((phpdbg_opline_ptr_t) execute_data->opline))) {
1131                 goto result;
1132         }
1133 
1134         if ((PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) && (base = phpdbg_find_breakpoint_opcode(execute_data->opline->opcode))) {
1135                 goto result;
1136         }
1137 
1138         return NULL;
1139 
1140 result:
1141         /* we return nothing for disable breakpoints */
1142         if (base->disabled) {
1143                 return NULL;
1144         }
1145 
1146         return base;
1147 } /* }}} */
1148 
1149 PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num) /* {{{ */
1150 {
1151         HashTable *table;
1152         phpdbg_breakbase_t *brake;
1153         zend_string *strkey;
1154         zend_ulong numkey;
1155 
1156         if ((brake = phpdbg_find_breakbase_ex(num, &table, &numkey, &strkey))) {
1157                 int type = brake->type;
1158                 char *name = NULL;
1159                 size_t name_len = 0L;
1160 
1161                 switch (type) {
1162                         case PHPDBG_BREAK_FILE:
1163                         case PHPDBG_BREAK_METHOD:
1164                                 if (zend_hash_num_elements(table) == 1) {
1165                                         name = estrdup(brake->name);
1166                                         name_len = strlen(name);
1167                                         if (zend_hash_num_elements(&PHPDBG_G(bp)[type]) == 1) {
1168                                                 PHPDBG_G(flags) &= ~(1<<(brake->type+1));
1169                                         }
1170                                 }
1171                         break;
1172 
1173                         default: {
1174                                 if (zend_hash_num_elements(table) == 1) {
1175                                         PHPDBG_G(flags) &= ~(1<<(brake->type+1));
1176                                 }
1177                         }
1178                 }
1179 
1180                 switch (type) {
1181                         case PHPDBG_BREAK_FILE_OPLINE:
1182                         case PHPDBG_BREAK_FUNCTION_OPLINE:
1183                         case PHPDBG_BREAK_METHOD_OPLINE:
1184                                 if (zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]) == 1) {
1185                                         PHPDBG_G(flags) &= PHPDBG_HAS_OPLINE_BP;
1186                                 }
1187                                 zend_hash_index_del(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], ((phpdbg_breakopline_t *) brake)->opline);
1188                 }
1189 
1190                 if (strkey) {
1191                         zend_hash_del(table, strkey);
1192                 } else {
1193                         zend_hash_index_del(table, numkey);
1194                 }
1195 
1196                 switch (type) {
1197                         case PHPDBG_BREAK_FILE:
1198                         case PHPDBG_BREAK_METHOD:
1199                                 if (name) {
1200                                         zend_hash_str_del(&PHPDBG_G(bp)[type], name, name_len);
1201                                         efree(name);
1202                                 }
1203                         break;
1204                 }
1205 
1206                 phpdbg_notice("breakpoint", "deleted=\"success\" id=\"%ld\"", "Deleted breakpoint #%ld", num);
1207                 PHPDBG_BREAK_UNMAPPING(num);
1208         } else {
1209                 phpdbg_error("breakpoint", "type=\"nobreakpoint\" deleted=\"fail\" id=\"%ld\"", "Failed to find breakpoint #%ld", num);
1210         }
1211 } /* }}} */
1212 
1213 PHPDBG_API void phpdbg_clear_breakpoints(void) /* {{{ */
1214 {
1215         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
1216         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
1217         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
1218         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
1219         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]);
1220         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]);
1221         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]);
1222         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
1223         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
1224         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
1225         zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
1226 
1227         PHPDBG_G(flags) &= ~PHPDBG_BP_MASK;
1228 
1229         PHPDBG_G(bp_count) = 0;
1230 } /* }}} */
1231 
1232 PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t *brake, zend_bool output) /* {{{ */
1233 {
1234         brake->hits++;
1235 
1236         if (output) {
1237                 phpdbg_print_breakpoint(brake);
1238         }
1239 } /* }}} */
1240 
1241 PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t *brake) /* {{{ */
1242 {
1243         if (!brake)
1244                 goto unknown;
1245 
1246         switch (brake->type) {
1247                 case PHPDBG_BREAK_FILE: {
1248                         phpdbg_notice("breakpoint", "id=\"%d\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d at %s:%ld, hits: %lu",
1249                                 ((phpdbg_breakfile_t*)brake)->id,
1250                                 ((phpdbg_breakfile_t*)brake)->filename,
1251                                 ((phpdbg_breakfile_t*)brake)->line,
1252                                 ((phpdbg_breakfile_t*)brake)->hits);
1253                 } break;
1254 
1255                 case PHPDBG_BREAK_SYM: {
1256                         phpdbg_notice("breakpoint", "id=\"%d\" function=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s() at %s:%u, hits: %lu",
1257                                 ((phpdbg_breaksymbol_t*)brake)->id,
1258                                 ((phpdbg_breaksymbol_t*)brake)->symbol,
1259                                 zend_get_executed_filename(),
1260                                 zend_get_executed_lineno(),
1261                                 ((phpdbg_breakfile_t*)brake)->hits);
1262                 } break;
1263 
1264                 case PHPDBG_BREAK_OPLINE: {
1265                         phpdbg_notice("breakpoint", "id=\"%d\" opline=\"%#lx\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %#lx at %s:%u, hits: %lu",
1266                                 ((phpdbg_breakline_t*)brake)->id,
1267                                 ((phpdbg_breakline_t*)brake)->opline,
1268                                 zend_get_executed_filename(),
1269                                 zend_get_executed_lineno(),
1270                                 ((phpdbg_breakline_t*)brake)->hits);
1271                 } break;
1272 
1273                 case PHPDBG_BREAK_METHOD_OPLINE: {
1274                          phpdbg_notice("breakpoint", "id=\"%d\" method=\"%s::%s\" num=\"%lu\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s::%s()#%lu at %s:%u, hits: %lu",
1275                                 ((phpdbg_breakopline_t*)brake)->id,
1276                                 ((phpdbg_breakopline_t*)brake)->class_name,
1277                                 ((phpdbg_breakopline_t*)brake)->func_name,
1278                                 ((phpdbg_breakopline_t*)brake)->opline_num,
1279                                 zend_get_executed_filename(),
1280                                 zend_get_executed_lineno(),
1281                                 ((phpdbg_breakopline_t*)brake)->hits);
1282                 } break;
1283 
1284                 case PHPDBG_BREAK_FUNCTION_OPLINE: {
1285                          phpdbg_notice("breakpoint", "id=\"%d\" num=\"%lu\" function=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s()#%lu at %s:%u, hits: %lu",
1286                                 ((phpdbg_breakopline_t*)brake)->id,
1287                                 ((phpdbg_breakopline_t*)brake)->func_name,
1288                                 ((phpdbg_breakopline_t*)brake)->opline_num,
1289                                 zend_get_executed_filename(),
1290                                 zend_get_executed_lineno(),
1291                                 ((phpdbg_breakopline_t*)brake)->hits);
1292                 } break;
1293 
1294                 case PHPDBG_BREAK_FILE_OPLINE: {
1295                          phpdbg_notice("breakpoint", "id=\"%d\" num=\"%lu\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in #%lu at %s:%u, hits: %lu",
1296                                 ((phpdbg_breakopline_t*)brake)->id,
1297                                 ((phpdbg_breakopline_t*)brake)->opline_num,
1298                                 zend_get_executed_filename(),
1299                                 zend_get_executed_lineno(),
1300                                 ((phpdbg_breakopline_t*)brake)->hits);
1301                 } break;
1302 
1303                 case PHPDBG_BREAK_OPCODE: {
1304                          phpdbg_notice("breakpoint", "id=\"%d\" opcode=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s at %s:%u, hits: %lu",
1305                                 ((phpdbg_breakop_t*)brake)->id,
1306                                 ((phpdbg_breakop_t*)brake)->name,
1307                                 zend_get_executed_filename(),
1308                                 zend_get_executed_lineno(),
1309                                 ((phpdbg_breakop_t*)brake)->hits);
1310                 } break;
1311 
1312                 case PHPDBG_BREAK_METHOD: {
1313                          phpdbg_notice("breakpoint", "id=\"%d\" method=\"%s::%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Breakpoint #%d in %s::%s() at %s:%u, hits: %lu",
1314                                 ((phpdbg_breakmethod_t*)brake)->id,
1315                                 ((phpdbg_breakmethod_t*)brake)->class_name,
1316                                 ((phpdbg_breakmethod_t*)brake)->func_name,
1317                                 zend_get_executed_filename(),
1318                                 zend_get_executed_lineno(),
1319                                 ((phpdbg_breakmethod_t*)brake)->hits);
1320                 } break;
1321 
1322                 case PHPDBG_BREAK_COND: {
1323                         if (((phpdbg_breakcond_t*)brake)->paramed) {
1324                                 char *param;
1325                                 phpdbg_notice("breakpoint", "id=\"%d\" location=\"%s\" eval=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Conditional breakpoint #%d: at %s if %s at %s:%u, hits: %lu",
1326                                         ((phpdbg_breakcond_t*)brake)->id,
1327                                         phpdbg_param_tostring(&((phpdbg_breakcond_t*)brake)->param, &param),
1328                                         ((phpdbg_breakcond_t*)brake)->code,
1329                                         zend_get_executed_filename(),
1330                                         zend_get_executed_lineno(),
1331                                         ((phpdbg_breakcond_t*)brake)->hits);
1332                                 if (param)
1333                                         free(param);
1334                         } else {
1335                                 phpdbg_notice("breakpoint", "id=\"%d\" eval=\"%s\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Conditional breakpoint #%d: on %s == true at %s:%u, hits: %lu",
1336                                         ((phpdbg_breakcond_t*)brake)->id,
1337                                         ((phpdbg_breakcond_t*)brake)->code,
1338                                         zend_get_executed_filename(),
1339                                         zend_get_executed_lineno(),
1340                                         ((phpdbg_breakcond_t*)brake)->hits);
1341                         }
1342 
1343                 } break;
1344 
1345                 default: {
1346 unknown:
1347                         phpdbg_notice("breakpoint", "id=\"\" file=\"%s\" line=\"%ld\" hits=\"%lu\"", "Unknown breakpoint at %s:%u",
1348                                 zend_get_executed_filename(),
1349                                 zend_get_executed_lineno());
1350                 }
1351         }
1352 } /* }}} */
1353 
1354 PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id) /* {{{ */
1355 {
1356         phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id);
1357 
1358         if (brake) {
1359                 brake->disabled = 0;
1360         }
1361 } /* }}} */
1362 
1363 PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id) /* {{{ */
1364 {
1365         phpdbg_breakbase_t *brake = phpdbg_find_breakbase(id);
1366 
1367         if (brake) {
1368                 brake->disabled = 1;
1369         }
1370 } /* }}} */
1371 
1372 PHPDBG_API void phpdbg_enable_breakpoints(void) /* {{{ */
1373 {
1374         PHPDBG_G(flags) |= PHPDBG_IS_BP_ENABLED;
1375 } /* }}} */
1376 
1377 PHPDBG_API void phpdbg_disable_breakpoints(void) { /* {{{ */
1378         PHPDBG_G(flags) &= ~PHPDBG_IS_BP_ENABLED;
1379 } /* }}} */
1380 
1381 PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id) /* {{{ */
1382 {
1383         HashTable *table;
1384         zend_string *strkey;
1385         zend_ulong numkey;
1386 
1387         return phpdbg_find_breakbase_ex(id, &table, &numkey, &strkey);
1388 } /* }}} */
1389 
1390 PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable **table, zend_ulong *numkey, zend_string **strkey) /* {{{ */
1391 {
1392         if ((*table = zend_hash_index_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], id))) {
1393                 phpdbg_breakbase_t *brake;
1394 
1395                 ZEND_HASH_FOREACH_KEY_PTR(*table, *numkey, *strkey, brake) {
1396                         if (brake->id == id) {
1397                                 return brake;
1398                         }
1399                 } ZEND_HASH_FOREACH_END();
1400         }
1401 
1402         return NULL;
1403 } /* }}} */
1404 
1405 PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type) /* {{{ */
1406 {
1407         phpdbg_xml("<breakpoints %r>");
1408 
1409         switch (type) {
1410                 case PHPDBG_BREAK_SYM: if ((PHPDBG_G(flags) & PHPDBG_HAS_SYM_BP)) {
1411                         phpdbg_breaksymbol_t *brake;
1412 
1413                         phpdbg_out(SEPARATE "\n");
1414                         phpdbg_out("Function Breakpoints:\n");
1415                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], brake) {
1416                                 phpdbg_writeln("function", "id=\"%d\" name=\"%s\" disabled=\"%s\"", "#%d\t\t%s%s",
1417                                         brake->id, brake->symbol,
1418                                         ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1419                         } ZEND_HASH_FOREACH_END();
1420                 } break;
1421 
1422                 case PHPDBG_BREAK_METHOD: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_BP)) {
1423                         HashTable *class_table;
1424 
1425                         phpdbg_out(SEPARATE "\n");
1426                         phpdbg_out("Method Breakpoints:\n");
1427                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_table) {
1428                                 phpdbg_breakmethod_t *brake;
1429 
1430                                 ZEND_HASH_FOREACH_PTR(class_table, brake) {
1431                                         phpdbg_writeln("method", "id=\"%d\" name=\"%s::%s\" disabled=\"%s\"", "#%d\t\t%s::%s%s",
1432                                                 brake->id, brake->class_name, brake->func_name,
1433                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1434                                 } ZEND_HASH_FOREACH_END();
1435                         } ZEND_HASH_FOREACH_END();
1436                 } break;
1437 
1438                 case PHPDBG_BREAK_FILE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP)) {
1439                         HashTable *points;
1440 
1441                         phpdbg_out(SEPARATE "\n");
1442                         phpdbg_out("File Breakpoints:\n");
1443                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], points) {
1444                                 phpdbg_breakfile_t *brake;
1445 
1446                                 ZEND_HASH_FOREACH_PTR(points, brake) {
1447                                         phpdbg_writeln("file", "id=\"%d\" name=\"%s\" line=\"%lu\" disabled=\"%s\"", "#%d\t\t%s:%lu%s",
1448                                                 brake->id, brake->filename, brake->line,
1449                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1450                                 } ZEND_HASH_FOREACH_END();
1451                         } ZEND_HASH_FOREACH_END();
1452                 }  if ((PHPDBG_G(flags) & PHPDBG_HAS_PENDING_FILE_BP)) {
1453                         HashTable *points;
1454 
1455                         phpdbg_out(SEPARATE "\n");
1456                         phpdbg_out("Pending File Breakpoints:\n");
1457                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], points) {
1458                                 phpdbg_breakfile_t *brake;
1459 
1460                                 ZEND_HASH_FOREACH_PTR(points, brake) {
1461                                         phpdbg_writeln("file", "id=\"%d\" name=\"%s\" line=\"%lu\" disabled=\"%s\" pending=\"pending\"", "#%d\t\t%s:%lu%s",
1462                                                 brake->id, brake->filename, brake->line,
1463                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1464                                 } ZEND_HASH_FOREACH_END();
1465                         } ZEND_HASH_FOREACH_END();
1466                 } break;
1467 
1468                 case PHPDBG_BREAK_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP)) {
1469                         phpdbg_breakline_t *brake;
1470 
1471                         phpdbg_out(SEPARATE "\n");
1472                         phpdbg_out("Opline Breakpoints:\n");
1473                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], brake) {
1474                                 const char *type;
1475                                 switch (brake->type) {
1476                                         case PHPDBG_BREAK_METHOD_OPLINE:
1477                                                 type = "method";
1478                                                 goto print_opline;
1479                                         case PHPDBG_BREAK_FUNCTION_OPLINE:
1480                                                 type = "function";
1481                                                 goto print_opline;
1482                                         case PHPDBG_BREAK_FILE_OPLINE:
1483                                                 type = "method";
1484 
1485                                         print_opline: {
1486                                                 if (brake->type == PHPDBG_BREAK_METHOD_OPLINE) {
1487                                                         type = "method";
1488                                                 } else if (brake->type == PHPDBG_BREAK_FUNCTION_OPLINE) {
1489                                                         type = "function";
1490                                                 } else if (brake->type == PHPDBG_BREAK_FILE_OPLINE) {
1491                                                         type = "file";
1492                                                 }
1493 
1494                                                 phpdbg_writeln("opline", "id=\"%d\" num=\"%#lx\" type=\"%s\" disabled=\"%s\"", "#%d\t\t%#lx\t\t(%s breakpoint)%s",
1495                                                         brake->id, brake->opline, type,
1496                                                         ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1497                                         } break;
1498 
1499                                         default:
1500                                                 phpdbg_writeln("opline", "id=\"%d\" num=\"%#lx\" disabled=\"%s\"", "#%d\t\t%#lx%s",
1501                                                         brake->id, brake->opline,
1502                                                         ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1503                                                 break;
1504                                 }
1505                         } ZEND_HASH_FOREACH_END();
1506                 } break;
1507 
1508                 case PHPDBG_BREAK_METHOD_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_METHOD_OPLINE_BP)) {
1509                         HashTable *class_table, *method_table;
1510 
1511                         phpdbg_out(SEPARATE "\n");
1512                         phpdbg_out("Method opline Breakpoints:\n");
1513                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], class_table) {
1514                                 ZEND_HASH_FOREACH_PTR(class_table, method_table) {
1515                                         phpdbg_breakopline_t *brake;
1516 
1517                                         ZEND_HASH_FOREACH_PTR(method_table, brake) {
1518                                                 phpdbg_writeln("methodopline", "id=\"%d\" name=\"%s::%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s::%s opline %ld%s",
1519                                                         brake->id, brake->class_name, brake->func_name, brake->opline_num,
1520                                                         ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1521                                         } ZEND_HASH_FOREACH_END();
1522                                 } ZEND_HASH_FOREACH_END();
1523                         } ZEND_HASH_FOREACH_END();
1524                 } break;
1525 
1526                 case PHPDBG_BREAK_FUNCTION_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FUNCTION_OPLINE_BP)) {
1527                         HashTable *function_table;
1528 
1529                         phpdbg_out(SEPARATE "\n");
1530                         phpdbg_out("Function opline Breakpoints:\n");
1531                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], function_table) {
1532                                 phpdbg_breakopline_t *brake;
1533 
1534                                 ZEND_HASH_FOREACH_PTR(function_table, brake) {
1535                                         phpdbg_writeln("functionopline", "id=\"%d\" name=\"%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s opline %ld%s",
1536                                                 brake->id, brake->func_name, brake->opline_num,
1537                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1538                                 } ZEND_HASH_FOREACH_END();
1539                         } ZEND_HASH_FOREACH_END();
1540                 } break;
1541 
1542                 case PHPDBG_BREAK_FILE_OPLINE: if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_OPLINE_BP)) {
1543                         HashTable *file_table;
1544 
1545                         phpdbg_out(SEPARATE "\n");
1546                         phpdbg_out("File opline Breakpoints:\n");
1547                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], file_table) {
1548                                 phpdbg_breakopline_t *brake;
1549 
1550                                 ZEND_HASH_FOREACH_PTR(file_table, brake) {
1551                                         phpdbg_writeln("fileopline", "id=\"%d\" name=\"%s\" num=\"%ld\" disabled=\"%s\"", "#%d\t\t%s opline %ld%s",
1552                                                 brake->id, brake->class_name, brake->opline_num,
1553                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1554                                 } ZEND_HASH_FOREACH_END();
1555                         } ZEND_HASH_FOREACH_END();
1556                 } break;
1557 
1558                 case PHPDBG_BREAK_COND: if ((PHPDBG_G(flags) & PHPDBG_HAS_COND_BP)) {
1559                         phpdbg_breakcond_t *brake;
1560 
1561                         phpdbg_out(SEPARATE "\n");
1562                         phpdbg_out("Conditional Breakpoints:\n");
1563                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], brake) {
1564                                 if (brake->paramed) {
1565                                         switch (brake->param.type) {
1566                                                 case STR_PARAM:
1567                                                         phpdbg_writeln("evalfunction", "id=\"%d\" name=\"%s\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s if %s%s",
1568                                                                 brake->id, brake->param.str, brake->code,
1569                                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1570                                                 break;
1571 
1572                                                 case NUMERIC_FUNCTION_PARAM:
1573                                                         phpdbg_writeln("evalfunctionopline", "id=\"%d\" name=\"%s\" num=\"%ld\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s#%ld if %s%s",
1574                                                                 brake->id, brake->param.str, brake->param.num, brake->code,
1575                                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1576                                                 break;
1577 
1578                                                 case METHOD_PARAM:
1579                                                         phpdbg_writeln("evalmethod", "id=\"%d\" name=\"%s::%s\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s::%s if %s%s",
1580                                                                 brake->id, brake->param.method.class, brake->param.method.name, brake->code,
1581                                                                 ((phpdbg_breakbase_t*)brake)->disabled ? " [disabled]" : "");
1582                                                 break;
1583 
1584                                                 case NUMERIC_METHOD_PARAM:
1585                                                         phpdbg_writeln("evalmethodopline", "id=\"%d\" name=\"%s::%s\" num=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s::%s#%ld if %s%s",
1586                                                                 brake->id, brake->param.method.class, brake->param.method.name, brake->param.num, brake->code,
1587                                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1588                                                 break;
1589 
1590                                                 case FILE_PARAM:
1591                                                         phpdbg_writeln("evalfile", "id=\"%d\" name=\"%s\" line=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat %s:%lu if %s%s",
1592                                                                 brake->id, brake->param.file.name, brake->param.file.line, brake->code,
1593                                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1594                                                 break;
1595 
1596                                                 case ADDR_PARAM:
1597                                                         phpdbg_writeln("evalopline", "id=\"%d\" opline=\"%#lx\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tat #%lx if %s%s",
1598                                                                 brake->id, brake->param.addr, brake->code,
1599                                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1600                                                 break;
1601 
1602                                                 default:
1603                                                         phpdbg_error("eval", "type=\"invalidparameter\"", "Invalid parameter type for conditional breakpoint");
1604                                                 return;
1605                                         }
1606                                 } else {
1607                                         phpdbg_writeln("eval", "id=\"%d\" eval=\"%s\" disabled=\"%s\"", "#%d\t\tif %s%s",
1608                                                 brake->id, brake->code,
1609                                                 ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1610                                 }
1611                         } ZEND_HASH_FOREACH_END();
1612                 } break;
1613 
1614                 case PHPDBG_BREAK_OPCODE: if (PHPDBG_G(flags) & PHPDBG_HAS_OPCODE_BP) {
1615                         phpdbg_breakop_t *brake;
1616 
1617                         phpdbg_out(SEPARATE "\n");
1618                         phpdbg_out("Opcode Breakpoints:\n");
1619                         ZEND_HASH_FOREACH_PTR(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], brake) {
1620                                 phpdbg_writeln("opcode", "id=\"%d\" name=\"%s\" disabled=\"%s\"", "#%d\t\t%s%s",
1621                                         brake->id, brake->name,
1622                                         ((phpdbg_breakbase_t *) brake)->disabled ? " [disabled]" : "");
1623                         } ZEND_HASH_FOREACH_END();
1624                 } break;
1625         }
1626 
1627         phpdbg_xml("</breakpoints>");
1628 } /* }}} */

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