Merge branch 'master' of sapi/phpdbg into PHP-5.6

Including phpdbg.
This commit is contained in:
Bob Weinand 2013-12-20 14:27:51 +01:00
commit cee72c13d7
48 changed files with 10215 additions and 0 deletions

10
sapi/phpdbg/.gdbinit Normal file
View File

@ -0,0 +1,10 @@
define ____phpdbg_globals
if basic_functions_module.zts
if !$tsrm_ls
set $tsrm_ls = ts_resource_ex(0, 0)
end
set $phpdbg = ((zend_phpdbg_globals*) (*((void ***) $tsrm_ls))[phpdbg_globals_id-1])
else
set $phpdbg = phpdbg_globals
end
end

5
sapi/phpdbg/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.libs/
./phpdbg
*.lo
*.o
build

105
sapi/phpdbg/.phpdbginit Normal file
View File

@ -0,0 +1,105 @@
##########################################################
# .phpdbginit
#
# Lines starting with # are ignored
# Code must start and end with <: and :> respectively
##########################################################
# Place initialization commands one per line
##########################################################
# exec sapi/phpdbg/test.php
# set color prompt white-bold
# set color notice green
# set color error red
##########################################################
# Embedding code in .phpdbginit
##########################################################
<:
/*
* This embedded PHP is executed at init time
*/
/*
* Functions defined and registered by init
* will persist across cleans
*/
/*
function my_debugging_function()
{
var_dump(func_get_args());
}
*/
/* phpdbg_break(PHPDBG_METHOD, "phpdbg::method"); */
/* phpdbg_break(PHPDBG_FUNC, "my_global_function"); */
/* phpdbg_break(PHPDBG_FILE, "/path/to/file.php:10"); */
/*
If readline is loaded, you might want to setup completion:
*/
if (function_exists('readline_completion_function')) {
readline_completion_function(function(){
return array_merge(
get_defined_functions()['user'],
array_keys(get_defined_constants())
);
});
}
/*
Setting argv made trivial ...
argv 1 2 3 4
^ set argv for next execution
argv
^ unset argv for next execution
*/
function argv()
{
$argv = func_get_args();
if (!$argv) {
$_SERVER['argv'] = array();
$_SERVER['argc'] = 0;
return;
}
$_SERVER['argv'] = array_merge
(
array("phpdbg"),
$argv
);
$_SERVER['argc'] = count($_SERVER['argv']);
return $_SERVER['argv'];
}
:>
##########################################################
# Now carry on initializing phpdbg ...
##########################################################
# R my_debugging_function
# R argv
##########################################################
# PHP has many functions that might be useful
# ... you choose ...
##########################################################
# R touch
# R unlink
# R scandir
# R glob
##########################################################
# Remember: *you have access to the shell*
##########################################################
# The output of registered function calls is not,
# by default, very pretty (unless you implement
# and register a new implementation for phpdbg)
# The output of shell commands will usually be more
# readable on the console
##########################################################
# TLDR; if you have a good shell, use it ...
##########################################################

3
sapi/phpdbg/.travis.yml Normal file
View File

@ -0,0 +1,3 @@
language: c
script: ./travis/ci.sh

52
sapi/phpdbg/Changelog.md Normal file
View File

@ -0,0 +1,52 @@
ChangeLog for phpdbg
====================
Version 0.3.0 2013-00-00
------------------------
1. Added ability to disable an enable a single breakpoint
2. Added ability to override SAPI name
3. Added extended conditional breakpoint support "break at"
4. Fix loading of zend extnsions with -z
5. Fix crash when loading .phpdbginit with command line switch
6. Fix crash on startup errors
7. Added init.d for remote console (redhat)
8. Added phpdbg_exec userland function
9. Added testing facilities
10. Added break on n-th opline support
11. Improved trace output
Version 0.2.0 2013-11-31
------------------------
1. Added "break delete <id>" command
2. Added "break opcode <opcode>" command
3. Added "set" command - control prompt and console colors
4. .phpdbginit now searched in (additional) ini dirs
5. Added source command - load additional .phpdbginit script during session
6. Added remote console mode
7. Added info memory command
Version 0.1.0 2013-11-23
------------------------
1. New commands:
- until (continue until the current line is executed)
- frame (switch to a frame in the current stack for inspection)
- info (quick access to useful information on the console)
- finish (continue until the current function has returned)
- leave (continue until the current function is returning)
- shell (shell a command)
- register (register a function for use as a command)
2. Added printers for class and method
3. Make uniform commands and aliases where possible
4. Include all alias information and sub-command information in help
5. Added signal handling to break execution (ctrl-c)
6. Fixed #13 (Output Buffering Control seems fail)
7. Fixed #14 (Fixed typo in Makefile.frag)
Version 0.0.1 2013-11-15
------------------------
1. Initial features

28
sapi/phpdbg/Makefile.frag Normal file
View File

@ -0,0 +1,28 @@
phpdbg: $(BUILD_BINARY)
phpdbg-shared: $(BUILD_SHARED)
$(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_PHPDBG_SHARED)
$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_PHPDBG)
install-phpdbg: $(BUILD_BINARY)
@echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
@$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
@$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
@$(INSTALL) -m 0755 $(BUILD_BINARY) $(INSTALL_ROOT)$(bindir)/$(program_prefix)phpdbg$(program_suffix)$(EXEEXT)
clean-phpdbg:
@echo "Cleaning phpdbg object files ..."
find sapi/phpdbg/ -name *.lo -o -name *.o | xargs rm -f
test-phpdbg:
@echo "Running phpdbg tests ..."
@$(top_builddir)/sapi/cli/php sapi/phpdbg/tests/run-tests.php --phpdbg sapi/phpdbg/phpdbg
.PHONY: clean-phpdbg test-phpdbg

83
sapi/phpdbg/README.md Normal file
View File

@ -0,0 +1,83 @@
The interactive PHP debugger
============================
Implemented as a SAPI module, phpdbg can excert complete control over the environment without impacting the functionality or performance of your code.
phpdbg aims to be a lightweight, powerful, easy to use debugging platform for PHP 5.4+
[![phpdbg on travis-ci](https://travis-ci.org/krakjoe/phpdbg.png?branch=master)](https://travis-ci.org/krakjoe/phpdbg)
Features
========
- Stepthrough Debugging
- Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
- Easy Access to PHP with built-in eval()
- Easy Access to Currently Executing Code
- Userland API
- SAPI Agnostic - Easily Integrated
- PHP Configuration File Support
- JIT Super Globals - Set Your Own!!
- Optional readline Support - Comfortable Terminal Operation
- Remote Debugging Support - Bundled Java GUI
- Easy Operation - See Help :)
Planned
=======
- Improve Everything :)
Installation
============
To install **phpdbg**, you must compile the source against your PHP installation sources, and enable the SAPI with the configure command.
```
cd /usr/src/php-src/sapi
git clone https://github.com/krakjoe/phpdbg
cd ../
./buildconf --force
./configure --enable-phpdbg
make -j8
make install-phpdbg
```
Where the source directory has been used previously to build PHP, there exists a file named *config.nice* which can be used to invoke configure with the same
parameters as were used by the last execution of *configure*.
**Note:** PHP must be configured with the switch --with-readline for phpdbg to support history, autocompletion, tab-listing etc.
Command Line Options
====================
The following switches are implemented (just like cli SAPI):
- -n ignore php ini
- -c search for php ini in path
- -z load zend extension
- -d define php ini entry
The following switches change the default behaviour of phpdbg:
- -v disables quietness
- -s enabled stepping
- -e sets execution context
- -b boring - disables use of colour on the console
- -I ignore .phpdbginit (default init file)
- -i override .phpgdbinit location (implies -I)
- -O set oplog output file
- -q do not print banner on startup
- -r jump straight to run
- -E enable step through eval()
- -l listen ports for remote mode
- -a listen address for remote mode
- -S override SAPI name
**Note:** Passing -rr will cause phpdbg to quit after execution, rather than returning to the console.
Getting Started
===============
See the website for tutorials/documentation
http://phpdbg.com

61
sapi/phpdbg/config.m4 Normal file
View File

@ -0,0 +1,61 @@
dnl
dnl $Id$
dnl
PHP_ARG_ENABLE(phpdbg, for phpdbg support,
[ --enable-phpdbg Build phpdbg], yes, yes)
PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
if test "$PHP_PHPDBG" != "no"; then
AC_DEFINE(HAVE_PHPDBG, 1, [ ])
if test "$PHP_PHPDBG_DEBUG" != "no"; then
AC_DEFINE(PHPDBG_DEBUG, 1, [ ])
else
AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
fi
PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c"
PHP_SUBST(PHP_PHPDBG_CFLAGS)
PHP_SUBST(PHP_PHPDBG_FILES)
PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag])
PHP_SELECT_SAPI(phpdbg, program, $PHP_PHPDBG_FILES, $PHP_PHPDBG_CFLAGS, [$(SAPI_PHPDBG_PATH)])
BUILD_BINARY="sapi/phpdbg/phpdbg"
BUILD_SHARED="sapi/phpdbg/libphpdbg.la"
BUILD_PHPDBG="\$(LIBTOOL) --mode=link \
\$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
\$(PHP_GLOBAL_OBJS) \
\$(PHP_BINARY_OBJS) \
\$(PHP_PHPDBG_OBJS) \
\$(EXTRA_LIBS) \
\$(PHPDBG_EXTRA_LIBS) \
\$(ZEND_EXTRA_LIBS) \
-o \$(BUILD_BINARY)"
BUILD_PHPDBG_SHARED="\$(LIBTOOL) --mode=link \
\$(CC) -shared -Wl,-soname,libphpdbg.so -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
\$(PHP_GLOBAL_OBJS) \
\$(PHP_BINARY_OBJS) \
\$(PHP_PHPDBG_OBJS) \
\$(EXTRA_LIBS) \
\$(PHPDBG_EXTRA_LIBS) \
\$(ZEND_EXTRA_LIBS) \
\-DPHPDBG_SHARED \
-o \$(BUILD_SHARED)"
PHP_SUBST(BUILD_BINARY)
PHP_SUBST(BUILD_SHARED)
PHP_SUBST(BUILD_PHPDBG)
PHP_SUBST(BUILD_PHPDBG_SHARED)
fi
dnl ## Local Variables:
dnl ## tab-width: 4
dnl ## End:

19
sapi/phpdbg/config.w32 Normal file
View File

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

1308
sapi/phpdbg/phpdbg.c Normal file

File diff suppressed because it is too large Load Diff

187
sapi/phpdbg/phpdbg.h Normal file
View File

@ -0,0 +1,187 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_H
#define PHPDBG_H
#ifdef PHP_WIN32
# define PHPDBG_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHPDBG_API __attribute__ ((visibility("default")))
#else
# define PHPDBG_API
#endif
#include "php.h"
#include "php_globals.h"
#include "php_variables.h"
#include "php_getopt.h"
#include "zend_builtin_functions.h"
#include "zend_extensions.h"
#include "zend_modules.h"
#include "zend_globals.h"
#include "zend_ini_scanner.h"
#include "zend_stream.h"
#include "SAPI.h"
#include <fcntl.h>
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__)
# include <windows.h>
# include "config.w32.h"
# undef strcasecmp
# undef strncasecmp
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
#else
# include "php_config.h"
#endif
#ifndef O_BINARY
# define O_BINARY 0
#endif
#include "php_main.h"
#ifdef ZTS
# include "TSRM.h"
#endif
#ifdef HAVE_LIBREADLINE
# include <readline/readline.h>
# include <readline/history.h>
#endif
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#ifdef ZTS
# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
#else
# define PHPDBG_G(v) (phpdbg_globals.v)
#endif
#define PHPDBG_NEXT 2
#define PHPDBG_UNTIL 3
#define PHPDBG_FINISH 4
#define PHPDBG_LEAVE 5
/*
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)
#define PHPDBG_HAS_OPLINE_BP (1<<3)
#define PHPDBG_HAS_METHOD_BP (1<<4)
#define PHPDBG_HAS_COND_BP (1<<5)
#define PHPDBG_HAS_OPCODE_BP (1<<6)
#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7)
#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8)
#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) /* }}} */
/*
END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
*/
#define PHPDBG_IN_COND_BP (1<<10)
#define PHPDBG_IN_EVAL (1<<11)
#define PHPDBG_IS_STEPPING (1<<12)
#define PHPDBG_IS_QUIET (1<<13)
#define PHPDBG_IS_QUITTING (1<<14)
#define PHPDBG_IS_COLOURED (1<<15)
#define PHPDBG_IS_CLEANING (1<<16)
#define PHPDBG_IN_UNTIL (1<<17)
#define PHPDBG_IN_FINISH (1<<18)
#define PHPDBG_IN_LEAVE (1<<19)
#define PHPDBG_IS_REGISTERED (1<<20)
#define PHPDBG_IS_STEPONEVAL (1<<21)
#define PHPDBG_IS_INITIALIZING (1<<22)
#define PHPDBG_IS_SIGNALED (1<<23)
#define PHPDBG_IS_INTERACTIVE (1<<24)
#define PHPDBG_IS_BP_ENABLED (1<<25)
#define PHPDBG_IS_REMOTE (1<<26)
#define PHPDBG_IS_DISCONNECTED (1<<27)
#define PHPDBG_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_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
# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED)
#else
# 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.3.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
/* }}} */
/* {{{ output descriptors */
#define PHPDBG_STDIN 0
#define PHPDBG_STDOUT 1
#define PHPDBG_STDERR 2
#define PHPDBG_IO_FDS 3 /* }}} */
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
HashTable registered; /* registered */
HashTable seek; /* seek oplines */
phpdbg_frame_t frame; /* frame */
char *exec; /* file to execute */
size_t exec_len; /* size of exec */
zend_op_array *ops; /* op_array */
zval *retval; /* return value */
int bp_count; /* breakpoint count */
int vmret; /* return from last opcode handler execution */
FILE *oplog; /* opline log */
FILE *io[PHPDBG_IO_FDS]; /* io */
char *prompt[2]; /* prompt */
const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
phpdbg_command_t *lcmd; /* last command */
phpdbg_param_t lparam; /* last param */
zend_ulong flags; /* phpdbg flags */
ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
#endif /* PHPDBG_H */

122
sapi/phpdbg/phpdbg.init.d Executable file
View File

@ -0,0 +1,122 @@
################################################################
# File: /etc/init.d/phpdbg #
# Author: krakjoe #
# Purpose: Daemonize phpdbg automatically on boot #
# chkconfig: 2345 07 09 #
# description: Starts, stops and restarts phpdbg daemon #
################################################################
LOCKFILE=/var/lock/subsys/phpdbg
PIDFILE=/var/run/phpdbg.pid
STDIN=4000
STDOUT=8000
################################################################
# Either set path to phpdbg here or rely on phpdbg in ENV/PATH #
################################################################
if [ "x${PHPDBG}" == "x" ]; then
PHPDBG=$(which phpdbg 2>/dev/null)
fi
################################################################
# Options to pass to phpdbg upon boot #
################################################################
OPTIONS=
LOGFILE=/var/log/phpdbg.log
################################################################
# STOP EDITING STOP EDITING STOP EDITING STOP EDITING #
################################################################
. /etc/rc.d/init.d/functions
RETVAL=1
################################################################
insanity()
{
if [ "x${PHPDBG}" == "x" ]; then
PHPDBG=$(which phpdbg 2>>/dev/null)
if [ $? != 0 ]; then
echo -n $"Fatal: cannot find phpdbg ${PHPDBG}"
echo_failure
echo
return 1
fi
else
if [ ! -x ${PHPDBG} ]; then
echo -n $"Fatal: cannot execute phpdbg ${PHPDBG}"
echo_failure
echo
return 1
fi
fi
return 0
}
start()
{
insanity
if [ $? -eq 1 ]; then
return $RETVAL
fi
echo -n $"Starting: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
nohup ${PHPDBG} -l${STDIN}/${STDOUT} ${OPTIONS} 2>>${LOGFILE} 1>/dev/null </dev/null &
PID=$!
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
echo $PID > $PIDFILE
echo_success
else
echo_failure
fi
echo
[ $RETVAL = 0 ] && touch ${LOCKFILE}
return $RETVAL
}
stop()
{
insanity
if [ $? -eq 1 ]; then
return $RETVAL
fi
if [ -f ${LOCKFILE} ] && [ -f ${PIDFILE} ]
then
echo -n $"Stopping: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
kill -s TERM $(cat $PIDFILE)
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
echo_success
else
echo_failure
fi
echo
[ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE}
else
echo -n $"Error: phpdbg not running"
echo_failure
echo
[ $RETVAL = 1 ]
fi
return $RETVAL
}
##################################################################
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $PHPDBG
;;
restart)
$0 stop
$0 start
;;
*)
echo "usage: $0 start|stop|restart|status"
;;
esac
###################################################################
exit $RETVAL

