mirror of
https://github.com/php/php-src.git
synced 2025-08-19 08:49:28 +02:00
Merge branch 'master' of https://github.com/krakjoe/phpdbg into frames
Conflicts: phpdbg.h phpdbg_prompt.c
This commit is contained in:
commit
c46a413697
15 changed files with 511 additions and 145 deletions
12
.phpdbginit
12
.phpdbginit
|
@ -31,6 +31,18 @@ function my_debugging_function()
|
|||
/* phpdbg_break(PHPDBG_METHOD, "phpdbg::method"); */
|
||||
/* phpdbg_break(PHPDBG_FUNC, "my_global_function"); */
|
||||
/* phpdbg_break(PHPDBG_FILE, "/path/to/file.php:10"); */
|
||||
|
||||
/*
|
||||
If readline is loaded, you might want to setup completion:
|
||||
*/
|
||||
if (function_exists('readline_completion_function')) {
|
||||
readline_completion_function(function(){
|
||||
return array_merge(
|
||||
get_defined_functions()['user'],
|
||||
array_keys(get_defined_constants())
|
||||
);
|
||||
});
|
||||
}
|
||||
:>
|
||||
##########################################################
|
||||
# Now carry on initializing phpdbg ...
|
||||
|
|
|
@ -8,7 +8,7 @@ install-phpdbg: $(BUILD_BINARY)
|
|||
@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
|
||||
@$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
|
||||
@$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
|
||||
@$(INSTALL) -m 0755 $(BUILD_BINARY) $(INSTALL_ROOT)$(sbindir)/$(program_prefix)phpdbg$(program_suffix)$(EXEEXT)
|
||||
@$(INSTALL) -m 0755 $(BUILD_BINARY) $(INSTALL_ROOT)$(bindir)/$(program_prefix)phpdbg$(program_suffix)$(EXEEXT)
|
||||
|
||||
clean-phpdbg:
|
||||
@echo "Cleaning phpdbg object files ..."
|
||||
|
|
29
phpdbg.c
29
phpdbg.c
|
@ -44,6 +44,9 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
|
|||
pg->lcmd = NULL;
|
||||
pg->flags = PHPDBG_DEFAULT_FLAGS;
|
||||
pg->oplog = NULL;
|
||||
pg->io[PHPDBG_STDIN] = NULL;
|
||||
pg->io[PHPDBG_STDOUT] = NULL;
|
||||
pg->io[PHPDBG_STDERR] = NULL;
|
||||
memset(&pg->lparam, 0, sizeof(phpdbg_param_t));
|
||||
pg->frame.num = 0;
|
||||
} /* }}} */
|
||||
|
@ -340,7 +343,9 @@ static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int len
|
|||
|
||||
static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
|
||||
{
|
||||
fflush(stdout);
|
||||
TSRMLS_FETCH();
|
||||
|
||||
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
|
||||
} /* }}} */
|
||||
|
||||
/* {{{ sapi_module_struct phpdbg_sapi_module
|
||||
|
@ -435,6 +440,23 @@ static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */
|
|||
}
|
||||
} /* }}} */
|
||||
|
||||
static inline void phpdbg_sigint_handler(int signo) /* {{{ */
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if (EG(in_execution)) {
|
||||
/* we don't want to set signalled while phpdbg is interactive */
|
||||
if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
|
||||
}
|
||||
} else {
|
||||
/* if we are not executing then just provide advice */
|
||||
phpdbg_writeln(EMPTY);
|
||||
phpdbg_error(
|
||||
"Please leave phpdbg gracefully !");
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
int main(int argc, char **argv) /* {{{ */
|
||||
{
|
||||
sapi_module_struct *phpdbg = &phpdbg_sapi_module;
|
||||
|
@ -620,6 +642,11 @@ phpdbg_main:
|
|||
|
||||
PG(modules_activated) = 0;
|
||||
|
||||
/* set up basic io here */
|
||||
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
|
||||
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
|
||||
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
|
||||
|
||||
if (exec) { /* set execution context */
|
||||
PHPDBG_G(exec) = phpdbg_resolve_path(
|
||||
exec TSRMLS_CC);
|
||||
|
|
7
phpdbg.h
7
phpdbg.h
|
@ -101,6 +101,7 @@
|
|||
#define PHPDBG_IS_STEPONEVAL (1<<17)
|
||||
#define PHPDBG_IS_INITIALIZING (1<<18)
|
||||
#define PHPDBG_IS_SIGNALED (1<<19)
|
||||
#define PHPDBG_IS_INTERACTIVE (1<<20)
|
||||
|
||||
#ifndef _WIN32
|
||||
# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED)
|
||||
|
@ -112,6 +113,11 @@
|
|||
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
|
||||
#define PHPDBG_VERSION "0.0.2-dev" /* }}} */
|
||||
|
||||
/* {{{ output descriptors */
|
||||
#define PHPDBG_STDIN 0
|
||||
#define PHPDBG_STDOUT 1
|
||||
#define PHPDBG_STDERR 2 /* }}} */
|
||||
|
||||
/* {{{ structs */
|
||||
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
|
||||
HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
|
||||
|
@ -128,6 +134,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
|
|||
zend_ulong flags; /* phpdbg flags */
|
||||
HashTable registered; /* registered */
|
||||
phpdbg_frame frame; /* frame */
|
||||
FILE *io[3]; /* stdin/stdout/stderr */
|
||||
ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
|
||||
|
||||
#endif /* PHPDBG_H */
|
||||
|
|
12
phpdbg_cmd.c
12
phpdbg_cmd.c
|
@ -218,7 +218,7 @@ phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
|||
#ifndef HAVE_LIBREADLINE
|
||||
char buf[PHPDBG_MAX_CMD];
|
||||
if (!phpdbg_write(PROMPT) ||
|
||||
!fgets(buf, PHPDBG_MAX_CMD, stdin)) {
|
||||
!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
|
||||
/* the user has gone away */
|
||||
phpdbg_error("Failed to read console !");
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
|
||||
|
@ -377,10 +377,12 @@ int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_
|
|||
}
|
||||
}
|
||||
|
||||
PHPDBG_G(lcmd) = (phpdbg_command_t*) command;
|
||||
phpdbg_clear_param(
|
||||
&PHPDBG_G(lparam) TSRMLS_CC);
|
||||
PHPDBG_G(lparam) = param;
|
||||
if (!(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
|
||||
PHPDBG_G(lcmd) = (phpdbg_command_t*) command;
|
||||
phpdbg_clear_param(
|
||||
&PHPDBG_G(lparam) TSRMLS_CC);
|
||||
PHPDBG_G(lparam) = param;
|
||||
}
|
||||
|
||||
rc = command->handler(¶m, input TSRMLS_CC);
|
||||
break;
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
*/
|
||||
|
||||
#include "php.h"
|
||||
#include "phpdbg.h"
|
||||
#include "phpdbg_utils.h"
|
||||
#include "phpdbg_info.h"
|
||||
|
||||
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
||||
|
||||
PHPDBG_INFO(files) /* {{{ */
|
||||
{
|
||||
HashPosition pos;
|
||||
|
@ -144,6 +147,45 @@ PHPDBG_INFO(vars) /* {{{ */
|
|||
return SUCCESS;
|
||||
} /* }}} */
|
||||
|
||||
PHPDBG_INFO(literal) /* {{{ */
|
||||
{
|
||||
if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) {
|
||||
zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops);
|
||||
zend_uint literal =0, count = ops->last_literal-1;
|
||||
|
||||
if (ops->function_name) {
|
||||
if (ops->scope) {
|
||||
phpdbg_notice(
|
||||
"Literal Constants in %s::%s() (%d)", ops->scope->name, ops->function_name, count);
|
||||
} else {
|
||||
phpdbg_notice(
|
||||
"Literal Constants in %s() (%d)", ops->function_name, count);
|
||||
}
|
||||
} else {
|
||||
if (ops->filename) {
|
||||
phpdbg_notice(
|
||||
"Literal Constants in %s (%d)", ops->filename, count);
|
||||
} else {
|
||||
phpdbg_notice(
|
||||
"Literal Constants @ %p (%d)", ops, count);
|
||||
}
|
||||
}
|
||||
|
||||
while (literal < ops->last_literal) {
|
||||
phpdbg_write("|-------- C%lu -------> [", literal);
|
||||
zend_print_zval(
|
||||
&ops->literals[literal].constant, 0);
|
||||
phpdbg_write("]");
|
||||
phpdbg_writeln(EMPTY);
|
||||
literal++;
|
||||
}
|
||||
} else {
|
||||
phpdbg_error("Not executing !");
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
} /* }}} */
|
||||
|
||||
static inline void phpdbg_print_class_name(zend_class_entry **ce TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
phpdbg_write(
|
||||
|
|
|
@ -29,13 +29,15 @@ PHPDBG_INFO(classes);
|
|||
PHPDBG_INFO(funcs);
|
||||
PHPDBG_INFO(error);
|
||||
PHPDBG_INFO(vars);
|
||||
PHPDBG_INFO(literal);
|
||||
|
||||
static const phpdbg_command_t phpdbg_info_commands[] = {
|
||||
PHPDBG_COMMAND_D_EX(files, "lists included files", 'F', info_files, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(classes, "lists loaded classes", 'c', info_classes, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(funcs, "lists loaded classes", 'f', info_funcs, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(error, "show the last error", 'e', info_error, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(files, "lists included files", 'F', info_files, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(classes, "lists loaded classes", 'c', info_classes, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(funcs, "lists loaded classes", 'f', info_funcs, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(error, "show the last error", 'e', info_error, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0),
|
||||
PHPDBG_END_COMMAND
|
||||
};
|
||||
|
||||
|
|
161
phpdbg_opcode.c
161
phpdbg_opcode.c
|
@ -17,9 +17,168 @@
|
|||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "phpdbg.h"
|
||||
#include "zend_vm_opcodes.h"
|
||||
#include "zend_compile.h"
|
||||
#include "phpdbg_opcode.h"
|
||||
#include "phpdbg_utils.h"
|
||||
|
||||
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
||||
|
||||
static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_uint iter = 0;
|
||||
|
||||
while (iter < ops->last_literal) {
|
||||
if (literal == &ops->literals[iter]) {
|
||||
return iter;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} /* }}} */
|
||||
|
||||
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
char *decode = NULL;
|
||||
|
||||
switch (type &~ EXT_TYPE_UNUSED) {
|
||||
case IS_CV:
|
||||
asprintf(&decode, "$%s", ops->vars[op->var].name);
|
||||
break;
|
||||
|
||||
case IS_VAR:
|
||||
case IS_TMP_VAR: {
|
||||
zend_ulong id = 0, *pid = NULL;
|
||||
if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) {
|
||||
id = zend_hash_num_elements(vars);
|
||||
zend_hash_index_update(
|
||||
vars, (zend_ulong) ops->vars - op->var,
|
||||
(void**) &id,
|
||||
sizeof(zend_ulong), NULL);
|
||||
} else id = *pid;
|
||||
asprintf(&decode, "@%lu", id);
|
||||
} break;
|
||||
|
||||
case IS_CONST:
|
||||
asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC));
|
||||
break;
|
||||
|
||||
case IS_UNUSED:
|
||||
asprintf(&decode, "<unused>");
|
||||
break;
|
||||
}
|
||||
return decode;
|
||||
} /* }}} */
|
||||
|
||||
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */
|
||||
{
|
||||
char *decode[4] = {NULL, NULL, NULL, NULL};
|
||||
|
||||
switch (op->opcode) {
|
||||
case ZEND_JMP:
|
||||
#ifdef ZEND_GOTO
|
||||
case ZEND_GOTO:
|
||||
#endif
|
||||
#ifdef ZEND_FAST_CALL
|
||||
case ZEND_FAST_CALL:
|
||||
#endif
|
||||
asprintf(&decode[1], "#%lu", op->op1.jmp_addr - ops->opcodes);
|
||||
goto format;
|
||||
|
||||
case ZEND_JMPZNZ:
|
||||
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
|
||||
asprintf(
|
||||
&decode[2], "#%u or #%lu", op->op2.opline_num, op->extended_value);
|
||||
goto result;
|
||||
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
|
||||
#ifdef ZEND_JMP_SET
|
||||
case ZEND_JMP_SET:
|
||||
#endif
|
||||
#ifdef ZEND_JMP_SET_VAR
|
||||
case ZEND_JMP_SET_VAR:
|
||||
#endif
|
||||
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
|
||||
asprintf(
|
||||
&decode[2], "#%lu", op->op2.jmp_addr - ops->opcodes);
|
||||
goto result;
|
||||
|
||||
case ZEND_RECV_INIT:
|
||||
goto result;
|
||||
|
||||
default: {
|
||||
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
|
||||
decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC);
|
||||
result:
|
||||
decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC);
|
||||
format:
|
||||
asprintf(
|
||||
&decode[0],
|
||||
"%-20s %-20s %-20s",
|
||||
decode[1] ? decode[1] : "",
|
||||
decode[2] ? decode[2] : "",
|
||||
decode[3] ? decode[3] : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (decode[1])
|
||||
free(decode[1]);
|
||||
if (decode[2])
|
||||
free(decode[2]);
|
||||
if (decode[3])
|
||||
free(decode[3]);
|
||||
|
||||
return decode[0];
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
/* force out a line while stepping so the user knows what is happening */
|
||||
if (ignore_flags ||
|
||||
(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
|
||||
(PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
|
||||
(PHPDBG_G(oplog)))) {
|
||||
|
||||
zend_op *opline = execute_data->opline;
|
||||
char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC);
|
||||
|
||||
if (ignore_flags ||
|
||||
(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
|
||||
(PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
|
||||
/* output line info */
|
||||
phpdbg_notice("#%- 5lu %16p %-30s %s %s",
|
||||
opline->lineno,
|
||||
opline,
|
||||
phpdbg_decode_opcode(opline->opcode),
|
||||
decode,
|
||||
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
|
||||
}
|
||||
|
||||
if (!ignore_flags && PHPDBG_G(oplog)) {
|
||||
phpdbg_log_ex(PHPDBG_G(oplog), "#%- 5lu %16p %-30s %s %s",
|
||||
opline->lineno,
|
||||
opline,
|
||||
phpdbg_decode_opcode(opline->opcode),
|
||||
decode,
|
||||
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
|
||||
}
|
||||
|
||||
if (decode) {
|
||||
free(decode);
|
||||
}
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC);
|
||||
} /* }}} */
|
||||
|
||||
const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
|
||||
{
|
||||
|
@ -197,7 +356,7 @@ const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
|
|||
CASE(ZEND_RECV_VARIADIC);
|
||||
#endif
|
||||
CASE(ZEND_OP_DATA);
|
||||
default:
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
} /* }}} */
|
||||
|
|
|
@ -23,5 +23,8 @@
|
|||
#include "zend_types.h"
|
||||
|
||||
const char *phpdbg_decode_opcode(zend_uchar);
|
||||
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC);
|
||||
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC);
|
||||
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC);
|
||||
|
||||
#endif /* PHPDBG_OPCODE_H */
|
||||
|
|
126
phpdbg_prompt.c
126
phpdbg_prompt.c
|
@ -35,7 +35,7 @@
|
|||
#include "phpdbg_frame.h"
|
||||
|
||||
/* {{{ command declarations */
|
||||
static const phpdbg_command_t phpdbg_prompt_commands[] = {
|
||||
const phpdbg_command_t phpdbg_prompt_commands[] = {
|
||||
PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1),
|
||||
PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0),
|
||||
PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1),
|
||||
|
@ -528,7 +528,7 @@ PHPDBG_COMMAND(eval) /* {{{ */
|
|||
PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
phpdbg_default_switch_case();
|
||||
}
|
||||
|
||||
|
@ -666,7 +666,7 @@ PHPDBG_COMMAND(info) /* {{{ */
|
|||
{
|
||||
phpdbg_error(
|
||||
"No information command selected !");
|
||||
|
||||
|
||||
return SUCCESS;
|
||||
} /* }}} */
|
||||
|
||||
|
@ -1034,8 +1034,10 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */
|
|||
{
|
||||
int ret = SUCCESS;
|
||||
|
||||
phpdbg_input_t *input = phpdbg_read_input(NULL TSRMLS_CC);
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
|
||||
|
||||
phpdbg_input_t *input = phpdbg_read_input(NULL TSRMLS_CC);
|
||||
|
||||
if (input && input->length > 0L) {
|
||||
do {
|
||||
switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
|
||||
|
@ -1075,112 +1077,12 @@ last:
|
|||
|
||||
out:
|
||||
phpdbg_destroy_input(&input TSRMLS_CC);
|
||||
if (EG(in_execution)) {
|
||||
restore_frame();
|
||||
}
|
||||
|
||||
PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE;
|
||||
|
||||
return ret;
|
||||
} /* }}} */
|
||||
|
||||
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
char *decode = NULL;
|
||||
|
||||
switch (type) {
|
||||
case IS_CV:
|
||||
asprintf(&decode, "$%s", ops->vars[op->var].name);
|
||||
break;
|
||||
|
||||
case IS_VAR:
|
||||
case IS_TMP_VAR: {
|
||||
zend_ulong id = 0;
|
||||
if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &id) != SUCCESS) {
|
||||
id = zend_hash_num_elements(vars);
|
||||
zend_hash_index_update(
|
||||
vars, (zend_ulong) ops->vars - op->var,
|
||||
(void**) &id,
|
||||
sizeof(zend_ulong), NULL);
|
||||
}
|
||||
|
||||
asprintf(&decode, "@%lu", id);
|
||||
} break;
|
||||
|
||||
case IS_CONST:
|
||||
asprintf(&decode, "<constant>");
|
||||
break;
|
||||
|
||||
case IS_UNUSED:
|
||||
asprintf(&decode, "<unused>");
|
||||
break;
|
||||
}
|
||||
return decode;
|
||||
} /* }}} */
|
||||
|
||||
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */
|
||||
{
|
||||
char *decode[3];
|
||||
|
||||
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
|
||||
decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC);
|
||||
|
||||
switch (op->opcode) {
|
||||
default: asprintf(
|
||||
&decode[0], "%-20s %-20s",
|
||||
decode[1], decode[2]
|
||||
);
|
||||
}
|
||||
|
||||
if (decode[1])
|
||||
free(decode[1]);
|
||||
if (decode[2])
|
||||
free(decode[2]);
|
||||
|
||||
return decode[0];
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
/* force out a line while stepping so the user knows what is happening */
|
||||
if (ignore_flags ||
|
||||
(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
|
||||
(PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
|
||||
(PHPDBG_G(oplog)))) {
|
||||
|
||||
zend_op *opline = execute_data->opline;
|
||||
char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC);
|
||||
|
||||
if (ignore_flags ||
|
||||
(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
|
||||
(PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
|
||||
/* output line info */
|
||||
phpdbg_notice("#%- 5lu %16p %-30s %s %s",
|
||||
opline->lineno,
|
||||
opline,
|
||||
phpdbg_decode_opcode(opline->opcode),
|
||||
decode,
|
||||
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
|
||||
}
|
||||
|
||||
if (!ignore_flags && PHPDBG_G(oplog)) {
|
||||
phpdbg_log_ex(PHPDBG_G(oplog), "#%- 5lu %16p %-30s %s %s",
|
||||
opline->lineno,
|
||||
opline,
|
||||
phpdbg_decode_opcode(opline->opcode),
|
||||
decode,
|
||||
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
|
||||
}
|
||||
|
||||
if (decode) {
|
||||
free(decode);
|
||||
}
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC);
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
/* this is implicitly required */
|
||||
|
@ -1197,12 +1099,6 @@ void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */
|
|||
}
|
||||
} /* }}} */
|
||||
|
||||
void phpdbg_sigint_handler(int signo)
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
|
||||
}
|
||||
|
||||
static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_array, zend_bool nested TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
#if PHP_VERSION_ID >= 50500
|
||||
|
@ -1277,7 +1173,7 @@ void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC) /* {{{ */
|
|||
#endif
|
||||
zend_bool original_in_execution = EG(in_execution);
|
||||
HashTable vars;
|
||||
|
||||
|
||||
#if PHP_VERSION_ID < 50500
|
||||
if (EG(exception)) {
|
||||
return;
|
||||
|
@ -1427,8 +1323,8 @@ next:
|
|||
DO_INTERACTIVE();
|
||||
}
|
||||
|
||||
PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
|
||||
|
||||
PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
|
||||
|
||||
if (PHPDBG_G(vmret) > 0) {
|
||||
switch (PHPDBG_G(vmret)) {
|
||||
case 1:
|
||||
|
|
|
@ -20,21 +20,13 @@
|
|||
#ifndef PHPDBG_PROMPT_H
|
||||
#define PHPDBG_PROMPT_H
|
||||
|
||||
/* {{{ */
|
||||
void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC);
|
||||
int phpdbg_interactive(TSRMLS_D);
|
||||
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC);
|
||||
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC);
|
||||
int phpdbg_compile(TSRMLS_D);
|
||||
void phpdbg_clean(zend_bool full TSRMLS_DC);
|
||||
void phpdbg_sigint_handler(int signo);
|
||||
void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
|
||||
|
||||
#if PHP_VERSION_ID >= 50500
|
||||
void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
|
||||
#else
|
||||
void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
|
||||
#endif
|
||||
|
||||
/* {{{ */
|
||||
/* {{{ phpdbg command handlers */
|
||||
PHPDBG_COMMAND(exec);
|
||||
PHPDBG_COMMAND(compile);
|
||||
PHPDBG_COMMAND(step);
|
||||
|
@ -60,4 +52,14 @@ PHPDBG_COMMAND(oplog);
|
|||
PHPDBG_COMMAND(register);
|
||||
PHPDBG_COMMAND(quit); /* }}} */
|
||||
|
||||
/* {{{ prompt commands */
|
||||
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
|
||||
|
||||
/* {{{ */
|
||||
#if PHP_VERSION_ID >= 50500
|
||||
void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
|
||||
#else
|
||||
void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
|
||||
#endif /* }}} */
|
||||
|
||||
#endif /* PHPDBG_PROMPT_H */
|
||||
|
|
|
@ -44,11 +44,11 @@ enum {
|
|||
|
||||
int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...);
|
||||
|
||||
#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, stdout, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, stdout, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, stdout, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, stdout, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_log(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, stdout, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_log(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||
|
||||
#define phpdbg_error_ex(out, fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, fmt, ##__VA_ARGS__)
|
||||
#define phpdbg_notice_ex(out, fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, fmt, ##__VA_ARGS__)
|
||||
|
@ -57,7 +57,7 @@ int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...);
|
|||
#define phpdbg_log_ex(out, fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, out, fmt, ##__VA_ARGS__)
|
||||
|
||||
#if PHPDBG_DEBUG
|
||||
# define phpdbg_debug(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, stderr, fmt, ##__VA_ARGS__)
|
||||
# define phpdbg_debug(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDERR], fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
# define phpdbg_debug(fmt, ...)
|
||||
#endif
|
||||
|
|
Binary file not shown.
82
tutorials/initializing.md
Normal file
82
tutorials/initializing.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
phpdbginit
|
||||
==========
|
||||
*Setting up your debugging session automatically*
|
||||
|
||||
By default, phpdbg looks for *.phpdbginit* in the current working directory, this location can be overrideen on the command line:
|
||||
|
||||
```
|
||||
phpdbg -imy.phpdbginit
|
||||
```
|
||||
|
||||
An init file should contain one command per line, any phpdbg command is supported during init.
|
||||
|
||||
In addition, *.phpdbginit* can contain embedded code, allowing, for example
|
||||
|
||||
- the setup of auto completion
|
||||
- the registration of functions
|
||||
- the acquisition and pre-compilation of code
|
||||
- bootstrapping a web application
|
||||
|
||||
It is common for C projects (PHP included) to include an init file for the GDB debugger; this would be a clever practice to adopt
|
||||
for those distributing PHP library code.
|
||||
|
||||
The default .phpdbginit
|
||||
=======================
|
||||
|
||||
We distribute the following init file by default, it should be copied to any directory you commonly execute in:
|
||||
|
||||
```
|
||||
##########################################################
|
||||
# .phpdbginit
|
||||
#
|
||||
# Lines starting with # are ignored
|
||||
# Code must start and end with <: and :> respectively
|
||||
##########################################################
|
||||
# Place initialization commands one per line
|
||||
##########################################################
|
||||
# exec sapi/phpdbg/test.php
|
||||
|
||||
##########################################################
|
||||
# Embedding code in .phpdbginit
|
||||
##########################################################
|
||||
<:
|
||||
/*
|
||||
If readline is loaded, you might want to setup completion:
|
||||
*/
|
||||
if (function_exists('readline_completion_function')) {
|
||||
readline_completion_function(function(){
|
||||
return array_merge(
|
||||
get_defined_functions()['user'],
|
||||
array_keys(get_defined_constants())
|
||||
);
|
||||
});
|
||||
}
|
||||
:>
|
||||
##########################################################
|
||||
# Now carry on initializing phpdbg ...
|
||||
##########################################################
|
||||
# R my_debugging_function
|
||||
|
||||
##########################################################
|
||||
# PHP has many functions that might be useful
|
||||
# ... you choose ...
|
||||
##########################################################
|
||||
# R touch
|
||||
# R unlink
|
||||
# R scandir
|
||||
# R glob
|
||||
|
||||
##########################################################
|
||||
# Remember: *you have access to the shell*
|
||||
##########################################################
|
||||
# The output of registered function calls is not,
|
||||
# by default, very pretty (unless you implement
|
||||
# and register a new implementation for phpdbg)
|
||||
# The output of shell commands will usually be more
|
||||
# readable on the console
|
||||
##########################################################
|
||||
# TLDR; if you have a good shell, use it ...
|
||||
##########################################################
|
||||
```
|
||||
|
||||
|
132
tutorials/simples.md
Normal file
132
tutorials/simples.md
Normal file
|
@ -0,0 +1,132 @@
|
|||
Simples
|
||||
=======
|
||||
*Everything is simple ...*
|
||||
|
||||
It is easy to imagine that debugging a whole web application might be difficult, in a console program, in fact this is not the case:
|
||||
|
||||
A web application is just an instance of the PHP interpreter with some very specific super globals set.
|
||||
|
||||
With everything in the correct place, your web application has no clue that it being served not to a client, but to you, probably in your underwear.
|
||||
|
||||
With the facts established, you can use phpdbg to debug anything that is PHP, with a bit of cleverness in the right places.
|
||||
|
||||
Very Simple
|
||||
===========
|
||||
*A good starting place ...*
|
||||
|
||||
```
|
||||
phpdbg -e/path/to/my/script.php
|
||||
```
|
||||
|
||||
The command above will result in a phpdbg console that has executed *.phpdbginit* (if it exists) and is ready to compile, inspect, step through
|
||||
and execute the code contained in */path/to/my/script.php*
|
||||
|
||||
The first thing to do upon being presented with the console, is decide what to break for ...
|
||||
|
||||
Many options exist for choosing (and setting) breakpoints:
|
||||
|
||||
```
|
||||
phpdbg> b phpdbg::isGreat
|
||||
phpdbg> b phpdbg_is_great
|
||||
```
|
||||
|
||||
The commands above will be the most common for most developers; that is, specifying the entry point of a function or method on which to break execution.
|
||||
|
||||
Introspection of code is supported on the console, for anything that is compiled, if help is required remembering the name of a particular method then issue:
|
||||
|
||||
```
|
||||
phpdbg> compile
|
||||
phpdbg> print class myClassName
|
||||
```
|
||||
|
||||
Introspection doesn't only provide you with names, but the addresses of every opcode in every statement, which you may use to specify with as much precision as
|
||||
is possible where to break execution.
|
||||
|
||||
**There is a print command for particular methods, useful if a class is very large and your screen is not !**
|
||||
|
||||
At this point, break points are set; the next thing to do is issue the run command:
|
||||
|
||||
```
|
||||
phpdbg> run
|
||||
```
|
||||
|
||||
This will cause execution of the context, if a breakpoint is reached, the interactive console returns such that more commands can be issued.
|
||||
|
||||
When a breakpoint is reached, several options for continuation exist:
|
||||
|
||||
- step through every instruction
|
||||
- run past the end of the stack
|
||||
- run to the end of the stack
|
||||
|
||||
*Q: WTF is a stack?*
|
||||
|
||||
*A: Generally the current codepath, more precisely: current function, method or file*
|
||||
|
||||
Just quickly; an explanation of what "to" and "past" the end of a "stack" means:
|
||||
|
||||
Assuming there is a break set for *my_function_somewhere* in the following code:
|
||||
|
||||
```
|
||||
function my_function_somewhere($input = array()) /* break here on entry to the function */
|
||||
{
|
||||
/* ... the stack ... */
|
||||
|
||||
if (count($input))
|
||||
{
|
||||
/* to -> */ return $input[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* to -> */ return rand(1, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/* past -> */ $result = my_function_somewhere();
|
||||
if ($result)
|
||||
{
|
||||
/* and so on */
|
||||
var_dump($result);
|
||||
}
|
||||
```
|
||||
|
||||
The commands *finish* and *leave*, run *past* and *to* the end of the stack respecitively.
|
||||
|
||||
**Note: the finish command does not automatically break; to break past the end of the stack enable stepping before issuing finish, the leave command breaks implicitly**
|
||||
|
||||
On with execution ...
|
||||
=====================
|
||||
*Stepping, slowly, is sometimes the way forward ...*
|
||||
|
||||
Should you want to inspect what happens during the execution of the stack, enable stepping:
|
||||
|
||||
```
|
||||
phpdbg> step 1
|
||||
```
|
||||
|
||||
In case it wasn't obvious, the next thing to do is nearly always *next*:
|
||||
|
||||
```
|
||||
phpdbg> next
|
||||
```
|
||||
|
||||
Which will cause the vm to assume control and continue execution until the next break point, or completion.
|
||||
|
||||
Stepping through every single instruction is not usually beneficial, issue:
|
||||
|
||||
```
|
||||
phpdbg> step 0
|
||||
```
|
||||
|
||||
To disable stepping again, and only interrupt execution for breakpoints.
|
||||
|
||||
As hard as it gets ...
|
||||
======================
|
||||
*Web Applications*
|
||||
|
||||
As mentioned, a web application is only a script executing with some specific super globals set;
|
||||
|
||||
**The mocking of any web request just requires that you set the super globals of the script accordingly**
|
||||
|
||||
We refer to this as "bootstrapping", mostly because I have always wanted a genuine reason to use that word.
|
||||
|
||||
See the example, for help with bootstrapping ...
|
Loading…
Add table
Add a link
Reference in a new issue