mirror of
https://github.com/php/php-src.git
synced 2024-09-26 20:37:29 +00:00
Merge branch 'PHP-5.6'
This commit is contained in:
commit
823ddab3f3
4
sapi/phpdbg/.gitignore
vendored
4
sapi/phpdbg/.gitignore
vendored
@ -1,5 +1,7 @@
|
||||
.libs/
|
||||
./phpdbg
|
||||
phpdbg
|
||||
*.lo
|
||||
*.o
|
||||
build
|
||||
phpdbg_parser.c
|
||||
phpdbg_parser.h
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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, ¶m TSRMLS_CC);
|
||||
|
||||
switch (type) {
|
||||
case METHOD_PARAM:
|
||||
phpdbg_do_break_method(¶m, NULL TSRMLS_CC);
|
||||
break;
|
||||
|
||||
case FILE_PARAM:
|
||||
phpdbg_do_break_file(¶m, NULL TSRMLS_CC);
|
||||
break;
|
||||
|
||||
case NUMERIC_PARAM:
|
||||
phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC);
|
||||
break;
|
||||
|
||||
case STR_PARAM:
|
||||
phpdbg_do_break_func(¶m, NULL TSRMLS_CC);
|
||||
break;
|
||||
|
||||
default: zend_error(
|
||||
E_WARNING, "unrecognized parameter type %ld", type);
|
||||
}
|
||||
|
||||
phpdbg_do_break(¶m TSRMLS_CC);
|
||||
phpdbg_clear_param(¶m 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
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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); /* }}} */
|
||||
|
@ -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;
|
||||
} /* }}} */
|
||||
|
@ -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
221
sapi/phpdbg/phpdbg_btree.c
Normal 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;
|
||||
}
|
65
sapi/phpdbg/phpdbg_btree.h
Normal file
65
sapi/phpdbg/phpdbg_btree.h
Normal 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
|
@ -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,
|
||||
¶m 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(¶m, 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(¶m 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);
|
||||
} /* }}} */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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
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
348
sapi/phpdbg/phpdbg_lexer.h
Normal 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
131
sapi/phpdbg/phpdbg_lexer.l
Normal 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 */ }
|
||||
%%
|
@ -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) /* {{{ */
|
||||
|
@ -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 */
|
||||
|
@ -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
168
sapi/phpdbg/phpdbg_parser.y
Normal 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; }
|
||||
;
|
||||
|
||||
%%
|
@ -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;
|
||||
} /* }}} */
|
||||
|
@ -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
@ -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[]; /* }}} */
|
||||
|
@ -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;
|
||||
} /* }}} */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
} /* }}} */
|
||||
|
@ -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
789
sapi/phpdbg/phpdbg_watch.c
Normal 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
112
sapi/phpdbg/phpdbg_watch.h
Normal 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
42
sapi/phpdbg/phpdbg_win.c
Normal 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
37
sapi/phpdbg/phpdbg_win.h
Normal 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
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user