1661
sapi/phpdbg/phpdbg_bp.c Normal file

File diff suppressed because it is too large Load Diff

146
sapi/phpdbg/phpdbg_bp.h Normal file
View File

@ -0,0 +1,146 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_BP_H
#define PHPDBG_BP_H
/* {{{ */
typedef struct _zend_op *phpdbg_opline_ptr_t; /* }}} */
/* {{{ breakpoint base structure */
#define phpdbg_breakbase(name) \
int id; \
zend_uchar type; \
zend_ulong hits; \
zend_bool disabled; \
const char *name /* }}} */
/* {{{ breakpoint base */
typedef struct _phpdbg_breakbase_t {
phpdbg_breakbase(name);
} phpdbg_breakbase_t; /* }}} */
/**
* Breakpoint file-based representation
*/
typedef struct _phpdbg_breakfile_t {
phpdbg_breakbase(filename);
long line;
} phpdbg_breakfile_t;
/**
* Breakpoint symbol-based representation
*/
typedef struct _phpdbg_breaksymbol_t {
phpdbg_breakbase(symbol);
} phpdbg_breaksymbol_t;
/**
* Breakpoint method based representation
*/
typedef struct _phpdbg_breakmethod_t {
phpdbg_breakbase(class_name);
size_t class_len;
const char *func_name;
size_t func_len;
} phpdbg_breakmethod_t;
/**
* Breakpoint opline num based representation
*/
typedef struct _phpdbg_breakopline_t {
phpdbg_breakbase(func_name);
size_t func_len;
const char *class_name;
size_t class_len;
zend_ulong opline_num;
zend_ulong opline;
} phpdbg_breakopline_t;
/**
* Breakpoint opline based representation
*/
typedef struct _phpdbg_breakline_t {
phpdbg_breakbase(name);
zend_ulong opline;
phpdbg_breakopline_t *base;
} phpdbg_breakline_t;
/**
* Breakpoint opcode based representation
*/
typedef struct _phpdbg_breakop_t {
phpdbg_breakbase(name);
zend_ulong hash;
} phpdbg_breakop_t;
/**
* Breakpoint condition based representation
*/
typedef struct _phpdbg_breakcond_t {
phpdbg_breakbase(code);
size_t code_len;
zend_bool paramed;
phpdbg_param_t param;
zend_ulong hash;
zend_op_array *ops;
} phpdbg_breakcond_t;
/* {{{ Opline breaks API */
PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC);
PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC);
PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Creation API */
PHPDBG_API void phpdbg_set_breakpoint_file(const char* filename, long lineno TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_symbol(const char* func_name, size_t func_name_len TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_method(const char* class_name, const char* func_name TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_opcode(const char* opname, size_t opname_len TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Detection API */
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
/* {{{ Misc Breakpoint API */
PHPDBG_API void phpdbg_hit_breakpoint(phpdbg_breakbase_t* brake, zend_bool output TSRMLS_DC);
PHPDBG_API void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC);
PHPDBG_API void phpdbg_print_breakpoint(phpdbg_breakbase_t* brake TSRMLS_DC);
PHPDBG_API void phpdbg_reset_breakpoints(TSRMLS_D);
PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D);
PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC);
PHPDBG_API void phpdbg_enable_breakpoints(TSRMLS_D);
PHPDBG_API void phpdbg_enable_breakpoint(zend_ulong id TSRMLS_DC);
PHPDBG_API void phpdbg_disable_breakpoint(zend_ulong id TSRMLS_DC);
PHPDBG_API void phpdbg_disable_breakpoints(TSRMLS_D); /* }}} */
/* {{{ Breakbase API */
PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase(zend_ulong id TSRMLS_DC);
PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakbase_ex(zend_ulong id, HashTable ***table, HashPosition *position TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Exportation API */
PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC); /* }}} */
#endif /* PHPDBG_BP_H */

155
sapi/phpdbg/phpdbg_break.c Normal file
View File

@ -0,0 +1,155 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_print.h"
#include "phpdbg_utils.h"
#include "phpdbg_opcode.h"
#include "phpdbg_break.h"
#include "phpdbg_bp.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_BREAK(file) /* {{{ */
{
switch (param->type) {
case FILE_PARAM:
phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM:
phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(address) /* {{{ */
{
switch (param->type) {
case ADDR_PARAM:
phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
break;
case NUMERIC_METHOD_PARAM:
phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
break;
case NUMERIC_FUNCTION_PARAM:
phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
break;
case FILE_PARAM:
phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(on) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(at) /* {{{ */
{
phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(lineno) /* {{{ */
{
switch (param->type) {
case NUMERIC_PARAM: {
if (PHPDBG_G(exec)) {
phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
} else {
phpdbg_error("Execution context not set!");
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(op) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(del) /* {{{ */
{
switch (param->type) {
case NUMERIC_PARAM: {
phpdbg_delete_breakpoint(param->num TSRMLS_CC);
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */

View File

@ -0,0 +1,58 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_BREAK_H
#define PHPDBG_BREAK_H
#include "TSRM.h"
#include "phpdbg_cmd.h"
#define PHPDBG_BREAK(name) PHPDBG_COMMAND(break_##name)
/**
* Printer Forward Declarations
*/
PHPDBG_BREAK(file);
PHPDBG_BREAK(func);
PHPDBG_BREAK(method);
PHPDBG_BREAK(address);
PHPDBG_BREAK(at);
PHPDBG_BREAK(op);
PHPDBG_BREAK(on);
PHPDBG_BREAK(lineno);
PHPDBG_BREAK(del);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_break_commands[] = {
PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1),
PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1),
PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1),
PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1),
PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1),
PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1),
PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
PHPDBG_END_COMMAND
};
#endif /* PHPDBG_BREAK_H */

669
sapi/phpdbg/phpdbg_cmd.c Normal file
View File

@ -0,0 +1,669 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
return "empty";
case ADDR_PARAM:
return "address";
case NUMERIC_PARAM:
return "numeric";
case METHOD_PARAM:
return "method";
case NUMERIC_FUNCTION_PARAM:
return "function opline";
case NUMERIC_METHOD_PARAM:
return "method opline";
case FILE_PARAM:
return "file or file opline";
case STR_PARAM:
return "string";
default: /* this is bad */
return "unknown";
}
}
PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
char *class_name, *func_name;
if (len == 0) {
param->type = EMPTY_PARAM;
goto parsed;
}
if (phpdbg_is_addr(str)) {
param->addr = strtoul(str, 0, 16);
param->type = ADDR_PARAM;
goto parsed;
} else if (phpdbg_is_numeric(str)) {
param->num = strtol(str, NULL, 0);
param->type = NUMERIC_PARAM;
goto parsed;
} else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) {
param->method.class = class_name;
param->method.name = func_name;
param->type = METHOD_PARAM;
goto parsed;
} else {
char *line_pos = strrchr(str, ':');
if (line_pos && phpdbg_is_numeric(line_pos+1)) {
if (strchr(str, ':') == line_pos) {
char path[MAXPATHLEN];
memcpy(path, str, line_pos - str);
path[line_pos - str] = 0;
*line_pos = 0;
param->file.name = phpdbg_resolve_path(path TSRMLS_CC);
param->file.line = strtol(line_pos+1, NULL, 0);
param->type = FILE_PARAM;
goto parsed;
}
}
line_pos = strrchr(str, '#');
if (line_pos && phpdbg_is_numeric(line_pos+1)) {
if (strchr(str, '#') == line_pos) {
param->num = strtol(line_pos + 1, NULL, 0);
if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) {
param->method.class = class_name;
param->method.name = func_name;
param->type = NUMERIC_METHOD_PARAM;
} else {
param->len = line_pos - str;
param->str = estrndup(str, param->len);
param->type = NUMERIC_FUNCTION_PARAM;
}
goto parsed;
}
}
}
param->str = estrndup(str, len);
param->len = len;
param->type = STR_PARAM;
parsed:
phpdbg_debug("phpdbg_parse_param(\"%s\", %lu): %s",
str, len, phpdbg_get_param_type(param TSRMLS_CC));
return param->type;
} /* }}} */
PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
if (param) {
switch (param->type) {
case FILE_PARAM:
efree(param->file.name);
break;
case METHOD_PARAM:
efree(param->method.class);
efree(param->method.name);
break;
case STR_PARAM:
efree(param->str);
break;
default:
break;
}
}
} /* }}} */
PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
asprintf(pointer,
"%s", param->str);
break;
case ADDR_PARAM:
asprintf(pointer,
"%#lx", param->addr);
break;
case NUMERIC_PARAM:
asprintf(pointer,
"%li",
param->num);
break;
case METHOD_PARAM:
asprintf(pointer,
"%s::%s",
param->method.class,
param->method.name);
break;
case FILE_PARAM:
if (param->num) {
asprintf(pointer,
"%s:%lu#%lu",
param->file.name,
param->file.line,
param->num);
} else {
asprintf(pointer,
"%s:%lu",
param->file.name,
param->file.line);
}
break;
case NUMERIC_FUNCTION_PARAM:
asprintf(pointer,
"%s#%lu", param->str, param->num);
break;
case NUMERIC_METHOD_PARAM:
asprintf(pointer,
"%s::%s#%lu",
param->method.class,
param->method.name,
param->num);
break;
default:
asprintf(pointer,
"%s", "unknown");
}
return *pointer;
} /* }}} */
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
{
switch ((dest->type = src->type)) {
case STR_PARAM:
dest->str = estrndup(src->str, src->len);
dest->len = src->len;
break;
case ADDR_PARAM:
dest->addr = src->addr;
break;
case NUMERIC_PARAM:
dest->num = src->num;
break;
case METHOD_PARAM:
dest->method.class = estrdup(src->method.class);
dest->method.name = estrdup(src->method.name);
break;
case FILE_PARAM:
dest->file.name = estrdup(src->file.name);
dest->file.line = src->file.line;
if (src->num)
dest->num = src->num;
break;
case NUMERIC_FUNCTION_PARAM:
dest->str = estrndup(src->str, src->len);
dest->num = src->num;
dest->len = src->len;
break;
case NUMERIC_METHOD_PARAM:
dest->method.class = estrdup(src->method.class);
dest->method.name = estrdup(src->method.name);
dest->num = src->num;
break;
case EMPTY_PARAM: { /* do nothing */ } break;
}
} /* }}} */
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
zend_ulong hash = param->type;
switch (param->type) {
case STR_PARAM:
hash += zend_inline_hash_func(param->str, param->len);
break;
case METHOD_PARAM:
hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
break;
case FILE_PARAM:
hash += zend_inline_hash_func(param->file.name, strlen(param->file.name));
hash += param->file.line;
if (param->num)
hash += param->num;
break;
case ADDR_PARAM:
hash += param->addr;
break;
case NUMERIC_PARAM:
hash += param->num;
break;
case NUMERIC_FUNCTION_PARAM:
hash += zend_inline_hash_func(param->str, param->len);
hash += param->num;
break;
case NUMERIC_METHOD_PARAM:
hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
if (param->num)
hash+= param->num;
break;
case EMPTY_PARAM: { /* do nothing */ } break;
}
return hash;
} /* }}} */
PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */
{
if (l && r) {
if (l->type == r->type) {
switch (l->type) {
case NUMERIC_FUNCTION_PARAM:
if (l->num != r->num) {
break;
}
/* break intentionally omitted */
case STR_PARAM:
return (l->len == r->len) &&
(memcmp(l->str, r->str, l->len) == SUCCESS);
case NUMERIC_PARAM:
return (l->num == r->num);
case ADDR_PARAM:
return (l->addr == r->addr);
case FILE_PARAM: {
if (l->file.line == r->file.line) {
size_t lengths[2] = {
strlen(l->file.name), strlen(r->file.name)};
if (lengths[0] == lengths[1]) {
if ((!l->num && !r->num) || (l->num == r->num)) {
return (memcmp(
l->file.name, r->file.name, lengths[0]) == SUCCESS);
}
}
}
} break;
case NUMERIC_METHOD_PARAM:
if (l->num != r->num) {
break;
}
/* break intentionally omitted */
case METHOD_PARAM: {
size_t lengths[2] = {
strlen(l->method.class), strlen(r->method.class)};
if (lengths[0] == lengths[1]) {
if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) {
lengths[0] = strlen(l->method.name);
lengths[1] = strlen(r->method.name);
if (lengths[0] == lengths[1]) {
return (memcmp(
l->method.name, r->method.name, lengths[0]) == SUCCESS);
}
}
}
} break;
case EMPTY_PARAM:
return 1;
}
}
}
return 0;
} /* }}} */
PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
{
char *p;
char b[PHPDBG_MAX_CMD];
int l=0;
enum states {
IN_BETWEEN,
IN_WORD,
IN_STRING
} state = IN_BETWEEN;
phpdbg_input_t **argv = NULL;
argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
(*argc) = 0;
#define RESET_STATE() do { \
phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
if (arg) { \
b[l]=0; \
arg->length = l; \
arg->string = estrndup(b, arg->length); \
arg->argv = NULL; \
arg->argc = 0; \
argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
argv[(*argc)++] = arg; \
l = 0; \
} \
state = IN_BETWEEN; \
} while (0)
for (p = buffer; *p != '\0'; p++) {
int c = (unsigned char) *p;
switch (state) {
case IN_BETWEEN:
if (isspace(c)) {
continue;
}
if (c == '"') {
state = IN_STRING;
continue;
}
state = IN_WORD;
b[l++]=c;
continue;
case IN_STRING:
if (c == '"') {
if (buffer[(p - buffer)-1] == '\\') {
b[l-1]=c;
continue;
}
RESET_STATE();
} else {
b[l++]=c;
}
continue;
case IN_WORD:
if (isspace(c)) {
RESET_STATE();
} else {
b[l++]=c;
}
continue;
}
}
switch (state) {
case IN_WORD: {
RESET_STATE();
} break;
case IN_STRING:
phpdbg_error(
"Malformed command line (unclosed quote) @ %ld: %s!",
(p - buffer)-1, &buffer[(p - buffer)-1]);
break;
case IN_BETWEEN:
break;
}
if ((*argc) == 0) {
/* not needed */
efree(argv);
/* to be sure */
return NULL;
}
return argv;
} /* }}} */
PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
{
phpdbg_input_t *buffer = NULL;
char *cmd = NULL;
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
(buffered == NULL)) {
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
}
if (buffered == NULL) {
#ifndef HAVE_LIBREADLINE
char buf[PHPDBG_MAX_CMD];
if ((!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && !phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) ||
!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
/* the user has gone away */
phpdbg_error("Failed to read console!");
PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
zend_bailout();
return NULL;
}
cmd = buf;
#else
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
char buf[PHPDBG_MAX_CMD];
if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
cmd = buf;
} else cmd = NULL;
} else cmd = readline(phpdbg_get_prompt(TSRMLS_C));
if (!cmd) {
/* the user has gone away */
phpdbg_error("Failed to read console!");
PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
zend_bailout();
return NULL;
}
if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
add_history(cmd);
}
#endif
} else cmd = buffered;
/* allocate and sanitize buffer */
buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
if (!buffer) {
return NULL;
}
buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
/* store constant pointer to start of buffer */
buffer->start = (char* const*) buffer->string;
buffer->argv = phpdbg_read_argv(
buffer->string, &buffer->argc TSRMLS_CC);
#ifdef PHPDBG_DEBUG
if (buffer->argc) {
int arg = 0;
while (arg < buffer->argc) {
phpdbg_debug(
"argv %d=%s", arg, buffer->argv[arg]->string);
arg++;
}
}
#endif
#ifdef HAVE_LIBREADLINE
if (!buffered && cmd &&
!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
free(cmd);
}
#endif
return buffer;
}
return NULL;
} /* }}} */
PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
{
if (argv) {
if (argc) {
int arg;
for (arg=0; arg<argc; arg++) {
phpdbg_destroy_input(
&argv[arg] TSRMLS_CC);
}
}
efree(argv);
}
} /* }}} */
PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
{
if (*input) {
if ((*input)->string) {
efree((*input)->string);
}
phpdbg_destroy_argv(
(*input)->argv, (*input)->argc TSRMLS_CC);
efree(*input);
}
} /* }}} */
PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
{
int rc = FAILURE;
if (input->argc > 0) {
while (command && command->name && command->handler) {
if (((command->name_len == input->argv[0]->length) &&
(memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
(command->alias &&
(input->argv[0]->length == 1) &&
(command->alias == *input->argv[0]->string))) {
phpdbg_param_t param;
param.type = EMPTY_PARAM;
if (input->argc > 1) {
if (command->subs) {
phpdbg_input_t sub = *input;
sub.string += input->argv[0]->length;
sub.length -= input->argv[0]->length;
sub.string = phpdbg_trim(
sub.string, sub.length, &sub.length);
sub.argc--;
sub.argv++;
phpdbg_debug(
"trying sub commands in \"%s\" for \"%s\" with %d arguments",
command->name, sub.argv[0]->string, sub.argc-1);
if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
efree(sub.string);
return SUCCESS;
}
efree(sub.string);
}
/* no sub command found */
{
char *store = input->string;
input->string += input->argv[0]->length;
input->length -= input->argv[0]->length;
input->string = phpdbg_trim(
input->string, input->length, &input->length);
efree(store);
}
/* pass parameter on */
phpdbg_parse_param(
input->string,
input->length,
&param TSRMLS_CC);
}
phpdbg_debug(
"found command %s for %s with %d arguments",
command->name, input->argv[0]->string, input->argc-1);
{
int arg;
for (arg=1; arg<input->argc; arg++) {
phpdbg_debug(
"\t#%d: [%s=%zu]",
arg,
input->argv[arg]->string,
input->argv[arg]->length);
}
}
rc = command->handler(&param, input TSRMLS_CC);
/* only set last command when it is worth it! */
if ((rc != FAILURE) &&
!(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
PHPDBG_G(lcmd) = (phpdbg_command_t*) command;
phpdbg_clear_param(
&PHPDBG_G(lparam) TSRMLS_CC);
PHPDBG_G(lparam) = param;
}
break;
}
command++;
}
} else {
/* this should NEVER happen */
phpdbg_error(
"No function executed!!");
}
return rc;
} /* }}} */

169
sapi/phpdbg/phpdbg_cmd.h Normal file
View File

@ -0,0 +1,169 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_CMD_H
#define PHPDBG_CMD_H
#include "TSRM.h"
typedef struct _phpdbg_command_t phpdbg_command_t;
/* {{{ Command and Parameter */
enum {
NO_ARG = 0,
REQUIRED_ARG,
OPTIONAL_ARG
};
typedef enum {
EMPTY_PARAM = 0,
ADDR_PARAM,
FILE_PARAM,
METHOD_PARAM,
STR_PARAM,
NUMERIC_PARAM,
NUMERIC_FUNCTION_PARAM,
NUMERIC_METHOD_PARAM
} phpdbg_param_type;
typedef struct _phpdbg_input_t phpdbg_input_t;
struct _phpdbg_input_t {
char * const *start;
char *string;
size_t length;
phpdbg_input_t **argv;
int argc;
};
typedef struct _phpdbg_param {
phpdbg_param_type type;
long num;
zend_ulong addr;
struct {
char *name;
long line;
} file;
struct {
char *class;
char *name;
} method;
char *str;
size_t len;
} phpdbg_param_t;
typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC);
struct _phpdbg_command_t {
const char *name; /* Command name */
size_t name_len; /* Command name length */
const char *tip; /* Menu tip */
size_t tip_len; /* Menu tip length */
char alias; /* Alias */
phpdbg_command_handler_t handler; /* Command handler */
const phpdbg_command_t *subs; /* Sub Commands */
char arg_type; /* Accept args? */
};
/* }}} */
/* {{{ misc */
#define PHPDBG_STRL(s) s, sizeof(s)-1
#define PHPDBG_MAX_CMD 500
#define PHPDBG_FRAME(v) (PHPDBG_G(frame).v)
#define PHPDBG_EX(v) (EG(current_execute_data)->v)
typedef struct {
int num;
zend_execute_data *execute_data;
} phpdbg_frame_t;
/* }}} */
/*
* Workflow:
* 1) read input
* input takes the line from console, creates argc/argv
* 2) parse parameters into suitable types based on arg_type
* takes input from 1) and arg_type and creates parameters
* 3) do command
* executes commands
* 4) destroy parameters
* cleans up what was allocated by creation of parameters
* 5) destroy input
* cleans up what was allocated by creation of input
*/
/*
* Input Management
*/
PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
/*
* Argument Management
*/
PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
#define phpdbg_argv_is(n, s) \
(memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
/*
* Parameter Management
*/
PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char*, size_t, phpdbg_param_t* TSRMLS_DC);
PHPDBG_API void phpdbg_clear_param(phpdbg_param_t* TSRMLS_DC);
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t*, phpdbg_param_t* TSRMLS_DC);
PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_param_t * TSRMLS_DC);
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC);
/*
* Command Executor
*/
PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC);
/**
* Command Declarators
*/
#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args}
#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \
{PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args}
#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC)
#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC
#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'}
/*
* Default Switch Case
*/
#define phpdbg_default_switch_case() \
default: \
phpdbg_error("Unsupported parameter type (%s) for command", phpdbg_get_param_type(param TSRMLS_CC)); \
break
#endif /* PHPDBG_CMD_H */

