xref: /php-src/sapi/phpdbg/phpdbg_print.c (revision 248b5f5b)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Felipe Pena <felipe@php.net>                                |
14    | Authors: Joe Watkins <joe.watkins@live.co.uk>                        |
15    | Authors: Bob Weinand <bwoebi@php.net>                                |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "phpdbg.h"
20 #include "phpdbg_print.h"
21 #include "phpdbg_utils.h"
22 #include "phpdbg_prompt.h"
23 
24 #include "Optimizer/zend_dump.h"
25 
26 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
27 
28 #define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s, flags) \
29 	PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[8], flags)
30 
31 const phpdbg_command_t phpdbg_print_commands[] = {
32 	PHPDBG_PRINT_COMMAND_D(exec,       "print out the instructions in the main execution context", 'e', print_exec,   NULL, 0, PHPDBG_ASYNC_SAFE),
33 	PHPDBG_PRINT_COMMAND_D(opline,     "print out the instruction in the current opline",          'o', print_opline, NULL, 0, PHPDBG_ASYNC_SAFE),
34 	PHPDBG_PRINT_COMMAND_D(class,      "print out the instructions in the specified class",        'c', print_class,  NULL, "s", PHPDBG_ASYNC_SAFE),
35 	PHPDBG_PRINT_COMMAND_D(method,     "print out the instructions in the specified method",       'm', print_method, NULL, "m", PHPDBG_ASYNC_SAFE),
36 	PHPDBG_PRINT_COMMAND_D(func,       "print out the instructions in the specified function",     'f', print_func,   NULL, "s", PHPDBG_ASYNC_SAFE),
37 	PHPDBG_PRINT_COMMAND_D(stack,      "print out the instructions in the current stack",          's', print_stack,  NULL, 0, PHPDBG_ASYNC_SAFE),
38 	PHPDBG_END_COMMAND
39 };
40 
PHPDBG_PRINT(opline)41 PHPDBG_PRINT(opline) /* {{{ */
42 {
43 	if (PHPDBG_G(in_execution) && EG(current_execute_data)) {
44 		phpdbg_print_opline(phpdbg_user_execute_data(EG(current_execute_data)), 1);
45 	} else {
46 		phpdbg_error("Not Executing!");
47 	}
48 
49 	return SUCCESS;
50 } /* }}} */
51 
phpdbg_print_function_helper(zend_function * method)52 static inline void phpdbg_print_function_helper(zend_function *method) /* {{{ */
53 {
54 	switch (method->type) {
55 		case ZEND_USER_FUNCTION: {
56 			zend_op_array* op_array = &(method->op_array);
57 
58 			zend_dump_op_array(op_array, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
59 
60 			for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
61 				zend_op_array *def = op_array->dynamic_func_defs[i];
62 				phpdbg_out("\ndynamic def: %i, function name: %.*s\n",
63 					i, (int) ZSTR_LEN(def->function_name), ZSTR_VAL(def->function_name));
64 				zend_dump_op_array(def, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
65 			}
66 		} break;
67 
68 		default: {
69 			if (method->common.scope) {
70 				phpdbg_writeln("\tInternal %s::%s()", ZSTR_VAL(method->common.scope->name), ZSTR_VAL(method->common.function_name));
71 			} else {
72 				phpdbg_writeln("\tInternal %s()", ZSTR_VAL(method->common.function_name));
73 			}
74 		}
75 	}
76 } /* }}} */
77 
PHPDBG_PRINT(exec)78 PHPDBG_PRINT(exec) /* {{{ */
79 {
80 	if (PHPDBG_G(exec)) {
81 		if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) {
82 			phpdbg_compile();
83 		}
84 
85 		if (PHPDBG_G(ops)) {
86 			phpdbg_notice("Context %s (%d ops)", PHPDBG_G(exec), PHPDBG_G(ops)->last);
87 
88 			phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops));
89 		}
90 	} else {
91 		phpdbg_error("No execution context set");
92 	}
93 
94 	return SUCCESS;
95 } /* }}} */
96 
PHPDBG_PRINT(stack)97 PHPDBG_PRINT(stack) /* {{{ */
98 {
99 	if (PHPDBG_G(in_execution) && EG(current_execute_data)) {
100 		zend_op_array *ops = &phpdbg_user_execute_data(EG(current_execute_data))->func->op_array;
101 		if (ops->function_name) {
102 			if (ops->scope) {
103 				phpdbg_notice("Stack in %s::%s() (%d ops)", ZSTR_VAL(ops->scope->name), ZSTR_VAL(ops->function_name), ops->last);
104 			} else {
105 				phpdbg_notice("Stack in %s() (%d ops)", ZSTR_VAL(ops->function_name), ops->last);
106 			}
107 		} else {
108 			if (ops->filename) {
109 				phpdbg_notice("Stack in %s (%d ops)", ZSTR_VAL(ops->filename), ops->last);
110 			} else {
111 				phpdbg_notice("Stack @ %p (%d ops)", ops, ops->last);
112 			}
113 		}
114 		phpdbg_print_function_helper((zend_function*) ops);
115 	} else {
116 		phpdbg_error("Not Executing!");
117 	}
118 
119 	return SUCCESS;
120 } /* }}} */
121 
PHPDBG_PRINT(class)122 PHPDBG_PRINT(class) /* {{{ */
123 {
124 	zend_class_entry *ce;
125 
126 	if (phpdbg_safe_class_lookup(param->str, param->len, &ce) == SUCCESS) {
127 		phpdbg_notice("%s %s: %s (%d methods)",
128 			(ce->type == ZEND_USER_CLASS) ?
129 				"User" : "Internal",
130 			(ce->ce_flags & ZEND_ACC_INTERFACE) ?
131 				"Interface" :
132 				(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
133 					"Abstract Class" :
134 					"Class",
135 			ZSTR_VAL(ce->name),
136 			zend_hash_num_elements(&ce->function_table));
137 
138 		if (zend_hash_num_elements(&ce->function_table)) {
139 			zend_function *method;
140 
141 			ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
142 				phpdbg_print_function_helper(method);
143 			} ZEND_HASH_FOREACH_END();
144 		}
145 	} else {
146 		phpdbg_error("The class %s could not be found", param->str);
147 	}
148 
149 	return SUCCESS;
150 } /* }}} */
151 
PHPDBG_PRINT(method)152 PHPDBG_PRINT(method) /* {{{ */
153 {
154 	zend_class_entry *ce;
155 
156 	if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) {
157 		zend_function *fbc;
158 		zend_string *lcname = zend_string_alloc(strlen(param->method.name), 0);
159 		zend_str_tolower_copy(ZSTR_VAL(lcname), param->method.name, ZSTR_LEN(lcname));
160 
161 		if ((fbc = zend_hash_find_ptr(&ce->function_table, lcname))) {
162 			phpdbg_notice("%s Method %s (%d ops)",
163 				(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
164 				ZSTR_VAL(fbc->common.function_name),
165 				(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
166 
167 			phpdbg_print_function_helper(fbc);
168 		} else {
169 			phpdbg_error("The method %s::%s could not be found", param->method.class, param->method.name);
170 		}
171 
172 		zend_string_release(lcname);
173 	} else {
174 		phpdbg_error("The class %s could not be found", param->method.class);
175 	}
176 
177 	return SUCCESS;
178 } /* }}} */
179 
PHPDBG_PRINT(func)180 PHPDBG_PRINT(func) /* {{{ */
181 {
182 	HashTable *func_table = EG(function_table);
183 	zend_function* fbc;
184 	const char *func_name = param->str;
185 	size_t func_name_len = param->len;
186 	zend_string *lcname;
187 	/* search active scope if begins with period */
188 	if (func_name[0] == '.') {
189 		zend_class_entry *scope = zend_get_executed_scope();
190 
191 		if (scope) {
192 			func_name++;
193 			func_name_len--;
194 
195 			func_table = &scope->function_table;
196 		} else {
197 			phpdbg_error("No active class");
198 			return SUCCESS;
199 		}
200 	} else if (!EG(function_table)) {
201 		phpdbg_error("No function table loaded");
202 		return SUCCESS;
203 	} else {
204 		func_table = EG(function_table);
205 	}
206 
207 	lcname = zend_string_alloc(func_name_len, 0);
208 	zend_str_tolower_copy(ZSTR_VAL(lcname), func_name, ZSTR_LEN(lcname));
209 
210 	phpdbg_try_access {
211 		if ((fbc = zend_hash_find_ptr(func_table, lcname))) {
212 			phpdbg_notice("%s %s %s (%d ops)",
213 				(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
214 				(fbc->common.scope) ? "Method" : "Function",
215 				ZSTR_VAL(fbc->common.function_name),
216 				(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
217 
218 			phpdbg_print_function_helper(fbc);
219 		} else {
220 			phpdbg_error("The function %s could not be found", func_name);
221 		}
222 	} phpdbg_catch_access {
223 		phpdbg_error("Couldn't fetch function %.*s, invalid data source", (int) func_name_len, func_name);
224 	} phpdbg_end_try_access();
225 
226 	efree(lcname);
227 
228 	return SUCCESS;
229 } /* }}} */
230 
phpdbg_print_opcodes_main(void)231 void phpdbg_print_opcodes_main(void) {
232 	phpdbg_print_function_helper((zend_function *) PHPDBG_G(ops));
233 }
234 
phpdbg_print_opcodes_function(const char * function,size_t len)235 void phpdbg_print_opcodes_function(const char *function, size_t len) {
236 	zend_function *func = zend_hash_str_find_ptr(EG(function_table), function, len);
237 
238 	if (!func) {
239 		phpdbg_error("The function %s could not be found", function);
240 		return;
241 	}
242 
243 	phpdbg_print_function_helper(func);
244 }
245 
phpdbg_print_opcodes_method_ce(zend_class_entry * ce,const char * function)246 static void phpdbg_print_opcodes_method_ce(zend_class_entry *ce, const char *function) {
247 	zend_function *func;
248 
249 	if (ce->type != ZEND_USER_CLASS) {
250 		phpdbg_out("function name: %s::%s (internal)\n", ce->name->val, function);
251 		return;
252 	}
253 
254 	if (!(func = zend_hash_str_find_ptr(&ce->function_table, function, strlen(function)))) {
255 		phpdbg_error("The method %s::%s could not be found", ZSTR_VAL(ce->name), function);
256 		return;
257 	}
258 
259 	phpdbg_print_function_helper(func);
260 }
261 
phpdbg_print_opcodes_method(const char * class,const char * function)262 void phpdbg_print_opcodes_method(const char *class, const char *function) {
263 	zend_class_entry *ce;
264 
265 	if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
266 		phpdbg_error("The class %s could not be found", class);
267 		return;
268 	}
269 
270 	phpdbg_print_opcodes_method_ce(ce, function);
271 }
272 
phpdbg_print_opcodes_ce(zend_class_entry * ce)273 static void phpdbg_print_opcodes_ce(zend_class_entry *ce) {
274 	zend_function *method;
275 	bool first = 1;
276 
277 	phpdbg_out("%s %s: %s\n",
278 		(ce->type == ZEND_USER_CLASS) ?
279 			"user" : "internal",
280 		(ce->ce_flags & ZEND_ACC_INTERFACE) ?
281 			"interface" :
282 			(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
283 				"abstract Class" :
284 				"class",
285 		ZSTR_VAL(ce->name));
286 
287 	if (ce->type != ZEND_USER_CLASS) {
288 		return;
289 	}
290 
291 	phpdbg_out("%d methods: ", zend_hash_num_elements(&ce->function_table));
292 	ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
293 		if (first) {
294 			first = 0;
295 		} else {
296 			phpdbg_out(", ");
297 		}
298 		phpdbg_out("%s", ZSTR_VAL(method->common.function_name));
299 	} ZEND_HASH_FOREACH_END();
300 	if (first) {
301 		phpdbg_out("-");
302 	}
303 	phpdbg_out("\n");
304 
305 	ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
306 		phpdbg_print_function_helper(method);
307 	} ZEND_HASH_FOREACH_END();
308 }
309 
phpdbg_print_opcodes_class(const char * class)310 void phpdbg_print_opcodes_class(const char *class) {
311 	zend_class_entry *ce;
312 
313 	if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
314 		phpdbg_error("The class %s could not be found", class);
315 		return;
316 	}
317 
318 	phpdbg_print_opcodes_ce(ce);
319 }
320 
phpdbg_print_opcodes(const char * function)321 void phpdbg_print_opcodes(const char *function)
322 {
323 	if (function == NULL) {
324 		phpdbg_print_opcodes_main();
325 	} else if (function[0] == '*' && function[1] == 0) {
326 		/* all */
327 		zend_string *name;
328 		zend_function *func;
329 		zend_class_entry *ce;
330 
331 		phpdbg_print_opcodes_main();
332 
333 		ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
334 			if (func->type == ZEND_USER_FUNCTION) {
335 				phpdbg_print_opcodes_function(ZSTR_VAL(name), ZSTR_LEN(name));
336 			}
337 		} ZEND_HASH_FOREACH_END();
338 
339 		ZEND_HASH_MAP_FOREACH_PTR(EG(class_table), ce) {
340 			if (ce->type == ZEND_USER_CLASS) {
341 				phpdbg_out("\n");
342 				phpdbg_print_opcodes_ce(ce);
343 			}
344 		} ZEND_HASH_FOREACH_END();
345 	} else {
346 		char *function_lowercase = zend_str_tolower_dup(function, strlen(function));
347 
348 		if (strstr(function_lowercase, "::") == NULL) {
349 			phpdbg_print_opcodes_function(function_lowercase, strlen(function_lowercase));
350 		} else {
351 			char *method_name, *class_name = strtok(function_lowercase, "::");
352 			if ((method_name = strtok(NULL, "::")) == NULL) {
353 				phpdbg_print_opcodes_class(class_name);
354 			} else {
355 				phpdbg_print_opcodes_method(class_name, method_name);
356 			}
357 		}
358 
359 		efree(function_lowercase);
360 	}
361 }
362 
phpdbg_print_opline(zend_execute_data * execute_data,bool ignore_flags)363 void phpdbg_print_opline(zend_execute_data *execute_data, bool ignore_flags) /* {{{ */
364 {
365 	if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) && (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
366 		zend_dump_op_line(&EX(func)->op_array, NULL, EX(opline), ZEND_DUMP_LINE_NUMBERS, NULL);
367 	}
368 
369 	if (PHPDBG_G(oplog_list)) {
370 		phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry));
371 		zend_op_array *op_array = &EX(func)->op_array;
372 		cur->op = (zend_op *) EX(opline);
373 		cur->opcodes = op_array->opcodes;
374 		cur->filename = op_array->filename;
375 		cur->scope = op_array->scope;
376 		cur->function_name = op_array->function_name;
377 		cur->next = NULL;
378 		PHPDBG_G(oplog_cur)->next = cur;
379 		PHPDBG_G(oplog_cur) = cur;
380 	}
381 } /* }}} */
382