Merge branch 'PHP-5.6'

This commit is contained in:
Bob Weinand 2014-04-21 23:32:57 +02:00
commit 823ddab3f3
39 changed files with 6825 additions and 2046 deletions

View File

@ -1,5 +1,7 @@
.libs/
./phpdbg
phpdbg
*.lo
*.o
build
phpdbg_parser.c
phpdbg_parser.h

View File

@ -8,6 +8,15 @@ $(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_PHPDBG)
$(builddir)/sapi/phpdbg/phpdbg_lexer.lo: $(srcdir)/sapi/phpdbg/phpdbg_parser.h
$(srcdir)/sapi/phpdbg/phpdbg_lexer.c: $(srcdir)/sapi/phpdbg/phpdbg_lexer.l
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date -cbdFo sapi/phpdbg/phpdbg_lexer.c sapi/phpdbg/phpdbg_lexer.l)
$(srcdir)/sapi/phpdbg/phpdbg_parser.h: $(srcdir)/sapi/phpdbg/phpdbg_parser.c
$(srcdir)/sapi/phpdbg/phpdbg_parser.c: $(srcdir)/sapi/phpdbg/phpdbg_parser.y
@$(YACC) -p phpdbg_ -v -d $(srcdir)/sapi/phpdbg/phpdbg_parser.y -o $@
install-phpdbg: $(BUILD_BINARY)
@echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
@ -25,4 +34,3 @@ test-phpdbg:
.PHONY: clean-phpdbg test-phpdbg

View File

@ -3,10 +3,10 @@ dnl $Id$
dnl
PHP_ARG_ENABLE(phpdbg, for phpdbg support,
[ --enable-phpdbg Build phpdbg], yes, yes)
[ --enable-phpdbg Build phpdbg], no, no)
PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
if test "$PHP_PHPDBG" != "no"; then
AC_DEFINE(HAVE_PHPDBG, 1, [ ])
@ -18,17 +18,22 @@ if test "$PHP_PHPDBG" != "no"; then
fi
PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c"
PHP_PHPDBG_FILES="phpdbg.c phpdbg_parser.c phpdbg_lexer.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_btree.c"
if test "$PHP_READLINE" != "no"; then
PHPDBG_EXTRA_LIBS="-lreadline"
fi
PHP_SUBST(PHP_PHPDBG_CFLAGS)
PHP_SUBST(PHP_PHPDBG_FILES)
PHP_SUBST(PHPDBG_EXTRA_LIBS)
PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag])
PHP_SELECT_SAPI(phpdbg, program, $PHP_PHPDBG_FILES, $PHP_PHPDBG_CFLAGS, [$(SAPI_PHPDBG_PATH)])
BUILD_BINARY="sapi/phpdbg/phpdbg"
BUILD_SHARED="sapi/phpdbg/libphpdbg.la"
BUILD_PHPDBG="\$(LIBTOOL) --mode=link \
\$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
\$(PHP_GLOBAL_OBJS) \

View File

@ -1,19 +1,18 @@
ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes');
ARG_ENABLE('phpdbg', 'Build phpdbg', 'no');
ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no');
PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c';
PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_win.c phpdbg_btree.c';
PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll';
PHPDBG_EXE='phpdbg.exe';
if (PHP_PHPDBG == "yes") {
/* build phpdbg binary */
SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa");
}
if (PHP_PHPDBGS == "yes") {
SAPI('phpdbgs', PHPDBG_SOURCES, PHPDBG_DLL, '/D PHP_PHPDBG_EXPORTS /I win32');
ADD_FLAG("LIBS_PHPDBGS", "ws2_32.lib user32.lib");
DEFINE("CFLAGS", configure_subst.item("CFLAGS") + " /EHa");
}

View File