206
sapi/phpdbg/phpdbg_frame.c Normal file
View File

@ -0,0 +1,206 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "phpdbg.h"
#include "phpdbg_utils.h"
#include "phpdbg_frame.h"
#include "phpdbg_list.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
void phpdbg_restore_frame(TSRMLS_D) /* {{{ */
{
if (PHPDBG_FRAME(num) == 0) {
return;
}
PHPDBG_FRAME(num) = 0;
/* move things back */
EG(current_execute_data) = PHPDBG_FRAME(execute_data);
EG(opline_ptr) = &PHPDBG_EX(opline);
EG(active_op_array) = PHPDBG_EX(op_array);
EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
EG(active_symbol_table) = PHPDBG_EX(symbol_table);
EG(This) = PHPDBG_EX(current_this);
EG(scope) = PHPDBG_EX(current_scope);
EG(called_scope) = PHPDBG_EX(current_called_scope);
} /* }}} */
void phpdbg_switch_frame(int frame TSRMLS_DC) /* {{{ */
{
zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data);
int i = 0;
if (PHPDBG_FRAME(num) == frame) {
phpdbg_notice("Already in frame #%d", frame);
return;
}
while (execute_data) {
if (i++ == frame) {
break;
}
do {
execute_data = execute_data->prev_execute_data;
} while (execute_data && execute_data->opline == NULL);
}
if (execute_data == NULL) {
phpdbg_error("No frame #%d", frame);
return;
}
phpdbg_restore_frame(TSRMLS_C);
if (frame > 0) {
PHPDBG_FRAME(num) = frame;
/* backup things and jump back */
PHPDBG_FRAME(execute_data) = EG(current_execute_data);
EG(current_execute_data) = execute_data;
EG(opline_ptr) = &PHPDBG_EX(opline);
EG(active_op_array) = PHPDBG_EX(op_array);
PHPDBG_FRAME(execute_data)->original_return_value = EG(return_value_ptr_ptr);
EG(return_value_ptr_ptr) = PHPDBG_EX(original_return_value);
EG(active_symbol_table) = PHPDBG_EX(symbol_table);
EG(This) = PHPDBG_EX(current_this);
EG(scope) = PHPDBG_EX(current_scope);
EG(called_scope) = PHPDBG_EX(current_called_scope);
}
phpdbg_notice("Switched to frame #%d", frame);
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
);
} /* }}} */
static void phpdbg_dump_prototype(zval **tmp TSRMLS_DC) /* {{{ */
{
zval **funcname, **class, **type, **args, **argstmp;
char is_class;
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);
} else {
zend_get_object_classname(*class, (const char **)&Z_STRVAL_PP(class),
(zend_uint *)&Z_STRLEN_PP(class) TSRMLS_CC);
}
if (is_class == SUCCESS) {
zend_hash_find(Z_ARRVAL_PP(tmp), "type", sizeof("type"), (void **)&type);
}
phpdbg_write("%s%s%s(",
is_class == FAILURE?"":Z_STRVAL_PP(class),
is_class == FAILURE?"":Z_STRVAL_PP(type),
Z_STRVAL_PP(funcname)
);
if (zend_hash_find(Z_ARRVAL_PP(tmp), "args", sizeof("args"),
(void **)&args) == SUCCESS) {
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;
zend_bool is_variadic = 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) {
if (j) {
phpdbg_write(", ");
}
if (m && j < m) {
#if PHP_VERSION_ID >= 50600
is_variadic = arginfo[j].is_variadic;
#endif
phpdbg_write("%s=%s",
arginfo[j].name, is_variadic ? "[": "");
}
++j;
zend_print_flat_zval_r(*argstmp TSRMLS_CC);
zend_hash_move_forward_ex(Z_ARRVAL_PP(args), &iterator);
}
if (is_variadic) {
phpdbg_write("]");
}
}
phpdbg_write(")");
}
void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
{
zval zbacktrace;
zval **tmp;
zval **file, **line;
HashPosition position;
int i = 1, limit = num;
int user_defined;
if (limit < 0) {
phpdbg_error("Invalid backtrace size %d", limit);
}
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);
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);
zend_hash_move_forward_ex(Z_ARRVAL(zbacktrace), &position);
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
(void**)&tmp, &position) == FAILURE) {
phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
break;
}
if (user_defined == SUCCESS) {
phpdbg_write("frame #%d: ", i++);
phpdbg_dump_prototype(tmp TSRMLS_CC);
phpdbg_writeln(" at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
} else {
phpdbg_write(" => ");
phpdbg_dump_prototype(tmp TSRMLS_CC);
phpdbg_writeln(" (internal function)");
}
}
phpdbg_writeln(EMPTY);
zval_dtor(&zbacktrace);
} /* }}} */

View File

@ -0,0 +1,30 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_FRAME_H
#define PHPDBG_FRAME_H
#include "TSRM.h"
void phpdbg_restore_frame(TSRMLS_D);
void phpdbg_switch_frame(int TSRMLS_DC);
void phpdbg_dump_backtrace(size_t TSRMLS_DC);
#endif /* PHPDBG_FRAME_H */

603
sapi/phpdbg/phpdbg_help.c Normal file
View File

