Merge branch 'master' into xml-protocol

Conflicts:
	phpdbg.c
	phpdbg.h
	phpdbg_cmd.c
	phpdbg_cmd.h
	phpdbg_frame.c
	phpdbg_info.c
	phpdbg_list.c
	phpdbg_print.c
	phpdbg_prompt.c
	phpdbg_utils.c
	phpdbg_utils.h
	phpdbg_watch.c
This commit is contained in:
Bob Weinand 2014-10-07 13:08:30 +02:00
commit 8bc2881260
29 changed files with 2404 additions and 590 deletions

View File

@ -3,12 +3,15 @@ dnl $Id$
dnl
PHP_ARG_ENABLE(phpdbg, for phpdbg support,
[ --enable-phpdbg Build phpdbg], no, no)
[ --enable-phpdbg Build phpdbg], no, no)
PHP_ARG_ENABLE(phpdbg-webhelper, for phpdbg web SAPI support,
[ --enable-phpdbg-webhelper Build phpdbg web SAPI support], yes, yes)
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
if test "$BUILD_PHPDBG" == "" && test "$PHP_PHPDBG" != "no"; then
AC_HEADER_TIOCGWINSZ
AC_DEFINE(HAVE_PHPDBG, 1, [ ])
@ -18,8 +21,19 @@ if test "$PHP_PHPDBG" != "no"; then
AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
fi
if test "$PHP_PHPDBG_WEBHELPER" != "no"; then
if ! test -d ext/phpdbg_webhelper; then
ln -s ../sapi/phpdbg ext/phpdbg_webhelper
fi
if test "$PHP_JSON" != "no"; then
PHP_NEW_EXTENSION(phpdbg_webhelper, phpdbg_rinit_hook.c phpdbg_webdata_transfer.c, $ext_shared)
else
AC_MSG_ERROR(Webhelper extension of phpdbg needs json enabled)
fi
fi
PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
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"
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 phpdbg_sigsafe.c phpdbg_wait.c"
if test "$PHP_READLINE" != "no" -o "$PHP_LIBEDIT" != "no"; then
PHPDBG_EXTRA_LIBS="$PHP_READLINE_LIBS"

168
phpdbg.c
View File

@ -36,6 +36,7 @@
# include <sys/select.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/poll.h>
# include <netinet/in.h>
# include <unistd.h>
# include <arpa/inet.h>
@ -43,6 +44,10 @@
ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals)
PHP_INI_END()
static zend_bool phpdbg_booted = 0;
#if PHP_VERSION_ID >= 50500
@ -69,19 +74,26 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
pg->bp_count = 0;
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->io, 0, sizeof(pg->io));
pg->frame.num = 0;
pg->sapi_name_ptr = NULL;
pg->socket_fd = -1;
pg->socket_server_fd = -1;
pg->req_id = 0;
pg->err_buf.active = 0;
pg->err_buf.type = 0;
pg->input_buflen = 0;
pg->sigsafe_mem.mem = NULL;
pg->sigsegv_bailout = NULL;
} /* }}} */
static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
{
ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
REGISTER_INI_ENTRIES();
#if PHP_VERSION_ID >= 50500
zend_execute_old = zend_execute_ex;
zend_execute_ex = phpdbg_execute_ex;
@ -184,6 +196,7 @@ 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(file_sources));
zend_hash_destroy(&PHPDBG_G(watchpoints));
zend_llist_destroy(&PHPDBG_G(watchlist_mem));
@ -451,7 +464,7 @@ static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */
}
do {
switch (phpdbg_interactive(TSRMLS_C)) {
switch (phpdbg_interactive(1 TSRMLS_CC)) {
case PHPDBG_LEAVE:
case PHPDBG_FINISH:
case PHPDBG_UNTIL:
@ -521,6 +534,9 @@ static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {
static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */
{
if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
send(PHPDBG_G(socket_fd), message, length, 0);
}
return phpdbg_script(P_STDOUT, "%.*s", length, message);
} /* }}} */
@ -563,7 +579,9 @@ static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
TSRMLS_FETCH();
#endif
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
if (!phpdbg_active_sigsafe_mem(TSRMLS_C)) {
fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
}
} /* }}} */
/* copied from sapi/cli/php_cli.c cli_register_file_handles */
@ -739,17 +757,27 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */
{
TSRMLS_FETCH();
if (EG(in_execution)) {
/* set signalled only when not interactive */
if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
}
} else {
if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
/* we quit remote consoles on recv SIGINT */
if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) {
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
zend_bailout();
}
} else {
/* set signalled only when not interactive */
if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
phpdbg_set_sigsafe_mem(mem TSRMLS_CC);
zend_try {
phpdbg_force_interruption(TSRMLS_C);
} zend_end_try()
phpdbg_clear_sigsafe_mem(TSRMLS_C);
return;
}
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
}
}
} /* }}} */
@ -810,12 +838,12 @@ static int phpdbg_remote_init(const char* address, short port, int *server, int
*server = phpdbg_open_socket(address, port);
if (*server < 0) {
phpdbg_rlog(stderr, "Initializing connection on %s:%d failed", address, port);
phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%d failed", address, port);
return FAILURE;
}
phpdbg_rlog(stderr, "accepting connections on %s:%d", address, port);
phpdbg_rlog(fileno(stderr), "accepting connections on %s:%d", address, port);
{
struct sockaddr_in address;
socklen_t size = sizeof(address);
@ -825,7 +853,7 @@ static int phpdbg_remote_init(const char* address, short port, int *server, int
*socket = accept(*server, (struct sockaddr *) &address, &size);
inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
phpdbg_rlog(stderr, "connection established from %s", buffer);
phpdbg_rlog(fileno(stderr), "connection established from %s", buffer);
}
dup2(*socket, fileno(stdout));
@ -835,9 +863,65 @@ static int phpdbg_remote_init(const char* address, short port, int *server, int
*stream = fdopen(*socket, "r+");
phpdbg_set_async_io(*socket);
return SUCCESS;
}
/* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */
void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */
{
int flags;
size_t newlen;
size_t i/*, last_nl*/;
TSRMLS_FETCH();
// if (!(info->si_band & POLLIN)) {
// return; /* Not interested in writeablility etc., just interested in incoming data */
// }
/* only non-blocking reading, avoid non-blocking writing */
flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0);
fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK);
do {
char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1];
size_t off = 0;
if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) {
break;
}
for (i = 0; i < newlen; i++) {
switch (mem[off + i]) {
case '\x03': /* ^C char */
if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) {
break; /* or quit ??? */
}
if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
phpdbg_set_sigsafe_mem(mem TSRMLS_CC);
zend_try {
phpdbg_force_interruption(TSRMLS_C);
} zend_end_try();
phpdbg_clear_sigsafe_mem(TSRMLS_C);
break;
}
if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) {
PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED;
}
break;
/* case '\n':
zend_llist_add_element(PHPDBG_G(stdin), strndup()
last_nl = PHPDBG_G(stdin_buf).len + i;
break;
*/ }
}
off += i;
} while (0);
fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags);
} /* }}} */
void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
{
int is_handled = FAILURE;
@ -846,6 +930,9 @@ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
switch (sig) {
case SIGBUS:
case SIGSEGV:
if (PHPDBG_G(sigsegv_bailout)) {
LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
}
is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC);
if (is_handled == FAILURE) {
#ifdef ZEND_SIGNALS
@ -931,9 +1018,13 @@ int main(int argc, char **argv) /* {{{ */
#endif
#ifndef _WIN32
struct sigaction sigio_struct;
struct sigaction signal_struct;
signal_struct.sa_sigaction = phpdbg_signal_handler;
signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
sigio_struct.sa_sigaction = phpdbg_sigio_handler;
sigio_struct.sa_flags = SA_SIGINFO;
address = strdup("127.0.0.1");
#endif
@ -1150,18 +1241,6 @@ phpdbg_main:
php_optind++;
}
#ifndef _WIN32
/* setup remote server if necessary */
if (!cleaning && listen > 0) {
if (phpdbg_remote_init(address, listen, &server, &socket, &stream) == FAILURE) {
exit(0);
}
/* set remote flag to stop service shutting down upon quit */
remote = 1;
}
#endif
if (sapi_name) {
phpdbg->name = sapi_name;
}
@ -1216,7 +1295,23 @@ phpdbg_main:
EXCEPTION_POINTERS *xp;
__try {
#endif
zend_mm_heap *mm_heap = phpdbg_mm_get_heap();
zend_mm_heap *mm_heap;
#ifndef _WIN32
/* setup remote server if necessary */
if (!cleaning && listen > 0) {
if (phpdbg_remote_init(address, listen, &server, &socket, &stream) == FAILURE) {
exit(0);
}
sigaction(SIGIO, &sigio_struct, NULL);
/* set remote flag to stop service shutting down upon quit */
remote = 1;
}
#endif
mm_heap = phpdbg_mm_get_heap();
if (mm_heap->use_zend_alloc) {
mm_heap->_malloc = phpdbg_malloc_wrapper;
@ -1227,6 +1322,8 @@ phpdbg_main:
zend_activate(TSRMLS_C);
phpdbg_init_list(TSRMLS_C);
PHPDBG_G(original_free_function) = mm_heap->_free;
mm_heap->_free = phpdbg_watch_efree;
@ -1246,6 +1343,8 @@ phpdbg_main:
sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
#endif
PHPDBG_G(sapi_name_ptr) = sapi_name;
php_output_activate(TSRMLS_C);
php_output_deactivate(TSRMLS_C);
@ -1292,9 +1391,12 @@ phpdbg_main:
}
#endif
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout;
PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout);
PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr;
PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr);
#ifndef _WIN32
PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write;
@ -1371,7 +1473,7 @@ phpdbg_interact:
/* phpdbg main() */
do {
zend_try {
phpdbg_interactive(TSRMLS_C);
phpdbg_interactive(1 TSRMLS_CC);
} zend_catch {
if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) {
FILE *bp_tmp_fp = fopen(bp_tmp_file, "w");
@ -1484,8 +1586,8 @@ phpdbg_out:
}
#endif
if (sapi_name) {
free(sapi_name);
if (PHPDBG_G(sapi_name_ptr)) {
free(PHPDBG_G(sapi_name_ptr));
}
#ifdef _WIN32

116
phpdbg.h
View File

@ -2,7 +2,7 @@
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2014 The PHP Group |
| Copyright (c) 7-4 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 |
@ -64,7 +64,7 @@
# include "TSRM.h"
#endif
#ifdef LIBREADLINE
#ifdef HAVE_LIBREADLINE
# include <readline/readline.h>
# include <readline/history.h>
#endif
@ -72,14 +72,25 @@
# include <editline/readline.h>
#endif
#include "phpdbg_lexer.h"
#include "phpdbg_cmd.h"
#include "phpdbg_bp.h"
#include "phpdbg_utils.h"
#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
/* {{{ remote console headers */
#ifndef _WIN32
# include <sys/socket.h>
# include <sys/un.h>
# include <sys/select.h>
# include <sys/types.h>
#endif /* }}} */
int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
/* {{{ strings */
#define PHPDBG_NAME "phpdbg"
#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.4.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
#define PHPDBG_DEFAULT_PROMPT "prompt>"
/* }}} */
#if !defined(PHPDBG_WEBDATA_TRANSFER_H) && !defined(PHPDBG_WEBHELPER_H)
#ifdef ZTS
# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
@ -87,6 +98,15 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
# define PHPDBG_G(v) (phpdbg_globals.v)
#endif
#include "phpdbg_sigsafe.h"
#include "phpdbg_lexer.h"
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_btree.h"
#include "phpdbg_watch.h"
int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
#define PHPDBG_NEXT 2
#define PHPDBG_UNTIL 3
#define PHPDBG_FINISH 4
@ -96,6 +116,19 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
*/
/* {{{ tables */
#define PHPDBG_BREAK_FILE 0
#define PHPDBG_BREAK_SYM 1
#define PHPDBG_BREAK_OPLINE 2
#define PHPDBG_BREAK_METHOD 3
#define PHPDBG_BREAK_COND 4
#define PHPDBG_BREAK_OPCODE 5
#define PHPDBG_BREAK_FUNCTION_OPLINE 6
#define PHPDBG_BREAK_METHOD_OPLINE 7
#define PHPDBG_BREAK_FILE_OPLINE 8
#define PHPDBG_BREAK_MAP 9
#define PHPDBG_BREAK_TABLES 10 /* }}} */
/* {{{ flags */
#define PHPDBG_HAS_FILE_BP (1<<1)
#define PHPDBG_HAS_SYM_BP (1<<2)
@ -137,8 +170,10 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
#define PHPDBG_SHOW_REFCOUNTS (1<<30)
#define PHPDBG_IN_SIGNAL_HANDLER (1<<30)
#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)
#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
#ifndef _WIN32
@ -147,22 +182,27 @@ int phpdbg_do_parse(phpdbg_param_t *stack, char *input TSRMLS_DC);
# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_BP_ENABLED)
#endif /* }}} */
/* {{{ strings */
#define PHPDBG_NAME "phpdbg"
#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.4.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
#define PHPDBG_DEFAULT_PROMPT "prompt>"
/* }}} */
/* {{{ output descriptors */
#define PHPDBG_STDIN 0
#define PHPDBG_STDOUT 1
#define PHPDBG_STDERR 2
#define PHPDBG_IO_FDS 3 /* }}} */
#define phpdbg_try_access \
{ \
JMP_BUF *__orig_bailout = PHPDBG_G(sigsegv_bailout); \
JMP_BUF __bailout; \
\
PHPDBG_G(sigsegv_bailout) = &__bailout; \
if (SETJMP(__bailout) == 0) {
#define phpdbg_catch_access \
} else { \
PHPDBG_G(sigsegv_bailout) = __orig_bailout;
#define phpdbg_end_try_access() \
} \
PHPDBG_G(sigsegv_bailout) = __orig_bailout; \
}
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
@ -192,8 +232,14 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
int bp_count; /* breakpoint count */
int vmret; /* return from last opcode handler execution */
zend_op_array *(*compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
HashTable file_sources;
FILE *oplog; /* opline log */
FILE *io[PHPDBG_IO_FDS]; /* io */
struct {
FILE *ptr;
int fd;
} io[PHPDBG_IO_FDS]; /* io */
#ifndef _WIN32
size_t (*php_stdiop_write)(php_stream *, const char *, size_t TSRMLS_DC);
#endif
@ -201,7 +247,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
struct {
zend_bool active;
int type;
FILE *fp;
int fd;
char *tag;
char *msg;
int msglen;
@ -215,22 +261,20 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
char *buffer; /* buffer */
zend_bool last_was_newline; /* check if we don't need to output a newline upon next phpdbg_error or phpdbg_notice */
char input_buffer[PHPDBG_MAX_CMD]; /* stdin input buffer */
int input_buflen; /* length of stdin input buffer */
phpdbg_signal_safe_mem sigsafe_mem; /* memory to use in async safe environment (only once!) */
JMP_BUF *sigsegv_bailout; /* bailout address for accesibility probing */
zend_ulong flags; /* phpdbg flags */
char *socket_path; /* phpdbg.path ini setting */
char *sapi_name_ptr; /* store sapi name to free it if necessary to not leak memory */
int socket_fd; /* file descriptor to socket (wait command) (-1 if unused) */
int socket_server_fd; /* file descriptor to master socket (wait command) (-1 if unused) */
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
#endif /* PHPDBG_H */

View File

@ -28,15 +28,15 @@
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#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])
#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s, flags) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10], flags)
/**
* 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_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c", 0),
PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n", 0),
PHPDBG_END_COMMAND
};

View File

@ -636,14 +636,14 @@ PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param
}
/* {{{ */
PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top TSRMLS_DC) {
PHPDBG_API const phpdbg_command_t *phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top TSRMLS_DC) {
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)) {
if (name->len == 1 || command->name_len >= name->len) {
/* match single letter alias */
if (command->alias && (name->len == 1)) {
if (command->alias == (*name->str)) {
@ -655,15 +655,15 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *
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)) {
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)
if (name->len == command->name_len) {
break;
}
} else {
break;
}
@ -677,11 +677,9 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *
switch (matches) {
case 0:
if (parent) {
phpdbg_error("command", "type=\"notfound\" command=\"%s\" subcommand=\"%s\"", "The command \"%s %s\" could not be found",
parent->name, name->str);
phpdbg_error("command", "type=\"notfound\" command=\"%s\" subcommand=\"%s\"", "The command \"%s %s\" could not be found", parent->name, name->str);
} else {
phpdbg_error("command", "type=\"notfound\" command=\"%s\"", "The command \"%s\" could not be found",
name->str);
phpdbg_error("command", "type=\"notfound\" command=\"%s\"", "The command \"%s\" could not be found", name->str);
}
return parent;
@ -698,9 +696,9 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *
while (it < matches) {
if (!list) {
list = emalloc(matched[it]->name_len + 1 + ((it + 1) < matches ? sizeof(", ") - 1 : 0));
list = emalloc(matched[it]->name_len + 1 + (it + 1 < matches ? sizeof(", ") - 1 : 0));
} else {
list = erealloc(list, (pos + matched[it]->name_len) + 1 + ((it + 1) < matches ? sizeof(", ") - 1 : 0));
list = erealloc(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;
@ -714,8 +712,7 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *
}
/* ", " separated matches */
phpdbg_error("command", "type=\"ambiguous\" command=\"%s\" matches=\"%lu\" matched=\"%s\"", "The command \"%s\" is ambigious, matching %lu commands (%s)",
name->str, matches, list);
phpdbg_error("command", "type=\"ambiguous\" command=\"%s\" matches=\"%lu\" matched=\"%s\"", "The command \"%s\" is ambigious, matching %lu commands (%s)", name->str, matches, list);
efree(list);
return NULL;
@ -732,7 +729,7 @@ PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *
} /* }}} */
/* {{{ */
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC) {
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, zend_bool allow_async_unsafe TSRMLS_DC) {
phpdbg_param_t *top = NULL;
const phpdbg_command_t *handler = NULL;
@ -746,7 +743,7 @@ PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC) {
return FAILURE;
}
top = (phpdbg_param_t*) stack->next;
top = (phpdbg_param_t *) stack->next;
switch (top->type) {
case EVAL_PARAM:
@ -755,11 +752,18 @@ PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC) {
return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC);
case RUN_PARAM:
if (!allow_async_unsafe) {
phpdbg_error("signalsegv", "command=\"run\"", "run command is disallowed during hard interrupt");
}
phpdbg_activate_err_buf(0 TSRMLS_CC);
phpdbg_free_err_buf(TSRMLS_C);
return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC);
case SHELL_PARAM:
if (!allow_async_unsafe) {
phpdbg_error("signalsegv", "command=\"sh\"", "sh command is disallowed during hard interrupt");
return FAILURE;
}
phpdbg_activate_err_buf(0 TSRMLS_CC);
phpdbg_free_err_buf(TSRMLS_C);
return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC);
@ -768,9 +772,14 @@ PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC) {
handler = phpdbg_stack_resolve(phpdbg_prompt_commands, NULL, &top TSRMLS_CC);
if (handler) {
if (!allow_async_unsafe && !(handler->flags & PHPDBG_ASYNC_SAFE)) {
phpdbg_error("signalsegv", "command=\"%s\"", "%s command is disallowed during hard interrupt", handler->name);
return FAILURE;
}
phpdbg_activate_err_buf(0 TSRMLS_CC);
phpdbg_free_err_buf(TSRMLS_C);
if (phpdbg_stack_verify(handler, &top TSRMLS_CC) == SUCCESS) {
phpdbg_activate_err_buf(0 TSRMLS_CC);
phpdbg_free_err_buf(TSRMLS_C);
return handler->handler(top TSRMLS_CC);
}
}
@ -787,15 +796,11 @@ PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC) {
PHPDBG_API char *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
{
char *cmd = NULL;
#if !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDIT)
char buf[PHPDBG_MAX_CMD];
#endif
char *buffer = NULL;
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
(buffered == NULL)) {
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && (buffered == NULL) && !phpdbg_active_sigsafe_mem(TSRMLS_C)) {
fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr);
}
if (buffered == NULL) {
@ -806,35 +811,56 @@ disconnect:
return NULL;
}
#if !defined(HAVE_LIBREADLINE) && !defined(HAVE_LIBEDIT)
#define USE_LIB_STAR (defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT))
#if !USE_LIB_STAR
if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
if (!phpdbg_out("%s", phpdbg_get_prompt(TSRMLS_C))) {
goto disconnect;
}
PHPDBG_G(last_was_newline) = 1;
}
#endif
/* note: EOF is ignored */
/* note: EOF makes readline write prompt again in local console mode - and ignored if compiled without readline */
/* strongly assuming to be in blocking mode... */
#if USE_LIB_STAR
readline:
if (!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
/* the user has gone away */
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
goto disconnect;
} else goto readline;
}
cmd = buf;
#else
/* note: EOF makes readline write prompt again in local console mode */
readline:
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE)
#endif
{
char buf[PHPDBG_MAX_CMD];
if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
cmd = buf;
} else goto disconnect;
} else {
int bytes = 0, len = PHPDBG_G(input_buflen);
if (PHPDBG_G(input_buflen)) {
memcpy(buf, PHPDBG_G(input_buffer), len);
}
while ((bytes = read(PHPDBG_G(io)[PHPDBG_STDIN].fd, buf + len, PHPDBG_MAX_CMD - len)) > 0) {
int i;
for (i = len; i < len + bytes; i++) {
if (buf[i] == '\n') {
PHPDBG_G(input_buflen) = len + bytes - 1 - i;
if (PHPDBG_G(input_buflen)) {
memcpy(PHPDBG_G(input_buffer), buf + i + 1, PHPDBG_G(input_buflen));
}
if (i != PHPDBG_MAX_CMD - 1) {
buf[i + 1] = 0;
}
cmd = buf;
goto end;
}
}
len += bytes;
}
if (bytes <= 0) {
goto disconnect;
}
cmd = buf;
}
#if USE_LIB_STAR
else {
cmd = readline(phpdbg_get_prompt(TSRMLS_C));
PHPDBG_G(last_was_newline) = 1;
}
if (!cmd) {
@ -845,13 +871,15 @@ readline:
add_history(cmd);
}
#endif
} else cmd = buffered;
} else {
cmd = buffered;
}
end:
PHPDBG_G(last_was_newline) = 1;
buffer = estrdup(cmd);
#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT)
if (!buffered && cmd &&
!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
#if USE_LIB_STAR
if (!buffered && cmd && !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
free(cmd);
}
#endif
@ -885,4 +913,3 @@ PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */
{
efree(*input);
} /* }}} */

View File

@ -86,6 +86,8 @@ struct _phpdbg_param {
#define YYSTYPE phpdbg_param_t
#endif
#define PHPDBG_ASYNC_SAFE 1
typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC);
typedef struct _phpdbg_command_t phpdbg_command_t;
@ -97,8 +99,9 @@ struct _phpdbg_command_t {
char alias; /* Alias */
phpdbg_command_handler_t handler; /* Command handler */
const phpdbg_command_t *subs; /* Sub Commands */
char *args; /* Argument Spec */
const phpdbg_command_t *parent; /* Parent Command */
char *args; /* Argument Spec */
const phpdbg_command_t *parent; /* Parent Command */
zend_bool flags; /* General flags */
};
/* }}} */
@ -133,9 +136,9 @@ PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC);
* 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 TSRMLS_DC);
PHPDBG_API const phpdbg_command_t *phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top TSRMLS_DC);
PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack TSRMLS_DC);
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack TSRMLS_DC);
PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, zend_bool allow_async_unsafe TSRMLS_DC);
PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack);
/*
@ -155,20 +158,20 @@ PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg)
*/
#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
#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_EXP(name, tip, alias, handler, children, args, parent, flags) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent, flags}
#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_D_EX(name, tip, alias, handler, children, args, flags) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL, flags}
#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_D(name, tip, alias, children, args, flags) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL, flags}
#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC)
#define PHPDBG_COMMAND_ARGS param TSRMLS_CC
#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL}
#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL, 0}
/*
* Default Switch Case

View File

@ -56,15 +56,20 @@ void phpdbg_switch_frame(int frame TSRMLS_DC) /* {{{ */
return;
}
while (execute_data) {
if (i++ == frame) {
break;
}
phpdbg_try_access {
while (execute_data) {
if (i++ == frame) {
break;
}
do {
execute_data = execute_data->prev_execute_data;
} while (execute_data && execute_data->opline == NULL);
}
do {
execute_data = execute_data->prev_execute_data;
} while (execute_data && execute_data->opline == NULL);
}
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Couldn't switch frames, invalid data source");
return;
} phpdbg_end_try_access();
if (execute_data == NULL) {
phpdbg_error("frame", "type=\"maxnum\" id=\"%d\"", "No frame #%d", frame);
@ -106,16 +111,12 @@ static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */
char is_class;
int has_args = FAILURE;
zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"),
(void **)&funcname);
zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), (void **) &funcname);
if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp),
"object", sizeof("object"), (void **)&class)) == FAILURE) {
is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"),
(void **)&class);
if ((is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "object", sizeof("object"), (void **) &class)) == FAILURE) {
is_class = zend_hash_find(Z_ARRVAL_PP(tmp), "class", sizeof("class"), (void **)&class);
} else {
zend_get_object_classname(*class, (const char **)&Z_STRVAL_PP(class),
(uint32_t *)&Z_STRLEN_PP(class) TSRMLS_CC);
zend_get_object_classname(*class, (const char **) &Z_STRVAL_PP(class), (zend_uint *) &Z_STRLEN_PP(class) TSRMLS_CC);
}
if (is_class == SUCCESS) {
@ -143,25 +144,32 @@ static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */
if (has_args) {
HashPosition iterator;
const zend_function *func = phpdbg_get_function(
Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC);
const zend_arg_info *arginfo = func ? func->common.arg_info : NULL;
int j = 0, m = func ? func->common.num_args : 0;
const zend_function *func = NULL;
const zend_arg_info *arginfo = NULL;
int j = 0, m;
zend_bool is_variadic = 0;
phpdbg_try_access {
/* assuming no autoloader call is necessary, class should have been loaded if it's in backtrace ... */
if ((func = phpdbg_get_function(Z_STRVAL_PP(funcname), is_class == FAILURE ? NULL : Z_STRVAL_PP(class) TSRMLS_CC))) {
arginfo = func->common.arg_info;
}
} phpdbg_end_try_access();
m = func ? func->common.num_args : 0;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(args), &iterator);
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args),
(void **) &argstmp, &iterator) == SUCCESS) {
while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(args), (void **) &argstmp, &iterator) == SUCCESS) {
if (j) {
phpdbg_out(", ");
}
phpdbg_xml("<arg %r");
if (m && j < m) {
#if PHP_VERSION_ID >= 50600
is_variadic = arginfo[j].is_variadic;
is_variadic = arginfo ? arginfo[j].is_variadic : 0;
#endif
phpdbg_xml(" variadic=\"%s\" name=\"%s\">", is_variadic ? "variadic" : "", arginfo[j].name);
phpdbg_out("%s=%s", arginfo[j].name, is_variadic ? "[": "");
phpdbg_xml(" variadic=\"%s\" name=\"%s\">", is_variadic ? "variadic" : "", arginfo ? arginfo[j].name : "");
phpdbg_out("%s=%s", arginfo ? arginfo[j].name : "?", is_variadic ? "[": "");
} else {
phpdbg_xml(">");
@ -192,22 +200,26 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
if (limit < 0) {
phpdbg_error("backtrace", "type=\"minnum\"", "Invalid backtrace size %d", limit);
return;
}
phpdbg_try_access {
zend_fetch_debug_backtrace(&zbacktrace, 0, 0, limit TSRMLS_CC);
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Couldn't fetch backtrace, invalid data source");
return;
} phpdbg_end_try_access();
phpdbg_xml("<backtrace %r>");
zend_fetch_debug_backtrace(
&zbacktrace, 0, 0, limit TSRMLS_CC);
zend_hash_internal_pointer_reset_ex(Z_ARRVAL(zbacktrace), &position);
zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void**)&tmp, &position);
zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void **) &tmp, &position);
while (1) {
user_defined = zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **)&file);
zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **)&line);
user_defined = zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **) &file);
zend_hash_find(Z_ARRVAL_PP(tmp), "line", sizeof("line"), (void **) &line);
zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position);
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
(void**)&tmp, &position) == FAILURE) {
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace), (void **) &tmp, &position) == FAILURE) {
phpdbg_write("frame", "id=\"%d\" symbol=\"{main}\" file=\"%s\" line=\"%d\"", "frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
break;
}
@ -226,7 +238,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
}
phpdbg_out("\n");
zval_dtor(&zbacktrace);
phpdbg_xml("</backtrace>");
zval_dtor(&zbacktrace);
} /* }}} */

View File

@ -27,19 +27,19 @@
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])
#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s, flags) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14], flags)
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(globals, "show superglobals", 'g', info_globals, 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_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_END_COMMAND
};
@ -63,15 +63,21 @@ PHPDBG_INFO(files) /* {{{ */
HashPosition pos;
char *fname;
phpdbg_notice("includedfilecount", "num=\"%d\"", "Included files: %d",
zend_hash_num_elements(&EG(included_files)));
phpdbg_try_access {
phpdbg_notice("includedfilecount", "num=\"%d\"", "Included files: %d", zend_hash_num_elements(&EG(included_files)));
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Could not fetch included file count, invalid data source");
} phpdbg_end_try_access();
zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos);
while (zend_hash_get_current_key_ex(&EG(included_files), &fname,
NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
phpdbg_writeln("includedfile", "name=\"%s\"", "File: %s", fname);
zend_hash_move_forward_ex(&EG(included_files), &pos);
}
phpdbg_try_access {
zend_hash_internal_pointer_reset_ex(&EG(included_files), &pos);
while (zend_hash_get_current_key_ex(&EG(included_files), &fname, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
phpdbg_writeln("includedfile", "name=\"%s\"", "File: %s", fname);
zend_hash_move_forward_ex(&EG(included_files), &pos);
}
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Could not fetch file name, invalid data source, aborting included file listing");
} phpdbg_end_try_access();
return SUCCESS;
} /* }}} */
@ -79,8 +85,11 @@ PHPDBG_INFO(files) /* {{{ */
PHPDBG_INFO(error) /* {{{ */
{
if (PG(last_error_message)) {
phpdbg_writeln("lasterror", "error=\"%s\" file=\"%s\" line=\"%d\"", "Last error: %s at %s line %d",
PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
phpdbg_try_access {
phpdbg_writeln("lasterror", "error=\"%s\" file=\"%s\" line=\"%d\"", "Last error: %s at %s line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
} phpdbg_catch_access {
phpdbg_notice("lasterror", "error=\"\"", "No error found!");
} phpdbg_end_try_access();
} else {
phpdbg_notice("lasterror", "error=\"\"", "No error found!");
}
@ -89,7 +98,11 @@ PHPDBG_INFO(error) /* {{{ */
static int phpdbg_arm_auto_global(zend_auto_global *auto_global TSRMLS_DC) {
if (auto_global->armed) {
auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC);
if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
phpdbg_notice("variableinfo", "unreachable=\"%.*s\"", "Cannot show information about superglobal variable %.*s", auto_global->name_len, auto_global->name);
} else {
auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC);
}
}
return 0;
@ -117,6 +130,7 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) {
if (show_globals) {
/* that array should only be manipulated during init, so safe for async access during execution */
zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_arm_auto_global TSRMLS_CC);
symtable = &EG(symbol_table);
} else {
@ -125,15 +139,18 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) {
zend_hash_init(&vars, 8, NULL, NULL, 0);
zend_hash_internal_pointer_reset_ex(symtable, &pos);
while (zend_hash_get_current_key_ex(symtable, &var,
NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
zend_hash_get_current_data_ex(symtable, (void **)&data, &pos);
if (zend_is_auto_global(var, strlen(var) TSRMLS_CC) ^ !show_globals) {
zend_hash_update(&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL);
phpdbg_try_access {
zend_hash_internal_pointer_reset_ex(symtable, &pos);
while (zend_hash_get_current_key_ex(symtable, &var, NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
zend_hash_get_current_data_ex(symtable, (void **)&data, &pos);
if (zend_is_auto_global(var, strlen(var) TSRMLS_CC) ^ !show_globals) {
zend_hash_update(&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL);
}
zend_hash_move_forward_ex(symtable, &pos);
}
zend_hash_move_forward_ex(symtable, &pos);
}
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Cannot fetch all data from the symbol table, invalid data source");
} phpdbg_end_try_access();
if (show_globals) {
phpdbg_notice("variableinfo", "num=\"%d\"", "Superglobal variables (%d)", zend_hash_num_elements(&vars));
@ -161,23 +178,35 @@ static int phpdbg_print_symbols(zend_bool show_globals TSRMLS_DC) {
zend_hash_get_current_data_ex(&vars, (void**) &data, &pos) == SUCCESS;
zend_hash_move_forward_ex(&vars, &pos)) {
char *var;
zend_bool invalid_data = 1;
zend_hash_get_current_key_ex(&vars, &var, NULL, NULL, 0, &pos);
if (*data) {
phpdbg_writeln("variable", "address=\"%p\" refcount=\"%d\" type=\"%s\"", "%p\t%d\t(%s)", *data, Z_REFCOUNT_PP(data), zend_zval_type_name(*data));
phpdbg_try_access {
if (!(invalid_data = !*data)) {
phpdbg_writeln("variable", "address=\"%p\" refcount=\"%d\" type=\"%s\"", "%p\t%d\t(%s)", *data, Z_REFCOUNT_PP(data), zend_zval_type_name(*data));
if (Z_TYPE_PP(data) == IS_RESOURCE) {
int type;
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" type=\"%s\"", "%s$%s\n|-------(typeof)------> (%s)\n", Z_ISREF_PP(data) ? "&": "", var, zend_list_find(Z_RESVAL_PP(data), &type) ? zend_rsrc_list_get_rsrc_type(type TSRMLS_CC) : "unknown");
} else if (Z_TYPE_PP(data) == IS_OBJECT) {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" instanceof=\"%s\"", "%s$%s\n|-----(instanceof)----> (%s)\n", Z_ISREF_PP(data) ? "&": "", var, Z_OBJCE_PP(data)->name);
} else {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\"", "%s$%s", Z_ISREF_PP(data) ? "&": "", var);
if (Z_TYPE_PP(data) == IS_RESOURCE) {
phpdbg_try_access {
int type;
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" type=\"%s\"", "%s$%s\n|-------(typeof)------> (%s)\n", Z_ISREF_PP(data) ? "&": "", var, zend_list_find(Z_RESVAL_PP(data), &type) ? zend_rsrc_list_get_rsrc_type(type TSRMLS_CC) : "unknown");
} phpdbg_catch_access {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" type=\"unknown\"", "%s$%s\n|-------(typeof)------> (unknown)\n", Z_ISREF_PP(data) ? "&": "", var);
} phpdbg_end_try_access();
} else if (Z_TYPE_PP(data) == IS_OBJECT) {
phpdbg_try_access {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" instanceof=\"%s\"", "%s$%s\n|-----(instanceof)----> (%s)\n", Z_ISREF_PP(data) ? "&": "", var, Z_OBJCE_PP(data)->name);
} phpdbg_catch_access {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\" instanceof=\"unknown\"", "%s$%s\n|-----(instanceof)----> (unknown)\n", Z_ISREF_PP(data) ? "&": "", var);
} phpdbg_end_try_access();
} else {
phpdbg_writeln("variabledetails", "refstatus=\"%s\" name=\"%s\"", "%s$%s", Z_ISREF_PP(data) ? "&": "", var);
}
}
} else {
phpdbg_writeln("variable", "address=\"\" type=\"unknown\" name=\"%s\"", "n/a\tn/a\tn/a\t$%s", var);
} phpdbg_end_try_access();
if (invalid_data) {
phpdbg_writeln("variabledetails", "name=\"%s\"", "n/a\tn/a\tn/a\t$%s", var);
}
}
}
@ -199,6 +228,7 @@ PHPDBG_INFO(globals) /* {{{ */
PHPDBG_INFO(literal) /* {{{ */
{
/* literals are assumed to not be manipulated during executing of their op_array and as such async safe */
if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) {
zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops);
int literal = 0, count = ops->last_literal-1;
@ -220,8 +250,7 @@ PHPDBG_INFO(literal) /* {{{ */
while (literal < ops->last_literal) {
if (Z_TYPE(ops->literals[literal].constant) != IS_NULL) {
phpdbg_write("literal", "id=\"%u\"", "|-------- C%u -------> [", literal);
zend_print_zval(
&ops->literals[literal].constant, 0);
zend_print_zval(&ops->literals[literal].constant, 0);
phpdbg_out("]\n");
}
literal++;
@ -235,14 +264,31 @@ PHPDBG_INFO(literal) /* {{{ */
PHPDBG_INFO(memory) /* {{{ */
{
if (is_zend_mm(TSRMLS_C)) {
size_t used, real, peak_used, peak_real;
zend_mm_heap *heap;
zend_bool is_mm;
if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC);
}
if ((is_mm = is_zend_mm(TSRMLS_C))) {
used = zend_memory_usage(0 TSRMLS_CC);
real = zend_memory_usage(1 TSRMLS_CC);
peak_used = zend_memory_peak_usage(0 TSRMLS_CC);
peak_real = zend_memory_peak_usage(1 TSRMLS_CC);
}
if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
zend_mm_set_heap(heap TSRMLS_CC);
}
if (is_mm) {
phpdbg_notice("meminfo", "", "Memory Manager Information");
phpdbg_notice("current", "", "Current");
phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (zend_memory_usage(0 TSRMLS_CC)/1024));
phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (zend_memory_usage(1 TSRMLS_CC)/1024));
phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (used / 1024));
phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (real / 1024));
phpdbg_notice("peak", "", "Peak");
phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (zend_memory_peak_usage(0 TSRMLS_CC)/1024));
phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (zend_memory_peak_usage(1 TSRMLS_CC)/1024));
phpdbg_writeln("used", "mem=\"%.3f\"", "|-------> Used:\t%.3f kB", (float) (peak_used / 1024));
phpdbg_writeln("real", "mem=\"%.3f\"", "|-------> Real:\t%.3f kB", (float) (peak_real / 1024));
} else {
phpdbg_error("inactive", "type=\"memory_manager\"", "Memory Manager Disabled!");
}
@ -270,20 +316,24 @@ PHPDBG_INFO(classes) /* {{{ */
zend_hash_init(&classes, 8, NULL, NULL, 0);
for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position);
zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(class_table), &position)) {
if ((*ce)->type == ZEND_USER_CLASS) {
zend_hash_next_index_insert(&classes, ce, sizeof(ce), NULL);
phpdbg_try_access {
for (zend_hash_internal_pointer_reset_ex(EG(class_table), &position);
zend_hash_get_current_data_ex(EG(class_table), (void**)&ce, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(class_table), &position)) {
if ((*ce)->type == ZEND_USER_CLASS) {
zend_hash_next_index_insert(&classes, ce, sizeof(ce), NULL);
}
}
}
} phpdbg_catch_access {
phpdbg_notice("signalsegv", "", "Not all classes could be fetched, possibly invalid data source");
} phpdbg_end_try_access();
phpdbg_notice("classinfo", "num=\"%d\"", "User Classes (%d)", zend_hash_num_elements(&classes));
/* once added, assume that classes are stable... until shutdown. */
for (zend_hash_internal_pointer_reset_ex(&classes, &position);
zend_hash_get_current_data_ex(&classes, (void**)&ce, &position) == SUCCESS;
zend_hash_move_forward_ex(&classes, &position)) {
zend_hash_get_current_data_ex(&classes, (void**)&ce, &position) == SUCCESS;
zend_hash_move_forward_ex(&classes, &position)) {
phpdbg_print_class_name(ce TSRMLS_CC);
@ -319,15 +369,17 @@ PHPDBG_INFO(funcs) /* {{{ */
zend_hash_init(&functions, 8, NULL, NULL, 0);
for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position);
zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(function_table), &position)) {
if (zf->type == ZEND_USER_FUNCTION) {
zend_hash_next_index_insert(
&functions, (void**) &zf, sizeof(zend_function), NULL);
phpdbg_try_access {
for (zend_hash_internal_pointer_reset_ex(EG(function_table), &position);
zend_hash_get_current_data_ex(EG(function_table), (void**)&zf, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(function_table), &position)) {
if (zf->type == ZEND_USER_FUNCTION) {
zend_hash_next_index_insert(&functions, (void**) &zf, sizeof(zend_function), NULL);
}
}
}
} phpdbg_catch_access {
phpdbg_notice("signalsegv", "", "Not all functions could be fetched, possibly invalid data source");
} phpdbg_end_try_access();
phpdbg_notice("functioninfo", "num=\"%d\"", "User Functions (%d)", zend_hash_num_elements(&functions));

View File

@ -34,14 +34,14 @@
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])
#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s, flags) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags)
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_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l", PHPDBG_ASYNC_SAFE),
PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m", PHPDBG_ASYNC_SAFE),
PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_END_COMMAND
};
@ -81,7 +81,7 @@ PHPDBG_LIST(method) /* {{{ */
{
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
if (phpdbg_safe_class_lookup(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));
@ -103,7 +103,7 @@ PHPDBG_LIST(class) /* {{{ */
{
zend_class_entry **ce;
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if (phpdbg_safe_class_lookup(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if ((*ce)->type == ZEND_USER_CLASS) {
if ((*ce)->info.user.filename) {
phpdbg_list_file(
@ -124,24 +124,13 @@ PHPDBG_LIST(class) /* {{{ */
return SUCCESS;
} /* }}} */
void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
void phpdbg_list_file(const char *filename, uint count, uint offset, uint highlight TSRMLS_DC) /* {{{ */
{
struct stat st;
char *opened = NULL;
char buffer[8096] = {0,};
long line = 0;
uint line, lastline;
phpdbg_file_source **data;
php_stream *stream = NULL;
if (VCWD_STAT(filename, &st) == FAILURE) {
phpdbg_error("list", "type=\"statfailure\"", "Failed to stat file %s", filename);
return;
}
stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened);
if (!stream) {
phpdbg_error("list", "type=\"openfailure\"", "Failed to open file %s to list", filename);
if (zend_hash_find(&PHPDBG_G(file_sources), filename, strlen(filename), (void **) &data) == FAILURE) {
phpdbg_error("list", "type=\"unknownfile\"", "Could not find information about included file...");
return;
}
@ -150,37 +139,35 @@ void phpdbg_list_file(const char *filename, long count, long offset, int highlig
offset = 0;
}
phpdbg_xml("<list file=\"%s\">", filename);
lastline = offset + count;
while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) {
long linelen = strlen(buffer);
if (lastline > (*data)->lines) {
lastline = (*data)->lines;
}
++line;
phpdbg_xml("<list %r file=\"%s\">", filename);
if (offset <= line) {
if (!highlight) {
phpdbg_write("line", "line=\"%d\" code=\"%s\"", " %05ld: %s", line, buffer);
for (line = offset; line < lastline;) {
uint linestart = (*data)->line[line++];
uint linelen = (*data)->line[line] - linestart;
char *buffer = (*data)->buf + linestart;
if (!highlight) {
phpdbg_write("line", "line=\"%u\" code=\"%s\"", " %05u: %s", line, buffer);
} else {
if (highlight != line) {
phpdbg_write("line", "line=\"%u\" code=\"%s\"", " %05u: %s", line, buffer);
} else {
if (highlight != line) {
phpdbg_write("line", "line=\"%ld\" code=\"%s\"", " %05ld: %s", line, buffer);
} else {
phpdbg_write("line", "line=\"%ld\" code=\"%s\" current=\"current\"", ">%05ld: %s", line, buffer);
}
}
if (buffer[linelen - 1] != '\n') {
phpdbg_out("\n");
phpdbg_write("line", "line=\"%u\" code=\"%s\" current=\"current\"", ">%05u: %s", line, buffer);
}
}
if (count > 0 && count + offset - 1 < line) {
break;
if (*(buffer + linelen - 1) != '\n') {
phpdbg_out("\n");
}
}
phpdbg_xml("</list>");
php_stream_close(stream);
} /* }}} */
void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
@ -192,10 +179,9 @@ void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
return;
}
ops = (zend_op_array*)fbc;
ops = (zend_op_array *)fbc;
phpdbg_list_file(ops->filename,
ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC);
phpdbg_list_file(ops->filename, ops->line_end - ops->line_start + 1, ops->line_start, 0 TSRMLS_CC);
} /* }}} */
void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ */
@ -226,12 +212,85 @@ void phpdbg_list_function_byname(const char *str, size_t len TSRMLS_DC) /* {{{ *
/* use lowercase names, case insensitive */
func_name = zend_str_tolower_dup(func_name, func_name_len);
if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) {
phpdbg_list_function(fbc TSRMLS_CC);
} else {
phpdbg_error("list", "type=\"nofunction\" function=\"%s\"", "Function %s not found", func_name);
}
phpdbg_try_access {
if (zend_hash_find(func_table, func_name, func_name_len+1, (void**)&fbc) == SUCCESS) {
phpdbg_list_function(fbc TSRMLS_CC);
} else {
phpdbg_error("list", "type=\"nofunction\" function=\"%s\"", "Function %s not found", func_name);
}
} phpdbg_catch_access {
phpdbg_error("signalsegv", "function=\"%s\"", "Could not list function %s, invalid data source", func_name);
} phpdbg_end_try_access();
efree(func_name);
} /* }}} */
zend_op_array *phpdbg_compile_file(zend_file_handle *file, int type TSRMLS_DC) {
phpdbg_file_source data, *dataptr;
zend_file_handle fake = {0};
zend_op_array *ret;
char *filename = (char *)(file->opened_path ? file->opened_path : file->filename);
uint line;
char *bufptr, *endptr;
zend_stream_fixup(file, &data.buf, &data.len TSRMLS_CC);
data.filename = filename;
data.line[0] = 0;
if (file->handle.stream.mmap.old_closer) {
/* do not unmap */
file->handle.stream.closer = file->handle.stream.mmap.old_closer;
}
#if HAVE_MMAP
if (file->handle.stream.mmap.map) {
data.map = file->handle.stream.mmap.map;
}
#endif
fake.type = ZEND_HANDLE_MAPPED;
fake.handle.stream.mmap.buf = data.buf;
fake.handle.stream.mmap.len = data.len;
fake.free_filename = 0;
fake.opened_path = file->opened_path;
fake.filename = filename;
fake.opened_path = file->opened_path;
*(dataptr = emalloc(sizeof(phpdbg_file_source) + sizeof(uint) * data.len)) = data;
zend_hash_add(&PHPDBG_G(file_sources), filename, strlen(filename), &dataptr, sizeof(phpdbg_file_source *), NULL);
for (line = 0, bufptr = data.buf - 1, endptr = data.buf + data.len; ++bufptr < endptr;) {
if (*bufptr == '\n') {
dataptr->line[++line] = (uint)(bufptr - data.buf) + 1;
}
}
dataptr = erealloc(dataptr, sizeof(phpdbg_file_source) + sizeof(uint) * line);
dataptr->lines = ++line;
ret = PHPDBG_G(compile_file)(&fake, type TSRMLS_CC);
fake.opened_path = NULL;
zend_file_handle_dtor(&fake TSRMLS_CC);
return ret;
}
void phpdbg_free_file_source(phpdbg_file_source *data) {
#if HAVE_MMAP
if (data->map) {
munmap(data->map, data->len + ZEND_MMAP_AHEAD);
} else
#endif
if (data->buf) {
efree(data->buf);
}
efree(data);
}
void phpdbg_init_list(TSRMLS_D) {
PHPDBG_G(compile_file) = zend_compile_file;
zend_hash_init(&PHPDBG_G(file_sources), 1, NULL, (dtor_func_t) phpdbg_free_file_source, 0);
zend_compile_file = phpdbg_compile_file;
}

View File

@ -34,8 +34,21 @@ PHPDBG_LIST(func);
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);
void phpdbg_list_file(const char*, uint, uint, uint TSRMLS_DC);
extern const phpdbg_command_t phpdbg_list_commands[];
void phpdbg_init_list(TSRMLS_D);
typedef struct {
char *filename;
char *buf;
size_t len;
#if HAVE_MMAP
void *map;
#endif
uint lines;
uint line[1];
} phpdbg_file_source;
#endif /* PHPDBG_LIST_H */

View File

@ -156,7 +156,7 @@ void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, ze
}
if (!ignore_flags && PHPDBG_G(oplog)) {
phpdbg_log_ex(PHPDBG_G(oplog), "L%-5u %16p %-30s %s %s",
phpdbg_log_ex(fileno(PHPDBG_G(oplog)), "L%-5u %16p %-30s %s %s",
opline->lineno,
opline,
phpdbg_decode_opcode(opline->opcode),

View File

@ -26,16 +26,16 @@
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])
#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s, flags) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9], flags)
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_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m", PHPDBG_ASYNC_SAFE),
PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_END_COMMAND
};
@ -109,7 +109,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC)
PHPDBG_PRINT(exec) /* {{{ */
{
if (PHPDBG_G(exec)) {
if (!PHPDBG_G(ops)) {
if (!PHPDBG_G(ops) && !(PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER)) {
phpdbg_compile(TSRMLS_C);
}
@ -155,7 +155,7 @@ PHPDBG_PRINT(class) /* {{{ */
{
zend_class_entry **ce;
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if (phpdbg_safe_class_lookup(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
phpdbg_notice("printinfo", "type=\"%s\" flag=\"%s\" class=\"%s\" num=\"%d\"", "%s %s: %s (%d methods)",
((*ce)->type == ZEND_USER_CLASS) ?
"User" : "Internal",
@ -192,7 +192,7 @@ PHPDBG_PRINT(method) /* {{{ */
{
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
if (phpdbg_safe_class_lookup(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));
@ -240,19 +240,23 @@ PHPDBG_PRINT(func) /* {{{ */
func_table = EG(function_table);
}
lcname = zend_str_tolower_dup(func_name, func_name_len);
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("printinfo", "type=\"%s\" flags=\"%s\" symbol=\"%s\" num=\"%d\"", "%s %s %s (%d ops)",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
(fbc->common.scope) ? "Method" : "Function",
fbc->common.function_name,
(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0 );
phpdbg_try_access {
if (zend_hash_find(func_table, lcname, func_name_len + 1, (void **) &fbc) == SUCCESS) {
phpdbg_notice("printinfo", "type=\"%s\" flags=\"%s\" symbol=\"%s\" num=\"%d\"", "%s %s %s (%d ops)",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
(fbc->common.scope) ? "Method" : "Function",
fbc->common.function_name,
(fbc->type == ZEND_USER_FUNCTION) ? fbc->op_array.last : 0);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("print", "type=\"nofunction\" function=\"%s\"", "The function %s could not be found", func_name);
}
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("print", "type=\"nofunction\" function=\"%s\"", "The function %s could not be found", func_name);
}
} phpdbg_catch_access {
phpdbg_error("signalsegv", "function=\"%.*s\"", "Couldn't fetch function %.*s, invalid data source", (int) func_name_len, func_name);
} phpdbg_end_try_access();
efree(lcname);

View File

@ -23,6 +23,7 @@
#include "zend.h"
#include "zend_compile.h"
#include "phpdbg.h"
#include "phpdbg_help.h"
#include "phpdbg_print.h"
#include "phpdbg_info.h"
@ -37,38 +38,54 @@
#include "phpdbg_frame.h"
#include "phpdbg_lexer.h"
#include "phpdbg_parser.h"
#include "phpdbg_wait.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
#ifdef HAVE_LIBDL
#ifdef PHP_WIN32
#include "win32/param.h"
#include "win32/winutil.h"
#define GET_DL_ERROR() php_win_err()
#elif defined(NETWARE)
#include <sys/param.h>
#define GET_DL_ERROR() dlerror()
#else
#include <sys/param.h>
#define GET_DL_ERROR() DL_ERROR()
#endif
#endif
/* {{{ command declarations */
const phpdbg_command_t phpdbg_prompt_commands[] = {
PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"),
PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0),
PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0),
PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"),
PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"),
PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0),
PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0),
PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0),
PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0),
PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"),
PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"),
PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"),
PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"),
PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"),
PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0),
PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0),
PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s"),
PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"),
PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"),
PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"),
PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"),
PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"),
PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0),
PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"),
PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0),
PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0),
PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0),
PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0),
PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0),
PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0, 0),
PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0),
PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0),
PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0),
PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s", 0),
PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0),
PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0),
PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE),
PHPDBG_COMMAND_D(wait, "wait for other process", 'W', NULL, 0, 0),
PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0),
PHPDBG_END_COMMAND
}; /* }}} */
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */
{
phpdbg_param_t *name = NULL;
@ -246,7 +263,7 @@ void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_
phpdbg_activate_err_buf(1 TSRMLS_CC);
if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) {
switch (phpdbg_stack_execute(&stack TSRMLS_CC)) {
switch (phpdbg_stack_execute(&stack, 1 /* allow_async_unsafe == 1 */ TSRMLS_CC)) {
case FAILURE:
phpdbg_activate_err_buf(0 TSRMLS_CC);
if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
@ -345,6 +362,8 @@ PHPDBG_COMMAND(exec) /* {{{ */
PHPDBG_G(exec) = res;
PHPDBG_G(exec_len) = res_len;
VCWD_CHDIR_FILE(res);
*SG(request_info).argv = PHPDBG_G(exec);
php_hash_environment(TSRMLS_C);
@ -621,6 +640,11 @@ PHPDBG_COMMAND(run) /* {{{ */
}
} zend_end_try();
if (PHPDBG_G(socket_fd) != -1) {
close(PHPDBG_G(socket_fd));
PHPDBG_G(socket_fd) = -1;
}
if (restore) {
if (EG(exception)) {
phpdbg_handle_exception(TSRMLS_C);
@ -639,11 +663,33 @@ out:
return SUCCESS;
} /* }}} */
int phpdbg_output_ev_variable(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv TSRMLS_DC) {
phpdbg_notice("eval", "variable=\"%.*s\"", "Printing variable %.*s", (int) len, name);
phpdbg_xml("<eval>");
zend_print_zval_r(*zv, 0 TSRMLS_CC);
phpdbg_xml("</eval>");
phpdbg_out("\n");
efree(name);
efree(keyname);
return SUCCESS;
}
PHPDBG_COMMAND(ev) /* {{{ */
{
zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING) == PHPDBG_IS_STEPPING);
zval retval;
if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
phpdbg_try_access {
phpdbg_parse_variable(param->str, param->len, &EG(symbol_table), 0, phpdbg_output_ev_variable, 0 TSRMLS_CC);
} phpdbg_catch_access {
phpdbg_error("signalsegv", "", "Could not fetch data, invalid data source");
} phpdbg_end_try_access();
return SUCCESS;
}
if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
}
@ -802,6 +848,193 @@ PHPDBG_COMMAND(sh) /* {{{ */
return SUCCESS;
} /* }}} */
static int add_module_info(zend_module_entry *module TSRMLS_DC) {
phpdbg_write("module", "name=\"%s\"", "%s\n", module->name);
return 0;
}
static int add_zendext_info(zend_extension *ext TSRMLS_DC) {
phpdbg_write("extension", "name=\"%s\"", "%s\n", ext->name);
return 0;
}
PHPDBG_API const char *phpdbg_load_module_or_extension(char **path, char **name TSRMLS_DC) {
DL_HANDLE handle;
char *extension_dir;
extension_dir = INI_STR("extension_dir");
if (strchr(*path, '/') != NULL || strchr(*path, DEFAULT_SLASH) != NULL) {
/* path is fine */
} else if (extension_dir && extension_dir[0]) {
char *libpath;
int extension_dir_len = strlen(extension_dir);
if (IS_SLASH(extension_dir[extension_dir_len-1])) {
spprintf(&libpath, 0, "%s%s", extension_dir, *path); /* SAFE */
} else {
spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, *path); /* SAFE */
}
efree(*path);
*path = libpath;
} else {
phpdbg_error("dl", "type=\"relpath\"", "Not a full path given or extension_dir ini setting is not set");
return NULL;
}
handle = DL_LOAD(*path);
if (!handle) {
#if PHP_WIN32
char *err = GET_DL_ERROR();
if (err && *err != "") {
phpdbg_error("dl", "type=\"unknown\"", "%s", err);
LocalFree(err);
} else {
phpdbg_error("dl", "type=\"unknown\"", "Unknown reason");
}
#else
phpdbg_error("dl", "type=\"unknown\"", "%s", GET_DL_ERROR());
#endif
return NULL;
}
#if ZEND_EXTENSIONS_SUPPORT
do {
zend_extension *new_extension;
zend_extension_version_info *extension_version_info;
extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info");
if (!extension_version_info) {
extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "_extension_version_info");
}
new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry");
if (!new_extension) {
new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry");
}
if (!extension_version_info || !new_extension) {
break;
}
if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) {
phpdbg_error("dl", "type=\"wrongapi\" extension=\"%s\" apineeded=\"%d\" apiinstalled=\"%d\"", "%s requires Zend Engine API version %d, which does not match the installed Zend Engine API version %d", new_extension->name, extension_version_info->zend_extension_api_no, ZEND_EXTENSION_API_NO);
goto quit;
} else if (strcmp(ZEND_EXTENSION_BUILD_ID, extension_version_info->build_id) && (!new_extension->build_id_check || new_extension->build_id_check(ZEND_EXTENSION_BUILD_ID) != SUCCESS)) {
phpdbg_error("dl", "type=\"wrongbuild\" extension=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", new_extension->name, extension_version_info->build_id, ZEND_EXTENSION_BUILD_ID);
goto quit;
}
*name = new_extension->name;
zend_register_extension(new_extension, handle);
if (new_extension->startup) {
if (new_extension->startup(new_extension) != SUCCESS) {
phpdbg_error("dl", "type=\"startupfailure\" extension=\"%s\"", "Unable to startup Zend extension %s", new_extension->name);
goto quit;
}
zend_append_version_info(new_extension);
}
return "Zend extension";
} while (0);
#endif
do {
zend_module_entry *module_entry;
zend_module_entry *(*get_module)(void);
get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");
if (!get_module) {
get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module");
}
if (!get_module) {
break;
}
module_entry = get_module();
*name = (char *) module_entry->name;
if (strcmp(ZEND_EXTENSION_BUILD_ID, module_entry->build_id)) {
phpdbg_error("dl", "type=\"wrongbuild\" module=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", module_entry->name, module_entry->build_id, ZEND_EXTENSION_BUILD_ID);
goto quit;
}
module_entry->type = MODULE_PERSISTENT;
module_entry->module_number = zend_next_free_module();
module_entry->handle = handle;
if ((module_entry = zend_register_module_ex(module_entry TSRMLS_CC)) == NULL) {
phpdbg_error("dl", "type=\"registerfailure\" module=\"%s\"", "Unable to register module %s", module_entry->name);
goto quit;
}
if (zend_startup_module_ex(module_entry TSRMLS_CC) == FAILURE) {
phpdbg_error("dl", "type=\"startupfailure\" module=\"%s\"", "Unable to startup module %s", module_entry->name);
goto quit;
}
if (module_entry->request_startup_func) {
if (module_entry->request_startup_func(MODULE_PERSISTENT, module_entry->module_number TSRMLS_CC) == FAILURE) {
phpdbg_error("dl", "type=\"initfailure\" module=\"%s\"", "Unable to initialize module %s", module_entry->name);
goto quit;
}
}
return "module";
} while (0);
phpdbg_error("dl", "type=\"nophpso\"", "This shared object is nor a Zend extension nor a module");
quit:
DL_UNLOAD(handle);
return NULL;
}
PHPDBG_COMMAND(dl) /* {{{ */
{
const char *type;
char *name, *path;
if (!param || param->type == EMPTY_PARAM) {
phpdbg_notice("dl", "extensiontype=\"Zend extension\"", "Zend extensions");
zend_llist_apply(&zend_extensions, (llist_apply_func_t) add_zendext_info TSRMLS_CC);
phpdbg_out("\n");
phpdbg_notice("dl", "extensiontype=\"module\"", "Modules");
zend_hash_apply(&module_registry, (apply_func_t) add_module_info TSRMLS_CC);
} else switch (param->type) {
case STR_PARAM:
#ifdef HAVE_LIBDL
path = estrndup(param->str, param->len);
phpdbg_activate_err_buf(1 TSRMLS_CC);
if ((type = phpdbg_load_module_or_extension(&path, &name TSRMLS_CC)) == NULL) {
phpdbg_error("dl", "path=\"%s\" %b", "Could not load %s, not found or invalid zend extension / module: %b", path);
efree(name);
} else {
phpdbg_notice("dl", "extensiontype=\"%s\" name=\"%s\" path=\"%s\"", "Successfully loaded the %s %s at path %s", type, name, path);
}
phpdbg_activate_err_buf(0 TSRMLS_CC);
phpdbg_free_err_buf(TSRMLS_C);
efree(path);
#else
phpdbg_error("dl", "type=\"unsupported\" path=\"%.*s\"", "Cannot dynamically load %.*s - dynamic modules are not supported", (int) param->len, param->str);
#endif
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(source) /* {{{ */
{
struct stat sb;
@ -947,7 +1180,7 @@ PHPDBG_COMMAND(watch) /* {{{ */
return SUCCESS;
} /* }}} */
int phpdbg_interactive(TSRMLS_D) /* {{{ */
int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC) /* {{{ */
{
int ret = SUCCESS;
char *input = NULL;
@ -964,10 +1197,10 @@ int phpdbg_interactive(TSRMLS_D) /* {{{ */
if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) {
phpdbg_activate_err_buf(1 TSRMLS_CC);
switch (ret = phpdbg_stack_execute(&stack TSRMLS_CC)) {
switch (ret = phpdbg_stack_execute(&stack, allow_async_unsafe TSRMLS_CC)) {
case FAILURE:
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
if (!allow_async_unsafe || phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
phpdbg_output_err_buf(NULL, "%b", "%b" TSRMLS_CC);
}
}
@ -1091,6 +1324,27 @@ static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_ar
#endif
} /* }}} */
#define DO_INTERACTIVE(allow_async_unsafe) do { \
if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \
phpdbg_list_file( \
zend_get_executed_filename(TSRMLS_C), \
3, \
zend_get_executed_lineno(TSRMLS_C)-1, \
zend_get_executed_lineno(TSRMLS_C) \
TSRMLS_CC \
); \
} \
\
switch (phpdbg_interactive(allow_async_unsafe TSRMLS_CC)) { \
case PHPDBG_LEAVE: \
case PHPDBG_FINISH: \
case PHPDBG_UNTIL: \
case PHPDBG_NEXT:{ \
goto next; \
} \
} \
} while (0)
#if PHP_VERSION_ID >= 50500
void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
{
@ -1127,41 +1381,17 @@ zend_vm_enter:
#endif
while (1) {
if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) {
/* resolve nth opline breakpoints */
phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC);
}
#ifdef ZEND_WIN32
if (EG(timed_out)) {
zend_timeout(0);
}
#endif
#define DO_INTERACTIVE() do { \
if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \
phpdbg_list_file( \
zend_get_executed_filename(TSRMLS_C), \
3, \
zend_get_executed_lineno(TSRMLS_C)-1, \
zend_get_executed_lineno(TSRMLS_C) \
TSRMLS_CC \
); \
} \
\
/* do { */\
switch (phpdbg_interactive(TSRMLS_C)) { \
case PHPDBG_LEAVE: \
case PHPDBG_FINISH: \
case PHPDBG_UNTIL: \
case PHPDBG_NEXT:{ \
goto next; \
} \
} \
/* } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); */\
} while (0)
/* allow conditional breakpoints and
initialization to access the vm uninterrupted */
if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) ||
@ -1205,7 +1435,7 @@ zend_vm_enter:
zend_get_executed_filename(TSRMLS_C),
zend_get_executed_lineno(TSRMLS_C)
);
DO_INTERACTIVE();
DO_INTERACTIVE(1);
} else {
/* skip possible breakpoints */
goto next;
@ -1218,13 +1448,13 @@ zend_vm_enter:
if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
DO_INTERACTIVE();
DO_INTERACTIVE(1);
}
/* check if some watchpoint was hit */
{
if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) {
DO_INTERACTIVE();
DO_INTERACTIVE(1);
}
}
@ -1236,26 +1466,36 @@ zend_vm_enter:
&& (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))
&& (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC);
DO_INTERACTIVE();
DO_INTERACTIVE(1);
}
}
next:
if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
phpdbg_out("\n");
phpdbg_notice("signal", "type=\"SIGINT\"", "Program received signal SIGINT");
PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED;
DO_INTERACTIVE();
DO_INTERACTIVE(1);
}
next:
PHPDBG_G(last_line) = execute_data->opline->lineno;
/* stupid hack to make zend_do_fcall_common_helper return ZEND_VM_ENTER() instead of recursively calling zend_execute() and eventually segfaulting */
if ((execute_data->opline->opcode == ZEND_DO_FCALL_BY_NAME || execute_data->opline->opcode == ZEND_DO_FCALL) && execute_data->function_state.function->type == ZEND_USER_FUNCTION) {
#if PHP_VERSION_ID < 50500
zend_execute = execute;
#else
zend_execute_ex = execute_ex;
#endif
}
PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
#if PHP_VERSION_ID < 50500
zend_execute = phpdbg_execute_ex;
#else
zend_execute_ex = phpdbg_execute_ex;
#endif
if (PHPDBG_G(vmret) > 0) {
switch (PHPDBG_G(vmret)) {
@ -1280,3 +1520,29 @@ next:
}
zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen");
} /* }}} */
/* only if *not* interactive and while executing */
void phpdbg_force_interruption(TSRMLS_D) {
zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */
PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER;
if (data) {
if (data->op_array) {
phpdbg_notice("hardinterrupt", "opline=\"%p\" num=\"%lu\" file=\"%s\" line=\"%u\"", "Current opline: %p (op #%lu) in %s:%u", data->opline, (data->opline - data->op_array->opcodes) / sizeof(data->opline), data->op_array->filename, data->opline->lineno);
} else {
phpdbg_notice("hardinterrupt", "opline=\"%p\"", "Current opline: %p (op_array information unavailable)", data->opline);
}
} else {
phpdbg_notice("hardinterrupt", "", "No information available about executing context");
}
DO_INTERACTIVE(0);
next:
PHPDBG_G(flags) &= ~PHPDBG_IN_SIGNAL_HANDLER;
if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
zend_bailout();
}
}

