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 "php.h"
20 #include "phpdbg.h"
21 #include "phpdbg_utils.h"
22 #include "phpdbg_info.h"
23 #include "phpdbg_bp.h"
24 #include "phpdbg_prompt.h"
25
26 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
27
28 #define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s, flags) \
29 PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags)
30
31 const phpdbg_command_t phpdbg_info_commands[] = {
32 PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE),
33 PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE),
34 PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE),
35 PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE),
36 PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE),
37 PHPDBG_INFO_COMMAND_D(constants, "show user defined constants", 'd', info_constants, NULL, 0, PHPDBG_ASYNC_SAFE),
38 PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE),
39 PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE),
40 PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE),
41 PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE),
42 PHPDBG_END_COMMAND
43 };
44
PHPDBG_INFO(break)45 PHPDBG_INFO(break) /* {{{ */
46 {
47 phpdbg_print_breakpoints(PHPDBG_BREAK_FILE);
48 phpdbg_print_breakpoints(PHPDBG_BREAK_SYM);
49 phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD);
50 phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE);
51 phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE);
52 phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE);
53 phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE);
54 phpdbg_print_breakpoints(PHPDBG_BREAK_COND);
55 phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE);
56
57 return SUCCESS;
58 } /* }}} */
59
PHPDBG_INFO(files)60 PHPDBG_INFO(files) /* {{{ */
61 {
62 zend_string *fname;
63
64 phpdbg_try_access {
65 phpdbg_notice("Included files: %d", zend_hash_num_elements(&EG(included_files)));
66 } phpdbg_catch_access {
67 phpdbg_error("Could not fetch included file count, invalid data source");
68 return SUCCESS;
69 } phpdbg_end_try_access();
70
71 phpdbg_try_access {
72 ZEND_HASH_MAP_FOREACH_STR_KEY(&EG(included_files), fname) {
73 phpdbg_writeln("File: %s", ZSTR_VAL(fname));
74 } ZEND_HASH_FOREACH_END();
75 } phpdbg_catch_access {
76 phpdbg_error("Could not fetch file name, invalid data source, aborting included file listing");
77 } phpdbg_end_try_access();
78
79 return SUCCESS;
80 } /* }}} */
81
PHPDBG_INFO(error)82 PHPDBG_INFO(error) /* {{{ */
83 {
84 if (PG(last_error_message)) {
85 phpdbg_try_access {
86 phpdbg_writeln("Last error: %s at %s line %d",
87 ZSTR_VAL(PG(last_error_message)),
88 ZSTR_VAL(PG(last_error_file)),
89 PG(last_error_lineno));
90 } phpdbg_catch_access {
91 phpdbg_notice("No error found!");
92 } phpdbg_end_try_access();
93 } else {
94 phpdbg_notice("No error found!");
95 }
96 return SUCCESS;
97 } /* }}} */
98
PHPDBG_INFO(constants)99 PHPDBG_INFO(constants) /* {{{ */
100 {
101 HashTable consts;
102 zend_constant *data;
103
104 zend_hash_init(&consts, 8, NULL, NULL, 0);
105
106 if (EG(zend_constants)) {
107 phpdbg_try_access {
108 ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), data) {
109 if (ZEND_CONSTANT_MODULE_NUMBER(data) == PHP_USER_CONSTANT) {
110 zend_hash_update_ptr(&consts, data->name, data);
111 }
112 } ZEND_HASH_FOREACH_END();
113 } phpdbg_catch_access {
114 phpdbg_error("Cannot fetch all the constants, invalid data source");
115 } phpdbg_end_try_access();
116 }
117
118 phpdbg_notice("User-defined constants (%d)", zend_hash_num_elements(&consts));
119
120 if (zend_hash_num_elements(&consts)) {
121 phpdbg_out("Address Refs Type Constant\n");
122 ZEND_HASH_MAP_FOREACH_PTR(&consts, data) {
123
124 #define VARIABLEINFO(msg, ...) \
125 phpdbg_writeln( \
126 "%-18p %-7d %-9s %.*s" msg, &data->value, \
127 Z_REFCOUNTED(data->value) ? Z_REFCOUNT(data->value) : 1, \
128 zend_get_type_by_const(Z_TYPE(data->value)), \
129 (int) ZSTR_LEN(data->name), ZSTR_VAL(data->name), ##__VA_ARGS__)
130
131 switch (Z_TYPE(data->value)) {
132 case IS_STRING:
133 phpdbg_try_access {
134 VARIABLEINFO("\nstring (%zd) \"%.*s%s\"", Z_STRLEN(data->value), Z_STRLEN(data->value) < 255 ? (int) Z_STRLEN(data->value) : 255, Z_STRVAL(data->value), Z_STRLEN(data->value) > 255 ? "..." : "");
135 } phpdbg_catch_access {
136 VARIABLEINFO("");
137 } phpdbg_end_try_access();
138 break;
139 case IS_TRUE:
140 VARIABLEINFO("\nbool (true)");
141 break;
142 case IS_FALSE:
143 VARIABLEINFO("\nbool (false)");
144 break;
145 case IS_LONG:
146 VARIABLEINFO("\nint ("ZEND_LONG_FMT")", Z_LVAL(data->value));
147 break;
148 case IS_DOUBLE:
149 VARIABLEINFO("\ndouble (%lf)", Z_DVAL(data->value));
150 break;
151 default:
152 VARIABLEINFO("");
153
154 #undef VARIABLEINFO
155 }
156 } ZEND_HASH_FOREACH_END();
157 }
158
159 return SUCCESS;
160 } /* }}} */
161
phpdbg_arm_auto_global(zval * ptrzv)162 static int phpdbg_arm_auto_global(zval *ptrzv) {
163 zend_auto_global *auto_global = Z_PTR_P(ptrzv);
164
165 if (auto_global->armed) {
166 if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
167 phpdbg_notice("Cannot show information about superglobal variable %.*s", (int) ZSTR_LEN(auto_global->name), ZSTR_VAL(auto_global->name));
168 } else {
169 auto_global->armed = auto_global->auto_global_callback(auto_global->name);
170 }
171 }
172
173 return 0;
174 }
175
phpdbg_print_symbols(bool show_globals)176 static int phpdbg_print_symbols(bool show_globals) {
177 HashTable vars;
178 zend_array *symtable;
179 zend_string *var;
180 zval *data;
181
182 if (!EG(current_execute_data) || !EG(current_execute_data)->func) {
183 phpdbg_error("No active op array!");
184 return SUCCESS;
185 }
186
187 if (show_globals) {
188 /* that array should only be manipulated during init, so safe for async access during execution */
189 zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_arm_auto_global);
190 symtable = &EG(symbol_table);
191 } else if (!(symtable = zend_rebuild_symbol_table())) {
192 phpdbg_error("No active symbol table!");
193 return SUCCESS;
194 }
195
196 zend_hash_init(&vars, 8, NULL, NULL, 0);
197
198 phpdbg_try_access {
199 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(symtable, var, data) {
200 if (zend_is_auto_global(var) ^ !show_globals) {
201 zend_hash_update(&vars, var, data);
202 }
203 } ZEND_HASH_FOREACH_END();
204 } phpdbg_catch_access {
205 phpdbg_error("Cannot fetch all data from the symbol table, invalid data source");
206 } phpdbg_end_try_access();
207
208 if (show_globals) {
209 phpdbg_notice("Superglobal variables (%d)", zend_hash_num_elements(&vars));
210 } else {
211 zend_op_array *ops = &EG(current_execute_data)->func->op_array;
212
213 if (ops->function_name) {
214 if (ops->scope) {
215 phpdbg_notice("Variables in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, zend_hash_num_elements(&vars));
216 } else {
217 phpdbg_notice("Variables in %s() (%d)", ZSTR_VAL(ops->function_name), zend_hash_num_elements(&vars));
218 }
219 } else {
220 if (ops->filename) {
221 phpdbg_notice("Variables in %s (%d)", ZSTR_VAL(ops->filename), zend_hash_num_elements(&vars));
222 } else {
223 phpdbg_notice("Variables @ %p (%d)", ops, zend_hash_num_elements(&vars));
224 }
225 }
226 }
227
228 if (zend_hash_num_elements(&vars)) {
229 phpdbg_out("Address Refs Type Variable\n");
230 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(&vars, var, data) {
231 phpdbg_try_access {
232 const char *isref = "";
233 #define VARIABLEINFO(msg, ...) \
234 phpdbg_writeln( \
235 "%-18p %-7d %-9s %s$%.*s" msg, data, Z_REFCOUNTED_P(data) ? Z_REFCOUNT_P(data) : 1, zend_get_type_by_const(Z_TYPE_P(data)), isref, (int) ZSTR_LEN(var), ZSTR_VAL(var), ##__VA_ARGS__)
236 retry_switch:
237 switch (Z_TYPE_P(data)) {
238 case IS_RESOURCE:
239 phpdbg_try_access {
240 const char *type = zend_rsrc_list_get_rsrc_type(Z_RES_P(data));
241 VARIABLEINFO("\n|-------(typeof)------> (%s)\n", type ? type : "unknown");
242 } phpdbg_catch_access {
243 VARIABLEINFO("\n|-------(typeof)------> (unknown)\n");
244 } phpdbg_end_try_access();
245 break;
246 case IS_OBJECT:
247 phpdbg_try_access {
248 VARIABLEINFO("\n|-----(instanceof)----> (%s)\n", ZSTR_VAL(Z_OBJCE_P(data)->name));
249 } phpdbg_catch_access {
250 VARIABLEINFO("\n|-----(instanceof)----> (unknown)\n");
251 } phpdbg_end_try_access();
252 break;
253 case IS_STRING:
254 phpdbg_try_access {
255 VARIABLEINFO("\nstring (%zd) \"%.*s%s\"", Z_STRLEN_P(data), Z_STRLEN_P(data) < 255 ? (int) Z_STRLEN_P(data) : 255, Z_STRVAL_P(data), Z_STRLEN_P(data) > 255 ? "..." : "");
256 } phpdbg_catch_access {
257 VARIABLEINFO("");
258 } phpdbg_end_try_access();
259 break;
260 case IS_TRUE:
261 VARIABLEINFO("\nbool (true)");
262 break;
263 case IS_FALSE:
264 VARIABLEINFO("\nbool (false)");
265 break;
266 case IS_LONG:
267 VARIABLEINFO("\nint ("ZEND_LONG_FMT")", Z_LVAL_P(data));
268 break;
269 case IS_DOUBLE:
270 VARIABLEINFO("\ndouble (%lf)", Z_DVAL_P(data));
271 break;
272 case IS_REFERENCE:
273 isref = "&";
274 data = Z_REFVAL_P(data);
275 goto retry_switch;
276 case IS_INDIRECT:
277 data = Z_INDIRECT_P(data);
278 goto retry_switch;
279 default:
280 VARIABLEINFO("");
281 }
282 #undef VARIABLEINFO
283 } phpdbg_catch_access {
284 phpdbg_writeln("%p\tn/a\tn/a\t$%s", data, ZSTR_VAL(var));
285 } phpdbg_end_try_access();
286 } ZEND_HASH_FOREACH_END();
287 }
288
289 zend_hash_destroy(&vars);
290
291 return SUCCESS;
292 } /* }}} */
293
PHPDBG_INFO(vars)294 PHPDBG_INFO(vars) /* {{{ */
295 {
296 return phpdbg_print_symbols(0);
297 }
298
PHPDBG_INFO(globals)299 PHPDBG_INFO(globals) /* {{{ */
300 {
301 return phpdbg_print_symbols(1);
302 }
303
PHPDBG_INFO(literal)304 PHPDBG_INFO(literal) /* {{{ */
305 {
306 /* literals are assumed to not be manipulated during executing of their op_array and as such async safe */
307 bool in_executor = PHPDBG_G(in_execution) && EG(current_execute_data) && EG(current_execute_data)->func;
308 if (in_executor || PHPDBG_G(ops)) {
309 zend_op_array *ops = in_executor ? &EG(current_execute_data)->func->op_array : PHPDBG_G(ops);
310 int literal = 0, count = ops->last_literal - 1;
311
312 if (ops->function_name) {
313 if (ops->scope) {
314 phpdbg_notice("Literal Constants in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, count);
315 } else {
316 phpdbg_notice("Literal Constants in %s() (%d)", ops->function_name->val, count);
317 }
318 } else {
319 if (ops->filename) {
320 phpdbg_notice("Literal Constants in %s (%d)", ZSTR_VAL(ops->filename), count);
321 } else {
322 phpdbg_notice("Literal Constants @ %p (%d)", ops, count);
323 }
324 }
325
326 while (literal < ops->last_literal) {
327 if (Z_TYPE(ops->literals[literal]) != IS_NULL) {
328 phpdbg_write("|-------- C%u -------> [", literal);
329 zend_print_zval(&ops->literals[literal], 0);
330 phpdbg_out("]\n");
331 }
332 literal++;
333 }
334 } else {
335 phpdbg_error("Not executing!");
336 }
337
338 return SUCCESS;
339 } /* }}} */
340
PHPDBG_INFO(memory)341 PHPDBG_INFO(memory) /* {{{ */
342 {
343 size_t used, real, peak_used, peak_real;
344 zend_mm_heap *orig_heap = NULL;
345 bool is_mm;
346
347 if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
348 orig_heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem());
349 }
350 if ((is_mm = is_zend_mm())) {
351 used = zend_memory_usage(0);
352 real = zend_memory_usage(1);
353 peak_used = zend_memory_peak_usage(0);
354 peak_real = zend_memory_peak_usage(1);
355 }
356 if (orig_heap) {
357 zend_mm_set_heap(orig_heap);
358 }
359
360 if (is_mm) {
361 phpdbg_notice("Memory Manager Information");
362 phpdbg_notice("Current");
363 phpdbg_writeln( "|-------> Used:\t%.3f kB", (float) (used / 1024));
364 phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (real / 1024));
365 phpdbg_notice("Peak");
366 phpdbg_writeln("|-------> Used:\t%.3f kB", (float) (peak_used / 1024));
367 phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (peak_real / 1024));
368 } else {
369 phpdbg_error("Memory Manager Disabled!");
370 }
371 return SUCCESS;
372 } /* }}} */
373
phpdbg_print_class_name(zend_class_entry * ce)374 static inline void phpdbg_print_class_name(zend_class_entry *ce) /* {{{ */
375 {
376 const char *visibility = ce->type == ZEND_USER_CLASS ? "User" : "Internal";
377 const char *type = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : (ce->ce_flags & ZEND_ACC_ABSTRACT) ? "Abstract Class" : "Class";
378
379 phpdbg_writeln("%s %s %.*s (%d)", visibility, type, (int) ZSTR_LEN(ce->name), ZSTR_VAL(ce->name), zend_hash_num_elements(&ce->function_table));
380 } /* }}} */
381
PHPDBG_INFO(classes)382 PHPDBG_INFO(classes) /* {{{ */
383 {
384 zend_class_entry *ce;
385 HashTable classes;
386
387 zend_hash_init(&classes, 8, NULL, NULL, 0);
388
389 phpdbg_try_access {
390 ZEND_HASH_MAP_FOREACH_PTR(EG(class_table), ce) {
391 if (ce->type == ZEND_USER_CLASS) {
392 zend_hash_next_index_insert_ptr(&classes, ce);
393 }
394 } ZEND_HASH_FOREACH_END();
395 } phpdbg_catch_access {
396 phpdbg_notice("Not all classes could be fetched, possibly invalid data source");
397 } phpdbg_end_try_access();
398
399 phpdbg_notice("User Classes (%d)", zend_hash_num_elements(&classes));
400
401 /* once added, assume that classes are stable... until shutdown. */
402 if (HT_IS_INITIALIZED(&classes)) {
403 ZEND_HASH_PACKED_FOREACH_PTR(&classes, ce) {
404 phpdbg_print_class_name(ce);
405
406 if (ce->parent) {
407 if (ce->ce_flags & ZEND_ACC_LINKED) {
408 zend_class_entry *pce = ce->parent;
409 do {
410 phpdbg_out("|-------- ");
411 phpdbg_print_class_name(pce);
412 } while ((pce = pce->parent));
413 } else {
414 phpdbg_writeln("|-------- User Class %s (not yet linked because declaration for parent was not encountered when declaring the class)", ZSTR_VAL(ce->parent_name));
415 }
416 }
417
418 if (ce->info.user.filename) {
419 phpdbg_writeln("|---- in %s on line %u", ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
420 } else {
421 phpdbg_writeln("|---- no source code");
422 }
423 } ZEND_HASH_FOREACH_END();
424 }
425
426 zend_hash_destroy(&classes);
427
428 return SUCCESS;
429 } /* }}} */
430
PHPDBG_INFO(funcs)431 PHPDBG_INFO(funcs) /* {{{ */
432 {
433 zend_function *zf;
434 HashTable functions;
435
436 zend_hash_init(&functions, 8, NULL, NULL, 0);
437
438 phpdbg_try_access {
439 ZEND_HASH_MAP_FOREACH_PTR(EG(function_table), zf) {
440 if (zf->type == ZEND_USER_FUNCTION) {
441 zend_hash_next_index_insert_ptr(&functions, zf);
442 }
443 } ZEND_HASH_FOREACH_END();
444 } phpdbg_catch_access {
445 phpdbg_notice("Not all functions could be fetched, possibly invalid data source");
446 } phpdbg_end_try_access();
447
448 phpdbg_notice("User Functions (%d)", zend_hash_num_elements(&functions));
449
450 if (HT_IS_INITIALIZED(&functions)) {
451 ZEND_HASH_PACKED_FOREACH_PTR(&functions, zf) {
452 zend_op_array *op_array = &zf->op_array;
453
454 phpdbg_write("|-------- %s", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}");
455
456 if (op_array->filename) {
457 phpdbg_writeln(" in %s on line %d", ZSTR_VAL(op_array->filename), op_array->line_start);
458 } else {
459 phpdbg_writeln(" (no source code)");
460 }
461 } ZEND_HASH_FOREACH_END();
462 }
463
464 zend_hash_destroy(&functions);
465
466 return SUCCESS;
467 } /* }}} */
468