@ -18,7 +18,7 @@
+----------------------------------------------------------------------+
*/
#ifndef ZEND_SIGNALS
#if !defined(ZEND_SIGNALS) || defined(_WIN32)
# include <signal.h>
#endif
#include "phpdbg.h"
@ -28,6 +28,7 @@
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
#include "zend_alloc.h"
/* {{{ remote console headers */
#ifndef _WIN32
@ -61,16 +62,15 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
pg->exec = NULL;
pg->exec_len = 0;
pg->buffer = NULL;
pg->ops = NULL;
pg->vmret = 0;
pg->bp_count = 0;
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;
} /* }}} */
@ -145,6 +145,7 @@ static void php_phpdbg_destroy_registered(void *data) /* {{{ */
function TSRMLS_CC);
} /* }}} */
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
{
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
@ -157,7 +158,9 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
phpdbg_setup_watchpoints(TSRMLS_C);
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
@ -178,7 +181,14 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
zend_hash_destroy(&PHPDBG_G(seek));
zend_hash_destroy(&PHPDBG_G(registered));
zend_hash_destroy(&PHPDBG_G(watchpoints));
zend_llist_destroy(&PHPDBG_G(watchlist_mem));
if (PHPDBG_G(buffer)) {
efree(PHPDBG_G(buffer));
PHPDBG_G(buffer) = NULL;
}
if (PHPDBG_G(exec)) {
efree(PHPDBG_G(exec));
PHPDBG_G(exec) = NULL;
@ -209,24 +219,24 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
return SUCCESS;
} /* }}} */
/* {{{ proto mixed phpdbg_exec(string context)
/* {{{ proto mixed phpdbg_exec(string context)
Attempt to set the execution context for phpdbg
If the execution context was set previously it is returned
If the execution context was not set previously boolean true is returned
If the execution context was not set previously boolean true is returned
If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
static PHP_FUNCTION(phpdbg_exec)
static PHP_FUNCTION(phpdbg_exec)
{
char *exec = NULL;
zend_ulong exec_len = 0L;
int exec_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) {
return;
}
{
struct stat sb;
zend_bool result = 1;
if (VCWD_STAT(exec, &sb) != FAILURE) {
if (sb.st_mode & (S_IFREG|S_IFLNK)) {
if (PHPDBG_G(exec)) {
@ -234,11 +244,11 @@ static PHP_FUNCTION(phpdbg_exec)
efree(PHPDBG_G(exec));
result = 0;
}
PHPDBG_G(exec) = estrndup(exec, exec_len);
PHPDBG_G(exec_len) = exec_len;
if (result)
if (result)
ZVAL_BOOL(return_value, 1);
} else {
zend_error(
@ -259,9 +269,9 @@ static PHP_FUNCTION(phpdbg_exec)
static PHP_FUNCTION(phpdbg_break)
{
if (ZEND_NUM_ARGS() > 0) {
long type;
long type = 0;
char *expr = NULL;
zend_uint expr_len = 0;
int expr_len = 0;
phpdbg_param_t param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) {
@ -269,28 +279,7 @@ static PHP_FUNCTION(phpdbg_break)
}
phpdbg_parse_param(expr, expr_len, &param TSRMLS_CC);
switch (type) {
case METHOD_PARAM:
phpdbg_do_break_method(&param, NULL TSRMLS_CC);
break;
case FILE_PARAM:
phpdbg_do_break_file(&param, NULL TSRMLS_CC);
break;
case NUMERIC_PARAM:
phpdbg_do_break_lineno(&param, NULL TSRMLS_CC);
break;
case STR_PARAM:
phpdbg_do_break_func(&param, NULL TSRMLS_CC);
break;
default: zend_error(
E_WARNING, "unrecognized parameter type %ld", type);
}
phpdbg_do_break(&param TSRMLS_CC);
phpdbg_clear_param(&param TSRMLS_CC);
} else if (EG(current_execute_data) && EG(active_op_array)) {
@ -319,9 +308,9 @@ static PHP_FUNCTION(phpdbg_clear)
/* {{{ proto void phpdbg_color(integer element, string color) */
static PHP_FUNCTION(phpdbg_color)
{
long element;
char *color;
zend_uint color_len;
long element = 0L;
char *color = NULL;
int color_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
return;
@ -341,8 +330,8 @@ static PHP_FUNCTION(phpdbg_color)
/* {{{ proto void phpdbg_prompt(string prompt) */
static PHP_FUNCTION(phpdbg_prompt)
{
char *prompt;
zend_uint prompt_len;
char *prompt = NULL;
int prompt_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
return;
@ -403,9 +392,9 @@ static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /*
if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
return FAILURE;
}
phpdbg_booted=1;
return SUCCESS;
} /* }}} */
@ -585,7 +574,6 @@ const opt_struct OPTIONS[] = { /* {{{ */
{'z', 1, "load zend_extension"},
/* phpdbg options */
{'q', 0, "no banner"},
{'e', 1, "exec"},
{'v', 0, "disable quietness"},
{'s', 0, "enable stepping"},
{'b', 0, "boring colours"},
@ -615,10 +603,10 @@ const char phpdbg_ini_hardcoded[] =
/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
#define INI_DEFAULT(name, value) \
Z_SET_REFCOUNT(tmp, 0); \
Z_UNSET_ISREF(tmp); \
ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
Z_SET_REFCOUNT(tmp, 0); \
Z_UNSET_ISREF(tmp); \
ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
{
@ -666,11 +654,11 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */
int phpdbg_open_socket(const char *interface, short port) /* {{{ */
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
switch (fd) {
case -1:
return -1;
default: {
int reuse = 1;
@ -678,27 +666,27 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
case -1:
close(fd);
return -2;
default: {
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_port = htons(port);
address.sin_family = AF_INET;
if ((*interface == '*')) {
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_addr.s_addr = htonl(INADDR_ANY);
} else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
close(fd);
return -3;
}
switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
case -1:
close(fd);
return -4;
default: {
listen(fd, 5);
}
@ -707,28 +695,28 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
}
}
}
return fd;
} /* }}} */
static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
{
{
if ((*socket)[0] >= 0) {
shutdown(
(*socket)[0], SHUT_RDWR);
close((*socket)[0]);
}
if (streams[0]) {
fclose(streams[0]);
}
if ((*socket)[1] >= 0) {
shutdown(
(*socket)[1], SHUT_RDWR);
close((*socket)[1]);
}
if (streams[1]) {
fclose(streams[1]);
}
@ -798,14 +786,34 @@ int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*sock
dup2((*socket)[0], fileno(stdin));
dup2((*socket)[1], fileno(stdout));
setbuf(stdout, NULL);
streams[0] = fdopen((*socket)[0], "r");
streams[1] = fdopen((*socket)[1], "w");
return SUCCESS;
} /* }}} */
void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) {
int is_handled = FAILURE;
TSRMLS_FETCH();
switch (sig) {
case SIGBUS:
case SIGSEGV:
is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC);
if (is_handled == FAILURE) {
#ifdef ZEND_SIGNALS
zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC);
#else
sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
#endif
}
break;
}
}
#endif
int main(int argc, char **argv) /* {{{ */
@ -852,6 +860,10 @@ int main(int argc, char **argv) /* {{{ */
#endif
#ifndef _WIN32
struct sigaction signal_struct;
signal_struct.sa_sigaction = phpdbg_signal_handler;
signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
address = strdup("127.0.0.1");
socket[0] = -1;
socket[1] = -1;
@ -888,17 +900,17 @@ phpdbg_main:
bp_tmp_file = NULL;
}
}
if (!bp_tmp_file) {
phpdbg_error("Unable to create temporary file");
return 1;
}
#else
if (!mkstemp(bp_tmp_file)) {
memset(bp_tmp_file, 0, sizeof(bp_tmp_file));
}
#endif
if (!bp_tmp_file) {
phpdbg_error(
"Unable to create temporary file");
return 1;
}
}
ini_entries = NULL;
ini_entries_len = 0;
@ -920,8 +932,7 @@ phpdbg_main:
run = 0;
step = 0;
sapi_name = NULL;
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
switch (opt) {
case 'r':
@ -965,7 +976,7 @@ phpdbg_main:
ini_entries_len += len + sizeof("=1\n\0") - 2;
}
} break;
case 'z':
zend_extensions_len++;
if (zend_extensions) {
@ -976,16 +987,6 @@ phpdbg_main:
/* begin phpdbg options */
case 'e': { /* set execution context */
exec_len = strlen(php_optarg);
if (exec_len) {
if (exec) {
free(exec);
}
exec = strdup(php_optarg);
}
} break;
case 'S': { /* set SAPI name */
if (sapi_name) {
free(sapi_name);
@ -1001,7 +1002,7 @@ phpdbg_main:
if (init_file) {
free(init_file);
}
init_file_len = strlen(php_optarg);
if (init_file_len) {
init_file = strdup(php_optarg);
@ -1038,7 +1039,7 @@ phpdbg_main:
#ifndef _WIN32
/* if you pass a listen port, we will accept input on listen port */
/* and write output to listen port * 2 */
case 'l': { /* set listen ports */
if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
@ -1050,7 +1051,7 @@ phpdbg_main:
}
}
} break;
case 'a': { /* set bind address */
free(address);
if (!php_optarg) {
@ -1076,6 +1077,19 @@ phpdbg_main:
} break;
}
}
/* set exec if present on command line */
if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS))
{
exec_len = strlen(argv[php_optind]);
if (exec_len) {
if (exec) {
free(exec);
}
exec = strdup(argv[php_optind]);
}
php_optind++;
}
#ifndef _WIN32
/* setup remote server if necessary */
@ -1093,7 +1107,7 @@ phpdbg_main:
if (sapi_name) {
phpdbg->name = sapi_name;
}
phpdbg->ini_defaults = phpdbg_ini_defaults;
phpdbg->phpinfo_as_text = 1;
phpdbg->php_ini_ignore_cwd = 1;
@ -1114,14 +1128,14 @@ phpdbg_main:
memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
}
ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
if (zend_extensions_len) {
zend_ulong zend_extension = 0L;
while (zend_extension < zend_extensions_len) {
const char *ze = zend_extensions[zend_extension];
size_t ze_len = strlen(ze);
ini_entries = realloc(
ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
@ -1133,40 +1147,81 @@ phpdbg_main:
free(zend_extensions[zend_extension]);
zend_extension++;
}
free(zend_extensions);
}
phpdbg->ini_entries = ini_entries;
if (phpdbg->startup(phpdbg) == SUCCESS) {
php_request_startup(TSRMLS_C);
#ifdef _WIN32
EXCEPTION_POINTERS *xp;
__try {
#endif
zend_mm_heap *mm_heap = zend_mm_set_heap(NULL TSRMLS_CC);
#if ZEND_DEBUG
if (!mm_heap->use_zend_alloc) {
mm_heap->_malloc = malloc;
mm_heap->_realloc = realloc;
mm_heap->_free = free;
#endif
PHPDBG_G(original_free_function) = mm_heap->_free;
mm_heap->_free = phpdbg_watch_efree;
mm_heap->use_zend_alloc = 0;
#if ZEND_DEBUG
}
#endif
zend_mm_set_heap(mm_heap TSRMLS_CC);
zend_activate(TSRMLS_C);
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
zend_try {
zend_signal_activate(TSRMLS_C);
} zend_end_try();
#endif
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
#elif !defined(_WIN32)
sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
#endif
if (php_request_startup(TSRMLS_C) == SUCCESS) {
int i;
SG(request_info).argc = argc - php_optind + 1;
SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
for (i = SG(request_info).argc; --i;) {
SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
}
SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup("");
php_hash_environment(TSRMLS_C);
}
/* make sure to turn off buffer for ev command */
php_output_activate(TSRMLS_C);
php_output_deactivate(TSRMLS_C);
/* do not install sigint handlers for remote consoles */
/* sending SIGINT then provides a decent way of shutting down the server */
#ifdef ZEND_SIGNALS
# ifndef _WIN32
#if defined(ZEND_SIGNALS) && !defined(_WIN32)
if (listen[0] < 0) {
# endif
zend_try {
zend_signal_activate(TSRMLS_C);
zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
} zend_end_try();
# ifndef _WIN32
zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try();
}
# endif
#else
# ifndef _WIN32
#elif !defined(_WIN32)
if (listen[0] < 0) {
# endif
#endif
signal(SIGINT, phpdbg_sigint_handler);
#ifndef _WIN32
}
#endif
#endif
PG(modules_activated) = 0;
/* set flags from command line */
PHPDBG_G(flags) = flags;
@ -1182,10 +1237,9 @@ phpdbg_main:
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);
PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC);
PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
free(exec);
@ -1213,6 +1267,11 @@ phpdbg_main:
phpdbg_welcome((cleaning > 0) TSRMLS_CC);
}
/* auto compile */
if (PHPDBG_G(exec)) {
phpdbg_compile(TSRMLS_C);
}
/* initialize from file */
PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
zend_try {
@ -1233,14 +1292,17 @@ phpdbg_main:
if (run) {
/* no need to try{}, run does it ... */
PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC);
PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC);
if (run > 1) {
/* if -r is on the command line more than once just quit */
goto phpdbg_out;
}
}
/* #ifndef for making compiler shutting up */
#ifndef _WIN32
phpdbg_interact:
#endif
/* phpdbg main() */
do {
zend_try {
@ -1282,21 +1344,42 @@ phpdbg_interact:
} zend_end_try();
} while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
phpdbg_out:
/* this must be forced */
CG(unclean_shutdown) = 0;
/* this is just helpful */
PG(report_memleaks) = 0;
#ifndef _WIN32
phpdbg_out:
if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
goto phpdbg_interact;
}
#endif
#ifdef _WIN32
} __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
phpdbg_error("Access violation (Segementation fault) encountered\ntrying to abort cleanly...");
}
phpdbg_out:
#endif
{
int i;
/* free argv */
for (i = SG(request_info).argc; --i;) {
efree(SG(request_info).argv[i]);
}
efree(SG(request_info).argv);
}
#ifndef ZTS
/* force cleanup of auto and core globals */
zend_hash_clean(CG(auto_globals));
memset(
&core_globals, 0, sizeof(php_core_globals));
#endif
if (ini_entries) {
free(ini_entries);
}
@ -1318,6 +1401,7 @@ phpdbg_out:
} zend_end_try();
sapi_shutdown();
}
if (cleaning || remote) {
@ -1331,7 +1415,7 @@ phpdbg_out:
#ifndef _WIN32
if (address) {
free(address);
free(address);
}
#endif

View File

@ -39,6 +39,9 @@
#include "zend_globals.h"
#include "zend_ini_scanner.h"
#include "zend_stream.h"
#ifndef _WIN32
# include "zend_signal.h"
#endif
#include "SAPI.h"
#include <fcntl.h>
#include <sys/types.h>
@ -68,6 +71,8 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
#ifdef ZTS
# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
@ -116,23 +121,26 @@
#define PHPDBG_IN_EVAL (1<<11)
#define PHPDBG_IS_STEPPING (1<<12)
#define PHPDBG_IS_QUIET (1<<13)
#define PHPDBG_IS_QUITTING (1<<14)
#define PHPDBG_IS_COLOURED (1<<15)
#define PHPDBG_IS_CLEANING (1<<16)
#define PHPDBG_STEP_OPCODE (1<<13)
#define PHPDBG_IS_QUIET (1<<14)
#define PHPDBG_IS_QUITTING (1<<15)
#define PHPDBG_IS_COLOURED (1<<16)
#define PHPDBG_IS_CLEANING (1<<17)
#define PHPDBG_IN_UNTIL (1<<17)
#define PHPDBG_IN_FINISH (1<<18)
#define PHPDBG_IN_LEAVE (1<<19)
#define PHPDBG_IN_UNTIL (1<<18)
#define PHPDBG_IN_FINISH (1<<19)
#define PHPDBG_IN_LEAVE (1<<20)
#define PHPDBG_IS_REGISTERED (1<<20)
#define PHPDBG_IS_STEPONEVAL (1<<21)
#define PHPDBG_IS_INITIALIZING (1<<22)
#define PHPDBG_IS_SIGNALED (1<<23)
#define PHPDBG_IS_INTERACTIVE (1<<24)
#define PHPDBG_IS_BP_ENABLED (1<<25)
#define PHPDBG_IS_REMOTE (1<<26)
#define PHPDBG_IS_DISCONNECTED (1<<27)
#define PHPDBG_IS_REGISTERED (1<<21)
#define PHPDBG_IS_STEPONEVAL (1<<22)
#define PHPDBG_IS_INITIALIZING (1<<23)
#define PHPDBG_IS_SIGNALED (1<<24)
#define PHPDBG_IS_INTERACTIVE (1<<25)
#define PHPDBG_IS_BP_ENABLED (1<<26)
#define PHPDBG_IS_REMOTE (1<<27)
#define PHPDBG_IS_DISCONNECTED (1<<28)
#define PHPDBG_SHOW_REFCOUNTS (1<<29)
#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE)
#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
@ -149,7 +157,7 @@
#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
#define PHPDBG_URL "http://phpdbg.com"
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
#define PHPDBG_VERSION "0.3.2"
#define PHPDBG_VERSION "0.4.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
/* }}} */
@ -159,12 +167,25 @@
#define PHPDBG_STDERR 2
#define PHPDBG_IO_FDS 3 /* }}} */
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
HashTable registered; /* registered */
HashTable seek; /* seek oplines */
phpdbg_frame_t frame; /* frame */
zend_uint last_line; /* last executed line */
#ifndef _WIN32
struct sigaction old_sigsegv_signal; /* segv signal handler */
#endif
phpdbg_btree watchpoint_tree; /* tree with watchpoints */
phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */
HashTable watchpoints; /* watchpoints */
zend_llist watchlist_mem; /* triggered watchpoints */
zend_bool watchpoint_hit; /* a watchpoint was hit */
void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */
char *exec; /* file to execute */
size_t exec_len; /* size of exec */
@ -178,11 +199,24 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
char *prompt[2]; /* prompt */
const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
phpdbg_command_t *lcmd; /* last command */
phpdbg_param_t lparam; /* last param */
char *buffer; /* buffer */
zend_ulong flags; /* phpdbg flags */
ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
/* the beginning (= the important part) of the _zend_mm_heap struct defined in Zend/zend_alloc.c
Needed for realizing watchpoints */
struct _zend_mm_heap {
int use_zend_alloc;
void *(*_malloc)(size_t);
void (*_free)(void *);
void *(*_realloc)(void *, size_t);
size_t free_bitmap;
size_t large_free_bitmap;
size_t block_size;
size_t compact_size;
zend_mm_segment *segments_list;
zend_mm_storage *storage;
};
#endif /* PHPDBG_H */

View File

@ -135,27 +135,27 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
switch (brake->type) {
case PHPDBG_BREAK_FILE: {
fprintf(handle,
"break file %s:%lu\n",
"break %s:%lu\n",
((phpdbg_breakfile_t*)brake)->filename,
((phpdbg_breakfile_t*)brake)->line);
} break;
case PHPDBG_BREAK_SYM: {
fprintf(handle,
"break func %s\n",
"break %s\n",
((phpdbg_breaksymbol_t*)brake)->symbol);
} break;
case PHPDBG_BREAK_METHOD: {
fprintf(handle,
"break method %s::%s\n",
"break %s::%s\n",
((phpdbg_breakmethod_t*)brake)->class_name,
((phpdbg_breakmethod_t*)brake)->func_name);
} break;
case PHPDBG_BREAK_METHOD_OPLINE: {
fprintf(handle,
"break address %s::%s#%ld\n",
"break %s::%s#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
@ -163,21 +163,21 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
case PHPDBG_BREAK_FUNCTION_OPLINE: {
fprintf(handle,
"break address %s#%ld\n",
"break %s#%ld\n",
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_FILE_OPLINE: {
fprintf(handle,
"break address %s:%ld\n",
"break %s:#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_OPCODE: {
fprintf(handle,
"break op %s\n",
"break %s\n",
((phpdbg_breakop_t*)brake)->name);
} break;
@ -209,7 +209,7 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
}
} else {
fprintf(
handle, "break on %s\n", conditional->code);
handle, "break if %s\n", conditional->code);
}
} break;
}
@ -221,14 +221,20 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
{
struct stat sb;
if (VCWD_STAT(path, &sb) != FAILURE) {
if (sb.st_mode & (S_IFREG|S_IFLNK)) {
php_stream_statbuf ssb;
char realpath[MAXPATHLEN];
if (php_stream_stat_path(path, &ssb) != FAILURE) {
if (ssb.sb.st_mode & (S_IFREG|S_IFLNK)) {
HashTable *broken;
phpdbg_breakfile_t new_break;
size_t path_len = strlen(path);
size_t path_len = 0L;
if (VCWD_REALPATH(path, realpath)) {
path = realpath;
}
path_len = strlen(path);
if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
path, path_len, (void**)&broken) == FAILURE) {
HashTable breaks;
@ -324,9 +330,9 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char
PHPDBG_BREAK_MAPPING(new_break.id, class_table);
} else {
phpdbg_notice("Breakpoint exists at %s::%s", class_name, func_name);
}
}
efree(lcname);
efree(lcname);
} /* }}} */
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */
@ -355,7 +361,7 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{
PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) /* {{{ */
{
phpdbg_breakline_t opline_break;
if (op_array->last < brake->opline_num) {
if (op_array->last <= brake->opline_num) {
if (brake->class_name == NULL) {
phpdbg_error("There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num);
} else if (brake->func_name == NULL) {
@ -760,56 +766,26 @@ PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_l
}
} /* }}} */
PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */
PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
if (input->argc > 3 && phpdbg_argv_is(2, "if")) {
phpdbg_breakcond_t new_break;
phpdbg_param_t new_param;
zend_ulong expr_hash = 0L;
size_t expr_len;
const char *join = strstr(input->string, "if");
const char *expr = (join) + sizeof("if");
expr_len = strlen(expr);
expr = phpdbg_trim(expr, expr_len, &expr_len);
expr_hash = zend_inline_hash_func(expr, expr_len);
{
/* get a clean parameter from input string */
size_t sparam_len = 0L;
char *sparam = input->string;
sparam[
strstr(input->string, " ") - input->string] = 0;
sparam_len = strlen(sparam);
switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) {
case EMPTY_PARAM:
case NUMERIC_PARAM:
phpdbg_clear_param(
&new_param TSRMLS_CC);
goto usage;
default: { /* do nothing */ } break;
}
expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC);
}
if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
phpdbg_breakcond_t new_break;
phpdbg_param_t *condition;
zend_ulong hash = 0L;
if (param->next) {
condition = param->next;
hash = zend_inline_hash_func(condition->str, condition->len);
if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
phpdbg_create_conditional_break(
&new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC);
&new_break, param,
condition->str, condition->len, hash TSRMLS_CC);
} else {
phpdbg_notice(
"Conditional break %s exists at the specified location", expr);
}
phpdbg_clear_param(&new_param TSRMLS_CC);
} else {
usage:
phpdbg_error("usage: break at <func|method|file:line|address> if <expression>");
"Conditional break %s exists at the specified location", condition->str);
}
}
} /* }}} */
static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
@ -992,7 +968,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execut
for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS;
zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
zval *retval = NULL;
int orig_interactive = CG(interactive);
zval **orig_retval = EG(return_value_ptr_ptr);

View File

@ -119,7 +119,7 @@ PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const cha
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Detection API */
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */

View File