@ -0,0 +1,603 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_help.h"
#include "phpdbg_print.h"
#include "phpdbg_utils.h"
#include "phpdbg_break.h"
#include "phpdbg_list.h"
#include "phpdbg_info.h"
#include "phpdbg_set.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_HELP(exec) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("\tWill attempt execution, if compilation has not yet taken place, it occurs now");
phpdbg_writeln("The execution context must be set before execution can take place");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(step) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("You can enable and disable stepping at any phpdbg prompt during execution");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sstepping 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%ss 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill enable stepping");
phpdbg_writeln(EMPTY);
phpdbg_writeln("While stepping is enabled you are presented with a prompt after the execution of each opcode");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(next) /* {{{ */
{
phpdbg_help_header();
phpdbg_write("Step back into the vm and execute the next opcode");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%snext", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sn", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: is only useful while executing");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(until) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Step back into the vm, skipping breakpoints until the next source line");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%suntil", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%su", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: is only useful while executing");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(finish) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Step back into the vm, skipping breakpoints until past the end of the current stack");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sfinish", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sF", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: this allows all breakpoints that would otherwise break execution in the current scope to be skipped");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(leave) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Step back into the vm, skipping breakpoints until the current stack is returning");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sleave", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sL", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill cause a break when instructed to leave the current context");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: this allows inspection of the return value before it is returned");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(compile) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Pre-compilation of the execution context provides the opportunity to inspect opcodes before execution");
phpdbg_writeln("The execution context must be set for compilation to succeed");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%scompile", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sc", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill compile the current execution context, populating class/function/constant/etc tables");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: It is a good idea to clean the environment between each compilation");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(print) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("By default, print will show information about the current execution context");
phpdbg_writeln("Other printing commands give access to instruction information");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sprint class \\my\\class", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp c \\my\\class", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for the methods in \\my\\class");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint method \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp m \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for \\my\\class::method");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint func .getSomething", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp f .getSomething", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for ::getSomething in the active scope");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint func my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp f my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for the global function my_function");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint opline", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp o", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instruction for the current opline");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint exec", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp e", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for the execution context");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sprint stack", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sp s", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the instructions for the current stack");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Specific printers loaded are show below:");
phpdbg_notice("Commands");
{
const phpdbg_command_t *print_command = phpdbg_print_commands;
phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
while (print_command && print_command->name) {
if (print_command->alias) {
phpdbg_writeln("\t[%c]\t%s\t\t%s", print_command->alias, print_command->name, print_command->tip);
} else {
phpdbg_writeln("\t[ ]\t%s\t\t%s", print_command->name, print_command->tip);
}
++print_command;
}
}
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(run) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Execute the current context inside the phpdbg vm");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%srun", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sr", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill cause execution of the context, if it is set.");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: The execution context must be set, but not necessarily compiled before execution occurs");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(eval) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Access to eval() allows you to change the environment during execution, careful though!!");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%seval $variable", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sE $variable", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print_r($variable) on the console, if it is defined");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%seval $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sE $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill set $variable in the current scope");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: eval() will always show the result; do not prefix the code with \"return\"");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(break) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Setting a breakpoint stops execution at a specific stage");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sbreak [file] test.php:1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [F] test.php:1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break execution on line 1 of test.php");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [func] my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [f] my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break execution on entry to my_function");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [method] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [m] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break execution on entry to \\my\\class::method");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [address] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [a] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at the opline with the address provided");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [address] my_function#1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [a] my_function#1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at the opline number 1 of the function my_function");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [address] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [a] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at the opline number 2 of the method \\my\\class::method");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak address test.php:3", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb a test.php:3", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at the opline number 3 of test.php");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak [lineno] 200", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb [l] 200", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at line 200 of the currently executing file");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break when the condition evaluates to true");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak at phpdbg::isGreat if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at every opcode in phpdbg::isGreat when the condition evaluates to true");
phpdbg_writeln("\t%sbreak at test.php:20 if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break at every opcode on line 20 of test.php when the condition evaluates to true");
phpdbg_write("\t");
phpdbg_notice("The location can be anything accepted by file, func, method, or address break commands");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill break on every occurence of the opcode provided");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%sbreak del 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sb d 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill remove the breakpoint with the given identifier");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: An address is only valid for the current compilation");
phpdbg_writeln(EMPTY);
phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Specific breakers loaded are show below:");
phpdbg_notice("Commands");
{
const phpdbg_command_t *break_command = phpdbg_break_commands;
phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
while (break_command && break_command->name) {
if (break_command->alias) {
phpdbg_writeln("\t[%c]\t%s\t\t%s", break_command->alias, break_command->name, break_command->tip);
} else {
phpdbg_writeln("\t[ ]\t%s\t\t%s", break_command->name, break_command->tip);
}
++break_command;
}
}
phpdbg_writeln("Note: Conditional breaks are costly, use them sparingly!");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(clean) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions");
phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(clear) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Clearing breakpoints means you can once again run code without interruption");
phpdbg_writeln("Note: all breakpoints are lost; be sure debugging is complete before clearing");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(info) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("info commands provide quick access to various types of information about the PHP environment");
phpdbg_writeln("Specific info commands are show below:");
phpdbg_notice("Commands");
{
const phpdbg_command_t *info_command = phpdbg_info_commands;
phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
while (info_command && info_command->name) {
if (info_command->alias) {
phpdbg_writeln("\t[%c]\t%s\t\t%s", info_command->alias, info_command->name, info_command->tip);
} else {
phpdbg_writeln("\t[ ]\t%s\t\t%s", info_command->name, info_command->tip);
}
++info_command;
}
}
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(quiet) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Setting quietness on will stop the OPLINE output during execution");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%squiet 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sQ 1", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill silence OPLINE output, while");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%squiet 0", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sQ 0", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill enable OPLINE output again");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: Quietness is disabled automatically while stepping");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(back) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("The backtrace is built with the default debug backtrace functionality");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sback 5", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%st 5", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill limit the number of frames to 5, the default is no limit");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: it is not necessary for an exception to be thrown to show a backtrace");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(frame) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("When viewing a backtrace, it is sometimes useful to jump to a frame in that trace");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sframe 2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sf 2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill go to frame 2, temporarily affecting scope and allowing access to the variables in that frame");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: the current frame is restored when execution continues");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(list) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("The list command displays source code for the given argument");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%slist [lines] 2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sl [l] 2", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print next 2 lines from the current file");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%slist [func] my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sl [f] my_function", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the source of the global function \"my_function\"");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%slist [func] .mine", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sl [f] .mine", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the source of the method \"mine\" from the active scope");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%slist [method] my::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sl [m] my::method", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the source of \"my::method\"");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%slist c myClass", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sl c myClass", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill print the source of \"myClass\"");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: before listing functions you must have a populated function table, try compile!!");
phpdbg_writeln(EMPTY);
phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Specific listers loaded are show below:");
phpdbg_notice("Commands");
{
const phpdbg_command_t *list_command = phpdbg_list_commands;
phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
while (list_command && list_command->name) {
if (list_command->alias) {
phpdbg_writeln("\t[%c]\t%s\t\t%s", list_command->alias, list_command->name, list_command->tip);
} else {
phpdbg_writeln("\t[ ]\t%s\t\t%s", list_command->name, list_command->tip);
}
++list_command;
}
}
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(oplog) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Even when quietness is enabled you may wish to save opline logs to a file");
phpdbg_writeln("Setting a new oplog closes the previously open log");
phpdbg_writeln("The log includes a high resolution timestamp on each entry");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%soplog /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sO /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill open the file /path/to/my.oplog for writing, creating it if it does not exist");
phpdbg_writeln(EMPTY);
phpdbg_writeln("\t%soplog 0", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sO 0", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill close the currently open log file, disabling oplog");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: upon failure to open a new oplog, the last oplog is held open");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(set) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Configure how phpdbg looks and behaves with the set command");
phpdbg_writeln("Specific set commands are show below:");
phpdbg_notice("Commands");
{
const phpdbg_command_t *set_command = phpdbg_set_commands;
phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
while (set_command && set_command->name) {
if (set_command->alias) {
phpdbg_writeln("\t[%c]\t%s\t\t%s", set_command->alias, set_command->name, set_command->tip);
} else {
phpdbg_writeln("\t[ ]\t%s\t\t%s", set_command->name, set_command->tip);
}
++set_command;
}
}
#ifndef _WIN32
phpdbg_notice("Colors");
{
const phpdbg_color_t *color = phpdbg_get_colors(TSRMLS_C);
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
phpdbg_writeln("\t%-20s\t\tExample", "Name");
} else {
phpdbg_writeln("\tName");
}
while (color && color->name) {
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
phpdbg_writeln(
"\t%-20s\t\t\033[%smphpdbg rocks :)\033[0m", color->name, color->code);
} else {
phpdbg_writeln("\t%s", color->name);
}
++color;
}
}
phpdbg_writeln("The <element> for set color can be \"prompt\", \"notice\", or \"error\"");
#endif
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(register) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Register any global function for use as a command in phpdbg console");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sregister scandir", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%sR scandir", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill register the scandir function for use in phpdbg");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: arguments passed as strings, return (if present) print_r'd on console");
if (zend_hash_num_elements(&PHPDBG_G(registered))) {
HashPosition position;
char *name = NULL;
zend_uint name_len = 0;
phpdbg_notice("Registered Functions (%d)", zend_hash_num_elements(&PHPDBG_G(registered)));
for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(registered), &position);
zend_hash_get_current_key_ex(&PHPDBG_G(registered), &name, &name_len, NULL, 1, &position) == HASH_KEY_IS_STRING;
zend_hash_move_forward_ex(&PHPDBG_G(registered), &position)) {
phpdbg_writeln("|-------> %s", name);
efree(name);
}
}
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(source) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Sourcing a phpdbginit during your debugging session might save some time");
phpdbg_writeln("The source command can also be used to export breakpoints to a phpdbginit file");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%ssource /my/init", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%s. /my/init", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill execute the phpdbginit file at /my/init");
phpdbg_writeln("\t%ssource export /my/init", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%s. export /my/init", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill export breakpoints to /my/init in phpdbginit file format");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(shell) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Direct access to shell commands saves having to switch windows/consoles");
phpdbg_writeln(EMPTY);
phpdbg_notice("Examples");
phpdbg_writeln("\t%sshell ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\t%s- ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
phpdbg_writeln("\tWill execute ls /usr/src/php-src, displaying the output in the console");
phpdbg_writeln(EMPTY);
phpdbg_writeln("Note: read only commands please!");
phpdbg_help_footer();
return SUCCESS;
} /* }}} */
PHPDBG_HELP(options) /* {{{ */
{
phpdbg_help_header();
phpdbg_writeln("Below are the command line options supported by phpdbg");
phpdbg_notice("Command Line Options and Flags");
phpdbg_writeln(" -c\t-c/my/php.ini\t\tSet php.ini file to load");
phpdbg_writeln(" -d\t-dmemory_limit=4G\tSet a php.ini directive");
phpdbg_writeln(" -n\tN/A\t\t\tDisable default php.ini");
phpdbg_writeln(" -q\tN/A\t\t\tSupress welcome banner");
phpdbg_writeln(" -e\t-emytest.php\t\tSet execution context");
phpdbg_writeln(" -v\tN/A\t\t\tEnable oplog output");
phpdbg_writeln(" -s\tN/A\t\t\tEnable stepping");
phpdbg_writeln(" -b\tN/A\t\t\tDisable colour");
phpdbg_writeln(" -i\t-imy.init\t\tSet .phpdbginit file");
phpdbg_writeln(" -I\tN/A\t\t\tIgnore default .phpdbginit");
phpdbg_writeln(" -O\t-Omy.oplog\t\tSets oplog output file");
phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
phpdbg_writeln(" -E\tN/A\t\t\tEnable step through eval, careful!");
phpdbg_writeln(" -S\t-Scli\t\t\tOverride SAPI name, careful!");
#ifndef _WIN32
phpdbg_writeln(" -l\t-l4000\t\t\tSetup remote console ports");
phpdbg_writeln(" -a\t-a192.168.0.3\t\tSetup remote console bind address");
#endif
phpdbg_writeln(" -V\tN/A\t\t\tVersion number");
phpdbg_notice("Passing -rr will quit automatically after execution");
#ifndef _WIN32
phpdbg_writeln("Remote Console Mode");
phpdbg_notice("For security, phpdbg will bind only to the loopback interface by default");
phpdbg_writeln("-a without an argument implies all; phpdbg will bind to all available interfaces.");
phpdbg_writeln("specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2.");
phpdbg_notice("Steps should be taken to secure this service if bound to a public interface/port");
#endif
phpdbg_help_footer();
return SUCCESS;
} /* }}} */

92
sapi/phpdbg/phpdbg_help.h Normal file
View File

@ -0,0 +1,92 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_HELP_H
#define PHPDBG_HELP_H
#include "TSRM.h"
#include "phpdbg.h"
#include "phpdbg_cmd.h"
#define PHPDBG_HELP(name) PHPDBG_COMMAND(help_##name)
/**
* Helper Forward Declarations
*/
PHPDBG_HELP(exec);
PHPDBG_HELP(compile);
PHPDBG_HELP(step);
PHPDBG_HELP(next);
PHPDBG_HELP(run);
PHPDBG_HELP(eval);
PHPDBG_HELP(until);
PHPDBG_HELP(finish);
PHPDBG_HELP(leave);
PHPDBG_HELP(print);
PHPDBG_HELP(break);
PHPDBG_HELP(clean);
PHPDBG_HELP(clear);
PHPDBG_HELP(info);
PHPDBG_HELP(back);
PHPDBG_HELP(frame);
PHPDBG_HELP(quiet);
PHPDBG_HELP(list);
PHPDBG_HELP(set);
PHPDBG_HELP(register);
PHPDBG_HELP(options);
PHPDBG_HELP(source);
PHPDBG_HELP(shell);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_help_commands[] = {
PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0),
PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0),
PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0),
PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0),
PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0),
PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0),
PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0),
PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0),
PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0),
PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0),
PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0),
PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0),
PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0),
PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0),
PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0),
PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0),
PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0),
PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0),
PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0),
PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0),
PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0),
PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0),
PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0),
PHPDBG_END_COMMAND
};
#define phpdbg_help_header() \
phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
#define phpdbg_help_footer() \
phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
#endif /* PHPDBG_HELP_H */

355
sapi/phpdbg/phpdbg_info.c Normal file
View File

@ -0,0 +1,355 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "phpdbg.h"
#include "phpdbg_utils.h"
#include "phpdbg_info.h"
#include "phpdbg_bp.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_INFO(break) /* {{{ */
{
phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_SYM TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_COND TSRMLS_CC);
phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_INFO(files) /* {{{ */
{
HashPosition pos;
char *fname;
phpdbg_notice("Included files: %d",
zend_hash_num_elements(&EG(included_files)));
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("File: %s", fname);
zend_hash_move_forward_ex(&EG(included_files), &pos);
}
return SUCCESS;
} /* }}} */
PHPDBG_INFO(error) /* {{{ */
{
if (PG(last_error_message)) {
phpdbg_writeln("Last error: %s at %s line %d",
PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
} else {
phpdbg_notice("No error found!");
}
return SUCCESS;
} /* }}} */
PHPDBG_INFO(vars) /* {{{ */
{
HashTable vars;
HashPosition pos;
char *var;
zval **data;
if (!EG(active_op_array)) {
phpdbg_error("No active op array!");
return SUCCESS;
}
if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C);
if (!EG(active_symbol_table)) {
phpdbg_error("No active symbol table!");
return SUCCESS;
}
}
zend_hash_init(&vars, 8, NULL, NULL, 0);
zend_hash_internal_pointer_reset_ex(EG(active_symbol_table), &pos);
while (zend_hash_get_current_key_ex(EG(active_symbol_table), &var,
NULL, NULL, 0, &pos) == HASH_KEY_IS_STRING) {
zend_hash_get_current_data_ex(EG(active_symbol_table), (void **)&data, &pos);
if (*var != '_') {
zend_hash_update(
&vars, var, strlen(var)+1, (void**)data, sizeof(zval*), NULL);
}
zend_hash_move_forward_ex(EG(active_symbol_table), &pos);
}
{
zend_op_array *ops = EG(active_op_array);
if (ops->function_name) {
if (ops->scope) {
phpdbg_notice(
"Variables in %s::%s() (%d)", ops->scope->name, ops->function_name, zend_hash_num_elements(&vars));
} else {
phpdbg_notice(
"Variables in %s() (%d)", ops->function_name, zend_hash_num_elements(&vars));
}
} else {
if (ops->filename) {
phpdbg_notice(
"Variables in %s (%d)", ops->filename, zend_hash_num_elements(&vars));
} else {
phpdbg_notice(
"Variables @ %p (%d)", ops, zend_hash_num_elements(&vars));
}
}
}
if (zend_hash_num_elements(&vars)) {
phpdbg_writeln("Address\t\tRefs\tType\t\tVariable");
for (zend_hash_internal_pointer_reset_ex(&vars, &pos);
zend_hash_get_current_data_ex(&vars, (void**) &data, &pos) == SUCCESS;
zend_hash_move_forward_ex(&vars, &pos)) {
char *var;
zend_hash_get_current_key_ex(&vars, &var, NULL, NULL, 0, &pos);
if (*data) {
phpdbg_write(
"%p\t%d\t",
*data,
Z_REFCOUNT_PP(data));
switch (Z_TYPE_PP(data)) {
case IS_STRING: phpdbg_write("(string)\t"); break;
case IS_LONG: phpdbg_write("(integer)\t"); break;
case IS_DOUBLE: phpdbg_write("(float)\t"); break;
case IS_RESOURCE: phpdbg_write("(resource)\t"); break;
case IS_ARRAY: phpdbg_write("(array)\t"); break;
case IS_OBJECT: phpdbg_write("(object)\t"); break;
case IS_NULL: phpdbg_write("(null)\t"); break;
}
if (Z_TYPE_PP(data) == IS_RESOURCE) {
int type;
phpdbg_writeln(
"%s$%s", Z_ISREF_PP(data) ? "&": "", var);
if (zend_list_find(Z_RESVAL_PP(data), &type)) {
phpdbg_write(
"|-------(typeof)------> (%s)",
zend_rsrc_list_get_rsrc_type(type TSRMLS_CC));
} else {
phpdbg_write(
"|-------(typeof)------> (unknown)");
}
phpdbg_writeln(EMPTY);
} else if (Z_TYPE_PP(data) == IS_OBJECT) {
phpdbg_writeln(
"%s$%s", Z_ISREF_PP(data) ? "&": "", var);
phpdbg_write(
"|-----(instanceof)----> (%s)", Z_OBJCE_PP(data)->name);
phpdbg_writeln(EMPTY);
} else {
phpdbg_write(
"%s$%s", Z_ISREF_PP(data) ? "&": "", var);
}
} else {
phpdbg_write(
"n/a\tn/a\tn/a\t$%s", var);
}
phpdbg_writeln(EMPTY);
}
}
zend_hash_destroy(&vars);
return SUCCESS;
} /* }}} */
PHPDBG_INFO(literal) /* {{{ */
{
if ((EG(in_execution) && EG(active_op_array)) || PHPDBG_G(ops)) {
zend_op_array *ops = EG(active_op_array) ? EG(active_op_array) : PHPDBG_G(ops);
int literal = 0, count = ops->last_literal-1;
if (ops->function_name) {
if (ops->scope) {
phpdbg_notice(
"Literal Constants in %s::%s() (%d)", ops->scope->name, ops->function_name, count);
} else {
phpdbg_notice(
"Literal Constants in %s() (%d)", ops->function_name, count);
}
} else {
if (ops->filename) {
phpdbg_notice(
"Literal Constants in %s (%d)", ops->filename, count);
} else {
phpdbg_notice(
"Literal Constants @ %p (%d)", ops, count);
}
}
while (literal < ops->last_literal) {
if (Z_TYPE(ops->literals[literal].constant) != IS_NULL) {
phpdbg_write("|-------- C%u -------> [", literal);
zend_print_zval(
&ops->literals[literal].constant, 0);
phpdbg_write("]");
phpdbg_writeln(EMPTY);
}
literal++;
}
} else {
phpdbg_error("Not executing!");
}
return SUCCESS;
} /* }}} */
PHPDBG_INFO(memory) /* {{{ */
{
if (is_zend_mm(TSRMLS_C)) {
phpdbg_notice("Memory Manager Information");
phpdbg_notice("Current");
phpdbg_writeln("|-------> Used:\t%.3f kB",
(float) (zend_memory_usage(0 TSRMLS_CC)/1024));
phpdbg_writeln("|-------> Real:\t%.3f kB",
(float) (zend_memory_usage(1 TSRMLS_CC)/1024));
phpdbg_notice("Peak");
phpdbg_writeln("|-------> Used:\t%.3f kB",
(float) (zend_memory_peak_usage(0 TSRMLS_CC)/1024));
phpdbg_writeln("|-------> Real:\t%.3f kB",
(float) (zend_memory_peak_usage(1 TSRMLS_CC)/1024));
} else {
phpdbg_error("Memory Manager Disabled!");
}
return SUCCESS;
} /* }}} */
static inline void phpdbg_print_class_name(zend_class_entry **ce TSRMLS_DC) /* {{{ */
{
phpdbg_write(
"%s %s %s (%d)",
((*ce)->type == ZEND_USER_CLASS) ?
"User" : "Internal",
((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
"Interface" :
((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
"Abstract Class" :
"Class",
(*ce)->name, zend_hash_num_elements(&(*ce)->function_table));
} /* }}} */
PHPDBG_INFO(classes) /* {{{ */
{
HashPosition position;
zend_class_entry **ce;
HashTable 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_notice("User Classes (%d)",
zend_hash_num_elements(&classes));
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)) {
phpdbg_print_class_name(ce TSRMLS_CC);
phpdbg_writeln(EMPTY);
if ((*ce)->parent) {
zend_class_entry *pce = (*ce)->parent;
do {
phpdbg_write("|-------- ");
phpdbg_print_class_name(&pce TSRMLS_CC);
phpdbg_writeln(EMPTY);
} while ((pce = pce->parent));
}
if ((*ce)->info.user.filename) {
phpdbg_writeln(
"|---- in %s on line %u",
(*ce)->info.user.filename,
(*ce)->info.user.line_start);
} else {
phpdbg_writeln("|---- no source code");
}
phpdbg_writeln(EMPTY);
}
zend_hash_destroy(&classes);
return SUCCESS;
} /* }}} */
PHPDBG_INFO(funcs) /* {{{ */
{
HashPosition position;
zend_function *zf, **pzf;
HashTable functions;
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_notice("User Functions (%d)",
zend_hash_num_elements(&functions));
for (zend_hash_internal_pointer_reset_ex(&functions, &position);
zend_hash_get_current_data_ex(&functions, (void**)&pzf, &position) == SUCCESS;
zend_hash_move_forward_ex(&functions, &position)) {
zend_op_array *op_array = &((*pzf)->op_array);
phpdbg_writeln(
"|-------- %s in %s on line %d",
op_array->function_name ? op_array->function_name : "{main}",
op_array->filename ? op_array->filename : "(no source code)",
op_array->line_start);
}
zend_hash_destroy(&functions);
return SUCCESS;
} /* }}} */

49
sapi/phpdbg/phpdbg_info.h Normal file
View File

@ -0,0 +1,49 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_INFO_H
#define PHPDBG_INFO_H
#include "phpdbg_cmd.h"
#define PHPDBG_INFO(name) PHPDBG_COMMAND(info_##name)
PHPDBG_INFO(files);
PHPDBG_INFO(break);
PHPDBG_INFO(classes);
PHPDBG_INFO(funcs);
PHPDBG_INFO(error);
PHPDBG_INFO(vars);
PHPDBG_INFO(literal);
PHPDBG_INFO(memory);
static const phpdbg_command_t phpdbg_info_commands[] = {
PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0),
PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
PHPDBG_COMMAND_D_EX(error, "show last error", 'e', info_error, NULL, 0),
PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0),
PHPDBG_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
PHPDBG_END_COMMAND
};
#endif /* PHPDBG_INFO_H */

