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