@ -24,132 +24,32 @@
#include "phpdbg_opcode.h"
#include "phpdbg_break.h"
#include "phpdbg_bp.h"
#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_BREAK(file) /* {{{ */
{
switch (param->type) {
case FILE_PARAM:
phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
break;
#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10])
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM:
phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(address) /* {{{ */
{
switch (param->type) {
case ADDR_PARAM:
phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
break;
case NUMERIC_METHOD_PARAM:
phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
break;
case NUMERIC_FUNCTION_PARAM:
phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
break;
case FILE_PARAM:
phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(on) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
/**
* Commands
*/
const phpdbg_command_t phpdbg_break_commands[] = {
PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c"),
PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n"),
PHPDBG_END_COMMAND
};
PHPDBG_BREAK(at) /* {{{ */
{
phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(lineno) /* {{{ */
{
switch (param->type) {
case NUMERIC_PARAM: {
if (PHPDBG_G(exec)) {
phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
} else {
phpdbg_error("Execution context not set!");
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(op) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
phpdbg_set_breakpoint_at(param TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(del) /* {{{ */
{
switch (param->type) {
case NUMERIC_PARAM: {
phpdbg_delete_breakpoint(param->num TSRMLS_CC);
} break;
phpdbg_default_switch_case();
}
phpdbg_delete_breakpoint(param->num TSRMLS_CC);
return SUCCESS;
} /* }}} */

View File

@ -29,30 +29,9 @@
/**
* Printer Forward Declarations
*/
PHPDBG_BREAK(file);
PHPDBG_BREAK(func);
PHPDBG_BREAK(method);
PHPDBG_BREAK(address);
PHPDBG_BREAK(at);
PHPDBG_BREAK(op);
PHPDBG_BREAK(on);
PHPDBG_BREAK(lineno);
PHPDBG_BREAK(del);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_break_commands[] = {
PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1),
PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1),
PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1),
PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1),
PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1),
PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1),
PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_break_commands[];
#endif /* PHPDBG_BREAK_H */

221
sapi/phpdbg/phpdbg_btree.c Normal file
View File

@ -0,0 +1,221 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg_btree.h"
#include "phpdbg.h"
#define CHOOSE_BRANCH(n) \
branch = branch->branches[!!(n)];
#ifdef _Win32
# define emalloc malloc
# define efree free
#endif
/* depth in bits */
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
tree->depth = depth;
tree->branch = NULL;
tree->count = 0;
}
phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx) {
phpdbg_btree_branch *branch = tree->branch;
int i = tree->depth - 1;
if (branch == NULL) {
return NULL;
}
do {
if ((idx >> i) % 2 == 1) {
if (branch->branches[1]) {
CHOOSE_BRANCH(1);
} else {
return NULL;
}
} else {
if (branch->branches[0]) {
CHOOSE_BRANCH(0);
} else {
return NULL;
}
}
} while (i--);
return &branch->result;
}
phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx) {
phpdbg_btree_branch *branch = tree->branch;
int i = tree->depth - 1, last_superior_i = -1;
zend_bool had_alternative_branch = 0;
if (branch == NULL) {
return NULL;
}
/* find nearest watchpoint */
do {
/* an impossible branch was found if: */
if (!had_alternative_branch && (idx >> i) % 2 == 0 && !branch->branches[0]) {
/* there's no lower branch than idx */
if (last_superior_i == -1) {
/* failure */
return NULL;
}
/* reset state */
branch = tree->branch;
i = tree->depth - 1;
/* follow branch according to bits in idx until the last lower branch before the impossible branch */
do {
CHOOSE_BRANCH((idx >> i) % 2 == 1 && branch->branches[1]);
} while (--i > last_superior_i);
/* use now the lower branch of which we can be sure that it contains only branches lower than idx */
CHOOSE_BRANCH(0);
/* and choose the highest possible branch in the branch containing only branches lower than idx */
while (i--) {
CHOOSE_BRANCH(branch->branches[1]);
}
break;
}
/* follow branch according to bits in idx until having found an impossible branch */
if (had_alternative_branch || (idx >> i) % 2 == 1) {
if (branch->branches[1]) {
if (branch->branches[0]) {
last_superior_i = i;
}
CHOOSE_BRANCH(1);
} else {
CHOOSE_BRANCH(0);
had_alternative_branch = 1;
}
} else {
CHOOSE_BRANCH(0);
}
} while (i--);
return &branch->result;
}
phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx) {
phpdbg_btree_position pos;
pos.tree = tree;
pos.end = lower_idx;
pos.cur = higher_idx;
return pos;
}
phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos) {
phpdbg_btree_result *result = phpdbg_btree_find_closest(pos->tree, pos->cur);
if (result == NULL || result->idx < pos->end) {
return NULL;
}
pos->cur = result->idx - 1;
return result;
}
int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags) {
int i = tree->depth - 1;
phpdbg_btree_branch **branch = &tree->branch;
do {
if (*branch == NULL) {
break;
}
branch = &(*branch)->branches[(idx >> i) % 2];
} while (i--);
if (*branch == NULL) {
if (!(flags & PHPDBG_BTREE_INSERT)) {
return FAILURE;
}
{
phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
do {
(*branch)->branches[!((idx >> i) % 2)] = NULL;
branch = &(*branch)->branches[(idx >> i) % 2];
*branch = ++memory;
} while (i--);
tree->count++;
}
} else if (!(flags & PHPDBG_BTREE_UPDATE)) {
return FAILURE;
}
(*branch)->result.idx = idx;
(*branch)->result.ptr = ptr;
return SUCCESS;
}
int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
int i = tree->depth;
phpdbg_btree_branch *branch = tree->branch;
int i_last_dual_branch = -1, last_dual_branch_branch;
phpdbg_btree_branch *last_dual_branch = NULL;
goto check_branch_existence;
do {
if (branch->branches[0] && branch->branches[1]) {
last_dual_branch = branch;
i_last_dual_branch = i;
last_dual_branch_branch = (idx >> i) % 2;
}
branch = branch->branches[(idx >> i) % 2];
check_branch_existence:
if (branch == NULL) {
return FAILURE;
}
} while (i--);
tree->count--;
if (i_last_dual_branch == -1) {
efree(tree->branch);
tree->branch = NULL;
} else {
if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
efree(last_dual_branch->branches[!last_dual_branch_branch]);
last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
branch = last_dual_branch->branches[!last_dual_branch_branch];
for (i = i_last_dual_branch; i--;) {
branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
}
} else {
efree(last_dual_branch->branches[last_dual_branch_branch]);
}
last_dual_branch->branches[last_dual_branch_branch] = NULL;
}
return SUCCESS;
}

View File

@ -0,0 +1,65 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_BTREE_H
#define PHPDBG_BTREE_H
#include "zend.h"
typedef struct {
zend_ulong idx;
void *ptr;
} phpdbg_btree_result;
typedef union _phpdbg_btree_branch phpdbg_btree_branch;
union _phpdbg_btree_branch {
phpdbg_btree_branch *branches[2];
phpdbg_btree_result result;
};
typedef struct {
zend_ulong count;
zend_ulong depth;
phpdbg_btree_branch *branch;
} phpdbg_btree;
typedef struct {
phpdbg_btree *tree;
zend_ulong cur;
zend_ulong end;
} phpdbg_btree_position;
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth);
phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx);
phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx);
phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx);
phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos);
int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx);
#define PHPDBG_BTREE_INSERT 1
#define PHPDBG_BTREE_UPDATE 2
#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE)
int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags);
#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT)
#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE)
#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE)
#endif

View File

