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