2013-11-12 16:26:11 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2019-01-30 17:00:23 +08:00
|
|
|
| Copyright (c) The PHP Group |
|
2013-11-12 16:26:11 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
|
|
| available through the world-wide-web at the following url: |
|
2021-05-06 18:16:35 +08:00
|
|
|
| https://www.php.net/license/3_01.txt |
|
2013-11-12 16:26:11 +08:00
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Felipe Pena <felipe@php.net> |
|
|
|
|
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
|
2013-12-18 18:02:50 +08:00
|
|
|
| Authors: Bob Weinand <bwoebi@php.net> |
|
2013-11-12 16:26:11 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "phpdbg.h"
|
|
|
|
#include "phpdbg_print.h"
|
2013-11-13 06:39:39 +08:00
|
|
|
#include "phpdbg_utils.h"
|
2013-11-18 05:01:41 +08:00
|
|
|
#include "phpdbg_prompt.h"
|
2013-11-12 16:26:11 +08:00
|
|
|
|
2021-07-13 21:32:14 +08:00
|
|
|
#include "Optimizer/zend_dump.h"
|
|
|
|
|
2016-02-17 06:47:37 +08:00
|
|
|
ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
|
2013-11-12 17:26:26 +08:00
|
|
|
|
2014-10-03 07:29:41 +08:00
|
|
|
#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s, flags) \
|
2015-03-22 06:19:12 +08:00
|
|
|
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[8], flags)
|
2014-02-20 04:18:49 +08:00
|
|
|
|
2014-02-03 00:35:28 +08:00
|
|
|
const phpdbg_command_t phpdbg_print_commands[] = {
|
2015-04-26 01:15:08 +08:00
|
|
|
PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the main execution context", 'e', print_exec, NULL, 0, PHPDBG_ASYNC_SAFE),
|
|
|
|
PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0, PHPDBG_ASYNC_SAFE),
|
|
|
|
PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s", PHPDBG_ASYNC_SAFE),
|
|
|
|
PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m", PHPDBG_ASYNC_SAFE),
|
|
|
|
PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s", PHPDBG_ASYNC_SAFE),
|
|
|
|
PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0, PHPDBG_ASYNC_SAFE),
|
2014-02-03 00:35:28 +08:00
|
|
|
PHPDBG_END_COMMAND
|
|
|
|
};
|
|
|
|
|
2013-11-12 17:26:26 +08:00
|
|
|
PHPDBG_PRINT(opline) /* {{{ */
|
2013-11-12 16:26:11 +08:00
|
|
|
{
|
2014-10-25 01:29:50 +08:00
|
|
|
if (PHPDBG_G(in_execution) && EG(current_execute_data)) {
|
2016-02-18 03:26:47 +08:00
|
|
|
phpdbg_print_opline(phpdbg_user_execute_data(EG(current_execute_data)), 1);
|
2013-11-12 17:26:26 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("Not Executing!");
|
2013-11-12 17:26:26 +08:00
|
|
|
}
|
2013-11-13 06:39:39 +08:00
|
|
|
|
2013-11-12 16:26:11 +08:00
|
|
|
return SUCCESS;
|
|
|
|
} /* }}} */
|
2013-11-13 08:36:47 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static inline void phpdbg_print_function_helper(zend_function *method) /* {{{ */
|
2013-11-19 05:55:29 +08:00
|
|
|
{
|
2013-11-26 18:02:58 +08:00
|
|
|
switch (method->type) {
|
|
|
|
case ZEND_USER_FUNCTION: {
|
|
|
|
zend_op_array* op_array = &(method->op_array);
|
2014-09-21 10:17:19 +08:00
|
|
|
|
2013-11-26 18:02:58 +08:00
|
|
|
if (op_array) {
|
2021-07-13 21:32:14 +08:00
|
|
|
zend_dump_op_array(op_array, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
|
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
2020-05-18 21:46:06 +08:00
|
|
|
|
|
|
|
for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
|
|
|
|
zend_op_array *def = op_array->dynamic_func_defs[i];
|
|
|
|
phpdbg_out("\ndynamic def: %i, function name: %.*s\n",
|
|
|
|
i, (int) ZSTR_LEN(def->function_name), ZSTR_VAL(def->function_name));
|
2021-07-13 21:32:14 +08:00
|
|
|
zend_dump_op_array(def, ZEND_DUMP_LINE_NUMBERS, NULL, NULL);
|
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
2020-05-18 21:46:06 +08:00
|
|
|
}
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
if (method->common.scope) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_writeln("\tInternal %s::%s()", ZSTR_VAL(method->common.scope->name), ZSTR_VAL(method->common.function_name));
|
2013-11-26 18:02:58 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_writeln("\tInternal %s()", ZSTR_VAL(method->common.function_name));
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-11-19 05:55:29 +08:00
|
|
|
} /* }}} */
|
2013-11-13 10:26:37 +08:00
|
|
|
|
2013-11-17 17:30:44 +08:00
|
|
|
PHPDBG_PRINT(exec) /* {{{ */
|
|
|
|
{
|
2013-11-26 18:02:58 +08:00
|
|
|
if (PHPDBG_G(exec)) {
|
2014-10-03 07:29:41 +08:00
|
|
|
if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) {
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_compile();
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (PHPDBG_G(ops)) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("Context %s (%d ops)", PHPDBG_G(exec), PHPDBG_G(ops)->last);
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops));
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("No execution context set");
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
|
2021-07-13 21:32:14 +08:00
|
|
|
return SUCCESS;
|
2013-11-17 17:30:44 +08:00
|
|
|
} /* }}} */
|
|
|
|
|
2013-11-18 22:24:58 +08:00
|
|
|
PHPDBG_PRINT(stack) /* {{{ */
|
|
|
|
{
|
2014-10-25 01:29:50 +08:00
|
|
|
if (PHPDBG_G(in_execution) && EG(current_execute_data)) {
|
2016-02-18 03:26:47 +08:00
|
|
|
zend_op_array *ops = &phpdbg_user_execute_data(EG(current_execute_data))->func->op_array;
|
2013-11-26 18:02:58 +08:00
|
|
|
if (ops->function_name) {
|
2013-11-25 18:17:57 +08:00
|
|
|
if (ops->scope) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("Stack in %s::%s() (%d ops)", ZSTR_VAL(ops->scope->name), ZSTR_VAL(ops->function_name), ops->last);
|
2013-11-25 18:17:57 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("Stack in %s() (%d ops)", ZSTR_VAL(ops->function_name), ops->last);
|
2013-11-25 18:17:57 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ops->filename) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("Stack in %s (%d ops)", ZSTR_VAL(ops->filename), ops->last);
|
2013-11-25 18:17:57 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("Stack @ %p (%d ops)", ops, ops->last);
|
2013-11-25 18:17:57 +08:00
|
|
|
}
|
|
|
|
}
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_print_function_helper((zend_function*) ops);
|
2013-11-26 18:02:58 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("Not Executing!");
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
2013-11-18 22:24:58 +08:00
|
|
|
|
2013-11-26 18:02:58 +08:00
|
|
|
return SUCCESS;
|
2013-11-18 22:24:58 +08:00
|
|
|
} /* }}} */
|
|
|
|
|
2013-11-13 08:36:47 +08:00
|
|
|
PHPDBG_PRINT(class) /* {{{ */
|
|
|
|
{
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_class_entry *ce;
|
2013-11-18 05:01:41 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (phpdbg_safe_class_lookup(param->str, param->len, &ce) == SUCCESS) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("%s %s: %s (%d methods)",
|
2014-10-25 01:29:50 +08:00
|
|
|
(ce->type == ZEND_USER_CLASS) ?
|
2014-02-22 04:46:56 +08:00
|
|
|
"User" : "Internal",
|
2014-10-25 01:29:50 +08:00
|
|
|
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
|
2014-02-22 04:46:56 +08:00
|
|
|
"Interface" :
|
2014-10-25 01:29:50 +08:00
|
|
|
(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
|
2014-02-22 04:46:56 +08:00
|
|
|
"Abstract Class" :
|
|
|
|
"Class",
|
2015-06-30 18:59:27 +08:00
|
|
|
ZSTR_VAL(ce->name),
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_hash_num_elements(&ce->function_table));
|
2014-09-21 10:17:19 +08:00
|
|
|
|
2014-10-25 01:29:50 +08:00
|
|
|
if (zend_hash_num_elements(&ce->function_table)) {
|
2014-02-22 04:46:56 +08:00
|
|
|
zend_function *method;
|
|
|
|
|
2021-11-03 20:18:26 +08:00
|
|
|
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_print_function_helper(method);
|
2014-10-25 01:29:50 +08:00
|
|
|
} ZEND_HASH_FOREACH_END();
|
2014-02-22 04:46:56 +08:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("The class %s could not be found", param->str);
|
2013-11-13 08:36:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
} /* }}} */
|
2013-11-13 10:26:37 +08:00
|
|
|
|
2013-11-13 13:36:01 +08:00
|
|
|
PHPDBG_PRINT(method) /* {{{ */
|
|
|
|
{
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_class_entry *ce;
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) {
|
2014-02-22 04:46:56 +08:00
|
|
|
zend_function *fbc;
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_string *lcname = zend_string_alloc(strlen(param->method.name), 0);
|
2015-06-30 18:59:27 +08:00
|
|
|
zend_str_tolower_copy(ZSTR_VAL(lcname), param->method.name, ZSTR_LEN(lcname));
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-10-25 01:29:50 +08:00
|
|
|
if ((fbc = zend_hash_find_ptr(&ce->function_table, lcname))) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("%s Method %s (%d ops)",
|
2014-02-22 04:46:56 +08:00
|
|
|
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
|
2015-06-30 18:59:27 +08:00
|
|
|
ZSTR_VAL(fbc->common.function_name),
|
2014-09-21 10:17:19 +08:00
|
|
|
(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_print_function_helper(fbc);
|
2014-02-22 04:46:56 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("The method %s::%s could not be found", param->method.class, param->method.name);
|
2014-02-22 04:46:56 +08:00
|
|
|
}
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_string_release(lcname);
|
2014-02-22 04:46:56 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("The class %s could not be found", param->method.class);
|
2013-11-26 18:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
2013-11-13 13:36:01 +08:00
|
|
|
} /* }}} */
|
|
|
|
|
2013-11-13 10:26:37 +08:00
|
|
|
PHPDBG_PRINT(func) /* {{{ */
|
|
|
|
{
|
2014-02-22 04:46:56 +08:00
|
|
|
HashTable *func_table = EG(function_table);
|
|
|
|
zend_function* fbc;
|
|
|
|
const char *func_name = param->str;
|
|
|
|
size_t func_name_len = param->len;
|
2014-10-25 01:29:50 +08:00
|
|
|
zend_string *lcname;
|
2014-02-22 04:46:56 +08:00
|
|
|
/* search active scope if begins with period */
|
|
|
|
if (func_name[0] == '.') {
|
2016-04-28 09:13:34 +08:00
|
|
|
zend_class_entry *scope = zend_get_executed_scope();
|
|
|
|
|
|
|
|
if (scope) {
|
2014-02-22 04:46:56 +08:00
|
|
|
func_name++;
|
|
|
|
func_name_len--;
|
|
|
|
|
2016-04-28 09:13:34 +08:00
|
|
|
func_table = &scope->function_table;
|
2014-02-22 04:46:56 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("No active class");
|
2014-02-22 04:46:56 +08:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
} else if (!EG(function_table)) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("No function table loaded");
|
2014-02-22 04:46:56 +08:00
|
|
|
return SUCCESS;
|
|
|
|
} else {
|
|
|
|
func_table = EG(function_table);
|
|
|
|
}
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-10-25 01:29:50 +08:00
|
|
|
lcname = zend_string_alloc(func_name_len, 0);
|
2015-06-30 18:59:27 +08:00
|
|
|
zend_str_tolower_copy(ZSTR_VAL(lcname), func_name, ZSTR_LEN(lcname));
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-10-03 07:29:41 +08:00
|
|
|
phpdbg_try_access {
|
2014-10-25 01:29:50 +08:00
|
|
|
if ((fbc = zend_hash_find_ptr(func_table, lcname))) {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_notice("%s %s %s (%d ops)",
|
2014-10-03 07:29:41 +08:00
|
|
|
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
|
|
|
|
(fbc->common.scope) ? "Method" : "Function",
|
2015-06-30 18:59:27 +08:00
|
|
|
ZSTR_VAL(fbc->common.function_name),
|
2014-10-07 19:08:30 +08:00
|
|
|
(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
phpdbg_print_function_helper(fbc);
|
2014-10-03 07:29:41 +08:00
|
|
|
} else {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("The function %s could not be found", func_name);
|
2014-10-03 07:29:41 +08:00
|
|
|
}
|
|
|
|
} phpdbg_catch_access {
|
2021-06-14 02:08:09 +08:00
|
|
|
phpdbg_error("Couldn't fetch function %.*s, invalid data source", (int) func_name_len, func_name);
|
2014-10-03 07:29:41 +08:00
|
|
|
} phpdbg_end_try_access();
|
2013-11-26 18:02:58 +08:00
|
|
|
|
2014-02-22 04:46:56 +08:00
|
|
|
efree(lcname);
|
|
|
|
|
2013-11-26 18:02:58 +08:00
|
|
|
return SUCCESS;
|
2013-11-13 10:26:37 +08:00
|
|
|
} /* }}} */
|
2015-04-20 03:53:49 +08:00
|
|
|
|
2021-05-12 21:55:23 +08:00
|
|
|
void phpdbg_print_opcodes_main(void) {
|
2015-04-20 03:53:49 +08:00
|
|
|
phpdbg_print_function_helper((zend_function *) PHPDBG_G(ops));
|
|
|
|
}
|
|
|
|
|
|
|
|
void phpdbg_print_opcodes_function(const char *function, size_t len) {
|
|
|
|
zend_function *func = zend_hash_str_find_ptr(EG(function_table), function, len);
|
|
|
|
|
|
|
|
if (!func) {
|
2021-07-13 21:32:14 +08:00
|
|
|
phpdbg_error("The function %s could not be found", function);
|
2015-04-20 03:53:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
phpdbg_print_function_helper(func);
|
|
|
|
}
|
|
|
|
|
2015-07-14 12:28:15 +08:00
|
|
|
static void phpdbg_print_opcodes_method_ce(zend_class_entry *ce, const char *function) {
|
2015-04-20 03:53:49 +08:00
|
|
|
zend_function *func;
|
|
|
|
|
|
|
|
if (ce->type != ZEND_USER_CLASS) {
|
2015-07-14 12:28:15 +08:00
|
|
|
phpdbg_out("function name: %s::%s (internal)\n", ce->name->val, function);
|
2015-04-20 03:53:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(func = zend_hash_str_find_ptr(&ce->function_table, function, strlen(function)))) {
|
2021-07-13 21:32:14 +08:00
|
|
|
phpdbg_error("The method %s::%s could not be found", ZSTR_VAL(ce->name), function);
|
2015-04-20 03:53:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
phpdbg_print_function_helper(func);
|
|
|
|
}
|
|
|
|
|
2015-07-14 12:28:15 +08:00
|
|
|
void phpdbg_print_opcodes_method(const char *class, const char *function) {
|
2015-04-20 03:53:49 +08:00
|
|
|
zend_class_entry *ce;
|
|
|
|
|
|
|
|
if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
|
2021-07-13 21:32:14 +08:00
|
|
|
phpdbg_error("The class %s could not be found", class);
|
2015-04-20 03:53:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-14 12:28:15 +08:00
|
|
|
phpdbg_print_opcodes_method_ce(ce, function);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void phpdbg_print_opcodes_ce(zend_class_entry *ce) {
|
|
|
|
zend_function *method;
|
2021-01-15 19:30:54 +08:00
|
|
|
bool first = 1;
|
2015-07-14 12:28:15 +08:00
|
|
|
|
2015-04-20 03:53:49 +08:00
|
|
|
phpdbg_out("%s %s: %s\n",
|
|
|
|
(ce->type == ZEND_USER_CLASS) ?
|
|
|
|
"user" : "internal",
|
|
|
|
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
|
|
|
|
"interface" :
|
|
|
|
(ce->ce_flags & ZEND_ACC_ABSTRACT) ?
|
|
|
|
"abstract Class" :
|
|
|
|
"class",
|
2015-06-30 18:59:27 +08:00
|
|
|
ZSTR_VAL(ce->name));
|
2015-04-20 03:53:49 +08:00
|
|
|
|
|
|
|
if (ce->type != ZEND_USER_CLASS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
phpdbg_out("%d methods: ", zend_hash_num_elements(&ce->function_table));
|
2021-11-03 20:18:26 +08:00
|
|
|
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
|
2015-04-20 03:53:49 +08:00
|
|
|
if (first) {
|
|
|
|
first = 0;
|
|
|
|
} else {
|
|
|
|
phpdbg_out(", ");
|
|
|
|
}
|
2015-06-30 18:59:27 +08:00
|
|
|
phpdbg_out("%s", ZSTR_VAL(method->common.function_name));
|
2015-04-20 03:53:49 +08:00
|
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
if (first) {
|
|
|
|
phpdbg_out("-");
|
|
|
|
}
|
|
|
|
phpdbg_out("\n");
|
|
|
|
|
2021-11-03 20:18:26 +08:00
|
|
|
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, method) {
|
2015-04-20 03:53:49 +08:00
|
|
|
phpdbg_print_function_helper(method);
|
|
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
}
|
|
|
|
|
2015-07-14 12:28:15 +08:00
|
|
|
void phpdbg_print_opcodes_class(const char *class) {
|
|
|
|
zend_class_entry *ce;
|
|
|
|
|
|
|
|
if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) {
|
2021-07-13 21:32:14 +08:00
|
|
|
phpdbg_error("The class %s could not be found", class);
|
2015-07-14 12:28:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
phpdbg_print_opcodes_ce(ce);
|
|
|
|
}
|
|
|
|
|
2021-07-13 21:32:14 +08:00
|
|
|
void phpdbg_print_opcodes(const char *function)
|
2015-04-20 03:53:49 +08:00
|
|
|
{
|
|
|
|
if (function == NULL) {
|
|
|
|
phpdbg_print_opcodes_main();
|
|
|
|
} else if (function[0] == '*' && function[1] == 0) {
|
|
|
|
/* all */
|
|
|
|
zend_string *name;
|
|
|
|
zend_function *func;
|
|
|
|
zend_class_entry *ce;
|
|
|
|
|
|
|
|
phpdbg_print_opcodes_main();
|
|
|
|
|
2021-11-03 20:18:26 +08:00
|
|
|
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(function_table), name, func) {
|
2015-04-20 03:53:49 +08:00
|
|
|
if (func->type == ZEND_USER_FUNCTION) {
|
2015-06-30 18:59:27 +08:00
|
|
|
phpdbg_print_opcodes_function(ZSTR_VAL(name), ZSTR_LEN(name));
|
2015-04-20 03:53:49 +08:00
|
|
|
}
|
|
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
|
2021-11-03 20:18:26 +08:00
|
|
|
ZEND_HASH_MAP_FOREACH_PTR(EG(class_table), ce) {
|
2015-04-20 03:53:49 +08:00
|
|
|
if (ce->type == ZEND_USER_CLASS) {
|
2021-07-13 21:32:14 +08:00
|
|
|
phpdbg_out("\n");
|
2015-07-14 12:28:15 +08:00
|
|
|
phpdbg_print_opcodes_ce(ce);
|
2015-04-20 03:53:49 +08:00
|
|
|
}
|
|
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
} else {
|
2020-06-07 17:01:19 +08:00
|
|
|
char *function_lowercase = zend_str_tolower_dup(function, strlen(function));
|
2015-07-22 21:33:44 +08:00
|
|
|
|
2020-06-07 17:01:19 +08:00
|
|
|
if (strstr(function_lowercase, "::") == NULL) {
|
|
|
|
phpdbg_print_opcodes_function(function_lowercase, strlen(function_lowercase));
|
2015-07-22 19:13:26 +08:00
|
|
|
} else {
|
2020-06-07 17:01:19 +08:00
|
|
|
char *method_name, *class_name = strtok(function_lowercase, "::");
|
2015-07-22 21:33:44 +08:00
|
|
|
if ((method_name = strtok(NULL, "::")) == NULL) {
|
|
|
|
phpdbg_print_opcodes_class(class_name);
|
|
|
|
} else {
|
|
|
|
phpdbg_print_opcodes_method(class_name, method_name);
|
|
|
|
}
|
2015-07-22 19:13:26 +08:00
|
|
|
}
|
2015-07-22 21:33:44 +08:00
|
|
|
|
2020-06-07 17:01:19 +08:00
|
|
|
efree(function_lowercase);
|
2015-04-20 03:53:49 +08:00
|
|
|
}
|
|
|
|
}
|
2021-07-13 21:32:14 +08:00
|
|
|
|
|
|
|
void phpdbg_print_opline(zend_execute_data *execute_data, bool ignore_flags) /* {{{ */
|
|
|
|
{
|
|
|
|
if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) && (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
|
|
|
|
zend_dump_op_line(&EX(func)->op_array, NULL, EX(opline), ZEND_DUMP_LINE_NUMBERS, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PHPDBG_G(oplog_list)) {
|
|
|
|
phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry));
|
|
|
|
zend_op_array *op_array = &EX(func)->op_array;
|
|
|
|
cur->op = (zend_op *) EX(opline);
|
|
|
|
cur->opcodes = op_array->opcodes;
|
|
|
|
cur->filename = op_array->filename;
|
|
|
|
cur->scope = op_array->scope;
|
|
|
|
cur->function_name = op_array->function_name;
|
|
|
|
cur->next = NULL;
|
|
|
|
PHPDBG_G(oplog_cur)->next = cur;
|
|
|
|
PHPDBG_G(oplog_cur) = cur;
|
|
|
|
}
|
|
|
|
} /* }}} */
|