@ -22,12 +22,32 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) {
size_t pos = 0;
if (command->parent) {
memcpy(&buffer[pos], command->parent->name, command->parent->name_len);
pos += command->parent->name_len;
memcpy(&buffer[pos], " ", sizeof(" ")-1);
pos += (sizeof(" ")-1);
}
memcpy(&buffer[pos], command->name, command->name_len);
pos += command->name_len;
buffer[pos] = 0;
return buffer;
}
PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
switch (param->type) {
case STACK_PARAM:
return "stack";
case EMPTY_PARAM:
return "empty";
case ADDR_PARAM:
@ -208,10 +228,19 @@ PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **point
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
{
switch ((dest->type = src->type)) {
case STACK_PARAM:
/* nope */
break;
case STR_PARAM:
dest->str = estrndup(src->str, src->len);
dest->len = src->len;
break;
case OP_PARAM:
dest->str = estrndup(src->str, src->len);
dest->len = src->len;
break;
case ADDR_PARAM:
dest->addr = src->addr;
@ -226,6 +255,7 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
dest->method.name = estrdup(src->method.name);
break;
case NUMERIC_FILE_PARAM:
case FILE_PARAM:
dest->file.name = estrdup(src->file.name);
dest->file.line = src->file.line;
@ -246,6 +276,10 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
break;
case EMPTY_PARAM: { /* do nothing */ } break;
default: {
/* not yet */
}
}
} /* }}} */
@ -254,6 +288,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
zend_ulong hash = param->type;
switch (param->type) {
case STACK_PARAM:
/* nope */
break;
case STR_PARAM:
hash += zend_inline_hash_func(param->str, param->len);
break;
@ -291,6 +329,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
break;
case EMPTY_PARAM: { /* do nothing */ } break;
default: {
/* not yet */
}
}
return hash;
@ -301,7 +343,11 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
if (l && r) {
if (l->type == r->type) {
switch (l->type) {
case STACK_PARAM:
/* nope, or yep */
return 1;
break;
case NUMERIC_FUNCTION_PARAM:
if (l->num != r->num) {
break;
@ -356,112 +402,400 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
case EMPTY_PARAM:
return 1;
default: {
/* not yet */
}
}
}
}
return 0;
} /* }}} */
PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
{
char *p;
char b[PHPDBG_MAX_CMD];
int l=0;
enum states {
IN_BETWEEN,
IN_WORD,
IN_STRING
} state = IN_BETWEEN;
phpdbg_input_t **argv = NULL;
argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
(*argc) = 0;
#define RESET_STATE() do { \
phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
if (arg) { \
b[l]=0; \
arg->length = l; \
arg->string = estrndup(b, arg->length); \
arg->argv = NULL; \
arg->argc = 0; \
argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
argv[(*argc)++] = arg; \
l = 0; \
} \
state = IN_BETWEEN; \
} while (0)
for (p = buffer; *p != '\0'; p++) {
int c = (unsigned char) *p;
switch (state) {
case IN_BETWEEN:
if (isspace(c)) {
continue;
}
if (c == '"') {
state = IN_STRING;
continue;
}
state = IN_WORD;
b[l++]=c;
continue;
case IN_STRING:
if (c == '"') {
if (buffer[(p - buffer)-1] == '\\') {
b[l-1]=c;
continue;
}
RESET_STATE();
} else {
b[l++]=c;
}
continue;
case IN_WORD:
if (isspace(c)) {
RESET_STATE();
} else {
b[l++]=c;
}
continue;
/* {{{ */
PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) {
if (param && param->type) {
switch (param->type) {
case STR_PARAM:
fprintf(stderr, "%s STR_PARAM(%s=%lu)\n", msg, param->str, param->len);
break;
case ADDR_PARAM:
fprintf(stderr, "%s ADDR_PARAM(%lu)\n", msg, param->addr);
break;
case NUMERIC_FILE_PARAM:
fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#%lu)\n", msg, param->file.name, param->file.line);
break;
case FILE_PARAM:
fprintf(stderr, "%s FILE_PARAM(%s:%lu)\n", msg, param->file.name, param->file.line);
break;
case METHOD_PARAM:
fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
break;
case NUMERIC_METHOD_PARAM:
fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
break;
case NUMERIC_FUNCTION_PARAM:
fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::%ld)\n", msg, param->str, param->num);
break;
case NUMERIC_PARAM:
fprintf(stderr, "%s NUMERIC_PARAM(%ld)\n", msg, param->num);
break;
case COND_PARAM:
fprintf(stderr, "%s COND_PARAM(%s=%lu)\n", msg, param->str, param->len);
break;
case OP_PARAM:
fprintf(stderr, "%s OP_PARAM(%s=%lu)\n", msg, param->str, param->len);
break;
default: {
/* not yet */
}
}
}
switch (state) {
case IN_WORD: {
RESET_STATE();
} break;
case IN_STRING:
phpdbg_error(
"Malformed command line (unclosed quote) @ %ld: %s!",
(p - buffer)-1, &buffer[(p - buffer)-1]);
break;
case IN_BETWEEN:
break;
}
if ((*argc) == 0) {
/* not needed */
efree(argv);
/* to be sure */
return NULL;
}
return argv;
} /* }}} */
PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
/* {{{ */
PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) {
if (stack && stack->next) {
phpdbg_param_t *remove = stack->next;
while (remove) {
phpdbg_param_t *next = NULL;
if (remove->next)
next = remove->next;
switch (remove->type) {
case NUMERIC_METHOD_PARAM:
case METHOD_PARAM:
if (remove->method.class)
free(remove->method.class);
if (remove->method.name)
free(remove->method.name);
break;
case NUMERIC_FUNCTION_PARAM:
case STR_PARAM:
case OP_PARAM:
if (remove->str)
free(remove->str);
break;
case NUMERIC_FILE_PARAM:
case FILE_PARAM:
if (remove->file.name)
free(remove->file.name);
break;
default: {
/* nothing */
}
}
free(remove);
remove = NULL;
if (next)
remove = next;
else break;
}
}
stack->next = NULL;
} /* }}} */
/* {{{ */
PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
if (!next)
return;
*(next) = *(param);
next->next = NULL;
if (stack->top == NULL) {
stack->top = next;
next->top = NULL;
stack->next = next;
} else {
stack->top->next = next;
next->top = stack->top;
stack->top = next;
}
stack->len++;
} /* }}} */
PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC) {
if (command) {
char buffer[128] = {0,};
const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL;
const char *arg = command->args;
size_t least = 0L,
received = 0L,
current = 0L;
zend_bool optional = 0;
/* check for arg spec */
if (!(arg) || !(*arg)) {
if (!top) {
return SUCCESS;
}
asprintf(why,
"The command \"%s\" expected no arguments",
phpdbg_command_name(command, buffer));
return FAILURE;
}
least = 0L;
/* count least amount of arguments */
while (arg && *arg) {
if (arg[0] == '|') {
break;
}
least++;
arg++;
}
arg = command->args;
#define verify_arg(e, a, t) if (!(a)) { \
if (!optional) { \
asprintf(why, \
"The command \"%s\" expected %s and got nothing at parameter %lu", \
phpdbg_command_name(command, buffer), \
(e), \
current); \
return FAILURE;\
} \
} else if ((a)->type != (t)) { \
asprintf(why, \
"The command \"%s\" expected %s and got %s at parameter %lu", \
phpdbg_command_name(command, buffer), \
(e),\
phpdbg_get_param_type((a) TSRMLS_CC), \
current); \
return FAILURE; \
}
while (arg && *arg) {
current++;
switch (*arg) {
case '|': {
current--;
optional = 1;
arg++;
} continue;
case 'i': verify_arg("raw input", top, STR_PARAM); break;
case 's': verify_arg("string", top, STR_PARAM); break;
case 'n': verify_arg("number", top, NUMERIC_PARAM); break;
case 'm': verify_arg("method", top, METHOD_PARAM); break;
case 'a': verify_arg("address", top, ADDR_PARAM); break;
case 'f': verify_arg("file:line", top, FILE_PARAM); break;
case 'c': verify_arg("condition", top, COND_PARAM); break;
case 'o': verify_arg("opcode", top, OP_PARAM); break;
case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break;
case '*': { /* do nothing */ } break;
}
if (top ) {
top = top->next;
} else break;
received++;
arg++;
}
#undef verify_arg
if ((received < least)) {
asprintf(why,
"The command \"%s\" expected at least %lu arguments (%s) and received %lu",
phpdbg_command_name(command, buffer),
least,
command->args,
received);
return FAILURE;
}
}
return SUCCESS;
}
/* {{{ */
PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why) {
const phpdbg_command_t *command = commands;
phpdbg_param_t *name = *top;
const phpdbg_command_t *matched[3] = {NULL, NULL, NULL};
ulong matches = 0L;
while (command && command->name && command->handler) {
if ((name->len == 1) || (command->name_len >= name->len)) {
/* match single letter alias */
if (command->alias && (name->len == 1)) {
if (command->alias == (*name->str)) {
matched[matches] = command;
matches++;
}
} else {
/* match full, case insensitive, command name */
if (strncasecmp(command->name, name->str, name->len) == SUCCESS) {
if (matches < 3) {
/* only allow abbreviating commands that can be aliased */
if (((name->len != command->name_len) && command->alias) ||
(name->len == command->name_len)) {
matched[matches] = command;
matches++;
}
/* exact match */
if (name->len == command->name_len)
break;
} else break;
}
}
}
command++;
}
switch (matches) {
case 0: {
if (parent) {
asprintf(
why,
"The command \"%s %s\" could not be found",
parent->name, name->str);
} else asprintf(
why,
"The command \"%s\" could not be found",
name->str);
} return parent;
case 1: {
(*top) = (*top)->next;
command = matched[0];
} break;
default: {
char *list = NULL;
zend_uint it = 0;
size_t pos = 0;
while (it < matches) {
if (!list) {
list = malloc(
matched[it]->name_len + 1 +
((it+1) < matches ? sizeof(", ")-1 : 0));
} else {
list = realloc(list,
(pos + matched[it]->name_len) + 1 +
((it+1) < matches ? sizeof(", ")-1 : 0));
}
memcpy(&list[pos], matched[it]->name, matched[it]->name_len);
pos += matched[it]->name_len;
if ((it+1) < matches) {
memcpy(&list[pos], ", ", sizeof(", ")-1);
pos += (sizeof(", ") - 1);
}
list[pos] = 0;
it++;
}
asprintf(
why,
"The command \"%s\" is ambigious, matching %lu commands (%s)",
name->str, matches, list);
free(list);
} return NULL;
}
if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
return phpdbg_stack_resolve(command->subs, command, top, why);
} else {
return command;
}
return NULL;
} /* }}} */
/* {{{ */
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) {
phpdbg_param_t *top = NULL;
const phpdbg_command_t *handler = NULL;
if (stack->type != STACK_PARAM) {
asprintf(
why, "The passed argument was not a stack !!");
return FAILURE;
}
if (!stack->len) {
asprintf(
why, "The stack contains nothing !!");
return FAILURE;
}
top = (phpdbg_param_t*) stack->next;
switch (top->type) {
case EVAL_PARAM:
return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC);
case RUN_PARAM:
return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC);
case SHELL_PARAM:
return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC);
case STR_PARAM: {
handler = phpdbg_stack_resolve(
phpdbg_prompt_commands, NULL, &top, why);
if (handler) {
if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) {
return handler->handler(top TSRMLS_CC);
}
}
} return FAILURE;
default:
asprintf(
why, "The first parameter makes no sense !!");
return FAILURE;
}
return SUCCESS;
} /* }}} */
PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
{
phpdbg_input_t *buffer = NULL;
char *cmd = NULL;
#ifndef HAVE_LIBREADLINE
char buf[PHPDBG_MAX_CMD];
#endif
char *buffer = NULL;
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
@ -513,32 +847,8 @@ readline:
}
#endif
} else cmd = buffered;
/* allocate and sanitize buffer */
buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
if (!buffer) {
return NULL;
}
buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
/* store constant pointer to start of buffer */
buffer->start = (char* const*) buffer->string;
buffer->argv = phpdbg_read_argv(
buffer->string, &buffer->argc TSRMLS_CC);
#ifdef PHPDBG_DEBUG
if (buffer->argc) {
int arg = 0;
while (arg < buffer->argc) {
phpdbg_debug(
"argv %d=%s", arg, buffer->argv[arg]->string);
arg++;
}
}
#endif
buffer = estrdup(cmd);
#ifdef HAVE_LIBREADLINE
if (!buffered && cmd &&
@ -546,144 +856,34 @@ readline:
free(cmd);
}
#endif
return buffer;
}
return NULL;
} /* }}} */
if (buffer && isspace(*buffer)) {
char *trimmed = buffer;
while (isspace(*trimmed))
trimmed++;
PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
{
if (argv) {
if (argc) {
int arg;
for (arg=0; arg<argc; arg++) {
phpdbg_destroy_input(
&argv[arg] TSRMLS_CC);
}
}
efree(argv);
trimmed = estrdup(trimmed);
efree(buffer);
buffer = trimmed;
}
} /* }}} */
PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
{
if (*input) {
if ((*input)->string) {
efree((*input)->string);
}
phpdbg_destroy_argv(
(*input)->argv, (*input)->argc TSRMLS_CC);
efree(*input);
}
} /* }}} */
PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
{
int rc = FAILURE;
if (input->argc > 0) {
while (command && command->name && command->handler) {
if (((command->name_len == input->argv[0]->length) &&
(memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
(command->alias &&
(input->argv[0]->length == 1) &&
(command->alias == *input->argv[0]->string))) {
phpdbg_param_t param;
phpdbg_command_t *initial_last_cmd;
phpdbg_param_t initial_last_param;
param.type = EMPTY_PARAM;
if (input->argc > 1) {
if (command->subs) {
phpdbg_input_t sub = *input;
sub.string += input->argv[0]->length;
sub.length -= input->argv[0]->length;
sub.string = phpdbg_trim(
sub.string, sub.length, &sub.length);
sub.argc--;
sub.argv++;
phpdbg_debug(
"trying sub commands in \"%s\" for \"%s\" with %d arguments",
command->name, sub.argv[0]->string, sub.argc-1);
if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
efree(sub.string);
return SUCCESS;
}
efree(sub.string);
}
/* no sub command found */
{
char *store = input->string;
input->string += input->argv[0]->length;
input->length -= input->argv[0]->length;
input->string = phpdbg_trim(
input->string, input->length, &input->length);
efree(store);
}
/* pass parameter on */
phpdbg_parse_param(
input->string,
input->length,
&param TSRMLS_CC);
}
phpdbg_debug(
"found command %s for %s with %d arguments",
command->name, input->argv[0]->string, input->argc-1);
{
int arg;
for (arg=1; arg<input->argc; arg++) {
phpdbg_debug(
"\t#%d: [%s=%zu]",
arg,
input->argv[arg]->string,
input->argv[arg]->length);
}
}
initial_last_param = PHPDBG_G(lparam);
initial_last_cmd = (phpdbg_command_t *)PHPDBG_G(lcmd);
PHPDBG_G(lparam) = param;
PHPDBG_G(lcmd) = (phpdbg_command_t *)command;
rc = command->handler(&param, input TSRMLS_CC);
/* only set last command when it is worth it! */
if (rc != FAILURE && !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
phpdbg_clear_param(&initial_last_param TSRMLS_CC);
} else if (PHPDBG_G(lcmd) == command && !memcmp(&PHPDBG_G(lparam),& initial_last_param, sizeof(phpdbg_param_t))) {
PHPDBG_G(lparam) = initial_last_param;
PHPDBG_G(lcmd) = initial_last_cmd;
phpdbg_clear_param(&param TSRMLS_CC);
}
break;
}
command++;
if (buffer && strlen(buffer)) {
if (PHPDBG_G(buffer)) {
efree(PHPDBG_G(buffer));
}
PHPDBG_G(buffer) = estrdup(buffer);
} else {
/* this should NEVER happen */
phpdbg_error(
"No function executed!!");
if (PHPDBG_G(buffer)) {
buffer = estrdup(PHPDBG_G(buffer));
}
}
return rc;
return buffer;
} /* }}} */
PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */
{
efree(*input);
} /* }}} */

View File

@ -23,8 +23,6 @@
#include "TSRM.h"
typedef struct _phpdbg_command_t phpdbg_command_t;
/* {{{ Command and Parameter */
enum {
NO_ARG = 0,
@ -36,24 +34,23 @@ typedef enum {
EMPTY_PARAM = 0,
ADDR_PARAM,
FILE_PARAM,
NUMERIC_FILE_PARAM,
METHOD_PARAM,
STR_PARAM,
NUMERIC_PARAM,
NUMERIC_FUNCTION_PARAM,
NUMERIC_METHOD_PARAM
NUMERIC_METHOD_PARAM,
STACK_PARAM,
EVAL_PARAM,
SHELL_PARAM,
COND_PARAM,
OP_PARAM,
ORIG_PARAM,
RUN_PARAM
} phpdbg_param_type;
typedef struct _phpdbg_input_t phpdbg_input_t;
struct _phpdbg_input_t {
char * const *start;
char *string;
size_t length;
phpdbg_input_t **argv;
int argc;
};
typedef struct _phpdbg_param {
typedef struct _phpdbg_param phpdbg_param_t;
struct _phpdbg_param {
phpdbg_param_type type;
long num;
zend_ulong addr;
@ -67,10 +64,31 @@ typedef struct _phpdbg_param {
} method;
char *str;
size_t len;
} phpdbg_param_t;
phpdbg_param_t *next;
phpdbg_param_t *top;
};
typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC);
#define phpdbg_init_param(v, t) do{ \
(v)->type = (t); \
(v)->addr = 0; \
(v)->num = 0; \
(v)->file.name = NULL; \
(v)->file.line = 0; \
(v)->method.class = NULL; \
(v)->method.name = NULL; \
(v)->str = NULL; \
(v)->len = 0; \
(v)->next = NULL; \
(v)->top = NULL; \
} while(0)
#ifndef YYSTYPE
#define YYSTYPE phpdbg_param_t
#endif
typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC);
typedef struct _phpdbg_command_t phpdbg_command_t;
struct _phpdbg_command_t {
const char *name; /* Command name */
size_t name_len; /* Command name length */
@ -79,7 +97,8 @@ struct _phpdbg_command_t {
char alias; /* Alias */
phpdbg_command_handler_t handler; /* Command handler */
const phpdbg_command_t *subs; /* Sub Commands */
char arg_type; /* Accept args? */
char *args; /* Argument Spec */
const phpdbg_command_t *parent; /* Parent Command */
};
/* }}} */
@ -95,35 +114,29 @@ typedef struct {
} phpdbg_frame_t;
/* }}} */
/*
* Workflow:
* 1) read input
* input takes the line from console, creates argc/argv
* 2) parse parameters into suitable types based on arg_type
* takes input from 1) and arg_type and creates parameters
* 3) do command
* executes commands
* 4) destroy parameters
* cleans up what was allocated by creation of parameters
* 5) destroy input
* cleans up what was allocated by creation of input
* 1) the lexer/parser creates a stack of commands and arguments from input
* 2) the commands at the top of the stack are resolved sensibly using aliases, abbreviations and case insensitive matching
* 3) the remaining arguments in the stack are verified (optionally) against the handlers declared argument specification
* 4) the handler is called passing the top of the stack as the only parameter
* 5) the stack is destroyed upon return from the handler
*/
/*
* Input Management
*/
PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC);
PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC);
/*
* Argument Management
*/
PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
#define phpdbg_argv_is(n, s) \
(memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
/**
* Stack Management
*/
PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param);
PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why);
PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC);
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC);
PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack);
/*
* Parameter Management
@ -135,28 +148,27 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_par
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC);
/*
* Command Executor
*/
PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC);
PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg);
/**
* Command Declarators
*/
#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args}
#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent}
#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args}
#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL}
#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC)
#define PHPDBG_COMMAND_D(name, tip, alias, children, args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL}
#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC
#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC)
#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'}
#define PHPDBG_COMMAND_ARGS param TSRMLS_CC
#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL}
/*
* Default Switch Case

View File

@ -167,7 +167,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
zval **tmp;
zval **file, **line;
HashPosition position;
int i = 1, limit = num;
int i = 0, limit = num;
int user_defined;
if (limit < 0) {
@ -186,7 +186,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
(void**)&tmp, &position) == FAILURE) {
phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
phpdbg_write("frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
break;
}

File diff suppressed because it is too large Load Diff

View File

@ -30,63 +30,19 @@
/**
* Helper Forward Declarations
*/
PHPDBG_HELP(exec);
PHPDBG_HELP(compile);
PHPDBG_HELP(step);
PHPDBG_HELP(next);
PHPDBG_HELP(run);
PHPDBG_HELP(eval);
PHPDBG_HELP(until);
PHPDBG_HELP(finish);
PHPDBG_HELP(leave);
PHPDBG_HELP(print);
PHPDBG_HELP(break);
PHPDBG_HELP(clean);
PHPDBG_HELP(clear);
PHPDBG_HELP(info);
PHPDBG_HELP(back);
PHPDBG_HELP(frame);
PHPDBG_HELP(quiet);
PHPDBG_HELP(list);
PHPDBG_HELP(set);
PHPDBG_HELP(register);
PHPDBG_HELP(options);
PHPDBG_HELP(source);
PHPDBG_HELP(shell);
PHPDBG_HELP(aliases);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_help_commands[] = {
PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0),
PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0),
PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0),
PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0),
PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0),
PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0),
PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0),
PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0),
PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0),
PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0),
PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0),
PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0),
PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0),
PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0),
PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0),
PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0),
PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0),
PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0),
PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0),
PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0),
PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0),
PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0),
PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_help_commands[];
#define phpdbg_help_header() \
phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
#define phpdbg_help_footer() \
phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
typedef struct _phpdbg_help_text_t {
char *key;
char *text;
} phpdbg_help_text_t;
extern phpdbg_help_text_t phpdbg_help_text[];
#endif /* PHPDBG_HELP_H */

View File