279
sapi/phpdbg/phpdbg_list.c Normal file
View File

@ -0,0 +1,279 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <sys/mman.h>
# include <unistd.h>
#endif
#include <fcntl.h>
#include "phpdbg.h"
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_LIST(lines) /* {{{ */
{
if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
phpdbg_error("Not executing, and execution context not set");
return SUCCESS;
}
switch (param->type) {
case NUMERIC_PARAM:
case EMPTY_PARAM:
phpdbg_list_file(phpdbg_current_file(TSRMLS_C),
param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num),
(param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
0 TSRMLS_CC);
break;
case FILE_PARAM:
phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_LIST(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM:
phpdbg_list_function_byname(
param->str, param->len TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_LIST(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM: {
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *function;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
phpdbg_list_function(function TSRMLS_CC);
} else {
phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
}
efree(lcname);
} else {
phpdbg_error("Could not find the class %s", param->method.class);
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_LIST(class) /* {{{ */
{
switch (param->type) {
case STR_PARAM: {
zend_class_entry **ce;
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
if ((*ce)->type == ZEND_USER_CLASS) {
if ((*ce)->info.user.filename) {
phpdbg_list_file(
(*ce)->info.user.filename,
(*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
(*ce)->info.user.line_start, 0 TSRMLS_CC
);
} else {
phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
}
} else {
phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
}
} else {
phpdbg_error("The requested class (%s) could not be found", param->str);
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
{
unsigned char *mem, *pos, *last_pos, *end_pos;
struct stat st;
#ifndef _WIN32
int fd;
#else
HANDLE fd, map;
#endif
int all_content = (count == 0);
int line = 0, displayed = 0;
if (VCWD_STAT(filename, &st) == FAILURE) {
phpdbg_error("Failed to stat file %s", filename);
return;
}
#ifndef _WIN32
if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) {
phpdbg_error("Failed to open file %s to list", filename);
return;
}
pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
end_pos = mem + st.st_size;
#else
fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fd == INVALID_HANDLE_VALUE) {
phpdbg_error("Failed to open file!");
return;
}
map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
if (map == NULL) {
phpdbg_error("Failed to map file!");
CloseHandle(fd);
return;
}
pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if (mem == NULL) {
phpdbg_error("Failed to map file in memory");
CloseHandle(map);
CloseHandle(fd);
return;
}
end_pos = mem + st.st_size;
#endif
while (1) {
if (pos == end_pos) {
break;
}
pos = memchr(last_pos, '\n', end_pos - last_pos);
if (!pos) {
/* No more line breaks */
pos = end_pos;
}
++line;
if (!offset || offset <= line) {
/* Without offset, or offset reached */
if (!highlight) {
phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos);
} else {
if (highlight != line) {
phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos);
} else {
phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos);
}
}
++displayed;
}
last_pos = pos + 1;
if (!all_content && displayed == count) {
/* Reached max line to display */
break;
}
}
#ifndef _WIN32
munmap(mem, st.st_size);
close(fd);
#else
UnmapViewOfFile(mem);
CloseHandle(map);
CloseHandle(fd);
#endif
} /* }}} */
void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
{
const zend_op_array *ops;
if (fbc->type != ZEND_USER_FUNCTION) {
phpdbg_error("The function requested (%s) is not user defined", fbc->common.function_name);
return;
}
ops = (zend_op_array*)fbc;
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) /* {{{ */
{
HashTable *func_table = EG(function_table);
zend_function* fbc;
char *func_name = (char*) str;
size_t func_name_len = len;
/* search active scope if begins with period */
if (func_name[0] == '.') {
if (EG(scope)) {
func_name++;
func_name_len--;
func_table = &EG(scope)->function_table;
} else {
phpdbg_error("No active class");
return;
}
} else if (!EG(function_table)) {
phpdbg_error("No function table loaded");
return;
} else {
func_table = EG(function_table);
}
/* 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("Function %s not found", func_name);
}
efree(func_name);
} /* }}} */

47
sapi/phpdbg/phpdbg_list.h Normal file
View File

@ -0,0 +1,47 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_LIST_H
#define PHPDBG_LIST_H
#include "TSRM.h"
#include "phpdbg_cmd.h"
#define PHPDBG_LIST(name) PHPDBG_COMMAND(list_##name)
#define PHPDBG_LIST_HANDLER(name) PHPDBG_COMMAND_HANDLER(list_##name)
PHPDBG_LIST(lines);
PHPDBG_LIST(class);
PHPDBG_LIST(method);
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);
static const phpdbg_command_t phpdbg_list_commands[] = {
PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1),
PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1),
PHPDBG_END_COMMAND
};
#endif /* PHPDBG_LIST_H */

361
sapi/phpdbg/phpdbg_opcode.c Normal file
View File

@ -0,0 +1,361 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "zend_vm_opcodes.h"
#include "zend_compile.h"
#include "phpdbg_opcode.h"
#include "phpdbg_utils.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */
{
int iter = 0;
while (iter < ops->last_literal) {
if (literal == &ops->literals[iter]) {
return iter;
}
iter++;
}
return 0;
} /* }}} */
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */
{
char *decode = NULL;
switch (type &~ EXT_TYPE_UNUSED) {
case IS_CV:
asprintf(&decode, "$%s", ops->vars[op->var].name);
break;
case IS_VAR:
case IS_TMP_VAR: {
zend_ulong id = 0, *pid = NULL;
if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) {
id = zend_hash_num_elements(vars);
zend_hash_index_update(
vars, (zend_ulong) ops->vars - op->var,
(void**) &id,
sizeof(zend_ulong), NULL);
} else id = *pid;
asprintf(&decode, "@%lu", id);
} break;
case IS_CONST:
asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC));
break;
case IS_UNUSED:
asprintf(&decode, "<unused>");
break;
}
return decode;
} /* }}} */
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */
{
char *decode[4] = {NULL, NULL, NULL, NULL};
switch (op->opcode) {
case ZEND_JMP:
#ifdef ZEND_GOTO
case ZEND_GOTO:
#endif
#ifdef ZEND_FAST_CALL
case ZEND_FAST_CALL:
#endif
asprintf(&decode[1], "J%ld", op->op1.jmp_addr - ops->opcodes);
goto format;
case ZEND_JMPZNZ:
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
asprintf(
&decode[2], "J%u or J%lu", op->op2.opline_num, op->extended_value);
goto result;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
#ifdef ZEND_JMP_SET
case ZEND_JMP_SET:
#endif
#ifdef ZEND_JMP_SET_VAR
case ZEND_JMP_SET_VAR:
#endif
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
asprintf(
&decode[2], "J%ld", op->op2.jmp_addr - ops->opcodes);
goto result;
case ZEND_RECV_INIT:
goto result;
default: {
decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC);
decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC);
result:
decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC);
format:
asprintf(
&decode[0],
"%-20s %-20s %-20s",
decode[1] ? decode[1] : "",
decode[2] ? decode[2] : "",
decode[3] ? decode[3] : "");
}
}
if (decode[1])
free(decode[1]);
if (decode[2])
free(decode[2]);
if (decode[3])
free(decode[3]);
return decode[0];
} /* }}} */
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
{
/* force out a line while stepping so the user knows what is happening */
if (ignore_flags ||
(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
(PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
(PHPDBG_G(oplog)))) {
zend_op *opline = execute_data->opline;
char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC);
if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
/* output line info */
phpdbg_notice("L%-5u %16p %-30s %s %s",
opline->lineno,
opline,
phpdbg_decode_opcode(opline->opcode),
decode,
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
}
if (!ignore_flags && PHPDBG_G(oplog)) {
phpdbg_log_ex(PHPDBG_G(oplog), "L%-5u %16p %-30s %s %s",
opline->lineno,
opline,
phpdbg_decode_opcode(opline->opcode),
decode,
execute_data->op_array->filename ? execute_data->op_array->filename : "unknown");
}
if (decode) {
free(decode);
}
}
} /* }}} */
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */
{
phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC);
} /* }}} */
const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
{
#define CASE(s) case s: return #s
switch (opcode) {
CASE(ZEND_NOP);
CASE(ZEND_ADD);
CASE(ZEND_SUB);
CASE(ZEND_MUL);
CASE(ZEND_DIV);
CASE(ZEND_MOD);
CASE(ZEND_SL);
CASE(ZEND_SR);
CASE(ZEND_CONCAT);
CASE(ZEND_BW_OR);
CASE(ZEND_BW_AND);
CASE(ZEND_BW_XOR);
CASE(ZEND_BW_NOT);
CASE(ZEND_BOOL_NOT);
CASE(ZEND_BOOL_XOR);
CASE(ZEND_IS_IDENTICAL);
CASE(ZEND_IS_NOT_IDENTICAL);
CASE(ZEND_IS_EQUAL);
CASE(ZEND_IS_NOT_EQUAL);
CASE(ZEND_IS_SMALLER);
CASE(ZEND_IS_SMALLER_OR_EQUAL);
CASE(ZEND_CAST);
CASE(ZEND_QM_ASSIGN);
CASE(ZEND_ASSIGN_ADD);
CASE(ZEND_ASSIGN_SUB);
CASE(ZEND_ASSIGN_MUL);
CASE(ZEND_ASSIGN_DIV);
CASE(ZEND_ASSIGN_MOD);
CASE(ZEND_ASSIGN_SL);
CASE(ZEND_ASSIGN_SR);
CASE(ZEND_ASSIGN_CONCAT);
CASE(ZEND_ASSIGN_BW_OR);
CASE(ZEND_ASSIGN_BW_AND);
CASE(ZEND_ASSIGN_BW_XOR);
CASE(ZEND_PRE_INC);
CASE(ZEND_PRE_DEC);
CASE(ZEND_POST_INC);
CASE(ZEND_POST_DEC);
CASE(ZEND_ASSIGN);
CASE(ZEND_ASSIGN_REF);
CASE(ZEND_ECHO);
CASE(ZEND_PRINT);
CASE(ZEND_JMP);
CASE(ZEND_JMPZ);
CASE(ZEND_JMPNZ);
CASE(ZEND_JMPZNZ);
CASE(ZEND_JMPZ_EX);
CASE(ZEND_JMPNZ_EX);
CASE(ZEND_CASE);
CASE(ZEND_SWITCH_FREE);
CASE(ZEND_BRK);
CASE(ZEND_CONT);
CASE(ZEND_BOOL);
CASE(ZEND_INIT_STRING);
CASE(ZEND_ADD_CHAR);
CASE(ZEND_ADD_STRING);
CASE(ZEND_ADD_VAR);
CASE(ZEND_BEGIN_SILENCE);
CASE(ZEND_END_SILENCE);
CASE(ZEND_INIT_FCALL_BY_NAME);
CASE(ZEND_DO_FCALL);
CASE(ZEND_DO_FCALL_BY_NAME);
CASE(ZEND_RETURN);
CASE(ZEND_RECV);
CASE(ZEND_RECV_INIT);
CASE(ZEND_SEND_VAL);
CASE(ZEND_SEND_VAR);
CASE(ZEND_SEND_REF);
CASE(ZEND_NEW);
CASE(ZEND_INIT_NS_FCALL_BY_NAME);
CASE(ZEND_FREE);
CASE(ZEND_INIT_ARRAY);
CASE(ZEND_ADD_ARRAY_ELEMENT);
CASE(ZEND_INCLUDE_OR_EVAL);
CASE(ZEND_UNSET_VAR);
CASE(ZEND_UNSET_DIM);
CASE(ZEND_UNSET_OBJ);
CASE(ZEND_FE_RESET);
CASE(ZEND_FE_FETCH);
CASE(ZEND_EXIT);
CASE(ZEND_FETCH_R);
CASE(ZEND_FETCH_DIM_R);
CASE(ZEND_FETCH_OBJ_R);
CASE(ZEND_FETCH_W);
CASE(ZEND_FETCH_DIM_W);
CASE(ZEND_FETCH_OBJ_W);
CASE(ZEND_FETCH_RW);
CASE(ZEND_FETCH_DIM_RW);
CASE(ZEND_FETCH_OBJ_RW);
CASE(ZEND_FETCH_IS);
CASE(ZEND_FETCH_DIM_IS);
CASE(ZEND_FETCH_OBJ_IS);
CASE(ZEND_FETCH_FUNC_ARG);
CASE(ZEND_FETCH_DIM_FUNC_ARG);
CASE(ZEND_FETCH_OBJ_FUNC_ARG);
CASE(ZEND_FETCH_UNSET);
CASE(ZEND_FETCH_DIM_UNSET);
CASE(ZEND_FETCH_OBJ_UNSET);
CASE(ZEND_FETCH_DIM_TMP_VAR);
CASE(ZEND_FETCH_CONSTANT);
CASE(ZEND_GOTO);
CASE(ZEND_EXT_STMT);
CASE(ZEND_EXT_FCALL_BEGIN);
CASE(ZEND_EXT_FCALL_END);
CASE(ZEND_EXT_NOP);
CASE(ZEND_TICKS);
CASE(ZEND_SEND_VAR_NO_REF);
CASE(ZEND_CATCH);
CASE(ZEND_THROW);
CASE(ZEND_FETCH_CLASS);
CASE(ZEND_CLONE);
CASE(ZEND_RETURN_BY_REF);
CASE(ZEND_INIT_METHOD_CALL);
CASE(ZEND_INIT_STATIC_METHOD_CALL);
CASE(ZEND_ISSET_ISEMPTY_VAR);
CASE(ZEND_ISSET_ISEMPTY_DIM_OBJ);
CASE(ZEND_PRE_INC_OBJ);
CASE(ZEND_PRE_DEC_OBJ);
CASE(ZEND_POST_INC_OBJ);
CASE(ZEND_POST_DEC_OBJ);
CASE(ZEND_ASSIGN_OBJ);
CASE(ZEND_INSTANCEOF);
CASE(ZEND_DECLARE_CLASS);
CASE(ZEND_DECLARE_INHERITED_CLASS);
CASE(ZEND_DECLARE_FUNCTION);
CASE(ZEND_RAISE_ABSTRACT_ERROR);
CASE(ZEND_DECLARE_CONST);
CASE(ZEND_ADD_INTERFACE);
CASE(ZEND_DECLARE_INHERITED_CLASS_DELAYED);
CASE(ZEND_VERIFY_ABSTRACT_CLASS);
CASE(ZEND_ASSIGN_DIM);
CASE(ZEND_ISSET_ISEMPTY_PROP_OBJ);
CASE(ZEND_HANDLE_EXCEPTION);
CASE(ZEND_USER_OPCODE);
#ifdef ZEND_JMP_SET
CASE(ZEND_JMP_SET);
#endif
CASE(ZEND_DECLARE_LAMBDA_FUNCTION);
#ifdef ZEND_ADD_TRAIT
CASE(ZEND_ADD_TRAIT);
#endif
#ifdef ZEND_BIND_TRAITS
CASE(ZEND_BIND_TRAITS);
#endif
#ifdef ZEND_SEPARATE
CASE(ZEND_SEPARATE);
#endif
#ifdef ZEND_QM_ASSIGN_VAR
CASE(ZEND_QM_ASSIGN_VAR);
#endif
#ifdef ZEND_JMP_SET_VAR
CASE(ZEND_JMP_SET_VAR);
#endif
#ifdef ZEND_DISCARD_EXCEPTION
CASE(ZEND_DISCARD_EXCEPTION);
#endif
#ifdef ZEND_YIELD
CASE(ZEND_YIELD);
#endif
#ifdef ZEND_GENERATOR_RETURN
CASE(ZEND_GENERATOR_RETURN);
#endif
#ifdef ZEND_FAST_CALL
CASE(ZEND_FAST_CALL);
#endif
#ifdef ZEND_FAST_RET
CASE(ZEND_FAST_RET);
#endif
#ifdef ZEND_RECV_VARIADIC
CASE(ZEND_RECV_VARIADIC);
#endif
CASE(ZEND_OP_DATA);
default:
return "UNKNOWN";
}
} /* }}} */

