1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 "php.h"
22 #include "phpdbg.h"
23 #include "phpdbg_utils.h"
24 #include "phpdbg_info.h"
25 #include "phpdbg_bp.h"
26 #include "phpdbg_prompt.h"
27
28 ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
29
30 #define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s, flags) \
31 PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags)
32
33 const phpdbg_command_t phpdbg_info_commands[] = {
34 PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE),
35 PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE),
36 PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE),
37 PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE),
38 PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE),
39 PHPDBG_INFO_COMMAND_D(constants, "show user defined constants", 'd', info_constants, NULL, 0, PHPDBG_ASYNC_SAFE),
40 PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE),
41 PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE),
42 PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE),
43 PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE),
44 PHPDBG_END_COMMAND
45 };
46
PHPDBG_INFO(break)47 PHPDBG_INFO(break) /* {{{ */
48 {
49 phpdbg_print_breakpoints(PHPDBG_BREAK_FILE);
50 phpdbg_print_breakpoints(PHPDBG_BREAK_SYM);
51 phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD);
52 phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE);
53 phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE);
54 phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE);
55 phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE);
56 phpdbg_print_breakpoints(PHPDBG_BREAK_COND);
57 phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE);
58
59 return SUCCESS;
60 } /* }}} */
61
PHPDBG_INFO(files)62 PHPDBG_INFO(files) /* {{{ */
63 {
64 zend_string *fname;
65
66 phpdbg_try_access {
67 phpdbg_notice("includedfilecount", "num=\"%d\"", "Included files: %d", zend_hash_num_elements(&EG(included_files)));
68 } phpdbg_catch_access {
69 phpdbg_error("signalsegv", "", "Could not fetch included file count, invalid data source");
70 return SUCCESS;
71 } phpdbg_end_try_access();
72
73 phpdbg_try_access {
74 ZEND_HASH_FOREACH_STR_KEY(&EG(included_files), fname) {
75 phpdbg_writeln("includedfile", "name=\"%s\"", "File: %s", ZSTR_VAL(fname));
76 } ZEND_HASH_FOREACH_END();
77 } phpdbg_catch_access {
78 phpdbg_error("signalsegv", "", "Could not fetch file name, invalid data source, aborting included file listing");
79 } phpdbg_end_try_access();
80
81 return SUCCESS;
82 } /* }}} */
83
PHPDBG_INFO(error)84 PHPDBG_INFO(error) /* {{{ */
85 {
86 if (PG(last_error_message)) {
87 phpdbg_try_access {
88 phpdbg_writeln("lasterror", "error=\"%s\" file=\"%s\" line=\"%d\"", "Last error: %s at %s line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
89 } phpdbg_catch_access {
90 phpdbg_notice("lasterror", "error=\"\"", "No error found!");
91 } phpdbg_end_try_access();
92 } else {
93 phpdbg_notice("lasterror", "error=\"\"", "No error found!");
94 }
95 return SUCCESS;
96 } /* }}} */
97
PHPDBG_INFO(constants)98 PHPDBG_INFO(constants) /* {{{ */
99 {
100 HashTable consts;
101 zend_constant *data;
102
103 zend_hash_init(&consts, 8, NULL, NULL, 0);
104
105 if (EG(zend_constants)) {
106 phpdbg_try_access {
107 ZEND_HASH_FOREACH_PTR(EG(zend_constants), data) {
108 if (ZEND_CONSTANT_MODULE_NUMBER(data) == PHP_USER_CONSTANT) {
109 zend_hash_update_ptr(&consts, data->name, data);
110 }
111 } ZEND_HASH_FOREACH_END();
112 } phpdbg_catch_access {
113 phpdbg_error("signalsegv", "", "Cannot fetch all the constants, invalid data source");
114 } phpdbg_end_try_access();
115 }
116
117 phpdbg_notice("constantinfo", "num=\"%d\"", "User-defined constants (%d)", zend_hash_num_elements(&consts));
118
119 if (zend_hash_num_elements(&consts)) {
120 phpdbg_out("Address Refs Type Constant\n");
121 ZEND_HASH_FOREACH_PTR(&consts, data) {
122
123 #define VARIABLEINFO(attrs, msg, ...) \
124 phpdbg_writeln("constant", \
125 "address=\"%p\" refcount=\"%d\" type=\"%s\" name=\"%.*s\" " attrs, \
126 "%-18p %-7d %-9s %.*s" msg, &data->value, \
127 Z_REFCOUNTED(data->value) ? Z_REFCOUNT(data->value) : 1, \
128 zend_zval_type_name(&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("length=\"%zd\" value=\"%.*s\"", "\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("value=\"true\"", "\nbool (true)");
141 break;
142 case IS_FALSE:
143 VARIABLEINFO("value=\"false\"", "\nbool (false)");
144 break;
145 case IS_LONG:
146 VARIABLEINFO("value=\"%ld\"", "\nint (%ld)", Z_LVAL(data->value));
147 break;
148 case IS_DOUBLE:
149 VARIABLEINFO("value=\"%lf\"", "\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("variableinfo", "unreachable=\"%.*s\"", "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(zend_bool show_globals)176 static int phpdbg_print_symbols(zend_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("inactive", "type=\"op_array\"", "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("inactive", "type=\"symbol_table\"", "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_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("signalsegv", "", "Cannot fetch all data from the symbol table, invalid data source");
206 } phpdbg_end_try_access();
207
208 if (show_globals) {
209 phpdbg_notice("variableinfo", "num=\"%d\"", "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("variableinfo", "method=\"%s::%s\" num=\"%d\"", "Variables in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, zend_hash_num_elements(&vars));
216 } else {
217 phpdbg_notice("variableinfo", "function=\"%s\" num=\"%d\"", "Variables in %s() (%d)", ZSTR_VAL(ops->function_name), zend_hash_num_elements(&vars));
218 }
219 } else {
220 if (ops->filename) {
221 phpdbg_notice("variableinfo", "file=\"%s\" num=\"%d\"", "Variables in %s (%d)", ZSTR_VAL(ops->filename), zend_hash_num_elements(&vars));
222 } else {
223 phpdbg_notice("variableinfo", "opline=\"%p\" num=\"%d\"", "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_FOREACH_STR_KEY_VAL(&vars, var, data) {
231 phpdbg_try_access {
232 const char *isref = "";
233 #define VARIABLEINFO(attrs, msg, ...) \
234 phpdbg_writeln("variable", \
235 "address=\"%p\" refcount=\"%d\" type=\"%s\" refstatus=\"%s\" name=\"%.*s\" " attrs, \
236 "%-18p %-7d %-9s %s$%.*s" msg, data, Z_REFCOUNTED_P(data) ? Z_REFCOUNT_P(data) : 1, zend_zval_type_name(data), isref, (int) ZSTR_LEN(var), ZSTR_VAL(var), ##__VA_ARGS__)
237 retry_switch:
238 switch (Z_TYPE_P(data)) {
239 case IS_RESOURCE:
240 phpdbg_try_access {
241 const char *type = zend_rsrc_list_get_rsrc_type(Z_RES_P(data));
242 VARIABLEINFO("type=\"%s\"", "\n|-------(typeof)------> (%s)\n", type ? type : "unknown");
243 } phpdbg_catch_access {
244 VARIABLEINFO("type=\"unknown\"", "\n|-------(typeof)------> (unknown)\n");
245 } phpdbg_end_try_access();
246 break;
247 case IS_OBJECT:
248 phpdbg_try_access {
249 VARIABLEINFO("instanceof=\"%s\"", "\n|-----(instanceof)----> (%s)\n", ZSTR_VAL(Z_OBJCE_P(data)->name));
250 } phpdbg_catch_access {
251 VARIABLEINFO("instanceof=\"%s\"", "\n|-----(instanceof)----> (unknown)\n");
252 } phpdbg_end_try_access();
253 break;
254 case IS_STRING:
255 phpdbg_try_access {
256 VARIABLEINFO("length=\"%zd\" value=\"%.*s\"", "\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 ? "..." : "");
257 } phpdbg_catch_access {
258 VARIABLEINFO("", "");
259 } phpdbg_end_try_access();
260 break;
261 case IS_TRUE:
262 VARIABLEINFO("value=\"true\"", "\nbool (true)");
263 break;
264 case IS_FALSE:
265 VARIABLEINFO("value=\"false\"", "\nbool (false)");
266 break;
267 case IS_LONG:
268 VARIABLEINFO("value=\"%ld\"", "\nint (%ld)", Z_LVAL_P(data));
269 break;
270 case IS_DOUBLE:
271 VARIABLEINFO("value=\"%lf\"", "\ndouble (%lf)", Z_DVAL_P(data));
272 break;
273 case IS_REFERENCE:
274 isref = "&";
275 data = Z_REFVAL_P(data);
276 goto retry_switch;
277 case IS_INDIRECT:
278 data = Z_INDIRECT_P(data);
279 goto retry_switch;
280 default:
281 VARIABLEINFO("", "");
282 }
283
284 #undef VARIABLEINFO
285 } phpdbg_catch_access {
286 phpdbg_writeln("variable", "address=\"%p\" name=\"%s\"", "%p\tn/a\tn/a\t$%s", data, ZSTR_VAL(var));
287 } phpdbg_end_try_access();
288 } ZEND_HASH_FOREACH_END();
289 }
290
291 zend_hash_destroy(&vars);
292
293 return SUCCESS;
294 } /* }}} */
295
PHPDBG_INFO(vars)296 PHPDBG_INFO(vars) /* {{{ */
297 {
298 return phpdbg_print_symbols(0);
299 }
300
PHPDBG_INFO(globals)301 PHPDBG_INFO(globals) /* {{{ */
302 {
303 return phpdbg_print_symbols(1);
304 }
305
PHPDBG_INFO(literal)306 PHPDBG_INFO(literal) /* {{{ */
307 {
308 /* literals are assumed to not be manipulated during executing of their op_array and as such async safe */
309 zend_bool in_executor = PHPDBG_G(in_execution) && EG(current_execute_data) && EG(current_execute_data)->func;
310 if (in_executor || PHPDBG_G(ops)) {
311 zend_op_array *ops = in_executor ? &EG(current_execute_data)->func->op_array : PHPDBG_G(ops);
312 int literal = 0, count = ops->last_literal - 1;
313
314 if (ops->function_name) {
315 if (ops->scope) {
316 phpdbg_notice("literalinfo", "method=\"%s::%s\" num=\"%d\"", "Literal Constants in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, count);
317 } else {
318 phpdbg_notice("literalinfo", "function=\"%s\" num=\"%d\"", "Literal Constants in %s() (%d)", ops->function_name->val, count);
319 }
320 } else {
321 if (ops->filename) {
322 phpdbg_notice("literalinfo", "file=\"%s\" num=\"%d\"", "Literal Constants in %s (%d)", ZSTR_VAL(ops->filename), count);
323 } else {
324 phpdbg_notice("literalinfo", "opline=\"%p\" num=\"%d\"", "Literal Constants @ %p (%d)", ops, count);
325 }
326 }
327
328 while (literal < ops->last_literal) {
329 if (Z_TYPE(ops->literals[literal]) != IS_NULL) {
330 phpdbg_write("literal", "id=\"%u\"", "|-------- C%u -------> [", literal);
331 zend_print_zval(&ops->literals[literal], 0);
332 phpdbg_out("]\n");
333 }
334 literal++;
335 }
336 } else {
337 phpdbg_error("inactive", "type=\"execution\"", "Not executing!");
338 }
339
340 return SUCCESS;
341 } /* }}} */
342
PHPDBG_INFO(memory)343 PHPDBG_INFO(memory) /* {{{ */
344 {
345 size_t used, real, peak_used, peak_real;
346 zend_mm_heap *orig_heap = NULL;
347 zend_bool is_mm;
348
349 if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
350 orig_heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem());
351 }
352 if ((is_mm = is_zend_mm())) {
353 used = zend_memory_usage(0);
354 real = zend_memory_usage(1);
355 peak_used = zend_memory_peak_usage(0);
356 peak_real = zend_memory_peak_usage(1);
357 }
358 if (orig_heap) {
359 zend_mm_set_heap(orig_heap);
360 }
361
362 if (is_mm) {
363 phpdbg_notice("meminfo", "", "Memory Manager Information");
364 phpdbg_notice("current", "", "Current");
365 phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (used / 1024));
366 phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (real / 1024));
367 phpdbg_notice("peak", "", "Peak");
368 phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (peak_used / 1024));
369 phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (peak_real / 1024));
370 } else {
371 phpdbg_error("inactive", "type=\"memory_manager\"", "Memory Manager Disabled!");
372 }
373 return SUCCESS;
374 } /* }}} */
375
phpdbg_print_class_name(zend_class_entry * ce)376 static inline void phpdbg_print_class_name(zend_class_entry *ce) /* {{{ */
377 {
378 const char *visibility = ce->type == ZEND_USER_CLASS ? "User" : "Internal";
379 const char *type = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : (ce->ce_flags & ZEND_ACC_ABSTRACT) ? "Abstract Class" : "Class";
380
381 phpdbg_writeln("class", "type=\"%s\" flags=\"%s\" name=\"%.*s\" methodcount=\"%d\"", "%s %s %.*s (%d)", visibility, type, (int) ZSTR_LEN(ce->name), ZSTR_VAL(ce->name), zend_hash_num_elements(&ce->function_table));
382 } /* }}} */
383
PHPDBG_INFO(classes)384 PHPDBG_INFO(classes) /* {{{ */
385 {
386 zend_class_entry *ce;
387 HashTable classes;
388
389 zend_hash_init(&classes, 8, NULL, NULL, 0);
390
391 phpdbg_try_access {
392 ZEND_HASH_FOREACH_PTR(EG(class_table), ce) {
393 if (ce->type == ZEND_USER_CLASS) {
394 zend_hash_next_index_insert_ptr(&classes, ce);
395 }
396 } ZEND_HASH_FOREACH_END();
397 } phpdbg_catch_access {
398 phpdbg_notice("signalsegv", "", "Not all classes could be fetched, possibly invalid data source");
399 } phpdbg_end_try_access();
400
401 phpdbg_notice("classinfo", "num=\"%d\"", "User Classes (%d)", zend_hash_num_elements(&classes));
402
403 /* once added, assume that classes are stable... until shutdown. */
404 ZEND_HASH_FOREACH_PTR(&classes, ce) {
405 phpdbg_print_class_name(ce);
406
407 if (ce->parent) {
408 zend_class_entry *pce;
409 phpdbg_xml("<parents %r>");
410 pce = ce->parent;
411 do {
412 phpdbg_out("|-------- ");
413 phpdbg_print_class_name(pce);
414 } while ((pce = pce->parent));
415 phpdbg_xml("</parents>");
416 }
417
418 if (ce->info.user.filename) {
419 phpdbg_writeln("classsource", "file=\"%s\" line=\"%u\"", "|---- in %s on line %u", ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
420 } else {
421 phpdbg_writeln("classsource", "", "|---- no source code");
422 }
423 } ZEND_HASH_FOREACH_END();
424
425 zend_hash_destroy(&classes);
426
427 return SUCCESS;
428 } /* }}} */
429
PHPDBG_INFO(funcs)430 PHPDBG_INFO(funcs) /* {{{ */
431 {
432 zend_function *zf;
433 HashTable functions;
434
435 zend_hash_init(&functions, 8, NULL, NULL, 0);
436
437 phpdbg_try_access {
438 ZEND_HASH_FOREACH_PTR(EG(function_table), zf) {
439 if (zf->type == ZEND_USER_FUNCTION) {
440 zend_hash_next_index_insert_ptr(&functions, zf);
441 }
442 } ZEND_HASH_FOREACH_END();
443 } phpdbg_catch_access {
444 phpdbg_notice("signalsegv", "", "Not all functions could be fetched, possibly invalid data source");
445 } phpdbg_end_try_access();
446
447 phpdbg_notice("functioninfo", "num=\"%d\"", "User Functions (%d)", zend_hash_num_elements(&functions));
448
449 ZEND_HASH_FOREACH_PTR(&functions, zf) {
450 zend_op_array *op_array = &zf->op_array;
451
452 phpdbg_write("function", "name=\"%s\"", "|-------- %s", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}");
453
454 if (op_array->filename) {
455 phpdbg_writeln("functionsource", "file=\"%s\" line=\"%d\"", " in %s on line %d", ZSTR_VAL(op_array->filename), op_array->line_start);
456 } else {
457 phpdbg_writeln("functionsource", "", " (no source code)");
458 }
459 } ZEND_HASH_FOREACH_END();
460
461 zend_hash_destroy(&functions);
462
463 return SUCCESS;
464 } /* }}} */
465