@ -23,9 +23,25 @@
#include "phpdbg_utils.h"
#include "phpdbg_info.h"
#include "phpdbg_bp.h"
#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14])
const phpdbg_command_t phpdbg_info_commands[] = {
PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0),
PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0),
PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0),
PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0),
PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0),
PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0),
PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
PHPDBG_END_COMMAND
};
PHPDBG_INFO(break) /* {{{ */
{
phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);

View File

@ -34,16 +34,6 @@ PHPDBG_INFO(vars);
PHPDBG_INFO(literal);
PHPDBG_INFO(memory);
static const phpdbg_command_t phpdbg_info_commands[] = {
PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0),
PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
PHPDBG_COMMAND_D_EX(error, "show 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_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_info_commands[];
#endif /* PHPDBG_INFO_H */

2271
sapi/phpdbg/phpdbg_lexer.c Normal file

File diff suppressed because it is too large Load Diff

348
sapi/phpdbg/phpdbg_lexer.h Normal file
View File

@ -0,0 +1,348 @@
#ifndef yyHEADER_H
#define yyHEADER_H 1
#define yyIN_HEADER 1
#line 6 "sapi/phpdbg/phpdbg_lexer.h"
#line 8 "sapi/phpdbg/phpdbg_lexer.h"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 37
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* An opaque pointer. */
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
/* For convenience, these vars (plus the bison vars far below)
are macros in the reentrant scanner. */
#define yyin yyg->yyin_r
#define yyout yyg->yyout_r
#define yyextra yyg->yyextra_r
#define yyleng yyg->yyleng_r
#define yytext yyg->yytext_r
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
#define yy_flex_debug yyg->yy_flex_debug_r
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
yy_size_t yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void yyrestart (FILE *input_file ,yyscan_t yyscanner );
void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
void yypop_buffer_state (yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
void *yyalloc (yy_size_t ,yyscan_t yyscanner );
void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
void yyfree (void * ,yyscan_t yyscanner );
/* Begin user sect3 */
#define yywrap(yyscanner) 1
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#define RAW 1
#define NORMAL 2
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include <unistd.h>
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
int yylex_init (yyscan_t* scanner);
int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int yylex_destroy (yyscan_t yyscanner );
int yyget_debug (yyscan_t yyscanner );
void yyset_debug (int debug_flag ,yyscan_t yyscanner );
YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
FILE *yyget_in (yyscan_t yyscanner );
void yyset_in (FILE * in_str ,yyscan_t yyscanner );
FILE *yyget_out (yyscan_t yyscanner );
void yyset_out (FILE * out_str ,yyscan_t yyscanner );
yy_size_t yyget_leng (yyscan_t yyscanner );
char *yyget_text (yyscan_t yyscanner );
int yyget_lineno (yyscan_t yyscanner );
void yyset_lineno (int line_number ,yyscan_t yyscanner );
int yyget_column (yyscan_t yyscanner );
void yyset_column (int column_no ,yyscan_t yyscanner );
YYSTYPE * yyget_lval (yyscan_t yyscanner );
void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int yywrap (yyscan_t yyscanner );
#else
extern int yywrap (yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 8192
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int yylex \
(YYSTYPE * yylval_param ,yyscan_t yyscanner);
#define YY_DECL int yylex \
(YYSTYPE * yylval_param , yyscan_t yyscanner)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
#line 347 "sapi/phpdbg/phpdbg_lexer.h"
#undef yyIN_HEADER
#endif /* yyHEADER_H */

131
sapi/phpdbg/phpdbg_lexer.l Normal file
View File

@ -0,0 +1,131 @@
%{
/*
* phpdbg_lexer.l
*/
#include "phpdbg.h"
#include "phpdbg_cmd.h"
#define YYSTYPE phpdbg_param_t
#include "phpdbg_parser.h"
%}
%s RAW
%s NORMAL
%option outfile="sapi/phpdbg/phpdbg_lexer.c" header-file="sapi/phpdbg/phpdbg_lexer.h"
%option warn nodefault
%option reentrant noyywrap never-interactive nounistd
%option bison-bridge
T_TRUE "true"
T_YES "yes"
T_ON "on"
T_ENABLED "enabled"
T_FALSE "false"
T_NO "no"
T_OFF "off"
T_DISABLED "disabled"
T_EVAL "ev"
T_SHELL "sh"
T_IF "if"
T_RUN "run"
T_RUN_SHORT "r"
WS [ \r\n\t]+
DIGITS [0-9\.]+
ID [^ \r\n\t:#]+
ADDR 0x[a-fA-F0-9]+
OPCODE (ZEND_|zend_)([A-Za-z])+
INPUT [^\n]+
%%
<INITIAL>{
{T_EVAL} {
BEGIN(RAW);
phpdbg_init_param(yylval, EMPTY_PARAM);
return T_EVAL;
}
{T_SHELL} {
BEGIN(RAW);
phpdbg_init_param(yylval, EMPTY_PARAM);
return T_SHELL;
}
{T_RUN}|{T_RUN_SHORT} {
BEGIN(RAW);
phpdbg_init_param(yylval, EMPTY_PARAM);
return T_RUN;
}
.+ {
BEGIN(NORMAL);
REJECT;
}
}
<NORMAL>{
{T_IF} {
BEGIN(RAW);
phpdbg_init_param(yylval, EMPTY_PARAM);
return T_IF;
}
}
<INITIAL,NORMAL>{
{ID}[:]{1}[//]{2} {
phpdbg_init_param(yylval, STR_PARAM);
yylval->str = strndup(yytext, yyleng);
yylval->len = yyleng;
return T_PROTO;
}
[#]{1} { return T_POUND; }
[:]{2} { return T_DCOLON; }
[:]{1} { return T_COLON; }
{T_YES}|{T_ON}|{T_ENABLED}|{T_TRUE} {
phpdbg_init_param(yylval, NUMERIC_PARAM);
yylval->num = 1;
return T_TRUTHY;
}
{T_NO}|{T_OFF}|{T_DISABLED}|{T_FALSE} {
phpdbg_init_param(yylval, NUMERIC_PARAM);
yylval->num = 0;
return T_FALSY;
}
{DIGITS} {
phpdbg_init_param(yylval, NUMERIC_PARAM);
yylval->num = atoi(yytext);
return T_DIGITS;
}
{ADDR} {
phpdbg_init_param(yylval, ADDR_PARAM);
yylval->addr = strtoul(yytext, 0, 16);
return T_ADDR;
}
{OPCODE} {
phpdbg_init_param(yylval, OP_PARAM);
yylval->str = strndup(yytext, yyleng);
yylval->len = yyleng;
return T_OPCODE;
}
{ID} {
phpdbg_init_param(yylval, STR_PARAM);
yylval->str = strndup(yytext, yyleng);
yylval->len = yyleng;
return T_ID;
}
}
<RAW>{INPUT} {
phpdbg_init_param(yylval, STR_PARAM);
yylval->str = strndup(yytext, yyleng);
yylval->len = yyleng;
BEGIN(INITIAL);
return T_INPUT;
}
{WS} { /* ignore whitespace */ }
%%

View File

@ -29,9 +29,22 @@
#include "phpdbg.h"
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
#include "phpdbg_prompt.h"
#include "php_streams.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13])
const phpdbg_command_t phpdbg_list_commands[] = {
PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l"),
PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s"),
PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m"),
PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s"),
PHPDBG_END_COMMAND
};
PHPDBG_LIST(lines) /* {{{ */
{
if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
@ -41,12 +54,12 @@ PHPDBG_LIST(lines) /* {{{ */
switch (param->type) {
case NUMERIC_PARAM:
case EMPTY_PARAM:
phpdbg_list_file(phpdbg_current_file(TSRMLS_C),
param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num),
(param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
(param->num < 0 ? 1 - param->num : param->num),
(param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
0 TSRMLS_CC);
break;
case FILE_PARAM:
phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC);
break;
@ -59,41 +72,28 @@ PHPDBG_LIST(lines) /* {{{ */
PHPDBG_LIST(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_list_function_byname(
param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_LIST(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM: {
zend_class_entry **ce;
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *function;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *function;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
phpdbg_list_function(function TSRMLS_CC);
} else {
phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
}
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
phpdbg_list_function(function TSRMLS_CC);
} else {
phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
}
efree(lcname);
} else {
phpdbg_error("Could not find the class %s", param->method.class);
}
} break;
phpdbg_default_switch_case();
efree(lcname);
} else {
phpdbg_error("Could not find the class %s", param->method.class);
}
return SUCCESS;
@ -101,30 +101,24 @@ PHPDBG_LIST(method) /* {{{ */
PHPDBG_LIST(class) /* {{{ */
{
switch (param->type) {
case STR_PARAM: {
zend_class_entry **ce;
zend_class_entry **ce;
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if ((*ce)->type == ZEND_USER_CLASS) {
if ((*ce)->info.user.filename) {
phpdbg_list_file(
(*ce)->info.user.filename,
(*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
(*ce)->info.user.line_start, 0 TSRMLS_CC
);
} else {
phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
}
} else {
phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
}
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if ((*ce)->type == ZEND_USER_CLASS) {
if ((*ce)->info.user.filename) {
phpdbg_list_file(
(*ce)->info.user.filename,
(*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
(*ce)->info.user.line_start, 0 TSRMLS_CC
);
} else {
phpdbg_error("The requested class (%s) could not be found", param->str);
phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
}
} break;
phpdbg_default_switch_case();
} else {
phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
}
} else {
phpdbg_error("The requested class (%s) could not be found", param->str);
}
return SUCCESS;
@ -132,97 +126,46 @@ PHPDBG_LIST(class) /* {{{ */
void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
{
unsigned char *mem, *pos, *last_pos, *end_pos;
struct stat st;
#ifndef _WIN32
int fd;
#else
HANDLE fd, map;
#endif
int all_content = (count == 0);
int line = 0, displayed = 0;
char *opened = NULL;
char buffer[8096] = {0,};
long line = 0;
php_stream *stream = NULL;
if (VCWD_STAT(filename, &st) == FAILURE) {
phpdbg_error("Failed to stat file %s", filename);
return;
}
#ifndef _WIN32
if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) {
stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened);
if (!stream) {
phpdbg_error("Failed to open file %s to list", filename);
return;
}
pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
end_pos = mem + st.st_size;
#else
fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fd == INVALID_HANDLE_VALUE) {
phpdbg_error("Failed to open file!");
return;
}
map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
if (map == NULL) {
phpdbg_error("Failed to map file!");
CloseHandle(fd);
return;
}
pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if (mem == NULL) {
phpdbg_error("Failed to map file in memory");
CloseHandle(map);
CloseHandle(fd);
return;
}
end_pos = mem + st.st_size;
#endif
while (1) {
if (pos == end_pos) {
break;
}
pos = memchr(last_pos, '\n', end_pos - last_pos);
if (!pos) {
/* No more line breaks */
pos = end_pos;
}
while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) {
++line;
if (!offset || offset <= line) {
/* Without offset, or offset reached */
if (!highlight) {
phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos);
phpdbg_write("%05ld: %s", line, buffer);
} else {
if (highlight != line) {
phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos);
phpdbg_write(" %05ld: %s", line, buffer);
} else {
phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos);
phpdbg_write(">%05ld: %s", line, buffer);
}
}
++displayed;
}
last_pos = pos + 1;
if (!all_content && displayed == count) {
/* Reached max line to display */
if ((count + (offset-1)) == line)
break;
}
}
#ifndef _WIN32
munmap(mem, st.st_size);
close(fd);
#else
UnmapViewOfFile(mem);
CloseHandle(map);
CloseHandle(fd);
#endif
php_stream_close(stream);
} /* }}} */
void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */

View File

@ -36,12 +36,6 @@ void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC);
void phpdbg_list_function(const zend_function* TSRMLS_DC);
void phpdbg_list_file(const char*, long, long, int TSRMLS_DC);
static const phpdbg_command_t phpdbg_list_commands[] = {
PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1),
PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_list_commands[];
#endif /* PHPDBG_LIST_H */

View File

@ -183,6 +183,7 @@ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags
const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
{
#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO
#define CASE(s) case s: return #s
switch (opcode) {
CASE(ZEND_NOP);
@ -360,4 +361,8 @@ const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
default:
return "UNKNOWN";
}
#else
const char *ret = zend_get_opcode_name(opcode);
return ret?ret:"UNKNOWN";
#endif
} /* }}} */

168
sapi/phpdbg/phpdbg_parser.y Normal file
View File

@ -0,0 +1,168 @@
%error-verbose
%{
/*
* phpdbg_parser.y
* (from php-src root)
* flex sapi/phpdbg/dev/phpdbg_lexer.l
* bison sapi/phpdbg/dev/phpdbg_parser.y
*/
#include "phpdbg.h"
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_cmd.h"
#include "phpdbg_prompt.h"
#define YYSTYPE phpdbg_param_t
#include "phpdbg_parser.h"
#include "phpdbg_lexer.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
int yyerror(phpdbg_param_t *stack, yyscan_t scanner, const char *msg) {
TSRMLS_FETCH();
phpdbg_error("Parse Error: %s", msg);
{
const phpdbg_param_t *top = stack;
while (top) {
phpdbg_param_debug(
top, "--> ");
top = top->next;
}
}
return 0;
}
%}
%code requires {
#include "phpdbg.h"
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
}
%output "sapi/phpdbg/phpdbg_parser.c"
%defines "sapi/phpdbg/phpdbg_parser.h"
%define api.pure
%lex-param { yyscan_t scanner }
%parse-param { phpdbg_param_t *stack }
%parse-param { yyscan_t scanner }
%token T_EVAL "eval"
%token T_RUN "run"
%token T_SHELL "shell"
%token T_IF "if (condition)"
%token T_TRUTHY "truthy (true, on, yes or enabled)"
%token T_FALSY "falsy (false, off, no or disabled)"
%token T_STRING "string (some input, perhaps)"
%token T_COLON ": (colon)"
%token T_DCOLON ":: (double colon)"
%token T_POUND "# (pound sign)"
%token T_PROTO "protocol (file://)"
%token T_DIGITS "digits (numbers)"
%token T_LITERAL "literal (string)"
%token T_ADDR "address"
%token T_OPCODE "opcode"
%token T_ID "identifier (command or function name)"
%token T_INPUT "input (input string or data)"
%token T_UNEXPECTED "input"
%%
input
: parameters
| /* nothing */
;
parameters
: parameter { phpdbg_stack_push(stack, &$1); }
| parameters parameter { phpdbg_stack_push(stack, &$2); }
;
parameter
: T_ID T_COLON T_DIGITS {
$$.type = FILE_PARAM;
$$.file.name = $2.str;
$$.file.line = $3.num;
}
| T_ID T_COLON T_POUND T_DIGITS {
$$.type = NUMERIC_FILE_PARAM;
$$.file.name = $1.str;
$$.file.line = $4.num;
}
| T_PROTO T_ID T_COLON T_DIGITS {
$$.type = FILE_PARAM;
$$.file.name = malloc($1.len +
$2.len + 1);
if ($$.file.name) {
memcpy(&$$.file.name[0], $1.str, $1.len);
memcpy(&$$.file.name[$1.len], $2.str, $2.len);
$$.file.name[$1.len + $2.len] = '\0';
}
$$.file.line = $4.num;
}
| T_PROTO T_ID T_COLON T_POUND T_DIGITS {
$$.type = NUMERIC_FILE_PARAM;
$$.file.name = malloc($1.len +
$2.len + 1);
if ($$.file.name) {
memcpy(&$$.file.name[0], $1.str, $1.len);
memcpy(&$$.file.name[$1.len], $2.str, $2.len);
$$.file.name[$1.len + $2.len] = '\0';
}
$$.file.line = $5.num;
}
| T_ID T_DCOLON T_ID {
$$.type = METHOD_PARAM;
$$.method.class = $1.str;
$$.method.name = $3.str;
}
| T_ID T_DCOLON T_ID T_POUND T_DIGITS {
$$.type = NUMERIC_METHOD_PARAM;
$$.method.class = $1.str;
$$.method.name = $3.str;
$$.num = $5.num;
}
| T_ID T_POUND T_DIGITS {
$$.type = NUMERIC_FUNCTION_PARAM;
$$.str = $1.str;
$$.len = $1.len;
$$.num = $3.num;
}
| T_IF T_INPUT {
$$.type = COND_PARAM;
$$.str = $2.str;
$$.len = $2.len;
}
| T_EVAL T_INPUT {
$$.type = EVAL_PARAM;
$$.str = $2.str;
$$.len = $2.len;
}
| T_SHELL T_INPUT {
$$.type = SHELL_PARAM;
$$.str = $2.str;
$$.len = $2.len;
}
| T_RUN {
$$.type = RUN_PARAM;
$$.len = 0;
}
| T_RUN T_INPUT {
$$.type = RUN_PARAM;
$$.str = $2.str;
$$.len = $2.len;
}
| T_OPCODE { $$ = $1; }
| T_ADDR { $$ = $1; }
| T_LITERAL { $$ = $1; }
| T_TRUTHY { $$ = $1; }
| T_FALSY { $$ = $1; }
| T_DIGITS { $$ = $1; }
| T_ID { $$ = $1; }
;
%%

View File

@ -26,6 +26,19 @@
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9])
const phpdbg_command_t phpdbg_print_commands[] = {
PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s"),
PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m"),
PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s"),
PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
PHPDBG_END_COMMAND
};
PHPDBG_PRINT(opline) /* {{{ */
{
if (EG(in_execution) && EG(current_execute_data)) {
@ -77,7 +90,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC)
phpdbg_error("\tFailed to decode opline %16p", opline);
}
opline++;
} while (++opcode < end);
} while (opcode++ < end);
zend_hash_destroy(&vars);
}
} break;
@ -141,36 +154,30 @@ PHPDBG_PRINT(class) /* {{{ */
{
zend_class_entry **ce;
switch (param->type) {
case STR_PARAM: {
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
phpdbg_notice("%s %s: %s",
((*ce)->type == ZEND_USER_CLASS) ?
"User" : "Internal",
((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
"Interface" :
((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
"Abstract Class" :
"Class",
(*ce)->name);
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
phpdbg_notice("%s %s: %s",
((*ce)->type == ZEND_USER_CLASS) ?
"User" : "Internal",
((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
"Interface" :
((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
"Abstract Class" :
"Class",
(*ce)->name);
phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
if (zend_hash_num_elements(&(*ce)->function_table)) {
HashPosition position;
zend_function *method;
phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
if (zend_hash_num_elements(&(*ce)->function_table)) {
HashPosition position;
zend_function *method;
for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
phpdbg_print_function_helper(method TSRMLS_CC);
}
}
} else {
phpdbg_error("The class %s could not be found", param->str);
for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
phpdbg_print_function_helper(method TSRMLS_CC);
}
} break;
phpdbg_default_switch_case();
}
} else {
phpdbg_error("The class %s could not be found", param->str);
}
return SUCCESS;
@ -178,31 +185,25 @@ PHPDBG_PRINT(class) /* {{{ */
PHPDBG_PRINT(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM: {
zend_class_entry **ce;
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *fbc;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *fbc;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s Method %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
fbc->common.function_name);
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s Method %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
fbc->common.function_name);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The method %s could not be found", param->method.name);
}
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The method %s could not be found", param->method.name);
}
efree(lcname);
} else {
phpdbg_error("The class %s could not be found", param->method.class);
}
} break;
phpdbg_default_switch_case();
efree(lcname);
} else {
phpdbg_error("The class %s could not be found", param->method.class);
}
return SUCCESS;
@ -210,49 +211,43 @@ PHPDBG_PRINT(method) /* {{{ */
PHPDBG_PRINT(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM: {
HashTable *func_table = EG(function_table);
zend_function* fbc;
const char *func_name = param->str;
size_t func_name_len = param->len;
char *lcname;
/* search active scope if begins with period */
if (func_name[0] == '.') {
if (EG(scope)) {
func_name++;
func_name_len--;
HashTable *func_table = EG(function_table);
zend_function* fbc;
const char *func_name = param->str;
size_t func_name_len = param->len;
char *lcname;
/* search active scope if begins with period */
if (func_name[0] == '.') {
if (EG(scope)) {
func_name++;
func_name_len--;
func_table = &EG(scope)->function_table;
} else {
phpdbg_error("No active class");
return SUCCESS;
}
} else if (!EG(function_table)) {
phpdbg_error("No function table loaded");
return SUCCESS;
} else {
func_table = EG(function_table);
}
lcname = zend_str_tolower_dup(func_name, func_name_len);
if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s %s %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
(fbc->common.scope) ? "Method" : "Function",
fbc->common.function_name);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The function %s could not be found", func_name);
}
efree(lcname);
} break;
phpdbg_default_switch_case();
func_table = &EG(scope)->function_table;
} else {
phpdbg_error("No active class");
return SUCCESS;
}
} else if (!EG(function_table)) {
phpdbg_error("No function table loaded");
return SUCCESS;
} else {
func_table = EG(function_table);
}
lcname = zend_str_tolower_dup(func_name, func_name_len);
if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s %s %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
(fbc->common.scope) ? "Method" : "Function",
fbc->common.function_name);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The function %s could not be found", func_name);
}
efree(lcname);
return SUCCESS;
} /* }}} */