View File

@ -24,9 +24,11 @@
/* {{{ */
void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC);
void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC);
int phpdbg_interactive(TSRMLS_D);
int phpdbg_interactive(zend_bool allow_async_unsafe TSRMLS_DC);
int phpdbg_compile(TSRMLS_D);
void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
void phpdbg_clean(zend_bool full TSRMLS_DC);
void phpdbg_force_interruption(TSRMLS_D);
/* }}} */
/* {{{ phpdbg command handlers */
PHPDBG_COMMAND(exec);
@ -47,12 +49,14 @@ PHPDBG_COMMAND(clean);
PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
PHPDBG_COMMAND(sh);
PHPDBG_COMMAND(dl);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
PHPDBG_COMMAND(export);
PHPDBG_COMMAND(register);
PHPDBG_COMMAND(quit);
PHPDBG_COMMAND(watch); /* }}} */
PHPDBG_COMMAND(watch);
PHPDBG_COMMAND(wait); /* }}} */
/* {{{ prompt commands */
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */

101
phpdbg_rinit_hook.c Normal file
View File

@ -0,0 +1,101 @@
/*
+----------------------------------------------------------------------+
| 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: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg_rinit_hook.h"
#include "php_ini.h"
#include <errno.h>
ZEND_DECLARE_MODULE_GLOBALS(phpdbg_webhelper);
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("phpdbg.auth", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, auth, zend_phpdbg_webhelper_globals, phpdbg_webhelper_globals)
STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, path, zend_phpdbg_webhelper_globals, phpdbg_webhelper_globals)
PHP_INI_END()
static inline void php_phpdbg_webhelper_globals_ctor(zend_phpdbg_webhelper_globals *pg) /* {{{ */
{
} /* }}} */
static PHP_MINIT_FUNCTION(phpdbg_webhelper) /* {{{ */
{
if (!strcmp(sapi_module.name, PHPDBG_NAME)) {
return SUCCESS;
}
ZEND_INIT_MODULE_GLOBALS(phpdbg_webhelper, php_phpdbg_webhelper_globals_ctor, NULL);
REGISTER_INI_ENTRIES();
return SUCCESS;
} /* }}} */
static PHP_RINIT_FUNCTION(phpdbg_webhelper) /* {{{ */
{
zval *cookies = PG(http_globals)[TRACK_VARS_COOKIE];
zval **auth;
if (!cookies || zend_hash_find(Z_ARRVAL_P(cookies), PHPDBG_NAME "_AUTH_COOKIE", sizeof(PHPDBG_NAME "_AUTH_COOKIE"), (void **) &auth) == FAILURE || Z_STRLEN_PP(auth) != strlen(PHPDBG_WG(auth)) || strcmp(Z_STRVAL_PP(auth), PHPDBG_WG(auth))) {
return SUCCESS;
}
#ifndef _WIN32
{
struct sockaddr_un sock;
int s = socket(AF_UNIX, SOCK_STREAM, 0);
int len = strlen(PHPDBG_WG(path)) + sizeof(sock.sun_family);
char buf[(1 << 8) + 1];
int buflen;
sock.sun_family = AF_UNIX;
strcpy(sock.sun_path, PHPDBG_WG(path));
if (connect(s, (struct sockaddr *)&sock, len) == -1) {
zend_error(E_ERROR, "Unable to connect to UNIX domain socket at %s defined by phpdbg.path ini setting. Reason: %s", PHPDBG_WG(path), strerror(errno));
}
char *msg = NULL;
char msglen[5] = {0};
phpdbg_webdata_compress(&msg, (int *)msglen TSRMLS_CC);
send(s, msglen, 4, 0);
send(s, msg, *(int *) msglen, 0);
while ((buflen = recv(s, buf, sizeof(buf) - 1, 0)) > 0) {
php_write(buf, buflen TSRMLS_CC);
}
close(s);
php_output_flush_all(TSRMLS_C);
zend_bailout();
}
#endif
return SUCCESS;
} /* }}} */
zend_module_entry phpdbg_webhelper_module_entry = {
STANDARD_MODULE_HEADER,
"phpdbg_webhelper",
NULL,
PHP_MINIT(phpdbg_webhelper),
NULL,
PHP_RINIT(phpdbg_webhelper),
NULL,
NULL,
PHPDBG_VERSION,
STANDARD_MODULE_PROPERTIES
};

