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