diff --git a/.phpdbginit b/.phpdbginit index 10c831b0329..9e02c6d8a54 100644 --- a/.phpdbginit +++ b/.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 ... diff --git a/Makefile.frag b/Makefile.frag index 5482a9e7fb6..8789bc782c7 100644 --- a/Makefile.frag +++ b/Makefile.frag @@ -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 ..." diff --git a/phpdbg.c b/phpdbg.c index 4b9d39b6388..235660f7f15 100644 --- a/phpdbg.c +++ b/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); diff --git a/phpdbg.h b/phpdbg.h index 98c007036e9..3e25965e912 100644 --- a/phpdbg.h +++ b/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 */ diff --git a/phpdbg_cmd.c b/phpdbg_cmd.c index d7d8c0d4fcd..28f085e8dba 100644 --- a/phpdbg_cmd.c +++ b/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; diff --git a/phpdbg_info.c b/phpdbg_info.c index 09c91d5af68..4b98267ab0f 100644 --- a/phpdbg_info.c +++ b/phpdbg_info.c @@ -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( diff --git a/phpdbg_info.h b/phpdbg_info.h index 5e88b731cb0..727a3d6f65d 100644 --- a/phpdbg_info.h +++ b/phpdbg_info.h @@ -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 }; diff --git a/phpdbg_opcode.c b/phpdbg_opcode.c index fe9470f5d87..74b0c7e0e3f 100644 --- a/phpdbg_opcode.c +++ b/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, ""); + 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"; } } /* }}} */ diff --git a/phpdbg_opcode.h b/phpdbg_opcode.h index 28fc806c1c0..dfa443a0644 100644 --- a/phpdbg_opcode.h +++ b/phpdbg_opcode.h @@ -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 */ diff --git a/phpdbg_prompt.c b/phpdbg_prompt.c index 3a7ebacdb8a..27f0b739914 100644 --- a/phpdbg_prompt.c +++ b/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, ""); - break; - - case IS_UNUSED: - asprintf(&decode, ""); - 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: diff --git a/phpdbg_prompt.h b/phpdbg_prompt.h index 0e52414d7c9..7392b968132 100644 --- a/phpdbg_prompt.h +++ b/phpdbg_prompt.h @@ -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 */ diff --git a/phpdbg_utils.h b/phpdbg_utils.h index 0a6ad4f4fa8..1178dcace86 100644 --- a/phpdbg_utils.h +++ b/phpdbg_utils.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 diff --git a/tutorials/Thumbs.db b/tutorials/Thumbs.db deleted file mode 100644 index d64654ac9ea..00000000000 Binary files a/tutorials/Thumbs.db and /dev/null differ diff --git a/tutorials/initializing.md b/tutorials/initializing.md new file mode 100644 index 00000000000..de01bb23fa0 --- /dev/null +++ b/tutorials/initializing.md @@ -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 ... +########################################################## +``` + + diff --git a/tutorials/simples.md b/tutorials/simples.md new file mode 100644 index 00000000000..d8b53d6979d --- /dev/null +++ b/tutorials/simples.md @@ -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 ...