41
phpdbg_rinit_hook.h Normal file
View File

@ -0,0 +1,41 @@
/*
+----------------------------------------------------------------------+
| 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_WEBHELPER_H
#define PHPDBG_WEBHELPER_H
#include "phpdbg_webdata_transfer.h"
extern zend_module_entry phpdbg_webhelper_module_entry;
#define phpext_phpdbg_webhelper_ptr &phpdbg_webhelper_module_entry
#ifdef ZTS
# define PHPDBG_WG(v) TSRMG(phpdbg_webhelper_globals_id, zend_phpdbg_webhelper_globals *, v)
#else
# define PHPDBG_WG(v) (phpdbg_webhelper_globals.v)
#endif
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg_webhelper)
char *auth;
char *path;
ZEND_END_MODULE_GLOBALS(phpdbg_webhelper) /* }}} */
#endif /* PHPDBG_WEBHELPER_H */

View File

@ -27,21 +27,21 @@
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])
#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s, flags) \
PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18], flags)
const phpdbg_command_t phpdbg_set_commands[] = {
PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s"),
PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s", 0),
#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"),
PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss", PHPDBG_ASYNC_SAFE),
PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b", PHPDBG_ASYNC_SAFE),
#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_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s", 0),
PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b", PHPDBG_ASYNC_SAFE),
PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b", PHPDBG_ASYNC_SAFE),
PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b", PHPDBG_ASYNC_SAFE),
PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s", PHPDBG_ASYNC_SAFE),
PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b", PHPDBG_ASYNC_SAFE),
PHPDBG_END_COMMAND
};

111
phpdbg_sigsafe.c Normal file
View File

@ -0,0 +1,111 @@
#include "phpdbg_sigsafe.h"
#include "phpdbg.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
static inline void zend_mm_init(zend_mm_heap *heap) {
zend_mm_free_block *p;
int i;
heap->free_bitmap = 0;
heap->large_free_bitmap = 0;
#if ZEND_MM_CACHE
heap->cached = 0;
memset(heap->cache, 0, sizeof(heap->cache));
#endif
#if ZEND_MM_CACHE_STAT
for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
heap->cache_stat[i].count = 0;
}
#endif
p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0);
for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) {
p->next_free_block = p;
p->prev_free_block = p;
p = (zend_mm_free_block *)((char *)p + sizeof(zend_mm_free_block *) * 2);
heap->large_free_buckets[i] = NULL;
}
heap->rest_buckets[0] = heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(heap);
heap->rest_count = 0;
}
static zend_mm_storage* zend_mm_mem_init(void *params) {
TSRMLS_FETCH();
return &PHPDBG_G(sigsafe_mem).storage;
}
static void zend_mm_mem_dummy(zend_mm_storage *storage) {
}
#define STR(x) #x
#define EXP_STR(x) STR(x)
static zend_mm_segment* zend_mm_mem_alloc(zend_mm_storage *storage, size_t size) {
TSRMLS_FETCH();
if (EXPECTED(size == PHPDBG_SIGSAFE_MEM_SIZE && !PHPDBG_G(sigsafe_mem).allocated)) {
PHPDBG_G(sigsafe_mem).allocated = 1;
return (zend_mm_segment *) PHPDBG_G(sigsafe_mem).mem;
}
write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Tried to allocate more than " EXP_STR(PHPDBG_SIGSAFE_MEM_SIZE) " bytes from stack memory in signal handler ... bailing out of signal handler\n"));
if (*EG(bailout)) {
LONGJMP(*EG(bailout), FAILURE);
}
write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("Bailed out without a bailout address in signal handler!\n"));
return NULL;
}
static zend_mm_segment* zend_mm_mem_realloc(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size) {
return zend_mm_mem_alloc(storage, size);
}
static void zend_mm_mem_free(zend_mm_storage *storage, zend_mm_segment *ptr) {
}
static const zend_mm_mem_handlers phpdbg_handlers_sigsafe_mem = { "stack", zend_mm_mem_init, zend_mm_mem_dummy, zend_mm_mem_dummy, zend_mm_mem_alloc, zend_mm_mem_realloc, zend_mm_mem_free };
void phpdbg_set_sigsafe_mem(char *buffer TSRMLS_DC) {
phpdbg_signal_safe_mem *mem = &PHPDBG_G(sigsafe_mem);
mem->old_heap = zend_mm_set_heap(&mem->heap TSRMLS_CC);
mem->mem = buffer;
mem->allocated = 0;
mem->storage.handlers = &phpdbg_handlers_sigsafe_mem;
mem->heap.storage = &mem->storage;
mem->heap.block_size = PHPDBG_SIGSAFE_MEM_SIZE;
mem->heap.compact_size = 0;
mem->heap.segments_list = NULL;
zend_mm_init(&mem->heap);
#if ZEND_MM_CACHE_STAT
memset(mem->heap.cache_stat, 0, sizeof(mem->heap.cache_stat));
#endif
mem->heap.use_zend_alloc = 1;
mem->heap.real_size = 0;
mem->heap.overflow = 0;
mem->heap.real_peak = 0;
mem->heap.limit = PHPDBG_SIGSAFE_MEM_SIZE;
mem->heap.size = 0;
mem->heap.peak = 0;
mem->heap.internal = 0;
mem->heap.reserve = NULL;
mem->heap.reserve_size = 0;
}
zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D) {
return PHPDBG_G(sigsafe_mem).old_heap;
}
void phpdbg_clear_sigsafe_mem(TSRMLS_D) {
zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem(TSRMLS_C) TSRMLS_CC);
PHPDBG_G(sigsafe_mem).mem = NULL;
}
zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D) {
return !!PHPDBG_G(sigsafe_mem).mem;
}

25
phpdbg_sigsafe.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef PHPDBG_SIGSAFE_H
#define PHPDBG_SIGSAFE_H
#include "zend_mm_structs.h"
#define PHPDBG_SIGSAFE_MEM_SIZE (1 << 20)
typedef struct {
char *mem;
zend_bool allocated;
zend_mm_heap heap;
zend_mm_heap *old_heap;
zend_mm_storage storage;
} phpdbg_signal_safe_mem;
#include "phpdbg.h"
zend_bool phpdbg_active_sigsafe_mem(TSRMLS_D);
void phpdbg_set_sigsafe_mem(char *mem TSRMLS_DC);
void phpdbg_clear_sigsafe_mem(TSRMLS_D);
zend_mm_heap *phpdbg_original_heap_sigsafe_mem(TSRMLS_D);
#endif

View File