View File

@ -0,0 +1,31 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_OPCODE_H
#define PHPDBG_OPCODE_H
#include "zend_types.h"
const char *phpdbg_decode_opcode(zend_uchar);
char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC);
void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC);
void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC);
#endif /* PHPDBG_OPCODE_H */

258
sapi/phpdbg/phpdbg_print.c Normal file
View File

@ -0,0 +1,258 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_print.h"
#include "phpdbg_utils.h"
#include "phpdbg_opcode.h"
#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_PRINT(opline) /* {{{ */
{
if (EG(in_execution) && EG(current_execute_data)) {
phpdbg_print_opline(EG(current_execute_data), 1 TSRMLS_CC);
} else {
phpdbg_error("Not Executing!");
}
return SUCCESS;
} /* }}} */
static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC) /* {{{ */
{
switch (method->type) {
case ZEND_USER_FUNCTION: {
zend_op_array* op_array = &(method->op_array);
HashTable vars;
if (op_array) {
zend_op *opline = &(op_array->opcodes[0]);
zend_uint opcode = 0,
end = op_array->last-1;
if (method->common.scope) {
phpdbg_writeln("\tL%d-%d %s::%s() %s",
op_array->line_start, op_array->line_end,
method->common.scope->name,
method->common.function_name,
op_array->filename ? op_array->filename : "unknown");
} else {
phpdbg_writeln("\tL%d-%d %s() %s",
method->common.function_name ? op_array->line_start : 0,
method->common.function_name ? op_array->line_end : 0,
method->common.function_name ? method->common.function_name : "{main}",
op_array->filename ? op_array->filename : "unknown");
}
zend_hash_init(&vars, op_array->last, NULL, NULL, 0);
do {
char *decode = phpdbg_decode_opline(op_array, opline, &vars TSRMLS_CC);
if (decode != NULL) {
phpdbg_writeln("\t\tL%u\t%p %-30s %s",
opline->lineno,
opline,
phpdbg_decode_opcode(opline->opcode),
decode);
free(decode);
} else {
phpdbg_error("\tFailed to decode opline %16p", opline);
}
opline++;
} while (++opcode < end);
zend_hash_destroy(&vars);
}
} break;
default: {
if (method->common.scope) {
phpdbg_writeln("\tInternal %s::%s()", method->common.scope->name, method->common.function_name);
} else {
phpdbg_writeln("\tInternal %s()", method->common.function_name);
}
}
}
} /* }}} */
PHPDBG_PRINT(exec) /* {{{ */
{
if (PHPDBG_G(exec)) {
if (!PHPDBG_G(ops)) {
phpdbg_compile(TSRMLS_C);
}
if (PHPDBG_G(ops)) {
phpdbg_notice("Context %s", PHPDBG_G(exec));
phpdbg_print_function_helper((zend_function*) PHPDBG_G(ops) TSRMLS_CC);
}
} else {
phpdbg_error("No execution context set");
}
return SUCCESS;
} /* }}} */
PHPDBG_PRINT(stack) /* {{{ */
{
zend_op_array *ops = EG(active_op_array);
if (EG(in_execution) && ops) {
if (ops->function_name) {
if (ops->scope) {
phpdbg_notice("Stack in %s::%s()", ops->scope->name, ops->function_name);
} else {
phpdbg_notice("Stack in %s()", ops->function_name);
}
} else {
if (ops->filename) {
phpdbg_notice("Stack in %s", ops->filename);
} else {
phpdbg_notice("Stack @ %p", ops);
}
}
phpdbg_print_function_helper((zend_function*) ops TSRMLS_CC);
} else {
phpdbg_error("Not Executing!");
}
return SUCCESS;
} /* }}} */
PHPDBG_PRINT(class) /* {{{ */
{
zend_class_entry **ce;
switch (param->type) {
case STR_PARAM: {
if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
phpdbg_notice("%s %s: %s",
((*ce)->type == ZEND_USER_CLASS) ?
"User" : "Internal",
((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
"Interface" :
((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
"Abstract Class" :
"Class",
(*ce)->name);
phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
if (zend_hash_num_elements(&(*ce)->function_table)) {
HashPosition position;
zend_function *method;
for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
phpdbg_print_function_helper(method TSRMLS_CC);
}
}
} else {
phpdbg_error("The class %s could not be found", param->str);
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_PRINT(method) /* {{{ */
{
switch (param->type) {
case METHOD_PARAM: {
zend_class_entry **ce;
if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
zend_function *fbc;
char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s Method %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
fbc->common.function_name);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The method %s could not be found", param->method.name);
}
efree(lcname);
} else {
phpdbg_error("The class %s could not be found", param->method.class);
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_PRINT(func) /* {{{ */
{
switch (param->type) {
case STR_PARAM: {
HashTable *func_table = EG(function_table);
zend_function* fbc;
const char *func_name = param->str;
size_t func_name_len = param->len;
char *lcname;
/* search active scope if begins with period */
if (func_name[0] == '.') {
if (EG(scope)) {
func_name++;
func_name_len--;
func_table = &EG(scope)->function_table;
} else {
phpdbg_error("No active class");
return SUCCESS;
}
} else if (!EG(function_table)) {
phpdbg_error("No function table loaded");
return SUCCESS;
} else {
func_table = EG(function_table);
}
lcname = zend_str_tolower_dup(func_name, func_name_len);
if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
phpdbg_notice("%s %s %s",
(fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
(fbc->common.scope) ? "Method" : "Function",
fbc->common.function_name);
phpdbg_print_function_helper(fbc TSRMLS_CC);
} else {
phpdbg_error("The function %s could not be found", func_name);
}
efree(lcname);
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */

View File

@ -0,0 +1,51 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_PRINT_H
#define PHPDBG_PRINT_H
#include "phpdbg_cmd.h"
#define PHPDBG_PRINT(name) PHPDBG_COMMAND(print_##name)
/**
* Printer Forward Declarations
*/
PHPDBG_PRINT(exec);
PHPDBG_PRINT(opline);
PHPDBG_PRINT(class);
PHPDBG_PRINT(method);
PHPDBG_PRINT(func);
PHPDBG_PRINT(stack);
/**
* Commands
*/
static const phpdbg_command_t phpdbg_print_commands[] = {
PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1),
PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1),
PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1),
PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
PHPDBG_END_COMMAND
};
#endif /* PHPDBG_PRINT_H */

1328
sapi/phpdbg/phpdbg_prompt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,68 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_PROMPT_H
#define PHPDBG_PROMPT_H
/* {{{ */
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_compile(TSRMLS_D);
void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
/* {{{ phpdbg command handlers */
PHPDBG_COMMAND(exec);
PHPDBG_COMMAND(compile);
PHPDBG_COMMAND(step);
PHPDBG_COMMAND(next);
PHPDBG_COMMAND(run);
PHPDBG_COMMAND(eval);
PHPDBG_COMMAND(until);
PHPDBG_COMMAND(finish);
PHPDBG_COMMAND(leave);
PHPDBG_COMMAND(frame);
PHPDBG_COMMAND(print);
PHPDBG_COMMAND(break);
PHPDBG_COMMAND(back);
PHPDBG_COMMAND(list);
PHPDBG_COMMAND(info);
PHPDBG_COMMAND(clean);
PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
PHPDBG_COMMAND(quiet);
PHPDBG_COMMAND(aliases);
PHPDBG_COMMAND(shell);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
PHPDBG_COMMAND(register);
PHPDBG_COMMAND(quit); /* }}} */
/* {{{ prompt commands */
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
/* {{{ */
#if PHP_VERSION_ID >= 50500
void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
#else
void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC);
#endif /* }}} */
#endif /* PHPDBG_PROMPT_H */

208
sapi/phpdbg/phpdbg_set.c Normal file
View File

@ -0,0 +1,208 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_cmd.h"
#include "phpdbg_set.h"
#include "phpdbg_utils.h"
#include "phpdbg_bp.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
PHPDBG_SET(prompt) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
break;
case STR_PARAM:
phpdbg_set_prompt(param->str TSRMLS_CC);
break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */
PHPDBG_SET(break) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_writeln("%s",
PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
break;
case STR_PARAM:
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
phpdbg_enable_breakpoints(TSRMLS_C);
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
phpdbg_disable_breakpoints(TSRMLS_C);
}
break;
case NUMERIC_PARAM: {
if (input->argc > 2) {
if (phpdbg_argv_is(2, "on")) {
phpdbg_enable_breakpoint(param->num TSRMLS_CC);
} else if (phpdbg_argv_is(2, "off")) {
phpdbg_disable_breakpoint(param->num TSRMLS_CC);
}
} else {
phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
if (brake) {
phpdbg_writeln(
"%s", brake->disabled ? "off" : "on");
} else {
phpdbg_error("Failed to find breakpoint #%lx", param->num);
}
}
} break;
default:
phpdbg_error(
"set break used incorrectly: set break [id] <on|off>");
}
return SUCCESS;
} /* }}} */
#ifndef _WIN32
PHPDBG_SET(color) /* {{{ */
{
if ((param->type == STR_PARAM) && (input->argc == 3)) {
const phpdbg_color_t *color = phpdbg_get_color(
input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
int element = PHPDBG_COLOR_INVALID;
/* @TODO(anyone) make this consistent with other set commands */
if (color) {
if (phpdbg_argv_is(1, "prompt")) {
phpdbg_notice(
"setting prompt color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_PROMPT;
if (PHPDBG_G(prompt)[1]) {
free(PHPDBG_G(prompt)[1]);
PHPDBG_G(prompt)[1]=NULL;
}
} else if (phpdbg_argv_is(1, "error")) {
phpdbg_notice(
"setting error color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_ERROR;
} else if (phpdbg_argv_is(1, "notice")) {
phpdbg_notice(
"setting notice color to %s (%s)", color->name, color->code);
element = PHPDBG_COLOR_NOTICE;
} else goto usage;
/* set color for element */
phpdbg_set_color(element, color TSRMLS_CC);
} else {
phpdbg_error(
"Failed to find the requested color (%s)", input->argv[2]->string);
}
} else {
usage:
phpdbg_error(
"set color used incorrectly: set color <prompt|error|notice> <color>");
}
return SUCCESS;
} /* }}} */
PHPDBG_SET(colors) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM: {
phpdbg_writeln(
"%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
goto done;
}
case STR_PARAM: {
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
PHPDBG_G(flags) |= PHPDBG_IS_COLOURED;
goto done;
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
goto done;
}
}
default:
phpdbg_error(
"set colors used incorrectly: set colors <on|off>");
}
done:
return SUCCESS;
} /* }}} */
#endif
PHPDBG_SET(oplog) /* {{{ */
{
switch (param->type) {
case EMPTY_PARAM:
phpdbg_notice(
"Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
break;
case NUMERIC_PARAM: switch (param->num) {
case 1:
phpdbg_error(
"An output file must be provided to enable oplog");
break;
case 0: {
if (PHPDBG_G(oplog)) {
phpdbg_notice("Disabling oplog");
fclose(
PHPDBG_G(oplog));
} else {
phpdbg_error("Oplog is not enabled!");
}
} break;
} break;
case STR_PARAM: {
/* open oplog */
FILE *old = PHPDBG_G(oplog);
PHPDBG_G(oplog) = fopen(param->str, "w+");
if (!PHPDBG_G(oplog)) {
phpdbg_error("Failed to open %s for oplog", param->str);
PHPDBG_G(oplog) = old;
} else {
if (old) {
phpdbg_notice("Closing previously open oplog");
fclose(old);
}
phpdbg_notice("Successfully opened oplog %s", param->str);
}
} break;
phpdbg_default_switch_case();
}
return SUCCESS;
} /* }}} */

47
sapi/phpdbg/phpdbg_set.h Normal file
View File

@ -0,0 +1,47 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_SET_H
#define PHPDBG_SET_H
#include "phpdbg_cmd.h"
#define PHPDBG_SET(name) PHPDBG_COMMAND(set_##name)
PHPDBG_SET(prompt);
#ifndef _WIN32
PHPDBG_SET(color);
PHPDBG_SET(colors);
#endif
PHPDBG_SET(oplog);
PHPDBG_SET(break);
static const phpdbg_command_t phpdbg_set_commands[] = {
PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
#ifndef _WIN32
PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
PHPDBG_COMMAND_D_EX(colors, "usage: set colors <on|off>", 'C', set_colors, NULL, 1),
#endif
PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
PHPDBG_END_COMMAND
};
#endif /* PHPDBG_SET_H */

386
sapi/phpdbg/phpdbg_utils.c Normal file
View File

@ -0,0 +1,386 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "zend.h"
#include "php.h"
#include "spprintf.h"
#include "phpdbg.h"
#include "phpdbg_opcode.h"
#include "phpdbg_utils.h"
#ifdef _WIN32
# include "win32/time.h"
#endif
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
/* {{{ color structures */
const static phpdbg_color_t colors[] = {
PHPDBG_COLOR_D("none", "0;0"),
PHPDBG_COLOR_D("white", "0;64"),
PHPDBG_COLOR_D("white-bold", "1;64"),
PHPDBG_COLOR_D("white-underline", "4;64"),
PHPDBG_COLOR_D("red", "0;31"),
PHPDBG_COLOR_D("red-bold", "1;31"),
PHPDBG_COLOR_D("red-underline", "4;31"),
PHPDBG_COLOR_D("green", "0;32"),
PHPDBG_COLOR_D("green-bold", "1;32"),
PHPDBG_COLOR_D("green-underline", "4;32"),
PHPDBG_COLOR_D("yellow", "0;33"),
PHPDBG_COLOR_D("yellow-bold", "1;33"),
PHPDBG_COLOR_D("yellow-underline", "4;33"),
PHPDBG_COLOR_D("blue", "0;34"),
PHPDBG_COLOR_D("blue-bold", "1;34"),
PHPDBG_COLOR_D("blue-underline", "4;34"),
PHPDBG_COLOR_D("purple", "0;35"),
PHPDBG_COLOR_D("purple-bold", "1;35"),
PHPDBG_COLOR_D("purple-underline", "4;35"),
PHPDBG_COLOR_D("cyan", "0;36"),
PHPDBG_COLOR_D("cyan-bold", "1;36"),
PHPDBG_COLOR_D("cyan-underline", "4;36"),
PHPDBG_COLOR_D("black", "0;30"),
PHPDBG_COLOR_D("black-bold", "1;30"),
PHPDBG_COLOR_D("black-underline", "4;30"),
PHPDBG_COLOR_END
}; /* }}} */
PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
{
if (!str)
return 0;
for (; *str; str++) {
if (isspace(*str) || *str == '-') {
continue;
}
return isdigit(*str);
}
return 0;
} /* }}} */
PHPDBG_API int phpdbg_is_empty(const char *str) /* {{{ */
{
if (!str)
return 1;
for (; *str; str++) {
if (isspace(*str)) {
continue;
}
return 0;
}
return 1;
} /* }}} */
PHPDBG_API int phpdbg_is_addr(const char *str) /* {{{ */
{
return str[0] && str[1] && memcmp(str, "0x", 2) == 0;
} /* }}} */
PHPDBG_API int phpdbg_is_class_method(const char *str, size_t len, char **class, char **method) /* {{{ */
{
char *sep = NULL;
if (strstr(str, "#") != NULL)
return 0;
if (strstr(str, " ") != NULL)
return 0;
sep = strstr(str, "::");
if (!sep || sep == str || sep+2 == str+len-1) {
return 0;
}
if (class != NULL) {
if (str[0] == '\\') {
str++;
len--;
}
*class = estrndup(str, sep - str);
(*class)[sep - str] = 0;
}
if (method != NULL) {
*method = estrndup(sep+2, str + len - (sep + 2));
}
return 1;
} /* }}} */
PHPDBG_API char *phpdbg_resolve_path(const char *path TSRMLS_DC) /* {{{ */
{
char resolved_name[MAXPATHLEN];
if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
return NULL;
}
return estrdup(resolved_name);
} /* }}} */
PHPDBG_API const char *phpdbg_current_file(TSRMLS_D) /* {{{ */
{
const char *file = zend_get_executed_filename(TSRMLS_C);
if (memcmp(file, "[no active file]", sizeof("[no active file]")) == 0) {
return PHPDBG_G(exec);
}
return file;
} /* }}} */
PHPDBG_API const zend_function *phpdbg_get_function(const char *fname, const char *cname TSRMLS_DC) /* {{{ */
{
zend_function *func = NULL;
size_t fname_len = strlen(fname);
char *lcname = zend_str_tolower_dup(fname, fname_len);
if (cname) {
zend_class_entry **ce;
size_t cname_len = strlen(cname);
char *lc_cname = zend_str_tolower_dup(cname, cname_len);
int ret = zend_lookup_class(lc_cname, cname_len, &ce TSRMLS_CC);
efree(lc_cname);
if (ret == SUCCESS) {
zend_hash_find(&(*ce)->function_table, lcname, fname_len+1,
(void**)&func);
}
} else {
zend_hash_find(EG(function_table), lcname, fname_len+1,
(void**)&func);
}
efree(lcname);
return func;
} /* }}} */
PHPDBG_API char *phpdbg_trim(const char *str, size_t len, size_t *new_len) /* {{{ */
{
const char *p = str;
char *new = NULL;
while (p && isspace(*p)) {
++p;
--len;
}
while (*p && isspace(*(p + len -1))) {
--len;
}
if (len == 0) {
new = estrndup("", sizeof(""));
*new_len = 0;
} else {
new = estrndup(p, len);
*(new + len) = '\0';
if (new_len) {
*new_len = len;
}
}
return new;
} /* }}} */
PHPDBG_API int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *format, ...) /* {{{ */
{
int rc = 0;
char *buffer = NULL;
va_list args;
if (format != NULL && strlen(format) > 0L) {
va_start(args, format);
vspprintf(&buffer, 0, format, args);
va_end(args);
}
/* TODO(anyone) colours */
switch (type) {
case P_ERROR:
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
rc = fprintf(fp,
"\033[%sm[%s]\033[0m\n",
PHPDBG_G(colors)[PHPDBG_COLOR_ERROR]->code, buffer);
} else {
rc = fprintf(fp, "[%s]\n", buffer);
}
break;
case P_NOTICE:
if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
rc = fprintf(fp,
"\033[%sm[%s]\033[0m\n",
PHPDBG_G(colors)[PHPDBG_COLOR_NOTICE]->code, buffer);
} else {
rc = fprintf(fp, "[%s]\n", buffer);
}
break;
case P_WRITELN: {
if (buffer) {
rc = fprintf(fp, "%s\n", buffer);
} else {
rc = fprintf(fp, "\n");
}
} break;
case P_WRITE:
if (buffer) {
rc = fprintf(fp, "%s", buffer);
}
break;
/* no formatting on logging output */
case P_LOG:
if (buffer) {
struct timeval tp;
if (gettimeofday(&tp, NULL) == SUCCESS) {
rc = fprintf(fp, "[%ld %.8F]: %s\n", tp.tv_sec, tp.tv_usec / 1000000.00, buffer);
} else {
rc = FAILURE;
}
}
break;
}
if (buffer) {
efree(buffer);
}
return rc;
} /* }}} */
PHPDBG_API int phpdbg_rlog(FILE *fp, const char *fmt, ...) { /* {{{ */
int rc = 0;
va_list args;
struct timeval tp;
va_start(args, fmt);
if (gettimeofday(&tp, NULL) == SUCCESS) {
char friendly[100];
char *format = NULL, *buffer = NULL;
strftime(friendly, 100, "%a %b %d %T.%%04d %Y", localtime(&tp.tv_sec));
asprintf(
&buffer, friendly, tp.tv_usec/1000);
asprintf(
&format, "[%s]: %s\n", buffer, fmt);
rc = vfprintf(
fp, format, args);
free(format);
free(buffer);
}
va_end(args);
return rc;
} /* }}} */
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC) /* {{{ */
{
const phpdbg_color_t *color = colors;
while (color && color->name) {
if (name_length == color->name_length &&
memcmp(name, color->name, name_length) == SUCCESS) {
phpdbg_debug(
"phpdbg_get_color(%s, %lu): %s", name, name_length, color->code);
return color;
}
++color;
}
phpdbg_debug(
"phpdbg_get_color(%s, %lu): failed", name, name_length);
return NULL;
} /* }}} */
PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC) /* {{{ */
{
PHPDBG_G(colors)[element] = color;
} /* }}} */
PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC) /* {{{ */
{
const phpdbg_color_t *color = phpdbg_get_color(name, name_length TSRMLS_CC);
if (color) {
phpdbg_set_color(element, color TSRMLS_CC);
} else PHPDBG_G(colors)[element] = colors;
} /* }}} */
PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */
{
return colors;
} /* }}} */
PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
{
/* free formatted prompt */
if (PHPDBG_G(prompt)[1]) {
free(PHPDBG_G(prompt)[1]);
PHPDBG_G(prompt)[1] = NULL;
}
/* free old prompt */
if (PHPDBG_G(prompt)[0]) {
free(PHPDBG_G(prompt)[0]);
PHPDBG_G(prompt)[0] = NULL;
}
/* copy new prompt */
PHPDBG_G(prompt)[0] = strdup(prompt);
} /* }}} */
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
{
/* find cached prompt */
if (PHPDBG_G(prompt)[1]) {
return PHPDBG_G(prompt)[1];
}
/* create cached prompt */
if ((PHPDBG_G(flags) & PHPDBG_IS_COLOURED)) {
asprintf(
&PHPDBG_G(prompt)[1], "\033[%sm%s\033[0m ",
PHPDBG_G(colors)[PHPDBG_COLOR_PROMPT]->code,
PHPDBG_G(prompt)[0]);
} else {
asprintf(
&PHPDBG_G(prompt)[1], "%s ",
PHPDBG_G(prompt)[0]);
}
return PHPDBG_G(prompt)[1];
} /* }}} */

110
sapi/phpdbg/phpdbg_utils.h Normal file
View File

@ -0,0 +1,110 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2013 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHPDBG_UTILS_H
#define PHPDBG_UTILS_H
/**
* Input scan functions
*/
PHPDBG_API int phpdbg_is_numeric(const char*);
PHPDBG_API int phpdbg_is_empty(const char*);
PHPDBG_API int phpdbg_is_addr(const char*);
PHPDBG_API int phpdbg_is_class_method(const char*, size_t, char**, char**);
PHPDBG_API const char *phpdbg_current_file(TSRMLS_D);
PHPDBG_API char *phpdbg_resolve_path(const char* TSRMLS_DC);
PHPDBG_API char *phpdbg_trim(const char*, size_t, size_t*);
PHPDBG_API const zend_function *phpdbg_get_function(const char*, const char* TSRMLS_DC);
/**
* Error/notice/formatting helpers
*/
enum {
P_ERROR = 1,
P_NOTICE,
P_WRITELN,
P_WRITE,
P_LOG
};
#ifdef ZTS
PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUTE_FORMAT(printf, 4, 5);
#else
PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
#endif
PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
#define phpdbg_error(fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
#define phpdbg_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
#define phpdbg_write(fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
#define phpdbg_log(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
#define phpdbg_error_ex(out, fmt, ...) phpdbg_print(P_ERROR TSRMLS_CC, out, fmt, ##__VA_ARGS__)
#define phpdbg_notice_ex(out, fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, out, fmt, ##__VA_ARGS__)
#define phpdbg_writeln_ex(out, fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, out, fmt, ##__VA_ARGS__)
#define phpdbg_write_ex(out, fmt, ...) phpdbg_print(P_WRITE TSRMLS_CC, out, fmt, ##__VA_ARGS__)
#define phpdbg_log_ex(out, fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, out, fmt, ##__VA_ARGS__)
#if PHPDBG_DEBUG
# define phpdbg_debug(fmt, ...) phpdbg_print(P_LOG TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDERR], fmt, ##__VA_ARGS__)
#else
# define phpdbg_debug(fmt, ...)
#endif
/* {{{ For writing blank lines */
#define EMPTY NULL /* }}} */
/* {{{ For prompt lines */
#define PROMPT "phpdbg>" /* }}} */
/* {{{ For separation */
#define SEPARATE "------------------------------------------------" /* }}} */
/* {{{ Color Management */
#define PHPDBG_COLOR_LEN 12
#define PHPDBG_COLOR_D(color, code) \
{color, sizeof(color)-1, code}
#define PHPDBG_COLOR_END \
{NULL, 0L, {0}}
#define PHPDBG_COLOR_INVALID -1
#define PHPDBG_COLOR_PROMPT 0
#define PHPDBG_COLOR_ERROR 1
#define PHPDBG_COLOR_NOTICE 2
#define PHPDBG_COLORS 3
typedef struct _phpdbg_color_t {
char *name;
size_t name_length;
const char code[PHPDBG_COLOR_LEN];
} phpdbg_color_t;
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC);
PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */
/* {{{ Prompt Management */
PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC);
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
#endif /* PHPDBG_UTILS_H */

51
sapi/phpdbg/test.php Normal file
View File

@ -0,0 +1,51 @@
<?php
if (isset($include)) {
include (sprintf("%s/web-bootstrap.php", dirname(__FILE__)));
}
$stdout = fopen("php://stdout", "w+");
class phpdbg {
public function isGreat($greeting = null) {
printf(
"%s: %s\n", __METHOD__, $greeting);
return $this;
}
}
function test($x, $y = 0) {
$var = $x + 1;
$var += 2;
$var <<= 3;
$foo = function () {
echo "bar!\n";
};
$foo();
yield $var;
}
$dbg = new phpdbg();
var_dump(
$dbg->isGreat("PHP Rocks!!"));
foreach (test(1,2) as $gen)
continue;
echo "it works!\n";
if (isset($dump))
var_dump($_SERVER);
function phpdbg_test_ob()
{
echo 'Start';
ob_start();
echo 'Hello';
$b = ob_get_clean();
echo 'End';
echo $b;
}

View File

@ -0,0 +1,8 @@
#######################################################
# name: basic
# purpose: check basic functionality of phpdbg console
# expect: TEST::EXACT
# options: -rr
#######################################################
# [Nothing to execute!]
#######################################################

View File

@ -0,0 +1,23 @@
#################################################
# name: set
# purpose: tests for set commands
# expect: TEST::CISTRING
# options: -rr
#################################################
# setting prompt color
# setting error color
# setting notice color
# Failed to find breakpoint #0
# oplog disabled
# not enabled
# opened oplog test.log
# nothing
#################################################
set color prompt none
set color error none
set color notice none
set prompt promot>
set break 0
set oplog
set oplog 0
set oplog test.log

View File

@ -0,0 +1,19 @@
#################################################
# name: info
# purpose: test info commands
# expect: TEST::FORMAT
# options: -rr
#################################################
#[User Classes (%d)]
#User Class test (3)
#|---- in phpdbginit code on line %d
#################################################
<:
class test {
public function testMethod(){}
private function testPrivateMethod(){}
protected function testProtectedMethod(){}
}
:>
info classes
q

View File

@ -0,0 +1,28 @@
#################################################
# name: print
# purpose: test print commands
# expect: TEST::FORMAT
# options: -rr
#################################################
#[User Class: test]
#Methods (3):
#L%d-%d test::testMethod() %s
# L%d %s ZEND_RETURN C%d <unused> <unused>
# L%d-%d test::testPrivateMethod() %s
# L%d %s ZEND_RETURN C%d <unused> <unused>
# L%d-%d test::testProtectedMethod() %s
# L%d %s ZEND_RETURN C%d <unused> <unused>
#[User Method testMethod]
# L%d-%d test::testMethod() %s
# L%d %s ZEND_RETURN C%d <unused> <unused>
#################################################
<:
class test {
public function testMethod(){}
private function testPrivateMethod(){}
protected function testProtectedMethod(){}
}
:>
print class test
print method test::testMethod
q

View File

@ -0,0 +1,28 @@
#################################################
# name: register
# purpose: test registration functions
# expect: TEST::FORMAT
# options: -rr
#################################################
#[Registered test_function]
#array(5) {
# [0]=>
# string(1) "1"
# [1]=>
# string(1) "2"
# [2]=>
# string(1) "3"
# [3]=>
# string(1) "4"
# [4]=>
# string(1) "5"
#}
#################################################
<:
function test_function() {
var_dump(func_get_args());
}
:>
R test_function
test_function 1 2 3 4 5
q

View File

@ -0,0 +1,15 @@
#################################################
# name: clean
# purpose: test cleaning environment
# expect: TEST::FORMAT
# options: -rr
#################################################
#[Cleaning Execution Environment]
#Classes %d
#Functions %d
#Constants %d
#Includes %d
#[Nothing to execute!]
#################################################
clean
quit

View File

