48 changed files with 10215 additions and 0 deletions
-
10sapi/phpdbg/.gdbinit
-
5sapi/phpdbg/.gitignore
-
105sapi/phpdbg/.phpdbginit
-
3sapi/phpdbg/.travis.yml
-
52sapi/phpdbg/Changelog.md
-
28sapi/phpdbg/Makefile.frag
-
83sapi/phpdbg/README.md
-
61sapi/phpdbg/config.m4
-
19sapi/phpdbg/config.w32
-
1308sapi/phpdbg/phpdbg.c
-
187sapi/phpdbg/phpdbg.h
-
122sapi/phpdbg/phpdbg.init.d
-
1661sapi/phpdbg/phpdbg_bp.c
-
146sapi/phpdbg/phpdbg_bp.h
-
155sapi/phpdbg/phpdbg_break.c
-
58sapi/phpdbg/phpdbg_break.h
-
669sapi/phpdbg/phpdbg_cmd.c
-
169sapi/phpdbg/phpdbg_cmd.h
-
206sapi/phpdbg/phpdbg_frame.c
-
30sapi/phpdbg/phpdbg_frame.h
-
603sapi/phpdbg/phpdbg_help.c
-
92sapi/phpdbg/phpdbg_help.h
-
355sapi/phpdbg/phpdbg_info.c
-
49sapi/phpdbg/phpdbg_info.h
-
279sapi/phpdbg/phpdbg_list.c
-
47sapi/phpdbg/phpdbg_list.h
-
361sapi/phpdbg/phpdbg_opcode.c
-
31sapi/phpdbg/phpdbg_opcode.h
-
258sapi/phpdbg/phpdbg_print.c
-
51sapi/phpdbg/phpdbg_print.h
-
1328sapi/phpdbg/phpdbg_prompt.c
-
68sapi/phpdbg/phpdbg_prompt.h
-
208sapi/phpdbg/phpdbg_set.c
-
47sapi/phpdbg/phpdbg_set.h
-
386sapi/phpdbg/phpdbg_utils.c
-
110sapi/phpdbg/phpdbg_utils.h
-
51sapi/phpdbg/test.php
-
8sapi/phpdbg/tests/commands/0001_basic.test
-
23sapi/phpdbg/tests/commands/0002_set.test
-
19sapi/phpdbg/tests/commands/0101_info.test
-
28sapi/phpdbg/tests/commands/0102_print.test
-
28sapi/phpdbg/tests/commands/0103_register.test
-
15sapi/phpdbg/tests/commands/0104_clean.test
-
18sapi/phpdbg/tests/commands/0105_clear.test
-
19sapi/phpdbg/tests/commands/0106_compile.test
-
583sapi/phpdbg/tests/run-tests.php
-
9sapi/phpdbg/travis/ci.sh
-
64sapi/phpdbg/web-bootstrap.php
@ -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 |
|||
@ -0,0 +1,5 @@ |
|||
.libs/ |
|||
./phpdbg |
|||
*.lo |
|||
*.o |
|||
build |
|||
@ -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 ... |
|||
########################################################## |
|||
@ -0,0 +1,3 @@ |
|||
language: c |
|||
|
|||
script: ./travis/ci.sh |
|||
@ -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 |
|||
@ -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 |
|||
|
|||
|
|||
@ -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+ |
|||
|
|||
[](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 |
|||
@ -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: |
|||
@ -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
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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 */ |
|||
@ -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
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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 */ |
|||
@ -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; |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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, |
|||
¶m TSRMLS_CC); |
|||
} |
|||
|
|||
phpdbg_debug( |
|||
"found command %s for %s with %d arguments", |
|||
command->name, input->argv[0]->string, input->argc-1); |
|||
{ |
|||
int arg; |
|||
for (arg=1; arg<input->argc; arg++) { |
|||
phpdbg_debug( |
|||
"\t#%d: [%s=%zu]", |
|||
arg, |
|||
input->argv[arg]->string, |
|||
input->argv[arg]->length); |
|||
} |
|||
} |
|||
|
|||
rc = command->handler(¶m, input TSRMLS_CC); |
|||
|
|||
/* only set last command when it is worth it! */ |
|||
if ((rc != FAILURE) && |
|||
!(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { |
|||
PHPDBG_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; |
|||
} /* }}} */ |
|||
|
|||
@ -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 */ |
|||
@ -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); |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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; |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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; |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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); |
|||
} /* }}} */ |
|||
|
|||
@ -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 */ |
|||
@ -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"; |
|||
} |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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; |
|||
} /* }}} */ |
|||
@ -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
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
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 */ |
|||
@ -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; |
|||
} /* }}} */ |
|||
|
|||
@ -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 */ |
|||
@ -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]; |
|||
} /* }}} */ |
|||
@ -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 */ |
|||
@ -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; |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
####################################################### |
|||
# name: basic |
|||
# purpose: check basic functionality of phpdbg console |
|||
# expect: TEST::EXACT |
|||
# options: -rr |
|||
####################################################### |
|||
# [Nothing to execute!] |
|||
####################################################### |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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 |
|||
@ -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(); |
|||
} |
|||
} |
|||
?>
|
|||
@ -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 |
|||
@ -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); |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue