xref: /PHP-8.2/sapi/phpdbg/phpdbg_print.c (revision 90b7bde6)
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 			if (op_array) {
59 				zend_dump_op_array(op_array, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
60 
61 				for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
62 					zend_op_array *def = op_array->dynamic_func_defs[i];
63 					phpdbg_out("\ndynamic def: %i, function name: %.*s\n",
64 						i, (int) ZSTR_LEN(def->function_name), ZSTR_VAL(def->function_name));
65 					zend_dump_op_array(def, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
66 				}
67 			}
68 		} break;
69 
70 		default: {
71 			if (method->common.scope) {
72 				phpdbg_writeln("\tInternal %s::%s()", ZSTR_VAL(method->common.scope->name), ZSTR_VAL(method->common.function_name));
73 			} else {
74 				phpdbg_writeln("\tInternal %s()", ZSTR_VAL(method->common.function_name));
75 			}
76 		}
77 	}
78 } /* }}} */
79 
PHPDBG_PRINT(exec)80 PHPDBG_PRINT(exec) /* {{{ */
81 {
82 	if (PHPDBG_G(exec)) {
83 		if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) {
84 			phpdbg_compile();
85 		}
86 
87 		if (PHPDBG_G(ops)) {
88 			phpdbg_notice("Context %s (%d ops)", PHPDBG_G(exec), PHPDBG_G(ops)->last);
89 
90 			phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops));
91 		}
92 	} else {
93 		phpdbg_error("No execution context set");
94 	}
95 
96 	return SUCCESS;
97 } /* }}} */
98 
PHPDBG_PRINT(stack)99 PHPDBG_PRINT(stack) /* {{{ */
100 {
101 	if (PHPDBG_G(in_execution) && EG(current_execute_data)) {
102 		zend_op_array *ops = &phpdbg_user_execute_data(EG(current_execute_data))->func->op_array;
103 		if (ops->function_name) {
104 			if (ops->scope) {
105 				phpdbg_notice("Stack in %s::%s() (%d ops)", ZSTR_VAL(ops->scope->name), ZSTR_VAL(ops->function_name), ops->last);
106 			} else {
107 				phpdbg_notice("Stack in %s() (%d ops)", ZSTR_VAL(ops->function_name), ops->last);
108 			}
109 		} else {
110 			if (ops->filename) {
111 				phpdbg_notice("Stack in %s (%d ops)", ZSTR_VAL(ops->filename), ops->last);
112 			} else {
113 				phpdbg_notice("Stack @ %p (%d ops)", ops, ops->last);
114 			}
115 		}
116 		phpdbg_print_function_helper((zend_function*) ops);
117 	} else {
118 		phpdbg_error("Not Executing!");
119 	}
120 
121 	return SUCCESS;
122 } /* }}} */
123 
PHPDBG_PRINT(class)124 PHPDBG_PRINT(class) /* {{{ */
125 {
126 	zend_class_entry *ce;
127 
128 	if (phpdbg_safe_class_lookup(param->str, param->len, &ce) == SUCCESS) {
129 		phpdbg_notice("%s %s: %s (%d methods)",
130 			(ce->type == ZEND_USER_CLASS) ?
131 				"User" : "Internal",
132 			(ce->ce_flags & ZEND_ACC_INTERFACE) ?
133 				"Interface" :
134 				(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
135 					"Abstract Class" :
136 					"Class",
137 			ZSTR_VAL(ce->name),
138 			zend_hash_num_elements(&ce->function_table));
139 
140 		if (zend_hash_num_elements(&ce->function_table)) {
141 			zend_function *method;
142 
143 			ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
144 				phpdbg_print_function_helper(method);
145 			} ZEND_HASH_FOREACH_END();
146 		}
147 	} else {
148 		phpdbg_error("The class %s could not be found", param->str);
149 	}
150 
151 	return SUCCESS;
152 } /* }}} */
153 
PHPDBG_PRINT(method)154 PHPDBG_PRINT(method) /* {{{ */
155 {
156 	zend_class_entry *ce;
157 
158 	if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) {
159 		zend_function *fbc;
160 		zend_string *lcname = zend_string_alloc(strlen(param->method.name), 0);
161 		zend_str_tolower_copy(ZSTR_VAL(lcname), param->method.name, ZSTR_LEN(lcname));
162 
163 		if ((fbc = zend_hash_find_ptr(&ce->function_table, lcname))) {
164 			phpdbg_notice("%s Method %s (%d ops)",
165 				(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
166 				ZSTR_VAL(fbc->common.function_name),
167 				(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
168 
169 			phpdbg_print_function_helper(fbc);
170 		} else {
171 			phpdbg_error("The method %s::%s could not be found", param->method.class, param->method.name);
172 		}
173 
174 		zend_string_release(lcname);
175 	} else {
176 		phpdbg_error("The class %s could not be found", param->method.class);
177 	}
178 
179 	return SUCCESS;
180 } /* }}} */
181 
PHPDBG_PRINT(func)182 PHPDBG_PRINT(func) /* {{{ */
183 {
184 	HashTable *func_table = EG(function_table);
185 	zend_function* fbc;
186 	const char *func_name = param->str;
187 	size_t func_name_len = param->len;
188 	zend_string *lcname;
189 	/* search active scope if begins with period */
190 	if (func_name[0] == '.') {
191 		zend_class_entry *scope = zend_get_executed_scope();
192 
193 		if (scope) {
194 			func_name++;
195 			func_name_len--;
196 
197 			func_table = &scope->function_table;
198 		} else {
199 			phpdbg_error("No active class");
200 			return SUCCESS;
201 		}
202 	} else if (!EG(function_table)) {
203 		phpdbg_error("No function table loaded");
204 		return SUCCESS;
205 	} else {
206 		func_table = EG(function_table);
207 	}
208 
209 	lcname = zend_string_alloc(func_name_len, 0);
210 	zend_str_tolower_copy(ZSTR_VAL(lcname), func_name, ZSTR_LEN(lcname));
211 
212 	phpdbg_try_access {
213 		if ((fbc = zend_hash_find_ptr(func_table, lcname))) {
214 			phpdbg_notice("%s %s %s (%d ops)",
215 				(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
216 				(fbc->common.scope) ? "Method" : "Function",
217 				ZSTR_VAL(fbc->common.function_name),
218 				(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
219 
220 			phpdbg_print_function_helper(fbc);
221 		} else {
222 			phpdbg_error("The function %s could not be found", func_name);
223 		}
224 	} phpdbg_catch_access {
225 		phpdbg_error("Couldn't fetch function %.*s, invalid data source", (int) func_name_len, func_name);
226 	} phpdbg_end_try_access();
227 
228 	efree(lcname);
229 
230 	return SUCCESS;
231 } /* }}} */
232 
phpdbg_print_opcodes_main(void)233 void phpdbg_print_opcodes_main(void) {
234 	phpdbg_print_function_helper((zend_function *) PHPDBG_G(ops));
235 }
236 
phpdbg_print_opcodes_function(const char * function,size_t len)237 void phpdbg_print_opcodes_function(const char *function, size_t len) {
238 	zend_function *func = zend_hash_str_find_ptr(EG(function_table), function, len);
239 
240 	if (!func) {
241 		phpdbg_error("The function %s could not be found", function);
242 		return;
243 	}
244 
245 	phpdbg_print_function_helper(func);
246 }
247 
phpdbg_print_opcodes_method_ce(zend_class_entry * ce,const char * function)248 static void phpdbg_print_opcodes_method_ce(zend_class_entry *ce, const char *function) {
249 	zend_function *func;
250 
251 	if (ce->type != ZEND_USER_CLASS) {
252 		phpdbg_out("function name: %s::%s (internal)\n", ce->name->val, function);
253 		return;
254 	}
255 
256 	if (!(func = zend_hash_str_find_ptr(&ce->function_table, function, strlen(function)))) {
257 		phpdbg_error("The method %s::%s could not be found", ZSTR_VAL(ce->name), function);
258 		return;
259 	}
260 
261 	phpdbg_print_function_helper(func);
262 }
263 
phpdbg_print_opcodes_method(const char * class,const char * function)264 void phpdbg_print_opcodes_method(const char *class, const char *function) {
265 	zend_class_entry *ce;
266 
267 	if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
268 		phpdbg_error("The class %s could not be found", class);
269 		return;
270 	}
271 
272 	phpdbg_print_opcodes_method_ce(ce, function);
273 }
274 
phpdbg_print_opcodes_ce(zend_class_entry * ce)275 static void phpdbg_print_opcodes_ce(zend_class_entry *ce) {
276 	zend_function *method;
277 	bool first = 1;
278 
279 	phpdbg_out("%s %s: %s\n",
280 		(ce->type == ZEND_USER_CLASS) ?
281 			"user" : "internal",
282 		(ce->ce_flags & ZEND_ACC_INTERFACE) ?
283 			"interface" :
284 			(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
285 				"abstract Class" :
286 				"class",
287 		ZSTR_VAL(ce->name));
288 
289 	if (ce->type != ZEND_USER_CLASS) {
290 		return;
291 	}
292 
293 	phpdbg_out("%d methods: ", zend_hash_num_elements(&ce->function_table));
294 	ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
295 		if (first) {
296 			first = 0;
297 		} else {
298 			phpdbg_out(", ");
299 		}
300 		phpdbg_out("%s", ZSTR_VAL(method->common.function_name));
301 	} ZEND_HASH_FOREACH_END();
302 	if (first) {
303 		phpdbg_out("-");
304 	}
305 	phpdbg_out("\n");
306 
307 	ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
308 		phpdbg_print_function_helper(method);
309 	} ZEND_HASH_FOREACH_END();
310 }
311 
phpdbg_print_opcodes_class(const char * class)312 void phpdbg_print_opcodes_class(const char *class) {
313 	zend_class_entry *ce;
314 
315 	if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
316 		phpdbg_error("The class %s could not be found", class);
317 		return;
318 	}
319 
320 	phpdbg_print_opcodes_ce(ce);
321 }
322 
phpdbg_print_opcodes(const char * function)323 void phpdbg_print_opcodes(const char *function)
324 {
325 	if (function == NULL) {
326 		phpdbg_print_opcodes_main();
327 	} else if (function[0] == '*' && function[1] == 0) {
328 		/* all */
329 		zend_string *name;
330 		zend_function *func;
331 		zend_class_entry *ce;
332 
333 		phpdbg_print_opcodes_main();
334 
335 		ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
336 			if (func->type == ZEND_USER_FUNCTION) {
337 				phpdbg_print_opcodes_function(ZSTR_VAL(name), ZSTR_LEN(name));
338 			}
339 		} ZEND_HASH_FOREACH_END();
340 
341 		ZEND_HASH_MAP_FOREACH_PTR(EG(class_table), ce) {
342 			if (ce->type == ZEND_USER_CLASS) {
343 				phpdbg_out("\n");
344 				phpdbg_print_opcodes_ce(ce);
345 			}
346 		} ZEND_HASH_FOREACH_END();
347 	} else {
348 		char *function_lowercase = zend_str_tolower_dup(function, strlen(function));
349 
350 		if (strstr(function_lowercase, "::") == NULL) {
351 			phpdbg_print_opcodes_function(function_lowercase, strlen(function_lowercase));
352 		} else {
353 			char *method_name, *class_name = strtok(function_lowercase, "::");
354 			if ((method_name = strtok(NULL, "::")) == NULL) {
355 				phpdbg_print_opcodes_class(class_name);
356 			} else {
357 				phpdbg_print_opcodes_method(class_name, method_name);
358 			}
359 		}
360 
361 		efree(function_lowercase);
362 	}
363 }
364 
phpdbg_print_opline(zend_execute_data * execute_data,bool ignore_flags)365 void phpdbg_print_opline(zend_execute_data *execute_data, bool ignore_flags) /* {{{ */
366 {
367 	if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) && (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
368 		zend_dump_op_line(&EX(func)->op_array, NULL, EX(opline), ZEND_DUMP_LINE_NUMBERS, NULL);
369 	}
370 
371 	if (PHPDBG_G(oplog_list)) {
372 		phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry));
373 		zend_op_array *op_array = &EX(func)->op_array;
374 		cur->op = (zend_op *) EX(opline);
375 		cur->opcodes = op_array->opcodes;
376 		cur->filename = op_array->filename;
377 		cur->scope = op_array->scope;
378 		cur->function_name = op_array->function_name;
379 		cur->next = NULL;
380 		PHPDBG_G(oplog_cur)->next = cur;
381 		PHPDBG_G(oplog_cur) = cur;
382 	}
383 } /* }}} */
384