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