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