@ -0,0 +1,18 @@
#################################################
# name: clear
# purpose: test clearing breakpoints
# expect: TEST::FORMAT
# options: -rr
#################################################
#[Clearing Breakpoints]
#File%w%d
#Functions%w%d
#Methods%w%d
#Oplines%w%d
#File oplines%w%d
#Function oplines%w%d
#Method oplines%w%d
#Conditionals%w%d
#################################################
clear
quit

View File

@ -0,0 +1,19 @@
#################################################
# name: compile
# purpose: test compiling code
# expect: TEST::FORMAT
# options: -rr
#################################################
#[Attempting compilation of %s]
#[Success]
#Hello World
#################################################
<:
define('OUT',
tempnam(null, "phpdbg"));
file_put_contents(OUT, "<?php echo \"Hello World\"; ?>");
phpdbg_exec(OUT);
:>
compile
run
quit

View File

@ -0,0 +1,583 @@
<?php
namespace phpdbg\testing {
/*
* Workaround ...
*/
if (!defined('DIR_SEP'))
define('DIR_SEP', '\\' . DIRECTORY_SEPARATOR);
/**
* TestConfigurationExceptions are thrown
* when the configuration prohibits tests executing
*
* @package phpdbg
* @subpackage testing
*/
class TestConfigurationException extends \Exception {
/**
*
* @param array Tests confguration
* @param message Exception message
* @param ... formatting parameters
*/
public function __construct() {
$argv = func_get_args();
if (count($argv)) {
$this->config = array_shift($argv);
$this->message = vsprintf(
array_shift($argv), $argv);
}
}
}
/**
*
* @package phpdbg
* @subpackage testing
*/
class TestsConfiguration implements \ArrayAccess {
/**
*
* @param array basic configuration
* @param array argv
*/
public function __construct($config, $cmd) {
$this->options = $config;
while (($key = array_shift($cmd))) {
switch (substr($key, 0, 1)) {
case '-': switch(substr($key, 1, 1)) {
case '-': {
$arg = substr($key, 2);
if (($e=strpos($arg, '=')) !== false) {
$key = substr($arg, 0, $e);
$value = substr($arg, $e+1);
} else {
$key = $arg;
$value = array_shift($cmd);
}
if (isset($key) && isset($value)) {
switch ($key) {
case 'phpdbg':
case 'width':
$this->options[$key] = $value;
break;
default: {
if (isset($config[$key])) {
if (is_array($config[$key])) {
$this->options[$key][] = $value;
} else {
$this->options[$key] = array($config[$key], $value);
}
} else {
$this->options[$key] = $value;
}
}
}
}
} break;
default:
$this->flags[] = substr($key, 1);
} break;
}
}
if (!is_executable($this->options['phpdbg'])) {
throw new TestConfigurationException(
$this->options, 'phpdbg could not be found at the specified path (%s)', $this->options['phpdbg']);
} else $this->options['phpdbg'] = realpath($this->options['phpdbg']);
$this->options['width'] = (integer) $this->options['width'];
/* display properly, all the time */
if ($this->options['width'] < 50) {
$this->options['width'] = 50;
}
/* calculate column widths */
$this->options['lwidth'] = ceil($this->options['width'] / 3);
$this->options['rwidth'] = ceil($this->options['width'] - $this->options['lwidth']) - 5;
}
public function hasFlag($flag) {
return in_array(
$flag, $this->flags);
}
public function offsetExists($offset) { return isset($this->options[$offset]); }
public function offsetGet($offset) { return $this->options[$offset]; }
public function offsetUnset($offset) { unset($this->options[$offset]); }
public function offsetSet($offset, $data) { $this->options[$offset] = $data; }
protected $options = array();
protected $flags = array();
}
/**
* Tests is the console programming API for the test suite
*
* @package phpdbg
* @subpackage testing
*/
class Tests {
/**
* Construct the console object
*
* @param array basic configuration
* @param array command line
*/
public function __construct(TestsConfiguration &$config) {
$this->config = &$config;
if ($this->config->hasFlag('help') ||
$this->config->hasFlag('h')) {
$this->showUsage();
exit;
}
}
/**
* Find valid paths as specified by configuration
*
*/
public function findPaths($in = null) {
$paths = array();
$where = ($in != null) ? array($in) : $this->config['path'];
foreach ($where as &$path) {
if ($path) {
if (is_dir($path)) {
$paths[] = $path;
foreach (scandir($path) as $child) {
if ($child != '.' && $child != '..') {
$paths = array_merge(
$paths, $this->findPaths("$path/$child"));
}
}
}
}
}
return $paths;
}
/**
*
* @param string the path to log
*/
public function logPath($path) {
printf(
'%s [%s]%s',
str_repeat(
'-', $this->config['width'] - strlen($path)),
$path, PHP_EOL);
}
/**
*
* @param string the path to log
*/
public function logPathStats($path) {
if (!isset($this->stats[$path])) {
return;
}
$total = array_sum($this->stats[$path]);
if ($total) {
@$this->totals[true] += $this->stats[$path][true];
@$this->totals[false] += $this->stats[$path][false];
$stats = @sprintf(
"%d/%d %%%d",
$this->stats[$path][true],
$this->stats[$path][false],
(100 / $total) * $this->stats[$path][true]);
printf(
'%s [%s]%s',
str_repeat(
' ', $this->config['width'] - strlen($stats)),
$stats, PHP_EOL);
printf("%s%s", str_repeat('-', $this->config['width']+3), PHP_EOL);
printf("%s", PHP_EOL);
}
}
/**
*
*/
public function logStats() {
$total = array_sum($this->totals);
$stats = @sprintf(
"%d/%d %%%d",
$this->totals[true],
$this->totals[false],
(100 / $total) * $this->totals[true]);
printf(
'%s [%s]%s',
str_repeat(
' ', $this->config['width'] - strlen($stats)),
$stats, PHP_EOL);
}
/**
*
*/
protected function showUsage() {
printf('usage: php %s [flags] [options]%s', $this->config['exec'], PHP_EOL);
printf('[options]:%s', PHP_EOL);
printf("\t--path\t\tadd a path to scan outside of tests directory%s", PHP_EOL);
printf("\t--width\t\tset line width%s", PHP_EOL);
printf("\t--options\toptions to pass to phpdbg%s", PHP_EOL);
printf("\t--phpdbg\tpath to phpdbg binary%s", PHP_EOL);
printf('[flags]:%s', PHP_EOL);
printf("\t-nodiff\t\tdo not write diffs on failure%s", PHP_EOL);
printf("\t-nolog\t\tdo not write logs on failure%s", PHP_EOL);
printf('[examples]:%s', PHP_EOL);
printf("\tphp %s --phpdbg=/usr/local/bin/phpdbg --path=/usr/src/phpdbg/tests --options -n%s",
$this->config['exec'], PHP_EOL);
}
/**
* Find valid tests at the specified path (assumed valid)
*
* @param string a valid path
*/
public function findTests($path) {
$tests = array();
foreach (scandir($path) as $file) {
if ($file == '.' || $file == '..')
continue;
$test = sprintf('%s/%s', $path, $file);
if (preg_match('~\.test$~', $test)) {
yield new Test($this->config, $test);
}
}
}
/**
*
* @param Test the test to log
*/
public function logTest($path, Test $test) {
@$this->stats[$path][($result=$test->getResult())]++;
printf(
"%-{$this->config['lwidth']}s %-{$this->config['rwidth']}s [%s]%s",
$test->name,
$test->purpose,
$result ? "PASS" : "FAIL",
PHP_EOL);
}
protected $config;
}
class Test {
/*
* Expect exact line for line match
*/
const EXACT = 0x00000001;
/*
* Expect strpos() !== false
*/
const STRING = 0x00000010;
/*
* Expect stripos() !== false
*/
const CISTRING = 0x00000100;
/*
* Formatted output
*/
const FORMAT = 0x00001000;
/**
* Format specifiers
*/
private static $format = array(
'search' => array(
'%e',
'%s',
'%S',
'%a',
'%A',
'%w',
'%i',
'%d',
'%x',
'%f',
'%c',
'%t',
'%T'
),
'replace' => array(
DIR_SEP,
'[^\r\n]+',
'[^\r\n]*',
'.+',
'.*',
'\s*',
'[+-]?\d+',
'\d+',
'[0-9a-fA-F]+',
'[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?',
'.',
'\t',
'\t+'
)
);
/**
* Constructs a new Test object given a specilized phpdbginit file
*
* @param array configuration
* @param string file
*/
public function __construct(TestsConfiguration &$config, &$file) {
if (($handle = fopen($file, 'r'))) {
while (($line = fgets($handle))) {
$trim = trim($line);
switch (substr($trim, 0, 1)) {
case '#': if (($chunks = array_map('trim', preg_split('~:~', substr($trim, 1), 2)))) {
if (property_exists($this, $chunks[0])) {
switch ($chunks[0]) {
case 'expect': {
if ($chunks[1]) {
switch (strtoupper($chunks[1])) {
case 'TEST::EXACT':
case 'EXACT': { $this->expect = TEST::EXACT; } break;
case 'TEST::STRING':
case 'STRING': { $this->expect = TEST::STRING; } break;
case 'TEST::CISTRING':
case 'CISTRING': { $this->expect = TEST::CISTRING; } break;
case 'TEST::FORMAT':
case 'FORMAT': { $this->expect = TEST::FORMAT; } break;
default:
throw new TestConfigurationException(
$this->config, "unknown type of expectation (%s)", $chunks[1]);
}
}
} break;
default: {
$this->$chunks[0] = $chunks[1];
}
}
} else switch(substr($trim, 1, 1)) {
case '#': { /* do nothing */ } break;
default: {
$line = preg_replace(
"~(\r\n)~", "\n", substr($trim, 1));
$line = trim($line);
switch ($this->expect) {
case TEST::FORMAT:
$this->match[] = str_replace(
self::$format['search'],
self::$format['replace'], preg_quote($line));
break;
default: $this->match[] = $line;
}
}
}
} break;
default:
break 2;
}
}
fclose($handle);
$this->config = &$config;
$this->file = &$file;
}
}
/**
* Obvious!!
*
*/
public function getResult() {
$options = sprintf(
'-i%s -qb', $this->file);
if ($this->options) {
$options = sprintf(
'%s %s %s',
$options,
$this->config['options'],
$this->options
);
} else {
$options = sprintf(
'%s %s', $options, $this->config['options']
);
}
$result = `{$this->config['phpdbg']} {$options}`;
if ($result) {
foreach (preg_split('~(\r|\n)~', $result) as $num => $line) {
if (!$line && !isset($this->match[$num]))
continue;
switch ($this->expect) {
case TEST::EXACT: {
if (strcmp($line, $this->match[$num]) !== 0) {
$this->diff['wants'][$num] = &$this->match[$num];
$this->diff['gets'][$num] = $line;
}
} continue 2;
case TEST::STRING: {
if (strpos($line, $this->match[$num]) === false) {
$this->diff['wants'][$num] = &$this->match[$num];
$this->diff['gets'][$num] = $line;
}
} continue 2;
case TEST::CISTRING: {
if (stripos($line, $this->match[$num]) === false) {
$this->diff['wants'][$num] = &$this->match[$num];
$this->diff['gets'][$num] = $line;
}
} continue 2;
case TEST::FORMAT: {
$line = trim($line);
if (!preg_match("/^{$this->match[$num]}\$/s", $line)) {
$this->diff['wants'][$num] = &$this->match[$num];
$this->diff['gets'][$num] = $line;
}
} continue 2;
}
}
}
$this->writeLog($result);
$this->writeDiff();
return (count($this->diff) == 0);
}
/**
* Write diff to disk if configuration allows it
*
*/
protected function writeDiff() {
$diff = sprintf(
'%s/%s.diff',
dirname($this->file), basename($this->file));
if (count($this->diff['wants'])) {
if (!in_array('nodiff', $this->config['flags'])) {
if (($diff = fopen($diff, 'w+'))) {
foreach ($this->diff['wants'] as $line => $want) {
$got = $this->diff['gets'][$line];
fprintf(
$diff, '(%d) -%s%s', $line+1, $want, PHP_EOL);
fprintf(
$diff, '(%d) +%s%s', $line+1, $got, PHP_EOL);
}
fclose($diff);
}
}
} else unlink($diff);
}
/**
* Write log to disk if configuration allows it
*
*/
protected function writeLog(&$result = null) {
$log = sprintf(
'%s/%s.log',
dirname($this->file), basename($this->file));
if (count($this->diff) && $result) {
if (!in_array('nolog', $this->config['flags'])) {
@file_put_contents(
$log, $result);
}
} else unlink($log);
}
public $name;
public $purpose;
public $file;
public $options;
public $expect;
protected $match;
protected $diff;
protected $stats;
protected $totals;
}
}
namespace {
use \phpdbg\Testing\Test;
use \phpdbg\Testing\Tests;
use \phpdbg\Testing\TestsConfiguration;
$cwd = dirname(__FILE__);
$cmd = $_SERVER['argv'];
{
$config = new TestsConfiguration(array(
'exec' => realpath(array_shift($cmd)),
'phpdbg' => realpath(sprintf(
'%s/../phpdbg', $cwd
)),
'path' => array(
realpath(dirname(__FILE__))
),
'flags' => array(),
'width' => 75
), $cmd);
$tests = new Tests($config);
foreach ($tests->findPaths() as $path) {
$tests->logPath($path);
foreach ($tests->findTests($path) as $test) {
$tests->logTest($path, $test);
}
$tests->logPathStats($path);
}
$tests->logStats();
}
}
?>

9
sapi/phpdbg/travis/ci.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env sh
git clone https://github.com/php/php-src
cd php-src/sapi
git clone https://github.com/krakjoe/phpdbg.git
cd ../
./buildconf --force
./configure --disable-all --enable-phpdbg --enable-maintainer-zts
make
make test-phpdbg

View File

@ -0,0 +1,64 @@
<?php
/**
* The following file shows how to bootstrap phpdbg so that you can mock specific server environments
*
* eval include("web-bootstrap.php")
* exec index.php
* compile
* break ...
* run
*/
if (!defined('PHPDBG_BOOTSTRAPPED'))
{
/* define these once */
define("PHPDBG_BOOTPATH", "/opt/php-zts/htdocs");
define("PHPDBG_BOOTSTRAP", "index.php");
define("PHPDBG_BOOTSTRAPPED", sprintf("/%s", PHPDBG_BOOTSTRAP));
}
/*
* Superglobals are JIT, phpdbg will not over-write whatever you set during bootstrap
*/
$_SERVER = array
(
'HTTP_HOST' => 'localhost',
'HTTP_CONNECTION' => 'keep-alive',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36',
'HTTP_ACCEPT_ENCODING' => 'gzip,deflate,sdch',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
'HTTP_COOKIE' => 'tz=Europe%2FLondon; __utma=1.347100075.1384196523.1384196523.1384196523.1; __utmc=1; __utmz=1.1384196523.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)',
'PATH' => '/usr/local/bin:/usr/bin:/bin',
'SERVER_SIGNATURE' => '<address>Apache/2.4.6 (Ubuntu) Server at phpdbg.com Port 80</address>',
'SERVER_SOFTWARE' => 'Apache/2.4.6 (Ubuntu)',
'SERVER_NAME' => 'localhost',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'REMOTE_ADDR' => '127.0.0.1',
'DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
'REQUEST_SCHEME' => 'http',
'CONTEXT_PREFIX' => '',
'CONTEXT_DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
'SERVER_ADMIN' => '[no address given]',
'SCRIPT_FILENAME' => sprintf(
'%s/%s', PHPDBG_BOOTPATH, PHPDBG_BOOTSTRAP
),
'REMOTE_PORT' => '47931',
'GATEWAY_INTERFACE' => 'CGI/1.1',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => '',
'REQUEST_URI' => PHPDBG_BOOTSTRAPPED,
'SCRIPT_NAME' => PHPDBG_BOOTSTRAPPED,
'PHP_SELF' => PHPDBG_BOOTSTRAPPED,
'REQUEST_TIME' => time(),
);
$_GET = array();
$_REQUEST = array();
$_POST = array();
$_COOKIE = array();
$_FILES = array();
chdir(PHPDBG_BOOTPATH);