View File

@ -35,17 +35,6 @@ PHPDBG_PRINT(method);
PHPDBG_PRINT(func);
PHPDBG_PRINT(stack);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_print_commands[] = {
PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1),
PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_print_commands[];
#endif /* PHPDBG_PRINT_H */

File diff suppressed because it is too large Load Diff

View File

@ -30,11 +30,10 @@ void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
/* {{{ phpdbg command handlers */
PHPDBG_COMMAND(exec);
PHPDBG_COMMAND(compile);
PHPDBG_COMMAND(step);
PHPDBG_COMMAND(next);
PHPDBG_COMMAND(continue);
PHPDBG_COMMAND(run);
PHPDBG_COMMAND(eval);
PHPDBG_COMMAND(ev);
PHPDBG_COMMAND(until);
PHPDBG_COMMAND(finish);
PHPDBG_COMMAND(leave);
@ -47,13 +46,13 @@ PHPDBG_COMMAND(info);
PHPDBG_COMMAND(clean);
PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
PHPDBG_COMMAND(quiet);
PHPDBG_COMMAND(aliases);
PHPDBG_COMMAND(shell);
PHPDBG_COMMAND(sh);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
PHPDBG_COMMAND(export);
PHPDBG_COMMAND(register);
PHPDBG_COMMAND(quit); /* }}} */
PHPDBG_COMMAND(quit);
PHPDBG_COMMAND(watch); /* }}} */
/* {{{ prompt commands */
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */

View File

@ -23,56 +23,52 @@
#include "phpdbg_set.h"
#include "phpdbg_utils.h"
#include "phpdbg_bp.h"
#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18])
const phpdbg_command_t phpdbg_set_commands[] = {
PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s"),
#ifndef _WIN32
PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss"),
PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b"),
#endif
PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s"),
PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b"),
PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b"),
PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b"),
PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s"),
PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b"),
PHPDBG_END_COMMAND
};
PHPDBG_SET(prompt) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
break;
case STR_PARAM:
phpdbg_set_prompt(param->str TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
} else phpdbg_set_prompt(param->str TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_SET(break) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_writeln("%s",
PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
break;
case STR_PARAM:
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
phpdbg_enable_breakpoints(TSRMLS_C);
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
phpdbg_disable_breakpoints(TSRMLS_C);
}
break;
case NUMERIC_PARAM: {
if (input->argc > 2) {
if (phpdbg_argv_is(2, "on")) {
phpdbg_enable_breakpoint(param->num TSRMLS_CC);
} else if (phpdbg_argv_is(2, "off")) {
phpdbg_disable_breakpoint(param->num TSRMLS_CC);
}
if (param->next) {
if (param->next->num) {
phpdbg_enable_breakpoint(param->num TSRMLS_CC);
} else phpdbg_disable_breakpoint(param->num TSRMLS_CC);
} else {
phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
if (brake) {
phpdbg_writeln(
"%s", brake->disabled ? "off" : "on");
} else {
phpdbg_error("Failed to find breakpoint #%lx", param->num);
phpdbg_error("Failed to find breakpoint #%ld", param->num);
}
}
} break;
@ -85,104 +81,96 @@ PHPDBG_SET(break) /* {{{ */
return SUCCESS;
} /* }}} */
PHPDBG_SET(breaks) /* {{{ */
{
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("%s",
PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
} else switch (param->type) {
case NUMERIC_PARAM: {
if (param->num) {
phpdbg_enable_breakpoints(TSRMLS_C);
} else phpdbg_disable_breakpoints(TSRMLS_C);
} break;
default:
phpdbg_error(
"set break used incorrectly: set break [id] <on|off>");
}
return SUCCESS;
} /* }}} */
#ifndef _WIN32
PHPDBG_SET(color) /* {{{ */
{
if ((param->type == STR_PARAM) && (input->argc == 3)) {
const phpdbg_color_t *color = phpdbg_get_color(
input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
int element = PHPDBG_COLOR_INVALID;
/* @TODO(anyone) make this consistent with other set commands */
if (color) {
if (phpdbg_argv_is(1, "prompt")) {
phpdbg_notice(
"setting prompt color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_PROMPT;
if (PHPDBG_G(prompt)[1]) {
free(PHPDBG_G(prompt)[1]);
PHPDBG_G(prompt)[1]=NULL;
}
} else if (phpdbg_argv_is(1, "error")) {
phpdbg_notice(
"setting error color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_ERROR;
} else if (phpdbg_argv_is(1, "notice")) {
phpdbg_notice(
"setting notice color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_NOTICE;
} else goto usage;
/* set color for element */
phpdbg_set_color(element, color TSRMLS_CC);
} else {
phpdbg_error(
"Failed to find the requested color (%s)", input->argv[2]->string);
}
} else {
usage:
const phpdbg_color_t *color = phpdbg_get_color(
param->next->str, param->next->len TSRMLS_CC);
if (!color) {
phpdbg_error(
"set color used incorrectly: set color <prompt|error|notice> <color>");
"Failed to find the requested color (%s)", param->next->str);
return SUCCESS;
}
switch (phpdbg_get_element(param->str, param->len TSRMLS_CC)) {
case PHPDBG_COLOR_PROMPT:
phpdbg_notice(
"setting prompt color to %s (%s)", color->name, color->code);
if (PHPDBG_G(prompt)[1]) {
free(PHPDBG_G(prompt)[1]);
PHPDBG_G(prompt)[1]=NULL;
}
phpdbg_set_color(PHPDBG_COLOR_PROMPT, color TSRMLS_CC);
break;
case PHPDBG_COLOR_ERROR:
phpdbg_notice(
"setting error color to %s (%s)", color->name, color->code);
phpdbg_set_color(PHPDBG_COLOR_ERROR, color TSRMLS_CC);
break;
case PHPDBG_COLOR_NOTICE:
phpdbg_notice(
"setting notice color to %s (%s)", color->name, color->code);
phpdbg_set_color(PHPDBG_COLOR_NOTICE, color TSRMLS_CC);
break;
default:
phpdbg_error(
"Failed to find the requested element (%s)", param->str);
}
return SUCCESS;
} /* }}} */
PHPDBG_SET(colors) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM: {
phpdbg_writeln(
"%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
goto done;
}
case STR_PARAM: {
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
} else switch (param->type) {
case NUMERIC_PARAM: {
if (param->num) {
PHPDBG_G(flags) |= PHPDBG_IS_COLOURED;
goto done;
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
} else {
PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
goto done;
}
}
} break;
default:
phpdbg_error(
"set colors used incorrectly: set colors <on|off>");
}
done:
return SUCCESS;
} /* }}} */
#endif
PHPDBG_SET(oplog) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_notice(
"Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
break;
case NUMERIC_PARAM: switch (param->num) {
case 1:
phpdbg_error(
"An output file must be provided to enable oplog");
break;
case 0: {
if (PHPDBG_G(oplog)) {
phpdbg_notice("Disabling oplog");
fclose(
PHPDBG_G(oplog));
} else {
phpdbg_error("Oplog is not enabled!");
}
} break;
} break;
if (!param || param->type == EMPTY_PARAM) {
phpdbg_notice("Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
} else switch (param->type) {
case STR_PARAM: {
/* open oplog */
FILE *old = PHPDBG_G(oplog);
@ -206,3 +194,65 @@ PHPDBG_SET(oplog) /* {{{ */
return SUCCESS;
} /* }}} */
PHPDBG_SET(quiet) /* {{{ */
{
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("Quietness %s",
PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
} else switch (param->type) {
case NUMERIC_PARAM: {
if (param->num) {
PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
} else {
PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_SET(stepping) /* {{{ */
{
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("Stepping %s",
PHPDBG_G(flags) & PHPDBG_STEP_OPCODE ? "opcode" : "line");
} else switch (param->type) {
case STR_PARAM: {
if ((param->len == sizeof("opcode")-1) &&
(memcmp(param->str, "opcode", sizeof("opcode")) == SUCCESS)) {
PHPDBG_G(flags) |= PHPDBG_STEP_OPCODE;
} else if ((param->len == sizeof("line")-1) &&
(memcmp(param->str, "line", sizeof("line")) == SUCCESS)) {
PHPDBG_G(flags) &= ~PHPDBG_STEP_OPCODE;
} else {
phpdbg_error("usage set stepping [<opcode|line>]");
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_SET(refcount) /* {{{ */
{
if (!param || param->type == EMPTY_PARAM) {
phpdbg_writeln("Refcount %s", PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
} else switch (param->type) {
case NUMERIC_PARAM: {
if (param->num) {
PHPDBG_G(flags) |= PHPDBG_SHOW_REFCOUNTS;
} else {
PHPDBG_G(flags) &= ~PHPDBG_SHOW_REFCOUNTS;
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */

View File

@ -32,16 +32,11 @@ PHPDBG_SET(colors);
#endif
PHPDBG_SET(oplog);
PHPDBG_SET(break);
PHPDBG_SET(breaks);
PHPDBG_SET(quiet);
PHPDBG_SET(stepping);
PHPDBG_SET(refcount);
static const phpdbg_command_t phpdbg_set_commands[] = {
PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
#ifndef _WIN32
PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
PHPDBG_COMMAND_D_EX(colors, "usage: set colors <on|off>", 'C', set_colors, NULL, 1),
#endif
PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
PHPDBG_END_COMMAND
};
extern const phpdbg_command_t phpdbg_set_commands[];
#endif /* PHPDBG_SET_H */

View File

@ -30,6 +30,8 @@
#ifdef _WIN32
# include "win32/time.h"
#elif defined(HAVE_SYS_IOCTL_H)
# include "sys/ioctl.h"
#endif
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
@ -65,6 +67,14 @@ const static phpdbg_color_t colors[] = {
PHPDBG_COLOR_END
}; /* }}} */
/* {{{ */
const static phpdbg_element_t elements[] = {
PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT),
PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR),
PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE),
PHPDBG_ELEMENT_END
}; /* }}} */
PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
{
if (!str)
@ -347,6 +357,21 @@ PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */
return colors;
} /* }}} */
PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) {
const phpdbg_element_t *element = elements;
while (element && element->name) {
if (len == element->name_length) {
if (strncasecmp(name, element->name, len) == SUCCESS) {
return element->id;
}
}
element++;
}
return PHPDBG_COLOR_INVALID;
}
PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
{
/* free formatted prompt */
@ -385,3 +410,39 @@ PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
return PHPDBG_G(prompt)[1];
} /* }}} */
int phpdbg_rebuild_symtable(TSRMLS_D) {
if (!EG(active_op_array)) {
phpdbg_error("No active op array!");
return FAILURE;
}
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
if (!EG(active_symbol_table)) {
phpdbg_error("No active symbol table!");
return FAILURE;
}
}
return SUCCESS;
}
PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */
{
int columns;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
#elif defined(HAVE_SYS_IOCTL_H)
struct winsize w;
columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100;
#else
columns = 100;
#endif
return columns;
} /* }}} */

View File

@ -85,12 +85,17 @@ PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
{color, sizeof(color)-1, code}
#define PHPDBG_COLOR_END \
{NULL, 0L, {0}}
#define PHPDBG_ELEMENT_LEN 3
#define PHPDBG_ELEMENT_D(name, id) \
{name, sizeof(name)-1, id}
#define PHPDBG_ELEMENT_END \
{NULL, 0L, 0}
#define PHPDBG_COLOR_INVALID -1
#define PHPDBG_COLOR_PROMPT 0
#define PHPDBG_COLOR_ERROR 1
#define PHPDBG_COLOR_NOTICE 2
#define PHPDBG_COLORS 3
#define PHPDBG_COLOR_PROMPT 0
#define PHPDBG_COLOR_ERROR 1
#define PHPDBG_COLOR_NOTICE 2
#define PHPDBG_COLORS 3
typedef struct _phpdbg_color_t {
char *name;
@ -98,13 +103,25 @@ typedef struct _phpdbg_color_t {
const char code[PHPDBG_COLOR_LEN];
} phpdbg_color_t;
typedef struct _phpdbg_element_t {
char *name;
size_t name_length;
int id;
} phpdbg_element_t;
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC);
PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */
PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D);
PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */
/* {{{ Prompt Management */
PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC);
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
/* {{{ Console Width */
PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */
int phpdbg_rebuild_symtable(TSRMLS_D);
#endif /* PHPDBG_UTILS_H */

789
sapi/phpdbg/phpdbg_watch.c Normal file
View File

@ -0,0 +1,789 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2014 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "phpdbg.h"
#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
#include "phpdbg_utils.h"
#ifndef _WIN32
# include <unistd.h>
# include <sys/mman.h>
#endif
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
typedef struct {
void *page;
size_t size;
char reenable_writing;
/* data must be last element */
void *data;
} phpdbg_watch_memdump;
#define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) {
phpdbg_watchpoint_t *watch;
phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
if (result == NULL) {
return NULL;
}
watch = result->ptr;
/* check if that addr is in a mprotect()'ed memory area */
if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) {
/* failure */
return NULL;
}
return watch;
}
static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) {
int m;
/* pagesize is assumed to be in the range of 2^x */
m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access);
}
static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
phpdbg_change_watchpoint_access(watch, PROT_READ TSRMLS_CC);
}
static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE TSRMLS_CC);
}
static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch);
}
static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr);
}
void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
watch->addr.ptr = addr;
watch->size = size;
}
void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
watch->type = WATCH_ON_ZVAL;
}
void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch);
watch->type = WATCH_ON_HASHTABLE;
}
void phpdbg_watch_HashTable_dtor(zval **ptr);
static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
watch->flags |= PHPDBG_WATCH_SIMPLE;
phpdbg_store_watchpoint(watch TSRMLS_CC);
zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL);
if (watch->type == WATCH_ON_ZVAL) {
phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor);
watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor;
}
phpdbg_activate_watchpoint(watch TSRMLS_CC);
return SUCCESS;
}
static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
HashTable *ht;
switch (Z_TYPE_P(watch->addr.zv)) {
case IS_ARRAY:
ht = Z_ARRVAL_P(watch->addr.zv);
break;
case IS_OBJECT:
ht = Z_OBJPROP_P(watch->addr.zv);
break;
default:
return FAILURE;
}
phpdbg_create_ht_watchpoint(ht, watch);
phpdbg_create_watchpoint(watch TSRMLS_CC);
return SUCCESS;
}
static char *phpdbg_get_property_key(char *key) {
if (*key != 0) {
return key;
}
return strchr(key + 1, 0) + 1;
}
static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
HashTable *ht;
if (watch->type != WATCH_ON_ZVAL) {
return FAILURE;
}
watch->flags |= PHPDBG_WATCH_RECURSIVE;
phpdbg_create_watchpoint(watch TSRMLS_CC);
switch (Z_TYPE_P(watch->addr.zv)) {
case IS_ARRAY:
ht = Z_ARRVAL_P(watch->addr.zv);
break;
case IS_OBJECT:
ht = Z_OBJPROP_P(watch->addr.zv);
break;
default:
return SUCCESS;
}
{
HashPosition position;
zval **zv;
zval key;
for (zend_hash_internal_pointer_reset_ex(ht, &position);
zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
zend_hash_move_forward_ex(ht, &position)) {
phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
new_watch->flags = PHPDBG_WATCH_RECURSIVE;
new_watch->parent = watch;
new_watch->parent_container = ht;
zend_hash_get_current_key_zval_ex(ht, &key, &position);
if (Z_TYPE(key) == IS_STRING) {
new_watch->name_in_parent = zend_strndup(Z_STRVAL(key), Z_STRLEN(key));
new_watch->name_in_parent_len = Z_STRLEN(key);
} else {
new_watch->name_in_parent = NULL;
new_watch->name_in_parent_len = asprintf(&new_watch->name_in_parent, "%ld", Z_LVAL(key));
}
new_watch->str = NULL;
new_watch->str_len = asprintf(&new_watch->str, "%.*s%s%s%s", (int)watch->str_len, watch->str, Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"]":"");
phpdbg_create_zval_watchpoint(*zv, new_watch);
phpdbg_create_recursive_watchpoint(new_watch TSRMLS_CC);
}
}
{
phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
new_watch->parent = watch;
new_watch->parent_container = watch->parent_container;
new_watch->name_in_parent = zend_strndup(watch->name_in_parent, watch->name_in_parent_len);
new_watch->name_in_parent_len = watch->name_in_parent_len;
new_watch->str = NULL;
new_watch->str_len = asprintf(&new_watch->str, "%.*s[]", (int)watch->str_len, watch->str);
new_watch->flags = PHPDBG_WATCH_RECURSIVE;
phpdbg_create_ht_watchpoint(ht, new_watch);
phpdbg_create_watchpoint(new_watch TSRMLS_CC);
}
return SUCCESS;
}
static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request TSRMLS_DC) {
if (watch->type == WATCH_ON_HASHTABLE || (watch->type == WATCH_ON_ZVAL && (Z_TYPE_P(watch->addr.zv) == IS_ARRAY || Z_TYPE_P(watch->addr.zv) == IS_OBJECT))) {
HashTable *ht;
phpdbg_btree_result *result;
if (watch->type == WATCH_ON_HASHTABLE && user_request) {
HashPosition position;
zval **zv;
zval key;
char *str;
int str_len;
phpdbg_watchpoint_t **watchpoint;
ht = watch->addr.ht;
for (zend_hash_internal_pointer_reset_ex(ht, &position);
zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
zend_hash_move_forward_ex(ht, &position)) {
zend_hash_get_current_key_zval_ex(ht, &key, &position);
str = NULL;
if (Z_TYPE(key) == IS_STRING) {
str_len = asprintf(&str, "%.*s%s%s%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(Z_STRVAL(key)), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
} else {
str_len = asprintf(&str, "%.*s%s%li%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", Z_LVAL(key), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
}
if (zend_hash_find(&PHPDBG_G(watchpoints), str, str_len, (void **) &watchpoint) == SUCCESS) {
phpdbg_delete_watchpoint_recursive(*watchpoint, 1 TSRMLS_CC);
}
}
} else {
switch (Z_TYPE_P(watch->addr.zv)) {
case IS_ARRAY:
ht = Z_ARRVAL_P(watch->addr.zv);
break;
case IS_OBJECT:
ht = Z_OBJPROP_P(watch->addr.zv);
break;
}
if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) {
phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request TSRMLS_CC);
}
}
}
return zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
}
static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) {
int ret;
phpdbg_watchpoint_t *watch;
phpdbg_btree_result *result;
if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)tmp_watch->addr.ptr)) == NULL) {
return FAILURE;
}
watch = result->ptr;
if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
ret = phpdbg_delete_watchpoint_recursive(watch, 1 TSRMLS_CC);
} else {
ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
}
free(tmp_watch->str);
efree(tmp_watch);
return ret;
}
static int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) {
int ret = FAILURE;
zend_bool new_index = 1;
char *last_index;
int index_len = 0;
zval **zv;
if (len < 2 || *input != '$') {
goto error;
}
while (i++ < len) {
if (i == len) {
new_index = 1;
} else {
switch (input[i]) {
case '[':
new_index = 1;
break;
case ']':
break;
case '>':
if (last_index[index_len - 1] == '-') {
new_index = 1;
index_len--;
}
break;
default:
if (new_index) {
last_index = input + i;
new_index = 0;
}
if (input[i - 1] == ']') {
goto error;
}
index_len++;
}
}
if (new_index && index_len == 0) {
HashPosition position;
for (zend_hash_internal_pointer_reset_ex(parent, &position);
zend_hash_get_current_data_ex(parent, (void **)&zv, &position) == SUCCESS;
zend_hash_move_forward_ex(parent, &position)) {
if (i == len || (i == len - 1 && input[len - 1] == ']')) {
zval *key = emalloc(sizeof(zval));
phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
watch->flags = 0;
zend_hash_get_current_key_zval_ex(parent, key, &position);
convert_to_string(key);
watch->str = malloc(i + Z_STRLEN_P(key) + 2);
watch->str_len = sprintf(watch->str, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
efree(key);
watch->name_in_parent = zend_strndup(last_index, index_len);
watch->name_in_parent_len = index_len;
watch->parent_container = parent;
phpdbg_create_zval_watchpoint(*zv, watch);
ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
} else if (Z_TYPE_PP(zv) == IS_OBJECT) {
phpdbg_watchpoint_parse_input(input, len, Z_OBJPROP_PP(zv), i, callback, silent TSRMLS_CC);
} else if (Z_TYPE_PP(zv) == IS_ARRAY) {
phpdbg_watchpoint_parse_input(input, len, Z_ARRVAL_PP(zv), i, callback, silent TSRMLS_CC);
} else {
/* Ignore silently */
}
}
return ret;
} else if (new_index) {
char last_chr = last_index[index_len];
last_index[index_len] = 0;
if (zend_symtable_find(parent, last_index, index_len + 1, (void **)&zv) == FAILURE) {
if (!silent) {
phpdbg_error("%.*s is undefined", (int)i, input);
}
return FAILURE;
}
last_index[index_len] = last_chr;
if (i == len) {
phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
watch->flags = 0;
watch->str = zend_strndup(input, len);
watch->str_len = len;
watch->name_in_parent = zend_strndup(last_index, index_len);
watch->name_in_parent_len = index_len;
watch->parent_container = parent;
phpdbg_create_zval_watchpoint(*zv, watch);
ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
} else if (Z_TYPE_PP(zv) == IS_OBJECT) {
parent = Z_OBJPROP_PP(zv);
} else if (Z_TYPE_PP(zv) == IS_ARRAY) {
parent = Z_ARRVAL_PP(zv);
} else {
phpdbg_error("%.*s is nor an array nor an object", (int)i, input);
return FAILURE;
}
index_len = 0;
}
}
return ret;
error:
phpdbg_error("Malformed input");
return FAILURE;
}
static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
if (EG(This) && len >= 5 && !memcmp("$this", input, 5)) {
zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), NULL);
}
if (zend_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1 TSRMLS_CC) != FAILURE) {
return SUCCESS;
}
return phpdbg_watchpoint_parse_input(input, len, EG(active_symbol_table), 0, callback, 0 TSRMLS_CC);
}
PHPDBG_WATCH(delete) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
if (phpdbg_delete_var_watchpoint(param->str, param->len TSRMLS_CC) == FAILURE) {
phpdbg_error("Nothing was deleted, no corresponding watchpoint found");
} else {
phpdbg_notice("Removed watchpoint %.*s", (int)param->len, param->str);
}
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_WATCH(recursive) /* {{{ */
{
if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
return SUCCESS;
}
switch (param->type) {
case STR_PARAM:
if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint TSRMLS_CC) != FAILURE) {
phpdbg_notice("Set recursive watchpoint on %.*s", (int)param->len, param->str);
}
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_WATCH(array) /* {{{ */
{
if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
return SUCCESS;
}
switch (param->type) {
case STR_PARAM:
if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint TSRMLS_CC) != FAILURE) {
phpdbg_notice("Set array watchpoint on %.*s", (int)param->len, param->str);
}
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
void phpdbg_watch_HashTable_dtor(zval **zv) {
TSRMLS_FETCH();
phpdbg_btree_result *result;
zval_ptr_dtor_wrapper(zv);
if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)*zv))) {
phpdbg_watchpoint_t *watch = result->ptr;
PHPDBG_G(watchpoint_hit) = 1;
phpdbg_notice("%.*s was removed, removing watchpoint%s", (int)watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE)?" recursively":"");
if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
phpdbg_delete_watchpoint_recursive(watch, 0 TSRMLS_CC);
} else {
zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
}
}
}
int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC) {
if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
return FAILURE;
}
return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_watchpoint TSRMLS_CC);
}
int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC) {
if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
return FAILURE;
}
return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_delete_watchpoint TSRMLS_CC);
}
#ifdef _WIN32
int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC) {
#else
int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) {
#endif
void *page;
phpdbg_watch_memdump *dump;
phpdbg_watchpoint_t *watch;
size_t size;
watch = phpdbg_check_for_watchpoint(
#ifdef _WIN32
addr
#else
info->si_addr
#endif
TSRMLS_CC);
if (watch == NULL) {
return FAILURE;
}
page = phpdbg_get_page_boundary(watch->addr.ptr);
size = phpdbg_get_total_page_size(watch->addr.ptr, watch->size);
/* re-enable writing */
mprotect(page, size, PROT_READ | PROT_WRITE);
dump = malloc(MEMDUMP_SIZE(size));
dump->page = page;
dump->size = size;
memcpy(&dump->data, page, size);
zend_llist_add_element(&PHPDBG_G(watchlist_mem), &dump);
return SUCCESS;
}
void phpdbg_watchpoints_clean(TSRMLS_D) {
zend_hash_clean(&PHPDBG_G(watchpoints));
}
static void phpdbg_watch_dtor(void *pDest) {
TSRMLS_FETCH();
phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest;
phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
phpdbg_remove_watchpoint(watch TSRMLS_CC);
free(watch->str);
free(watch->name_in_parent);
efree(watch);
}
static void phpdbg_watch_mem_dtor(void *llist_data) {
phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data;
/* Disble writing again */
if (dump->reenable_writing) {
mprotect(dump->page, dump->size, PROT_READ);
}
free(*(void **)llist_data);
}
void phpdbg_setup_watchpoints(TSRMLS_D) {
#if _SC_PAGE_SIZE
phpdbg_pagesize = sysconf(_SC_PAGE_SIZE);
#elif _SC_PAGESIZE
phpdbg_pagesize = sysconf(_SC_PAGESIZE);
#elif _SC_NUTC_OS_PAGESIZE
phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE);
#else
phpdbg_pagesize = 4096; /* common pagesize */
#endif
zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1);
phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8);
phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8);
_zend_hash_init(&PHPDBG_G(watchpoints), 8, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC);
}
static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump TSRMLS_DC) {
/* fetch all changes between dump->page and dump->page + dump->size */
phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong)dump->page, (zend_ulong)dump->page + dump->size);
phpdbg_btree_result *result, *htresult;
int elementDiff;
void *curTest;
dump->reenable_writing = 0;
while ((result = phpdbg_btree_next(&pos))) {
phpdbg_watchpoint_t *watch = result->ptr, *htwatch;
void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page);
char reenable = 1;
if (watch->addr.ptr < dump->page || watch->addr.ptr + watch->size > dump->page + dump->size) {
continue;
}
/* Test if the zval was separated and if necessary move the watchpoint */
if (zend_hash_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1, &curTest) == SUCCESS) {
if (watch->type == WATCH_ON_HASHTABLE) {
switch (Z_TYPE_PP((zval **)curTest)) {
case IS_ARRAY:
curTest = (void *)Z_ARRVAL_PP((zval **)curTest);
break;
case IS_OBJECT:
curTest = (void *)Z_OBJPROP_PP((zval **)curTest);
break;
}
} else {
curTest = *(void **)curTest;
}
if (curTest != watch->addr.ptr) {
phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
phpdbg_remove_watchpoint(watch TSRMLS_CC);
watch->addr.ptr = curTest;
phpdbg_store_watchpoint(watch TSRMLS_CC);
phpdbg_activate_watchpoint(watch TSRMLS_CC);
reenable = 0;
}
}
/* Show to the user what changed and delete watchpoint upon removal */
if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) {
if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS || (watch->type == WATCH_ON_ZVAL && memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value))) || (watch->type == WATCH_ON_HASHTABLE
#if ZEND_DEBUG
&& !watch->addr.ht->inconsistent
#endif
&& zend_hash_num_elements((HashTable *)oldPtr) != zend_hash_num_elements(watch->addr.ht))) {
PHPDBG_G(watchpoint_hit) = 1;
phpdbg_notice("Breaking on watchpoint %s", watch->str);
}
switch (watch->type) {
case WATCH_ON_ZVAL: {
int removed = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc && !zend_symtable_exists(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1);
int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value));
int show_ref = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc || ((zval *)oldPtr)->is_ref__gc != watch->addr.zv->is_ref__gc;
if (removed || show_value) {
phpdbg_write("Old value: ");
if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) {
phpdbg_writeln("Value inaccessible, HashTable already destroyed");
} else {
zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC);
phpdbg_writeln("");
}
}
if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) {
phpdbg_writeln("Old refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc);
}
/* check if zval was removed */
if (removed) {
phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
reenable = 0;
if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) {
goto remove_ht_watch;
}
break;
}
if (show_value) {
phpdbg_write("New value: ");
zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC);
phpdbg_writeln("");
}
if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) {
phpdbg_writeln("New refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc);
}
if ((Z_TYPE_P(watch->addr.zv) == IS_ARRAY && Z_ARRVAL_P(watch->addr.zv) != Z_ARRVAL_P((zval *)oldPtr)) || (Z_TYPE_P(watch->addr.zv) != IS_OBJECT && Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
/* add new watchpoints if necessary */
if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
}
}
if ((Z_TYPE_P((zval *)oldPtr) != IS_ARRAY || Z_ARRVAL_P(watch->addr.zv) == Z_ARRVAL_P((zval *)oldPtr)) && (Z_TYPE_P((zval *)oldPtr) != IS_OBJECT || Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
break;
}
remove_ht_watch:
if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) {
htwatch = htresult->ptr;
zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len);
}
break;
}
case WATCH_ON_HASHTABLE:
#ifdef ZEND_DEBUG
if (watch->addr.ht->inconsistent) {
phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
reenable = 0;
break;
}
#endif
elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht);
if (elementDiff) {
if (elementDiff > 0) {
phpdbg_writeln("%d elements were removed from the array", elementDiff);
} else {
phpdbg_writeln("%d elements were added to the array", -elementDiff);
/* add new watchpoints if necessary */
if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
}
}
}
if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) {
phpdbg_writeln("Internal pointer of array was changed");
}
break;
}
}
dump->reenable_writing = dump->reenable_writing | reenable;
}
}
int phpdbg_print_changed_zvals(TSRMLS_D) {
zend_llist_position pos;
phpdbg_watch_memdump **dump;
int ret;
if (zend_llist_count(&PHPDBG_G(watchlist_mem)) == 0) {
return FAILURE;
}
dump = (phpdbg_watch_memdump **)zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos);
do {
phpdbg_print_changed_zval(*dump TSRMLS_CC);
} while ((dump = (phpdbg_watch_memdump **)zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos)));
zend_llist_clean(&PHPDBG_G(watchlist_mem));
ret = PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE;
PHPDBG_G(watchpoint_hit) = 0;
return ret;
}
void phpdbg_list_watchpoints(TSRMLS_D) {
HashPosition position;
phpdbg_watchpoint_t **watch;
for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(watchpoints), &position);
zend_hash_get_current_data_ex(&PHPDBG_G(watchpoints), (void**) &watch, &position) == SUCCESS;
zend_hash_move_forward_ex(&PHPDBG_G(watchpoints), &position)) {
phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str);
}
}
void phpdbg_watch_efree(void *ptr) {
TSRMLS_FETCH();
phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr);
if (result) {
phpdbg_watchpoint_t *watch = result->ptr;
if ((size_t)watch->addr.ptr + watch->size > (size_t)ptr) {
zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
}
}
PHPDBG_G(original_free_function)(ptr);
}