@ -716,7 +716,7 @@ static int format_converter(register buffy *odp, const char *fmt, zend_bool esca
int old_slen = s_len, i = 0;
char *old_s = s, *s_ptr;
free_s = s_ptr = s = malloc(old_slen * 6 + 1);
free_s = s_ptr = s = emalloc(old_slen * 6 + 1);
do {
if (old_s[i] == '&' || old_s[i] == '"') {
*s_ptr++ = '&';
@ -772,7 +772,7 @@ static int format_converter(register buffy *odp, const char *fmt, zend_bool esca
case 'r':
s_len = asprintf(&s, "req=\"%lu\"", PHPDBG_G(req_id));
s_len = spprintf(&s, 0, "req=\"%lu\"", PHPDBG_G(req_id));
free_s = s;
break;
@ -983,7 +983,7 @@ fmt_error:
}
skip_output:
if (free_s) {
free(free_s);
efree(free_s);
free_s = NULL;
}
@ -1040,9 +1040,9 @@ PHPAPI int phpdbg_xml_vasprintf(char **buf, const char *format, zend_bool escape
*buf = NULL;
if (cc >= 0) {
if ((*buf = malloc(++cc)) != NULL) {
if ((*buf = emalloc(++cc)) != NULL) {
if ((cc = phpdbg_xml_vsnprintf(*buf, cc, format, escape_xml, ap TSRMLS_CC)) < 0) {
free(*buf);
efree(*buf);
*buf = NULL;
}
}
@ -1052,13 +1052,36 @@ PHPAPI int phpdbg_xml_vasprintf(char **buf, const char *format, zend_bool escape
}
/* copy end */
static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char *msg, int msglen, const char *xml, int xmllen TSRMLS_DC) {
char *msgout, *buf;
static int phpdbg_encode_xml(char **buf, char *msg, int msglen, char from, char *to) {
int i;
char *tmp = *buf = emalloc(msglen * strlen(to));
int tolen = strlen(to);
for (i = 0; i++ < msglen; msg++) {
if (*msg == '&') {
memcpy(tmp, ZEND_STRL("&amp;"));
tmp += sizeof("&amp;") - 1;
} else if (*msg == from) {
memcpy(tmp, to, tolen);
tmp += tolen;
} else {
*tmp++ = *msg;
}
}
{
int len = tmp - *buf;
*buf = erealloc(*buf, len);
return len;
}
}
static int phpdbg_process_print(int fd, int type, const char *tag, const char *msg, int msglen, const char *xml, int xmllen TSRMLS_DC) {
char *msgout = NULL, *buf;
int msgoutlen, xmloutlen, buflen;
const char *severity;
if ((PHPDBG_G(flags) & PHPDBG_WRITE_XML) && PHPDBG_G(in_script_xml) && PHPDBG_G(in_script_xml) != type) {
fprintf(fp, "</stream>");
write(fd, ZEND_STRL("</stream>"));
PHPDBG_G(in_script_xml) = 0;
}
@ -1069,16 +1092,16 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
severity = "error";
if (!PHPDBG_G(last_was_newline)) {
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
fprintf(fp, "<phpdbg>\n</phpdbg>");
write(fd, ZEND_STRL("<phpdbg>\n</phpdbg>"));
} else {
fprintf(fp, "\n");
write(fd, ZEND_STRL("\n"));
}
PHPDBG_G(last_was_newline) = 1;
}
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
msgoutlen = asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, msglen, msg);
msgoutlen = spprintf(&msgout, 0, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, msglen, msg);
} else {
msgoutlen = asprintf(&msgout, "[%.*s]\n", msglen, msg);
msgoutlen = spprintf(&msgout, 0, "[%.*s]\n", msglen, msg);
}
break;
@ -1086,26 +1109,26 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
severity = "notice";
if (!PHPDBG_G(last_was_newline)) {
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
fprintf(fp, "<phpdbg>\n</phpdbg>");
write(fd, ZEND_STRL("<phpdbg>\n</phpdbg>"));
} else {
fprintf(fp, "\n");
write(fd, ZEND_STRL("\n"));
}
PHPDBG_G(last_was_newline) = 1;
}
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
msgoutlen = asprintf(&msgout, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, msglen, msg);
msgoutlen = spprintf(&msgout, 0, "\033[%sm[%.*s]\033[0m\n", PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, msglen, msg);
} else {
msgoutlen = asprintf(&msgout, "[%.*s]\n", msglen, msg);
msgoutlen = spprintf(&msgout, 0, "[%.*s]\n", msglen, msg);
}
break;
case P_WRITELN:
severity = "normal";
if (msg) {
msgoutlen = asprintf(&msgout, "%.*s\n", msglen, msg);
msgoutlen = spprintf(&msgout, 0, "%.*s\n", msglen, msg);
} else {
msgoutlen = 1;
msgout = strdup("\n");
msgout = estrdup("\n");
}
PHPDBG_G(last_was_newline) = 1;
break;
@ -1113,12 +1136,12 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
case P_WRITE:
severity = "normal";
if (msg) {
msgout = strndup(msg, msglen);
msgout = estrndup(msg, msglen);
msgoutlen = msglen;
PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
} else {
msgoutlen = 0;
msgout = strdup("");
msgout = estrdup("");
}
break;
@ -1128,14 +1151,17 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
PHPDBG_G(last_was_newline) = msg[msglen - 1] == '\n';
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
if (PHPDBG_G(in_script_xml) != type) {
fprintf(fp, "<stream type=\"%s\">", type == P_STDERR ? "stderr" : "stdout");
char *stream_buf;
int stream_buflen = spprintf(&stream_buf, 0, "<stream type=\"%s\">", type == P_STDERR ? "stderr" : "stdout");
write(fd, stream_buf, stream_buflen);
efree(stream_buf);
PHPDBG_G(in_script_xml) = type;
}
buf = php_escape_html_entities((unsigned char *) msg, msglen, (size_t *) &buflen, 0, ENT_NOQUOTES, PG(internal_encoding) && PG(internal_encoding)[0] ? PG(internal_encoding) : (SG(default_charset) ? SG(default_charset) : "UTF-8") TSRMLS_CC);
fprintf(fp, "%.*s", buflen, buf);
write(fd, buf, buflen);
efree(buf);
} else {
fprintf(fp, "%.*s", msglen, msg);
write(fd, msg, msglen);
}
return msglen;
}
@ -1147,7 +1173,7 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
if (msg) {
struct timeval tp;
if (gettimeofday(&tp, NULL) == SUCCESS) {
msgoutlen = asprintf(&msgout, "[%ld %.8F]: %.*s\n", tp.tv_sec, tp.tv_usec / 1000000., msglen, msg);
msgoutlen = spprintf(&msgout, 0, "[%ld %.8F]: %.*s\n", tp.tv_sec, tp.tv_usec / 1000000., msglen, msg);
} else {
msgoutlen = FAILURE;
}
@ -1155,37 +1181,43 @@ static int phpdbg_process_print(FILE *fp, int type, const char *tag, const char
break;
}
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
char *xmlout;
if (PHPDBG_G(req_id)) {
char *xmlbuf = NULL;
xmllen = asprintf(&xmlbuf, "req=\"%lu\" %.*s", PHPDBG_G(req_id), xmllen, xml);
xmllen = spprintf(&xmlbuf, 0, "req=\"%lu\" %.*s", PHPDBG_G(req_id), xmllen, xml);
xml = xmlbuf;
}
if (msgout) {
buf = php_escape_html_entities((unsigned char *) msgout, msgoutlen, (size_t *) &buflen, 0, ENT_COMPAT, PG(internal_encoding) && PG(internal_encoding)[0] ? PG(internal_encoding) : (SG(default_charset) ? SG(default_charset) : "UTF-8") TSRMLS_CC);
xmloutlen = fprintf(fp, "<%s severity=\"%s\" %.*s msgout=\"%.*s\" />", tag, severity, xmllen, xml, buflen, buf);
// unsigned char *tmp = msgout;
// buf = php_escape_html_entities(tmp, msgoutlen, (size_t *) &buflen, 0, ENT_COMPAT, PG(internal_encoding) && PG(internal_encoding)[0] ? PG(internal_encoding) : (SG(default_charset) ? SG(default_charset) : "UTF-8") TSRMLS_CC);
buflen = phpdbg_encode_xml(&buf, msgout, msgoutlen, '"', "&quot;");
xmloutlen = spprintf(&xmlout, 0, "<%s severity=\"%s\" %.*s msgout=\"%.*s\" />", tag, severity, xmllen, xml, buflen, buf);
efree(buf);
} else {
xmloutlen = fprintf(fp, "<%s severity=\"%s\" %.*s msgout=\"\" />", tag, severity, xmllen, xml);
xmloutlen = spprintf(&xmlout, 0, "<%s severity=\"%s\" %.*s msgout=\"\" />", tag, severity, xmllen, xml);
}
write(fd, xmlout, xmloutlen);
efree(xmlout);
} else if (msgout) {
fprintf(fp, "%.*s", msgoutlen, msgout);
write(fd, msgout, msgoutlen);
}
if (PHPDBG_G(req_id) && (PHPDBG_G(flags) & PHPDBG_WRITE_XML)) {
free((char *) xml);
efree((char *) xml);
}
if (msgout) {
free(msgout);
efree(msgout);
}
return msgout ? msgoutlen : xmloutlen;
} /* }}} */
PHPDBG_API int phpdbg_vprint(int type TSRMLS_DC, FILE *fp, const char *tag, const char *xmlfmt, const char *strfmt, va_list args) {
PHPDBG_API int phpdbg_vprint(int type TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, va_list args) {
char *msg = NULL, *xml = NULL;
int msglen = 0, xmllen = 0;
int len;
@ -1207,8 +1239,8 @@ PHPDBG_API int phpdbg_vprint(int type TSRMLS_DC, FILE *fp, const char *tag, cons
if (PHPDBG_G(err_buf).active && type != P_STDOUT && type != P_STDERR) {
PHPDBG_G(err_buf).type = type;
PHPDBG_G(err_buf).fp = fp;
PHPDBG_G(err_buf).tag = strdup(tag);
PHPDBG_G(err_buf).fd = fd;
PHPDBG_G(err_buf).tag = estrdup(tag);
PHPDBG_G(err_buf).msg = msg;
PHPDBG_G(err_buf).msglen = msglen;
PHPDBG_G(err_buf).xml = xml;
@ -1217,14 +1249,14 @@ PHPDBG_API int phpdbg_vprint(int type TSRMLS_DC, FILE *fp, const char *tag, cons
return msglen;
}
len = phpdbg_process_print(fp, type, tag, msg, msglen, xml, xmllen TSRMLS_CC);
len = phpdbg_process_print(fd, type, tag, msg, msglen, xml, xmllen TSRMLS_CC);
if (msg) {
free(msg);
efree(msg);
}
if (xml) {
free(xml);
efree(xml);
}
return len;
@ -1237,9 +1269,9 @@ PHPDBG_API void phpdbg_free_err_buf(TSRMLS_D) {
PHPDBG_G(err_buf).type = 0;
free(PHPDBG_G(err_buf).tag);
free(PHPDBG_G(err_buf).msg);
free(PHPDBG_G(err_buf).xml);
efree(PHPDBG_G(err_buf).tag);
efree(PHPDBG_G(err_buf).msg);
efree(PHPDBG_G(err_buf).xml);
}
PHPDBG_API void phpdbg_activate_err_buf(zend_bool active TSRMLS_DC) {
@ -1258,7 +1290,7 @@ PHPDBG_API int phpdbg_output_err_buf(const char *tag, const char *xmlfmt, const
#else
va_start(args, strfmt);
#endif
len = phpdbg_vprint(PHPDBG_G(err_buf).type TSRMLS_CC, PHPDBG_G(err_buf).fp, tag ? tag : PHPDBG_G(err_buf).tag, xmlfmt, strfmt, args);
len = phpdbg_vprint(PHPDBG_G(err_buf).type TSRMLS_CC, PHPDBG_G(err_buf).fd, tag ? tag : PHPDBG_G(err_buf).tag, xmlfmt, strfmt, args);
va_end(args);
PHPDBG_G(err_buf).active = errbuf_active;
@ -1267,18 +1299,20 @@ PHPDBG_API int phpdbg_output_err_buf(const char *tag, const char *xmlfmt, const
return len;
}
PHPDBG_API int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *tag, const char *xmlfmt, const char *strfmt, ...) {
PHPDBG_API int phpdbg_print(int type TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) {
va_list args;
int len;
va_start(args, strfmt);
len = phpdbg_vprint(type TSRMLS_CC, fp, tag, xmlfmt, strfmt, args);
len = phpdbg_vprint(type TSRMLS_CC, fd, tag, xmlfmt, strfmt, args);
va_end(args);
return len;
}
PHPDBG_API int phpdbg_xml_internal(FILE *fp TSRMLS_DC, const char *fmt, ...) {
PHPDBG_API int phpdbg_xml_internal(int fd TSRMLS_DC, const char *fmt, ...) {
int len = 0;
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
va_list args;
char *buffer;
@ -1289,32 +1323,38 @@ PHPDBG_API int phpdbg_xml_internal(FILE *fp TSRMLS_DC, const char *fmt, ...) {
va_end(args);
if (PHPDBG_G(in_script_xml)) {
fprintf(fp, "</stream>");
write(fd, ZEND_STRL("</stream>"));
PHPDBG_G(in_script_xml) = 0;
}
return fprintf(fp, "%.*s", buflen, buffer);
len = write(fd, buffer, buflen);
efree(buffer);
}
return 0;
return len;
}
PHPDBG_API int phpdbg_log_internal(FILE *fp TSRMLS_DC, const char *fmt, ...) {
PHPDBG_API int phpdbg_log_internal(int fd TSRMLS_DC, const char *fmt, ...) {
va_list args;
char *buffer;
int buflen;
int len = 0;
va_start(args, fmt);
buflen = vspprintf(&buffer, 0, fmt, args);
va_end(args);
return fprintf(fp, "%.*s", buflen, buffer);
len = write(fd, buffer, buflen);
efree(buffer);
return len;
}
PHPDBG_API int phpdbg_out_internal(FILE *fp TSRMLS_DC, const char *fmt, ...) {
PHPDBG_API int phpdbg_out_internal(int fd TSRMLS_DC, const char *fmt, ...) {
va_list args;
char *buffer;
int buflen;
int len = 0;
va_start(args, fmt);
buflen = vspprintf(&buffer, 0, fmt, args);
@ -1322,19 +1362,22 @@ PHPDBG_API int phpdbg_out_internal(FILE *fp TSRMLS_DC, const char *fmt, ...) {
if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) {
if (PHPDBG_G(in_script_xml)) {
fprintf(fp, "</stream>");
write(fd, ZEND_STRL("</stream>"));
PHPDBG_G(in_script_xml) = 0;
}
fprintf(fp, "<phpdbg>%.*s</phpdbg>", buflen, buffer);
return buflen;
write(fd, ZEND_STRL("<phpdbg>"));
len = write(fd, buffer, buflen);
write(fd, ZEND_STRL("</phpdbg>"));
} else {
return fprintf(fp, "%.*s", buflen, buffer);
len = write(fd, buffer, buflen);
}
return len;
}
PHPDBG_API int phpdbg_rlog(FILE *fp, const char *fmt, ...) { /* {{{ */
PHPDBG_API int phpdbg_rlog(int fd, const char *fmt, ...) { /* {{{ */
int rc = 0;
va_list args;
@ -1343,16 +1386,21 @@ PHPDBG_API int phpdbg_rlog(FILE *fp, const char *fmt, ...) { /* {{{ */
va_start(args, fmt);
if (gettimeofday(&tp, NULL) == SUCCESS) {
char friendly[100];
char *format = NULL, *buffer = NULL;
char *format = NULL, *buffer = NULL, *outbuf = NULL;
const time_t tt = tp.tv_sec;
strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tt));
asprintf(&buffer, friendly, tp.tv_usec/1000);
asprintf(&format, "[%s]: %s\n", buffer, fmt);
rc = vfprintf(fp, format, args);
spprintf(&buffer, 0, friendly, tp.tv_usec/1000);
spprintf(&format, 0, "[%s]: %s\n", buffer, fmt);
rc = vspprintf(&outbuf, 0, format, args);
free(format);
free(buffer);
if (outbuf) {
rc = write(fd, outbuf, rc);
efree(outbuf);
}
efree(format);
efree(buffer);
}
va_end(args);
@ -1489,3 +1537,157 @@ PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */
#endif
return columns;
} /* }}} */
PHPDBG_API void phpdbg_set_async_io(int fd) {
#ifndef _WIN32
int flags;
fcntl(STDIN_FILENO, F_SETOWN, getpid());
flags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, flags | FASYNC);
#endif
}
int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC) {
if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
char *lc_name, *lc_free;
int lc_length, ret = FAILURE;
if (name == NULL || !name_length) {
return FAILURE;
}
lc_free = lc_name = emalloc(name_length + 1);
zend_str_tolower_copy(lc_name, name, name_length);
lc_length = name_length + 1;
if (lc_name[0] == '\\') {
lc_name += 1;
lc_length -= 1;
}
phpdbg_try_access {
ret = zend_hash_find(EG(class_table), lc_name, lc_length, (void **) &ce);
} phpdbg_catch_access {
phpdbg_error("signalsegv", "class=\"%.*s\"", "Could not fetch class %.*s, invalid data source", name_length, name);
} phpdbg_end_try_access();
efree(lc_free);
return ret;
} else {
return zend_lookup_class(name, name_length, ce TSRMLS_CC);
}
}
char *phpdbg_get_property_key(char *key) {
if (*key != 0) {
return key;
}
return strchr(key + 1, 0) + 1;
}
static int phpdbg_parse_variable_arg_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, phpdbg_parse_var_func callback TSRMLS_DC) {
return callback(name, len, keyname, keylen, parent, zv TSRMLS_CC);
}
PHPDBG_API int phpdbg_parse_variable(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_func callback, zend_bool silent TSRMLS_DC) {
return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_parse_variable_arg_wrapper, silent, callback TSRMLS_CC);
}
PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, zend_bool silent, void *arg TSRMLS_DC) {
int ret = FAILURE;
zend_bool new_index = 1;
char *last_index;
size_t 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));
size_t namelen;
char *name;
char *keyname = estrndup(last_index, index_len);
zend_hash_get_current_key_zval_ex(parent, key, &position);
convert_to_string(key);
name = emalloc(i + Z_STRLEN_P(key) + 2);
namelen = sprintf(name, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
efree(key);
ret = callback(name, namelen, keyname, index_len, parent, zv, arg TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
} else if (Z_TYPE_PP(zv) == IS_OBJECT) {
phpdbg_parse_variable_with_arg(input, len, Z_OBJPROP_PP(zv), i, callback, silent, arg TSRMLS_CC);
} else if (Z_TYPE_PP(zv) == IS_ARRAY) {
phpdbg_parse_variable_with_arg(input, len, Z_ARRVAL_PP(zv), i, callback, silent, arg 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("variable", "type=\"undefined\" variable=\"%.*s\"", "%.*s is undefined", (int) i, input);
}
return FAILURE;
}
last_index[index_len] = last_chr;
if (i == len) {
char *name = estrndup(input, len);
char *keyname = estrndup(last_index, index_len);
ret = callback(name, len, keyname, index_len, parent, zv, arg 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("variable", "type=\"notiterable\" variable=\"%.*s\"", "%.*s is nor an array nor an object", (int) i, input);
return FAILURE;
}
index_len = 0;
}
}
return ret;
error:
phpdbg_error("variable", "type=\"invalidinput\"", "Malformed input");
return FAILURE;
}

View File

@ -47,37 +47,37 @@ enum {
};
#ifdef ZTS
PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE *, const char *, const char *, const char *, ...) PHP_ATTRIBUTE_FORMAT(printf, 6, 7);
PHPDBG_API int phpdbg_print(int severity TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 6, 7);
#else
PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE *, const char *, const char *, const char *, ...) PHP_ATTRIBUTE_FORMAT(printf, 5, 6);
PHPDBG_API int phpdbg_print(int severity TSRMLS_DC, int fd, const char *tag, const char *xmlfmt, const char *strfmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 5, 6);
#endif
PHPDBG_API int phpdbg_xml_internal(FILE *fp TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_log_internal(FILE *fp TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_out_internal(FILE *fp TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_xml_internal(int fd TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_log_internal(int fd TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_out_internal(int fd TSRMLS_DC, const char *fmt, ...);
PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
PHPDBG_API int phpdbg_rlog(int fd, const char *fmt, ...);
#define phpdbg_error(tag, xmlfmt, strfmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_notice(tag, xmlfmt, strfmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_writeln(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_write(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_script(type, fmt, ...) phpdbg_print(type TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], NULL, NULL, fmt, ##__VA_ARGS__)
#define phpdbg_log(fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_xml(fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_out(fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_error(tag, xmlfmt, strfmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_notice(tag, xmlfmt, strfmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_writeln(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_write(tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_script(type, fmt, ...) phpdbg_print(type TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT].fd, NULL, NULL, fmt, ##__VA_ARGS__)
#define phpdbg_log(fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_xml(fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_out(fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_error_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_notice_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_writeln_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_write_ex(out, tag, xmlfmt, strfmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, out, tag, xmlfmt, strfmt, ##__VA_ARGS__)
#define phpdbg_script_ex(out, type, fmt, ...) phpdbg_print(type TSRMLS_CC, out, NULL, NULL, fmt, ##__VA_ARGS__)
#define phpdbg_log_ex(out, fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_xml_ex(out, fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_out_ex(out, fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT] TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_log_ex(out, fmt, ...) phpdbg_log_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_xml_ex(out, fmt, ...) phpdbg_xml_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#define phpdbg_out_ex(out, fmt, ...) phpdbg_out_internal(PHPDBG_G(io)[PHPDBG_STDOUT].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#if PHPDBG_DEBUG
# define phpdbg_debug(fmt, ...) phpdbg_log_ex(PHPDBG_G(io)[PHPDBG_STDERR] TSRMLS_CC, fmt, ##__VA_ARGS__)
# define phpdbg_debug(fmt, ...) phpdbg_log_ex(PHPDBG_G(io)[PHPDBG_STDERR].fd TSRMLS_CC, fmt, ##__VA_ARGS__)
#else
# define phpdbg_debug(fmt, ...)
#endif
@ -133,6 +133,8 @@ PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
/* {{{ Console Width */
PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */
PHPDBG_API void phpdbg_set_async_io(int fd);
int phpdbg_rebuild_symtable(TSRMLS_D);
#if PHP_VERSION_ID < 50500
@ -155,4 +157,14 @@ static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, Ha
}
#endif
int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry ***ce TSRMLS_DC);
char *phpdbg_get_property_key(char *key);
typedef int (*phpdbg_parse_var_func)(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv TSRMLS_DC);
typedef int (*phpdbg_parse_var_with_arg_func)(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, void *arg TSRMLS_DC);
PHPDBG_API int phpdbg_parse_variable(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_func callback, zend_bool silent TSRMLS_DC);
PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable *parent, size_t i, phpdbg_parse_var_with_arg_func callback, zend_bool silent, void *arg TSRMLS_DC);
#endif /* PHPDBG_UTILS_H */

416
phpdbg_wait.c Normal file
View File

@ -0,0 +1,416 @@
/*
+----------------------------------------------------------------------+
| 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: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg_wait.h"
#include "phpdbg_prompt.h"
#include "ext/json/JSON_parser.h"
#include "ext/standard/basic_functions.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
ZEND_EXTERN_MODULE_GLOBALS(json);
static void phpdbg_rebuild_http_globals_array(int type, const char *name TSRMLS_DC) {
zval **zvpp;
if (PG(http_globals)[type]) {
zval_dtor(PG(http_globals)[type]);
}
if (zend_hash_find(&EG(symbol_table), name, strlen(name) + 1, (void **) &zvpp) == SUCCESS) {
Z_SET_REFCOUNT_PP(zvpp, 2);
PG(http_globals)[type] = *zvpp;
}
}
static int phpdbg_dearm_autoglobals(zend_auto_global *auto_global TSRMLS_DC) {
if (auto_global->name_len != sizeof("GLOBALS") - 1 || memcmp(auto_global->name, "GLOBALS", sizeof("GLOBALS") - 1)) {
auto_global->armed = 0;
}
return ZEND_HASH_APPLY_KEEP;
}
typedef struct {
HashTable *ht[2];
HashPosition pos[2];
} phpdbg_intersect_ptr;
static int phpdbg_array_data_compare(const void *a, const void *b TSRMLS_DC) {
Bucket *f, *s;
zval result;
zval *first, *second;
f = *((Bucket **) a);
s = *((Bucket **) b);
first = *((zval **) f->pData);
second = *((zval **) s->pData);
if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) {
return 0;
}
if (Z_LVAL(result) < 0) {
return -1;
} else if (Z_LVAL(result) > 0) {
return 1;
}
return 0;
}
static void phpdbg_array_intersect_init(phpdbg_intersect_ptr *info, HashTable *ht1, HashTable *ht2 TSRMLS_DC) {
info->ht[0] = ht1;
info->ht[1] = ht2;
zend_hash_sort(info->ht[0], zend_qsort, (compare_func_t) phpdbg_array_data_compare, 0 TSRMLS_CC);
zend_hash_sort(info->ht[1], zend_qsort, (compare_func_t) phpdbg_array_data_compare, 0 TSRMLS_CC);
zend_hash_internal_pointer_reset_ex(info->ht[0], &info->pos[0]);
zend_hash_internal_pointer_reset_ex(info->ht[1], &info->pos[1]);
}
/* -1 => first array, 0 => both arrays equal, 1 => second array */
static int phpdbg_array_intersect(phpdbg_intersect_ptr *info, zval ***ptr) {
int ret;
zval **zvpp[2];
int invalid = !info->ht[0] + !info->ht[1];
if (invalid > 0) {
invalid = !info->ht[0];
if (zend_hash_get_current_data_ex(info->ht[invalid], (void **) ptr, &info->pos[invalid]) == FAILURE) {
*ptr = NULL;
return 0;
}
zend_hash_move_forward_ex(info->ht[invalid], &info->pos[invalid]);
return invalid ? 1 : -1;
}
if (zend_hash_get_current_data_ex(info->ht[0], (void **) &zvpp[0], &info->pos[0]) == FAILURE) {
info->ht[0] = NULL;
return phpdbg_array_intersect(info, ptr);
}
if (zend_hash_get_current_data_ex(info->ht[1], (void **) &zvpp[1], &info->pos[1]) == FAILURE) {
info->ht[1] = NULL;
return phpdbg_array_intersect(info, ptr);
}
ret = zend_binary_zval_strcmp(*zvpp[0], *zvpp[1]);
if (ret <= 0) {
*ptr = zvpp[0];
zend_hash_move_forward_ex(info->ht[0], &info->pos[0]);
}
if (ret >= 0) {
*ptr = zvpp[1];
zend_hash_move_forward_ex(info->ht[1], &info->pos[1]);
}
return ret;
}
void phpdbg_webdata_decompress(char *msg, int len TSRMLS_DC) {
zval *free_zv = NULL;
zval zv, **zvpp;
HashTable *ht;
php_json_decode(&zv, msg, len, 1, 1000 /* enough */ TSRMLS_CC);
if (JSON_G(error_code) != PHP_JSON_ERROR_NONE) {
phpdbg_error("wait", "type=\"invaliddata\" import=\"fail\"", "Malformed JSON was sent to this socket, arborting");
return;
}
ht = Z_ARRVAL(zv);
/* Reapply symbol table */
if (zend_hash_find(ht, "GLOBALS", sizeof("GLOBALS"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
{
zval **srv;
if (zend_hash_find(Z_ARRVAL_PP(zvpp), "_SERVER", sizeof("_SERVER"), (void **) &srv) == SUCCESS && Z_TYPE_PP(srv) == IS_ARRAY) {
zval **script;
if (zend_hash_find(Z_ARRVAL_PP(srv), "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &script) == SUCCESS && Z_TYPE_PP(script) == IS_STRING) {
phpdbg_param_t param;
param.str = Z_STRVAL_PP(script);
PHPDBG_COMMAND_HANDLER(exec)(&param TSRMLS_CC);
}
}
}
PG(auto_globals_jit) = 0;
zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_dearm_autoglobals TSRMLS_CC);
zend_hash_clean(&EG(symbol_table));
EG(symbol_table) = *Z_ARRVAL_PP(zvpp);
/* Rebuild cookies, env vars etc. from GLOBALS (PG(http_globals)) */
phpdbg_rebuild_http_globals_array(TRACK_VARS_POST, "_POST" TSRMLS_CC);
phpdbg_rebuild_http_globals_array(TRACK_VARS_GET, "_GET" TSRMLS_CC);
phpdbg_rebuild_http_globals_array(TRACK_VARS_COOKIE, "_COOKIE" TSRMLS_CC);
phpdbg_rebuild_http_globals_array(TRACK_VARS_SERVER, "_SERVER" TSRMLS_CC);
phpdbg_rebuild_http_globals_array(TRACK_VARS_ENV, "_ENV" TSRMLS_CC);
phpdbg_rebuild_http_globals_array(TRACK_VARS_FILES, "_FILES" TSRMLS_CC);
Z_ADDREF_PP(zvpp);
free_zv = *zvpp;
}
if (zend_hash_find(ht, "input", sizeof("input"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_STRING) {
if (SG(request_info).request_body) {
php_stream_close(SG(request_info).request_body);
}
SG(request_info).request_body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
php_stream_truncate_set_size(SG(request_info).request_body, 0);
php_stream_write(SG(request_info).request_body, Z_STRVAL_PP(zvpp), Z_STRLEN_PP(zvpp));
}
if (zend_hash_find(ht, "cwd", sizeof("cwd"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_STRING) {
if (VCWD_CHDIR(Z_STRVAL_PP(zvpp)) == SUCCESS) {
if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) {
efree(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL;
}
if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) {
efree(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL;
}
}
}
if (zend_hash_find(ht, "sapi_name", sizeof("sapi_name"), (void **) &zvpp) == SUCCESS && (Z_TYPE_PP(zvpp) == IS_STRING || Z_TYPE_PP(zvpp) == IS_NULL)) {
if (PHPDBG_G(sapi_name_ptr)) {
free(PHPDBG_G(sapi_name_ptr));
}
if (Z_TYPE_PP(zvpp) == IS_STRING) {
PHPDBG_G(sapi_name_ptr) = sapi_module.name = strdup(Z_STRVAL_PP(zvpp));
} else {
PHPDBG_G(sapi_name_ptr) = sapi_module.name = NULL;
}
}
if (zend_hash_find(ht, "modules", sizeof("modules"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
HashPosition position;
phpdbg_intersect_ptr pos;
zval **module;
zend_module_entry *mod;
HashTable zv_registry;
/* intersect modules, unregister modules loaded "too much", announce not yet registered modules (phpdbg_notice) */
zend_hash_init(&zv_registry, zend_hash_num_elements(&module_registry), 0, ZVAL_PTR_DTOR, 0);
for (zend_hash_internal_pointer_reset_ex(&module_registry, &position);
zend_hash_get_current_data_ex(&module_registry, (void **) &mod, &position) == SUCCESS;
zend_hash_move_forward_ex(&module_registry, &position)) {
if (mod->name) {
zval **value = emalloc(sizeof(zval *));
MAKE_STD_ZVAL(*value);
ZVAL_STRING(*value, mod->name, 1);
zend_hash_next_index_insert(&zv_registry, value, sizeof(zval *), NULL);
}
}
phpdbg_array_intersect_init(&pos, &zv_registry, Z_ARRVAL_PP(zvpp) TSRMLS_CC);
do {
int mode = phpdbg_array_intersect(&pos, &module);
if (mode < 0) {
// loaded module, but not needed
if (strcmp(PHPDBG_NAME, Z_STRVAL_PP(module))) {
zend_hash_del(&module_registry, Z_STRVAL_PP(module), Z_STRLEN_PP(module) + 1);
}
} else if (mode > 0) {
// not loaded module
if (!sapi_module.name || strcmp(sapi_module.name, Z_STRVAL_PP(module))) {
phpdbg_notice("wait", "missingmodule=\"%.*s\"", "The module %.*s isn't present in " PHPDBG_NAME ", you still can load via dl /path/to/module/%.*s.so", Z_STRLEN_PP(module), Z_STRVAL_PP(module), Z_STRLEN_PP(module), Z_STRVAL_PP(module));
}
}
} while (module);
zend_hash_clean(&zv_registry);
}
if (zend_hash_find(ht, "extensions", sizeof("extensions"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
zend_extension *extension;
zend_llist_position pos;
HashPosition hpos;
zval **name, key;
extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
while (extension) {
extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
/* php_serach_array() body should be in some ZEND_API function */
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &hpos);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &name, &hpos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &hpos)) {
if (Z_TYPE_PP(name) == IS_STRING && !zend_binary_strcmp(extension->name, strlen(extension->name), Z_STRVAL_PP(name), Z_STRLEN_PP(name))) {
break;
}
}
if (zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &zvpp, &hpos) == FAILURE) {
/* sigh, breaking the encapsulation, there aren't any functions manipulating the llist at the place of the zend_llist_position */
zend_llist_element *elm = pos;
if (elm->prev) {
elm->prev->next = elm->next;
} else {
zend_extensions.head = elm->next;
}
if (elm->next) {
elm->next->prev = elm->prev;
} else {
zend_extensions.tail = elm->prev;
}
#if ZEND_EXTENSIONS_SUPPORT
if (extension->shutdown) {
extension->shutdown(extension);
}
#endif
if (zend_extensions.dtor) {
zend_extensions.dtor(elm->data);
}
pefree(elm, zend_extensions.persistent);
zend_extensions.count--;
} else {
zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &hpos);
if (Z_TYPE(key) == IS_LONG) {
zend_hash_index_del(Z_ARRVAL_PP(zvpp), Z_LVAL(key));
}
}
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &hpos);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void **) &name, &hpos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &hpos)) {
phpdbg_notice("wait", "missingextension=\"%.*s\"", "The Zend extension %.*s isn't present in " PHPDBG_NAME ", you still can load via dl /path/to/extension.so", Z_STRLEN_PP(name), Z_STRVAL_PP(name));
}
}
}
zend_ini_deactivate(TSRMLS_C);
if (zend_hash_find(ht, "systemini", sizeof("systemini"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
HashPosition position;
zval **ini_entry;
zend_ini_entry *original_ini;
zval key;
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &position);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void**) &ini_entry, &position) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &position)) {
zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &position);
if (Z_TYPE(key) == IS_STRING) {
if (Z_TYPE_PP(ini_entry) == IS_STRING) {
if (zend_hash_find(EG(ini_directives), Z_STRVAL(key), Z_STRLEN(key) + 1, (void **) &original_ini) == SUCCESS) {
if (!original_ini->on_modify || original_ini->on_modify(original_ini, Z_STRVAL_PP(ini_entry), Z_STRLEN_PP(ini_entry), original_ini->mh_arg1, original_ini->mh_arg2, original_ini->mh_arg3, ZEND_INI_STAGE_ACTIVATE TSRMLS_CC) == SUCCESS) {
if (original_ini->modified && original_ini->orig_value != original_ini->value) {
efree(original_ini->value);
}
original_ini->value = Z_STRVAL_PP(ini_entry);
original_ini->value_length = Z_STRLEN_PP(ini_entry);
Z_TYPE_PP(ini_entry) = IS_NULL; /* don't free the value */
}
}
}
efree(Z_STRVAL(key));
}
}
}
if (zend_hash_find(ht, "userini", sizeof("userini"), (void **) &zvpp) == SUCCESS && Z_TYPE_PP(zvpp) == IS_ARRAY) {
HashPosition position;
zval **ini_entry;
zval key;
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(zvpp), &position);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(zvpp), (void**) &ini_entry, &position) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(zvpp), &position)) {
zend_hash_get_current_key_zval_ex(Z_ARRVAL_PP(zvpp), &key, &position);
if (Z_TYPE(key) == IS_STRING) {
if (Z_TYPE_PP(ini_entry) == IS_STRING) {
zend_alter_ini_entry_ex(Z_STRVAL(key), Z_STRLEN(key) + 1, Z_STRVAL_PP(ini_entry), Z_STRLEN_PP(ini_entry), ZEND_INI_PERDIR, ZEND_INI_STAGE_HTACCESS, 1 TSRMLS_CC);
}
efree(Z_STRVAL(key));
}
}
}
zval_dtor(&zv);
if (free_zv) {
/* separate freeing to not dtor the symtable too, just the container zval... */
efree(free_zv);
}
/* Reapply raw input */
/* ??? */
}
PHPDBG_COMMAND(wait) /* {{{ */
{
struct sockaddr_un local, remote;
int rlen, sr, sl;
unlink(PHPDBG_G(socket_path));
if (PHPDBG_G(socket_server_fd) == -1) {
int len;
PHPDBG_G(socket_server_fd) = sl = socket(AF_UNIX, SOCK_STREAM, 0);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, PHPDBG_G(socket_path));
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(sl, (struct sockaddr *)&local, len) == -1) {
phpdbg_error("wait", "type=\"nosocket\" import=\"fail\"", "Unable to connect to UNIX domain socket at %s defined by phpdbg.path ini setting", PHPDBG_G(socket_path));
return FAILURE;
}
chmod(PHPDBG_G(socket_path), 0666);
listen(sl, 2);
} else {
sl = PHPDBG_G(socket_server_fd);
}
rlen = sizeof(remote);
sr = accept(sl, (struct sockaddr *) &remote, (socklen_t *) &rlen);
char msglen[5];
int recvd = 4;
do {
recvd -= recv(sr, &(msglen[4 - recvd]), recvd, 0);
} while (recvd > 0);
recvd = *(size_t *) msglen;
char *data = emalloc(recvd);
do {
recvd -= recv(sr, &(data[(*(int *) msglen) - recvd]), recvd, 0);
} while (recvd > 0);
phpdbg_webdata_decompress(data, *(int *) msglen TSRMLS_CC);
if (PHPDBG_G(socket_fd) != -1) {
close(PHPDBG_G(socket_fd));
}
PHPDBG_G(socket_fd) = sr;
efree(data);
phpdbg_notice("wait", "import=\"success\"", "Successfully imported request data, stopped before executing");
return SUCCESS;
} /* }}} */

29
phpdbg_wait.h Normal file
View File

@ -0,0 +1,29 @@
/*
+----------------------------------------------------------------------+
| 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: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_WAIT_H
#define PHPDBG_WAIT_H
#include "zend.h"
#include "phpdbg.h"
PHPDBG_COMMAND(wait);
void phpdbg_webdata_decompress(char *msg, int len TSRMLS_DC);
#endif /* PHPDBG_WAIT_H */

View File

@ -30,7 +30,6 @@
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
typedef struct {
void *page;
size_t size;
@ -138,13 +137,6 @@ static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC)
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;
@ -281,118 +273,37 @@ static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) {
ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
}
free(tmp_watch->str);
efree(tmp_watch->str);
efree(tmp_watch->name_in_parent);
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;
static int phpdbg_watchpoint_parse_wrapper(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval **zv, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
int ret;
phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
watch->flags = 0;
watch->str = name;
watch->str_len = len;
watch->name_in_parent = keyname;
watch->name_in_parent_len = keylen;
watch->parent_container = parent;
phpdbg_create_zval_watchpoint(*zv, watch);
if (len < 2 || *input != '$') {
goto error;
}
ret = callback(watch TSRMLS_CC);
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("watch", "type=\"undefined\" variable=\"%.*s\"", "%.*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("watch", "type=\"notiterable\" variable=\"%.*s\"", "%.*s is nor an array nor an object", (int) i, input);
return FAILURE;
}
index_len = 0;
}
if (ret != SUCCESS) {
efree(watch);
efree(name);
efree(keyname);
}
return ret;
error:
phpdbg_error("watch", "type=\"invalidinput\"", "Malformed input");
return FAILURE;
}
PHPDBG_API 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) {
phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, 0, callback TSRMLS_CC);
}
static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
@ -550,8 +461,8 @@ static void phpdbg_watch_dtor(void *pDest) {
phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
phpdbg_remove_watchpoint(watch TSRMLS_CC);
free(watch->str);
free(watch->name_in_parent);
efree(watch->str);
efree(watch->name_in_parent);
efree(watch);
}

