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