112
sapi/phpdbg/phpdbg_watch.h Normal file
View File

@ -0,0 +1,112 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2014 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_WATCH_H
#define PHPDBG_WATCH_H
#include "TSRM.h"
#include "phpdbg_cmd.h"
#ifdef _WIN32
# include "phpdbg_win.h"
#endif
#define PHPDBG_WATCH(name) PHPDBG_COMMAND(watch_##name)
/**
* Printer Forward Declarations
*/
PHPDBG_WATCH(array);
PHPDBG_WATCH(delete);
PHPDBG_WATCH(recursive);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_watch_commands[] = {
PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s"),
PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s"),
PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s"),
PHPDBG_END_COMMAND
};
/* Watchpoint functions/typedefs */
typedef enum {
WATCH_ON_ZVAL,
WATCH_ON_HASHTABLE,
} phpdbg_watchtype;
#define PHPDBG_WATCH_SIMPLE 0x0
#define PHPDBG_WATCH_RECURSIVE 0x1
typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t;
struct _phpdbg_watchpoint_t {
phpdbg_watchpoint_t *parent;
HashTable *parent_container;
char *name_in_parent;
size_t name_in_parent_len;
char *str;
size_t str_len;
union {
zval *zv;
HashTable *ht;
void *ptr;
} addr;
size_t size;
phpdbg_watchtype type;
char flags;
};
void phpdbg_setup_watchpoints(TSRMLS_D);
#ifndef _WIN32
int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC);
#else
int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC);
#endif
void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch);
void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch);
int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC);
int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC);
int phpdbg_print_changed_zvals(TSRMLS_D);
void phpdbg_list_watchpoints(TSRMLS_D);
void phpdbg_watch_efree(void *ptr);
static long phpdbg_pagesize;
static zend_always_inline void *phpdbg_get_page_boundary(void *addr) {
return (void *)((size_t)addr & ~(phpdbg_pagesize - 1));
}
static zend_always_inline size_t phpdbg_get_total_page_size(void *addr, size_t size) {
return (size_t)phpdbg_get_page_boundary((void *)((size_t)addr + size - 1)) - (size_t)phpdbg_get_page_boundary(addr) + phpdbg_pagesize;
}
#endif

42
sapi/phpdbg/phpdbg_win.c Normal file
View File

@ -0,0 +1,42 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2014 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "phpdbg.h"
int mprotect(void *addr, size_t size, int protection) {
int var;
return (int)VirtualProtect(addr, size, protection == (PROT_READ | PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, &var);
}
int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp) {
EXCEPTION_RECORD *xr = xp->ExceptionRecord;
CONTEXT *xc = xp->ContextRecord;
if(xr->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
TSRMLS_FETCH();
if (phpdbg_watchpoint_segfault_handler((void *)xr->ExceptionInformation[1] TSRMLS_CC) == SUCCESS) {
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}

37
sapi/phpdbg/phpdbg_win.h Normal file
View File

@ -0,0 +1,37 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2014 The PHP Group |
+----------------------------------------------------------------------+
| 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: |
| http://www.php.net/license/3_01.txt |
| 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> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_WIN_H
#define PHPDBG_WIN_H
#include "winbase.h"
#include "windows.h"
#include "excpt.h"
#define PROT_READ 1
#define PROT_WRITE 2
int mprotect(void *addr, size_t size, int protection);
void phpdbg_win_set_mm_heap(zend_mm_heap *heap);
int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp);
#endif

View File

@ -6,11 +6,16 @@ if (isset($include)) {
$stdout = fopen("php://stdout", "w+");
class phpdbg {
public function isGreat($greeting = null) {
printf(
"%s: %s\n", __METHOD__, $greeting);
return $this;
}
private $sprintf = "%s: %s\n";
public function isGreat($greeting = null) {
printf($this->sprintf, __METHOD__, $greeting);
return $this;
}
}
function mine() {
var_dump(func_get_args());
}
function test($x, $y = 0) {
@ -49,3 +54,34 @@ function phpdbg_test_ob()
echo 'End';
echo $b;
}
$array = [
1,
2,
[3, 4],
[5, 6],
];
$array[] = 7;
array_walk($array, function (&$item) {
if (is_array($item))
$item[0] += 2;
else
$item -= 1;
});
class testClass {
public $a = 2;
protected $b = [1, 3];
private $c = 7;
}
$obj = new testClass;
$test = $obj->a;
$obj->a += 2;
$test -= 2;
unset($obj);