View File

@ -42,9 +42,9 @@ PHPDBG_WATCH(recursive);
*/
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_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s", 0),
PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s", 0),
PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s", 0),
PHPDBG_END_COMMAND
};

183
phpdbg_webdata_transfer.c Normal file
View File

@ -0,0 +1,183 @@
/*
+----------------------------------------------------------------------+
| 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: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg_webdata_transfer.h"
#include "ext/json/php_json.h"
PHPDBG_API void phpdbg_webdata_compress(char **msg, int *len TSRMLS_DC) {
smart_str buf = {0};
zval array;
HashTable *ht;
/* I really need to change that to an array of zvals... */
zval zv1 = {{0}}, *zvp1 = &zv1;
zval zv2 = {{0}}, *zvp2 = &zv2;
zval zv3 = {{0}}, *zvp3 = &zv3;
zval zv4 = {{0}}, *zvp4 = &zv4;
zval zv5 = {{0}}, *zvp5 = &zv5;
zval zv6 = {{0}}, *zvp6 = &zv6;
zval zv7 = {{0}}, *zvp7 = &zv7;
zval zv8 = {{0}}, *zvp8 = &zv8;
array_init(&array);
ht = Z_ARRVAL(array);
/* fetch superglobals */
{
zend_is_auto_global(ZEND_STRL("GLOBALS") TSRMLS_CC);
/* might be JIT */
zend_is_auto_global(ZEND_STRL("_ENV") TSRMLS_CC);
zend_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC);
zend_is_auto_global(ZEND_STRL("_REQUEST") TSRMLS_CC);
array_init(&zv1);
zend_hash_copy(Z_ARRVAL(zv1), &EG(symbol_table), NULL, (void *) NULL, sizeof(zval *));
Z_ARRVAL(zv1)->pDestructor = NULL; /* we're operating on a copy! Don't double free zvals */
zend_hash_del(Z_ARRVAL(zv1), "GLOBALS", sizeof("GLOBALS")); /* do not use the reference to itself in json */
zend_hash_add(ht, "GLOBALS", sizeof("GLOBALS"), &zvp1, sizeof(zval *), NULL);
}
/* save php://input */
{
php_stream *stream;
int len;
char *contents;
stream = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
if ((len = php_stream_copy_to_mem(stream, &contents, PHP_STREAM_COPY_ALL, 0)) > 0) {
ZVAL_STRINGL(&zv2, contents, len, 0);
} else {
ZVAL_EMPTY_STRING(&zv2);
}
Z_SET_REFCOUNT(zv2, 2);
zend_hash_add(ht, "input", sizeof("input"), &zvp2, sizeof(zval *), NULL);
}
/* change sapi name */
{
if (sapi_module.name) {
ZVAL_STRING(&zv6, sapi_module.name, 0);
} else {
Z_TYPE(zv6) = IS_NULL;
}
Z_SET_REFCOUNT(zv6, 2);
zend_hash_add(ht, "sapi_name", sizeof("sapi_name"), &zvp6, sizeof(zval *), NULL);
}
/* handle modules / extensions */
{
HashPosition position;
zend_module_entry *module;
zend_extension *extension;
zend_llist_position pos;
array_init(&zv7);
for (zend_hash_internal_pointer_reset_ex(&module_registry, &position);
zend_hash_get_current_data_ex(&module_registry, (void**) &module, &position) == SUCCESS;
zend_hash_move_forward_ex(&module_registry, &position)) {
zval **value = emalloc(sizeof(zval *));
ALLOC_ZVAL(*value);
ZVAL_STRING(*value, module->name, 1);
zend_hash_next_index_insert(Z_ARRVAL(zv7), value, sizeof(zval *), NULL);
}
zend_hash_add(ht, "modules", sizeof("modules"), &zvp7, sizeof(zval *), NULL);
array_init(&zv8);
extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
while (extension) {
zval **value = emalloc(sizeof(zval *));
ALLOC_ZVAL(*value);
ZVAL_STRING(*value, extension->name, 1);
zend_hash_next_index_insert(Z_ARRVAL(zv8), value, sizeof(zval *), NULL);
extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
}
zend_hash_add(ht, "extensions", sizeof("extensions"), &zvp8, sizeof(zval *), NULL);
}
/* switch cwd */
if (SG(options) & SAPI_OPTION_NO_CHDIR) {
char *ret = NULL;
char path[MAXPATHLEN];
#if HAVE_GETCWD
ret = VCWD_GETCWD(path, MAXPATHLEN);
#elif HAVE_GETWD
ret = VCWD_GETWD(path);
#endif
if (ret) {
ZVAL_STRING(&zv5, path, 1);
Z_SET_REFCOUNT(zv5, 1);
zend_hash_add(ht, "cwd", sizeof("cwd"), &zvp5, sizeof(zval *), NULL);
}
}
/* get system ini entries */
{
HashPosition position;
zend_ini_entry *ini_entry;
array_init(&zv3);
for (zend_hash_internal_pointer_reset_ex(EG(ini_directives), &position);
zend_hash_get_current_data_ex(EG(ini_directives), (void**) &ini_entry, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(ini_directives), &position)) {
zval **value = emalloc(sizeof(zval *));
if (ini_entry->modified) {
if (!ini_entry->orig_value) {
efree(value);
continue;
}
ALLOC_ZVAL(*value);
ZVAL_STRINGL(*value, ini_entry->orig_value, ini_entry->orig_value_length, 1);
} else {
if (!ini_entry->value) {
efree(value);
continue;
}
ALLOC_ZVAL(*value);
ZVAL_STRINGL(*value, ini_entry->value, ini_entry->value_length, 1);
}
zend_hash_add(Z_ARRVAL(zv3), ini_entry->name, ini_entry->name_length, value, sizeof(zval *), NULL);
}
zend_hash_add(ht, "systemini", sizeof("systemini"), &zvp3, sizeof(zval *), NULL);
}
/* get perdir ini entries */
if (EG(modified_ini_directives)) {
HashPosition position;
zend_ini_entry *ini_entry;
array_init(&zv4);
for (zend_hash_internal_pointer_reset_ex(EG(modified_ini_directives), &position);
zend_hash_get_current_data_ex(EG(modified_ini_directives), (void**) &ini_entry, &position) == SUCCESS;
zend_hash_move_forward_ex(EG(modified_ini_directives), &position)) {
zval **value = emalloc(sizeof(zval *));
if (!ini_entry->value) {
efree(value);
continue;
}
ALLOC_ZVAL(*value);
ZVAL_STRINGL(*value, ini_entry->value, ini_entry->value_length, 1);
zend_hash_add(Z_ARRVAL(zv4), ini_entry->name, ini_entry->name_length, value, sizeof(zval *), NULL);
}
zend_hash_add(ht, "userini", sizeof("userini"), &zvp4, sizeof(zval *), NULL);
}
/* encode data */
php_json_encode(&buf, &array, 0 TSRMLS_CC);
*msg = buf.c;
*len = buf.len;
zval_dtor(&array);
}

27
phpdbg_webdata_transfer.h Normal file
View File

@ -0,0 +1,27 @@
/*
+----------------------------------------------------------------------+
| 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: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_WEBDATA_TRANSFER_H
#define PHPDBG_WEBDATA_TRANSFER_H
#include "zend.h"
#include "phpdbg.h"
PHPDBG_API void phpdbg_webdata_compress(char **msg, int *len TSRMLS_DC);
#endif /* PHPDBG_WEBDATA_TRANSFER_H */

66
xml.md
View File

@ -18,6 +18,11 @@ msgout
- text message output related to the xml data (e.g. &lt;intro severity="normal" help="help" msgout="To get help using phpdbg type &amp;quot;help&amp;quot; and press enter" />)
req
---
- the request id, if one was passed to the last command (via -r %d, where %d is the id) (and the output is related to that message)
file
----
@ -222,7 +227,7 @@ frame
- maxnum: tried to access a frame with a number heigher than existing (or &lt; 0)
### attributes on &lt;arg> ###
v
- variadic: has a non-empty value if the argument is variadic
- name: variable name of parameter
@ -382,8 +387,8 @@ exec
- invalid: given context (attribute) is not matching a valid file or symlink
- notfound: given context (attribute) does not exist
run / <stop> tag
----------------
run / &lt;stop> tag
-------------------
- runs the script (set via exec command)
- &lt;stop type="end" />: script execution ended normally
@ -562,21 +567,65 @@ set
- generally enables / disables showing of refcount in watchpoint breaks silently with no further xml answer
- if the boolean switch is omitted, it emits current state in a &lt;setrefcount active="" /> where active is on or off
wait
----
- internally executes exec, so exec will output first (if binding to socket worked)
### attributes ###
- import: has value "success"/"fail"
- missingmodule/missingextension: modules/extensions loaded in the target SAPI, but not in phpdbg
### errors (by type) ###
- nosocket: couldn't establish socket
- invaliddata: invalid JSON passed to socket
dl
--
- loads a module or Zend extension at a given path
- if a relative path is passed, it's relative to the extension_dir ini setting
### attributes ###
- extensiontype: "Zend extension" or "module"
- name: the extension name
- path: the path where it was loaded
### errors (by type) ###
- unsupported: dynamic extension loading is unsupported
- relpath: relative path given, but no extension_dir defined
- unknown: general error with internal DL_LOAD() (for message see msg attribute)
- wrongapi: wrong Zend engine version (apineeded / apiinstalled attributes give information about the API versions)
- wrongbuild: unmatched build versions (buildneeded / buildinstalled attributes give information about the build versions)
- registerfailure: registering module failed
- startupfailure: couldn't startup Zend extension / module
- initfailure: couldn't initialize module
- nophpso: passed shared object is not a valid Zend extension nor module
- errors may have the module or extension attribute when their name is already known at the point of failure
Other tags
==========
&lt;signal>
--------
-----------
- received caught signal
### attributes ###
- type: type of signal
- type: type of signal (e.g. SIGINT)
### by type ###
- SIGINT: interactive mode is entered...
&lt;watch*>
--------
-----------
- generally emitted on hit watchpoint
- &lt;watchdelete variable="" />: when a variable was unset, the watchpoint is removed too
@ -593,3 +642,8 @@ Other tags
- removed: number of elements removed
- added: number of elements added
- &lt;watcharrayptr>: if this tag appears, the internal pointer of the array way changed
&lt;signalsegv>
---------------
- generally emitted when data couldn't be fetched (e.g. by accessing inconsistent data); only used in hard interrupt mode

102
zend_mm_structs.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef ZEND_MM_STRUCTS_H
#define ZEND_MM_STRUCTS_H
/* structs and macros defined in Zend/zend_alloc.c
Needed for realizing watchpoints and sigsafe memory */
#include "zend.h"
#ifndef ZEND_MM_COOKIES
# define ZEND_MM_COOKIES ZEND_DEBUG
#endif
#define ZEND_MM_CACHE 1
#ifndef ZEND_MM_CACHE_STAT
# define ZEND_MM_CACHE_STAT 0
#endif
typedef struct _zend_mm_block_info {
#if ZEND_MM_COOKIES
size_t _cookie;
#endif
size_t _size;
size_t _prev;
} zend_mm_block_info;
typedef struct _zend_mm_small_free_block {
zend_mm_block_info info;
#if ZEND_DEBUG
unsigned int magic;
#ifdef ZTS
THREAD_T thread_id;
#endif
#endif
struct _zend_mm_free_block *prev_free_block;
struct _zend_mm_free_block *next_free_block;
} zend_mm_small_free_block;
typedef struct _zend_mm_free_block {
zend_mm_block_info info;
#if ZEND_DEBUG
unsigned int magic;
#ifdef ZTS
THREAD_T thread_id;
#endif
#endif
struct _zend_mm_free_block *prev_free_block;
struct _zend_mm_free_block *next_free_block;
struct _zend_mm_free_block **parent;
struct _zend_mm_free_block *child[2];
} zend_mm_free_block;
#define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \
(zend_mm_free_block *) ((char *)&heap->free_buckets[index * 2] + \
sizeof(zend_mm_free_block *) * 2 - \
sizeof(zend_mm_small_free_block))
#define ZEND_MM_REST_BUCKET(heap) \
(zend_mm_free_block *)((char *)&heap->rest_buckets[0] + \
sizeof(zend_mm_free_block *) * 2 - \
sizeof(zend_mm_small_free_block))
#define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3)
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;
size_t real_size;
size_t real_peak;
size_t limit;
size_t size;
size_t peak;
size_t reserve_size;
void *reserve;
int overflow;
int internal;
#if ZEND_MM_CACHE
unsigned int cached;
zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS];
#endif
zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];
zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];
zend_mm_free_block *rest_buckets[2];
int rest_count;
#if ZEND_MM_CACHE_STAT
struct {
int count;
int max_count;
int hit;
int miss;
} cache_stat[ZEND_MM_NUM_BUCKETS+1];
#endif
};
#endif