xref: /PHP-5.6/sapi/phpdbg/phpdbg_bp.c (revision 49493a2d)
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, &param 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