Merge branch 'master' of https://github.com/krakjoe/phpdbg into frames

Conflicts:
	phpdbg.h
	phpdbg_prompt.c
This commit is contained in:
Bob Weinand 2013-11-22 18:32:00 +01:00
commit c46a413697
15 changed files with 511 additions and 145 deletions

View file

@ -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 ...

View file

@ -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 ..."

View file

@ -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);

View file

@ -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 */

View file

@ -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(&param, input TSRMLS_CC);
break;

View file

@ -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(

View file

@ -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
};

View file

@ -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) /* {{{ */
{

View file

@ -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 */

View file

@ -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),
@ -1034,6 +1034,8 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */
{
int ret = SUCCESS;
PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
phpdbg_input_t *input = phpdbg_read_input(NULL TSRMLS_CC);
if (input && input->length > 0L) {
@ -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
@ -1427,7 +1323,7 @@ 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)) {

View file

@ -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 */

View file

@ -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
View 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
View 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 ...