Adapted opline_num breakpoints to master branch
4
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
|||
.libs/
|
||||
phpdbg
|
||||
./phpdbg
|
||||
*.lo
|
||||
*.o
|
||||
|
||||
build
|
||||
|
|
13
Changelog.md
|
@ -1,15 +1,24 @@
|
|||
ChangeLog for phpdbg
|
||||
====================
|
||||
|
||||
Version 0.2.0 2013-00-00
|
||||
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
|
||||
|
||||
Version 0.2.0 2013-11-31
|
||||
------------------------
|
||||
(this needs tidying before release)
|
||||
|
||||
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
|
||||
------------------------
|
||||
|
|
|
@ -17,6 +17,7 @@ Features
|
|||
- 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
|
||||
|
@ -63,6 +64,9 @@ The following switches change the default behaviour of phpdbg:
|
|||
- -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*
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ if test "$PHP_PHPDBG" != "no"; then
|
|||
AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
|
||||
fi
|
||||
|
||||
PHP_PHPDBG_CFLAGS="-I$abc_srcdir"
|
||||
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"
|
||||
|
||||
PHP_SUBST(PHP_PHPDBG_CFLAGS)
|
||||
|
|
348
phpdbg.c
|
@ -27,6 +27,16 @@
|
|||
#include "phpdbg_utils.h"
|
||||
#include "phpdbg_set.h"
|
||||
|
||||
/* {{{ remote console headers */
|
||||
#ifndef _WIN32
|
||||
# include <sys/socket.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
# include <arpa/inet.h>
|
||||
#endif /* }}} */
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
|
||||
|
||||
#if PHP_VERSION_ID >= 50500
|
||||
|
@ -84,7 +94,7 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
|
|||
|
||||
static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */
|
||||
{
|
||||
zend_llist_destroy((zend_llist*)brake);
|
||||
zend_hash_destroy((HashTable*)brake);
|
||||
} /* }}} */
|
||||
|
||||
static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */
|
||||
|
@ -115,11 +125,11 @@ static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */
|
|||
brake->ops TSRMLS_CC);
|
||||
efree(brake->ops);
|
||||
}
|
||||
zval_dtor(&brake->code);
|
||||
efree((char*)brake->code);
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
static void php_phpdbg_destroy_registered(void *data)
|
||||
static void php_phpdbg_destroy_registered(void *data) /* {{{ */
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
|
||||
|
@ -127,7 +137,7 @@ static void php_phpdbg_destroy_registered(void *data)
|
|||
|
||||
destroy_zend_function(
|
||||
function TSRMLS_CC);
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
|
||||
{
|
||||
|
@ -140,6 +150,8 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
|
|||
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 0);
|
||||
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
|
||||
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
|
||||
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
|
||||
|
||||
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
|
||||
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
|
||||
|
||||
|
@ -157,6 +169,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
|
|||
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]);
|
||||
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
|
||||
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
|
||||
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
|
||||
zend_hash_destroy(&PHPDBG_G(seek));
|
||||
zend_hash_destroy(&PHPDBG_G(registered));
|
||||
|
||||
|
@ -514,6 +527,11 @@ const opt_struct OPTIONS[] = { /* {{{ */
|
|||
{'O', 1, "opline log"},
|
||||
{'r', 0, "run"},
|
||||
{'E', 0, "step-through-eval"},
|
||||
{'S', 1, "sapi-name"},
|
||||
#ifndef _WIN32
|
||||
{'l', 1, "listen"},
|
||||
{'a', 1, "address-or-any"},
|
||||
#endif
|
||||
{'-', 0, NULL}
|
||||
}; /* }}} */
|
||||
|
||||
|
@ -572,11 +590,160 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */
|
|||
}
|
||||
} /* }}} */
|
||||
|
||||
#ifndef _WIN32
|
||||
int phpdbg_open_socket(const char *interface, short port) /* {{{ */
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
switch (fd) {
|
||||
case -1:
|
||||
return -1;
|
||||
|
||||
default: {
|
||||
int reuse = 1;
|
||||
|
||||
switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) {
|
||||
case -1:
|
||||
close(fd);
|
||||
return -2;
|
||||
|
||||
default: {
|
||||
struct sockaddr_in address;
|
||||
|
||||
memset(&address, 0, sizeof(address));
|
||||
|
||||
address.sin_port = htons(port);
|
||||
address.sin_family = AF_INET;
|
||||
|
||||
if ((*interface == '*')) {
|
||||
address.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
} else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
|
||||
switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
|
||||
case -1:
|
||||
close(fd);
|
||||
return -4;
|
||||
|
||||
default: {
|
||||
listen(fd, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
} /* }}} */
|
||||
|
||||
static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
|
||||
{
|
||||
if ((*socket)[0] >= 0) {
|
||||
shutdown(
|
||||
(*socket)[0], SHUT_RDWR);
|
||||
close((*socket)[0]);
|
||||
}
|
||||
|
||||
if (streams[0]) {
|
||||
fclose(streams[0]);
|
||||
}
|
||||
|
||||
if ((*socket)[1] >= 0) {
|
||||
shutdown(
|
||||
(*socket)[1], SHUT_RDWR);
|
||||
close((*socket)[1]);
|
||||
}
|
||||
|
||||
if (streams[1]) {
|
||||
fclose(streams[1]);
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
/* don't inline this, want to debug it easily, will inline when done */
|
||||
|
||||
int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */
|
||||
{
|
||||
if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) {
|
||||
((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]);
|
||||
((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]);
|
||||
}
|
||||
|
||||
streams[0] = NULL;
|
||||
streams[1] = NULL;
|
||||
|
||||
if ((*listen)[0] < 0 || (*listen)[1] < 0) {
|
||||
if ((*listen)[0] < 0) {
|
||||
phpdbg_rlog(stderr,
|
||||
"console failed to initialize (stdin) on %s:%d", address, port[0]);
|
||||
}
|
||||
|
||||
if ((*listen)[1] < 0) {
|
||||
phpdbg_rlog(stderr,
|
||||
"console failed to initialize (stdout) on %s:%d", address, port[1]);
|
||||
}
|
||||
|
||||
if ((*listen)[0] >= 0) {
|
||||
close((*listen)[0]);
|
||||
}
|
||||
|
||||
if ((*listen)[1] >= 0) {
|
||||
close((*listen)[1]);
|
||||
}
|
||||
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
phpdbg_close_sockets(socket, streams);
|
||||
|
||||
phpdbg_rlog(stderr,
|
||||
"accepting connections on %s:%d/%d", address, port[0], port[1]);
|
||||
{
|
||||
struct sockaddr_in address;
|
||||
socklen_t size = sizeof(address);
|
||||
char buffer[20] = {0};
|
||||
|
||||
{
|
||||
memset(&address, 0, size);
|
||||
(*socket)[0] = accept(
|
||||
(*listen)[0], (struct sockaddr *) &address, &size);
|
||||
inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
|
||||
|
||||
phpdbg_rlog(stderr, "connection (stdin) from %s", buffer);
|
||||
}
|
||||
|
||||
{
|
||||
memset(&address, 0, size);
|
||||
(*socket)[1] = accept(
|
||||
(*listen)[1], (struct sockaddr *) &address, &size);
|
||||
inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer));
|
||||
|
||||
phpdbg_rlog(stderr, "connection (stdout) from %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
dup2((*socket)[0], fileno(stdin));
|
||||
dup2((*socket)[1], fileno(stdout));
|
||||
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
streams[0] = fdopen((*socket)[0], "r");
|
||||
streams[1] = fdopen((*socket)[1], "w");
|
||||
|
||||
return SUCCESS;
|
||||
} /* }}} */
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) /* {{{ */
|
||||
{
|
||||
sapi_module_struct *phpdbg = &phpdbg_sapi_module;
|
||||
char *sapi_name;
|
||||
char *ini_entries;
|
||||
int ini_entries_len;
|
||||
char **zend_extensions = NULL;
|
||||
zend_ulong zend_extensions_len = 0L;
|
||||
zend_bool ini_ignore;
|
||||
char *ini_override;
|
||||
char *exec;
|
||||
|
@ -590,14 +757,34 @@ int main(int argc, char **argv) /* {{{ */
|
|||
char *php_optarg;
|
||||
int php_optind, opt, show_banner = 1;
|
||||
long cleaning = 0;
|
||||
zend_bool remote = 0;
|
||||
int run = 0;
|
||||
int step = 0;
|
||||
char *bp_tmp_file;
|
||||
#ifndef _WIN32
|
||||
char *address;
|
||||
int listen[2];
|
||||
int server[2];
|
||||
int socket[2];
|
||||
FILE* streams[2] = {NULL, NULL};
|
||||
#endif
|
||||
|
||||
#ifdef ZTS
|
||||
void ***tsrm_ls;
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
address = strdup("127.0.0.1");
|
||||
socket[0] = -1;
|
||||
socket[1] = -1;
|
||||
listen[0] = -1;
|
||||
listen[1] = -1;
|
||||
server[0] = -1;
|
||||
server[1] = -1;
|
||||
streams[0] = NULL;
|
||||
streams[1] = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
_fmode = _O_BINARY; /* sets default for file streams to binary */
|
||||
setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
|
||||
|
@ -623,6 +810,8 @@ phpdbg_main:
|
|||
ini_entries_len = 0;
|
||||
ini_ignore = 0;
|
||||
ini_override = NULL;
|
||||
zend_extensions = NULL;
|
||||
zend_extensions_len = 0L;
|
||||
exec = NULL;
|
||||
exec_len = 0;
|
||||
init_file = NULL;
|
||||
|
@ -636,6 +825,8 @@ phpdbg_main:
|
|||
opt = 0;
|
||||
run = 0;
|
||||
step = 0;
|
||||
sapi_name = NULL;
|
||||
|
||||
|
||||
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
|
||||
switch (opt) {
|
||||
|
@ -680,8 +871,13 @@ phpdbg_main:
|
|||
ini_entries_len += len + sizeof("=1\n\0") - 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'z':
|
||||
zend_load_extension(php_optarg);
|
||||
zend_extensions_len++;
|
||||
if (zend_extensions) {
|
||||
zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len);
|
||||
} else zend_extensions = malloc(sizeof(char*) * zend_extensions_len);
|
||||
zend_extensions[zend_extensions_len-1] = strdup(php_optarg);
|
||||
break;
|
||||
|
||||
/* begin phpdbg options */
|
||||
|
@ -689,10 +885,20 @@ phpdbg_main:
|
|||
case 'e': { /* set execution context */
|
||||
exec_len = strlen(php_optarg);
|
||||
if (exec_len) {
|
||||
if (exec) {
|
||||
free(exec);
|
||||
}
|
||||
exec = strdup(php_optarg);
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'S': { /* set SAPI name */
|
||||
if (sapi_name) {
|
||||
free(sapi_name);
|
||||
}
|
||||
sapi_name = strdup(php_optarg);
|
||||
} break;
|
||||
|
||||
case 'I': { /* ignore .phpdbginit */
|
||||
init_file_default = 0;
|
||||
} break;
|
||||
|
@ -730,8 +936,49 @@ phpdbg_main:
|
|||
case 'q': /* hide banner */
|
||||
show_banner = 0;
|
||||
break;
|
||||
|
||||
#ifndef _WIN32
|
||||
/* if you pass a listen port, we will accept input on listen port */
|
||||
/* and write output to listen port * 2 */
|
||||
|
||||
case 'l': { /* set listen ports */
|
||||
if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
|
||||
if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
|
||||
/* default to hardcoded ports */
|
||||
listen[0] = 4000;
|
||||
listen[1] = 8000;
|
||||
} else {
|
||||
listen[1] = (listen[0] * 2);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'a': { /* set bind address */
|
||||
free(address);
|
||||
if (!php_optarg) {
|
||||
address = strdup("*");
|
||||
} else address = strdup(php_optarg);
|
||||
} break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* setup remote server if necessary */
|
||||
if (!cleaning &&
|
||||
(listen[0] > 0 && listen[1] > 0)) {
|
||||
if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) {
|
||||
remote = 0;
|
||||
exit(0);
|
||||
}
|
||||
/* set remote flag to stop service shutting down upon quit */
|
||||
remote = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sapi_name) {
|
||||
phpdbg->name = sapi_name;
|
||||
}
|
||||
|
||||
phpdbg->ini_defaults = phpdbg_ini_defaults;
|
||||
phpdbg->phpinfo_as_text = 1;
|
||||
|
@ -754,6 +1001,28 @@ phpdbg_main:
|
|||
}
|
||||
ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
|
||||
|
||||
if (zend_extensions_len) {
|
||||
zend_ulong zend_extension = 0L;
|
||||
|
||||
while (zend_extension < zend_extensions_len) {
|
||||
const char *ze = zend_extensions[zend_extension];
|
||||
size_t ze_len = strlen(ze);
|
||||
|
||||
ini_entries = realloc(
|
||||
ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
|
||||
memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
|
||||
ini_entries_len += (sizeof("zend_extension=")-1);
|
||||
memcpy(&ini_entries[ini_entries_len], ze, ze_len);
|
||||
ini_entries_len += ze_len;
|
||||
memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1));
|
||||
|
||||
free(zend_extensions[zend_extension]);
|
||||
zend_extension++;
|
||||
}
|
||||
|
||||
free(zend_extensions);
|
||||
}
|
||||
|
||||
phpdbg->ini_entries = ini_entries;
|
||||
|
||||
if (phpdbg->startup(phpdbg) == SUCCESS) {
|
||||
|
@ -761,18 +1030,43 @@ phpdbg_main:
|
|||
|
||||
zend_activate(TSRMLS_C);
|
||||
|
||||
/* do not install sigint handlers for remote consoles */
|
||||
/* sending SIGINT then provides a decent way of shutting down the server */
|
||||
#ifdef ZEND_SIGNALS
|
||||
# ifndef _WIN32
|
||||
if (listen[0] < 0) {
|
||||
# endif
|
||||
zend_try {
|
||||
zend_signal_activate(TSRMLS_C);
|
||||
zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
|
||||
} zend_end_try();
|
||||
# ifndef _WIN32
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
# ifndef _WIN32
|
||||
if (listen[0] < 0) {
|
||||
# endif
|
||||
signal(SIGINT, phpdbg_sigint_handler);
|
||||
#ifndef _WIN32
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PG(modules_activated) = 0;
|
||||
|
||||
/* set up basic io here */
|
||||
/* set flags from command line */
|
||||
PHPDBG_G(flags) = flags;
|
||||
|
||||
#ifndef _WIN32
|
||||
/* setup io here */
|
||||
if (streams[0] && streams[1]) {
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
#endif
|
||||
|
||||
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
|
||||
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
|
||||
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
|
||||
|
@ -794,9 +1088,6 @@ phpdbg_main:
|
|||
free(oplog_file);
|
||||
}
|
||||
|
||||
/* set flags from command line */
|
||||
PHPDBG_G(flags) = flags;
|
||||
|
||||
/* set default colors */
|
||||
phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC);
|
||||
phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC);
|
||||
|
@ -846,6 +1137,7 @@ phpdbg_main:
|
|||
}
|
||||
}
|
||||
|
||||
phpdbg_interact:
|
||||
/* phpdbg main() */
|
||||
do {
|
||||
zend_try {
|
||||
|
@ -860,14 +1152,40 @@ phpdbg_main:
|
|||
} else {
|
||||
cleaning = 0;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
/* remote client disconnected */
|
||||
if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
|
||||
|
||||
/* renegociate connections */
|
||||
phpdbg_open_sockets(
|
||||
address, listen, &server, &socket, streams);
|
||||
|
||||
/* set streams */
|
||||
if (streams[0] && streams[1]) {
|
||||
PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING;
|
||||
}
|
||||
|
||||
/* this must be forced */
|
||||
CG(unclean_shutdown) = 0;
|
||||
}
|
||||
#endif
|
||||
if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
|
||||
goto phpdbg_out;
|
||||
}
|
||||
} zend_end_try();
|
||||
} while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
|
||||
|
||||
/* this must be forced */
|
||||
CG(unclean_shutdown) = 0;
|
||||
|
||||
phpdbg_out:
|
||||
#ifndef _WIN32
|
||||
if (PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED) {
|
||||
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
|
||||
goto phpdbg_interact;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ini_entries) {
|
||||
free(ini_entries);
|
||||
}
|
||||
|
@ -901,7 +1219,7 @@ phpdbg_out:
|
|||
sapi_shutdown();
|
||||
}
|
||||
|
||||
if (cleaning) {
|
||||
if (cleaning || remote) {
|
||||
goto phpdbg_main;
|
||||
}
|
||||
|
||||
|
@ -910,6 +1228,16 @@ phpdbg_out:
|
|||
/* tsrm_shutdown(); */
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
if (address) {
|
||||
free(address);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sapi_name) {
|
||||
free(sapi_name);
|
||||
}
|
||||
|
||||
free(bp_tmp_file);
|
||||
|
||||
return 0;
|
||||
|
|
16
phpdbg.h
|
@ -79,6 +79,10 @@
|
|||
#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
|
||||
|
@ -89,7 +93,8 @@
|
|||
#define PHPDBG_BREAK_FUNCTION_OPLINE 6
|
||||
#define PHPDBG_BREAK_METHOD_OPLINE 7
|
||||
#define PHPDBG_BREAK_FILE_OPLINE 8
|
||||
#define PHPDBG_BREAK_TABLES 9 /* }}} */
|
||||
#define PHPDBG_BREAK_MAP 9
|
||||
#define PHPDBG_BREAK_TABLES 10 /* }}} */
|
||||
|
||||
/* {{{ flags */
|
||||
#define PHPDBG_HAS_FILE_BP (1<<1)
|
||||
|
@ -103,6 +108,10 @@
|
|||
#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9)
|
||||
#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)
|
||||
|
||||
/*
|
||||
END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
|
||||
*/
|
||||
|
||||
#define PHPDBG_IN_COND_BP (1<<8)
|
||||
#define PHPDBG_IN_EVAL (1<<9)
|
||||
|
||||
|
@ -123,6 +132,8 @@
|
|||
#define PHPDBG_IS_SIGNALED (1<<21)
|
||||
#define PHPDBG_IS_INTERACTIVE (1<<22)
|
||||
#define PHPDBG_IS_BP_ENABLED (1<<23)
|
||||
#define PHPDBG_IS_REMOTE (1<<24)
|
||||
#define PHPDBG_IS_DISCONNECTED (1<<25)
|
||||
|
||||
#ifndef _WIN32
|
||||
# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED)
|
||||
|
@ -135,7 +146,7 @@
|
|||
#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
|
||||
#define PHPDBG_URL "http://phpdbg.com"
|
||||
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
|
||||
#define PHPDBG_VERSION "0.2.0-dev"
|
||||
#define PHPDBG_VERSION "0.3.0-dev"
|
||||
#define PHPDBG_INIT_FILENAME ".phpdbginit"
|
||||
/* }}} */
|
||||
|
||||
|
@ -157,7 +168,6 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
|
|||
zend_op_array *ops; /* op_array */
|
||||
zval *retval; /* return value */
|
||||
int bp_count; /* breakpoint count */
|
||||
int del_bp_num; /* breakpoint to delete */
|
||||
int vmret; /* return from last opcode handler execution */
|
||||
|
||||
FILE *oplog; /* opline log */
|
||||
|
|
122
phpdbg.init.d
Executable file
|
@ -0,0 +1,122 @@
|
|||
################################################################
|
||||
# File: /etc/init.d/phpdbg #
|
||||
# Author: krakjoe #
|
||||
# Purpose: Daemonize phpdbg automatically on boot #
|
||||
# chkconfig: 2345 07 09 #
|
||||
# description:Starts, stops and restarts phpdbg daemon #
|
||||
################################################################
|
||||
LOCKFILE=/var/lock/subsys/phpdbg
|
||||
PIDFILE=/var/run/phpdbg.pid
|
||||
STDIN=4000
|
||||
STDOUT=8000
|
||||
################################################################
|
||||
# Either set path to phpdbg here or rely on phpdbg in ENV/PATH #
|
||||
################################################################
|
||||
if [ "x${PHPDBG}" == "x" ]; then
|
||||
PHPDBG=$(which phpdbg 2>/dev/null)
|
||||
fi
|
||||
################################################################
|
||||
# Options to pass to phpdbg upon boot #
|
||||
################################################################
|
||||
OPTIONS=
|
||||
LOGFILE=/var/log/phpdbg.log
|
||||
################################################################
|
||||
# STOP EDITING STOP EDITING STOP EDITING STOP EDITING #
|
||||
################################################################
|
||||
. /etc/rc.d/init.d/functions
|
||||
RETVAL=1
|
||||
################################################################
|
||||
insanity()
|
||||
{
|
||||
if [ "x${PHPDBG}" == "x" ]; then
|
||||
PHPDBG=$(which phpdbg 2>>/dev/null)
|
||||
if [ $? != 0 ]; then
|
||||
echo -n $"Fatal: cannot find phpdbg ${PHPDBG}"
|
||||
echo_failure
|
||||
echo
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if [ ! -x ${PHPDBG} ]; then
|
||||
echo -n $"Fatal: cannot execute phpdbg ${PHPDBG}"
|
||||
echo_failure
|
||||
echo
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
insanity
|
||||
|
||||
if [ $? -eq 1 ]; then
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
echo -n $"Starting: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
|
||||
nohup ${PHPDBG} -l${STDIN}/${STDOUT} ${OPTIONS} 2>>${LOGFILE} 1>/dev/null </dev/null &
|
||||
PID=$!
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
echo $PID > $PIDFILE
|
||||
echo_success
|
||||
else
|
||||
echo_failure
|
||||
fi
|
||||
echo
|
||||
[ $RETVAL = 0 ] && touch ${LOCKFILE}
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
insanity
|
||||
|
||||
if [ $? -eq 1 ]; then
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
if [ -f ${LOCKFILE} ] && [ -f ${PIDFILE} ]
|
||||
then
|
||||
echo -n $"Stopping: phpdbg ${OPTIONS} on ${STDIN}/${STDOUT} "
|
||||
kill -s TERM $(cat $PIDFILE)
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
echo_success
|
||||
else
|
||||
echo_failure
|
||||
fi
|
||||
echo
|
||||
[ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE}
|
||||
else
|
||||
echo -n $"Error: phpdbg not running"
|
||||
echo_failure
|
||||
echo
|
||||
[ $RETVAL = 1 ]
|
||||
fi
|
||||
return $RETVAL
|
||||
}
|
||||
##################################################################
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status $PHPDBG
|
||||
;;
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 start|stop|restart|status"
|
||||
;;
|
||||
esac
|
||||
###################################################################
|
||||
exit $RETVAL
|
1237
phpdbg_bp.c
97
phpdbg_bp.h
|
@ -23,102 +23,123 @@
|
|||
/* {{{ */
|
||||
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 {
|
||||
const char *filename;
|
||||
phpdbg_breakbase(filename);
|
||||
long line;
|
||||
int id;
|
||||
} phpdbg_breakfile_t;
|
||||
|
||||
/**
|
||||
* Breakpoint symbol-based representation
|
||||
*/
|
||||
typedef struct _phpdbg_breaksymbol_t {
|
||||
const char *symbol;
|
||||
int id;
|
||||
phpdbg_breakbase(symbol);
|
||||
} phpdbg_breaksymbol_t;
|
||||
|
||||
/**
|
||||
* Breakpoint method based representation
|
||||
*/
|
||||
typedef struct _phpdbg_breakmethod_t {
|
||||
const char *class_name;
|
||||
phpdbg_breakbase(class_name);
|
||||
size_t class_len;
|
||||
const char *func_name;
|
||||
size_t func_len;
|
||||
int id;
|
||||
} 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;
|
||||
const char *func_name;
|
||||
size_t func_len;
|
||||
int opline;
|
||||
int id;
|
||||
zend_ulong opline_num;
|
||||
zend_ulong opline;
|
||||
} phpdbg_breakopline_t;
|
||||
|
||||
/**
|
||||
* Breakpoint opline based representation
|
||||
*/
|
||||
typedef struct _phpdbg_breakline_t {
|
||||
const char *name;
|
||||
phpdbg_breakbase(name);
|
||||
zend_ulong opline;
|
||||
zend_uchar type;
|
||||
int id;
|
||||
phpdbg_breakopline_t *base;
|
||||
} phpdbg_breakline_t;
|
||||
|
||||
/**
|
||||
* Breakpoint opcode based representation
|
||||
*/
|
||||
typedef struct _phpdbg_breakop_t {
|
||||
phpdbg_breakbase(name);
|
||||
zend_ulong hash;
|
||||
const char *name;
|
||||
int id;
|
||||
} 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;
|
||||
zval code;
|
||||
zend_op_array *ops;
|
||||
int id;
|
||||
} 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);
|
||||
PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC); /* }}} */
|
||||
|
||||
PHPDBG_API void phpdbg_set_breakpoint_file(const char*, long TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_symbol(const char*, size_t TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_method(const char*, const char* TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_opcode(const char*, size_t TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, int opline TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int opline TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, int opline TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_set_breakpoint_expression(const char*, size_t 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); /* }}} */
|
||||
|
||||
int phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC);
|
||||
int phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC);
|
||||
int phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC);
|
||||
int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
|
||||
int phpdbg_find_breakpoint_opcode(zend_uchar TSRMLS_DC);
|
||||
int phpdbg_find_conditional_breakpoint(TSRMLS_D);
|
||||
int phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC);
|
||||
/* {{{ Breakpoint Detection API */
|
||||
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
|
||||
|
||||
PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC);
|
||||
PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D);
|
||||
/* {{{ 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); /* }}} */
|
||||
|
||||
PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC);
|
||||
/* {{{ 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 */
|
||||
|
|
|
@ -90,6 +90,13 @@ PHPDBG_BREAK(on) /* {{{ */
|
|||
return SUCCESS;
|
||||
} /* }}} */
|
||||
|
||||
PHPDBG_BREAK(at) /* {{{ */
|
||||
{
|
||||
phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
|
||||
|
||||
return SUCCESS;
|
||||
} /* }}} */
|
||||
|
||||
PHPDBG_BREAK(lineno) /* {{{ */
|
||||
{
|
||||
switch (param->type) {
|
||||
|
|
|
@ -29,12 +29,13 @@
|
|||
* 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(func);
|
||||
PHPDBG_BREAK(del);
|
||||
|
||||
/**
|
||||
|
@ -42,12 +43,13 @@ PHPDBG_BREAK(del);
|
|||
*/
|
||||
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 expression", 'o', break_on, 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(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
|
||||
PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
|
||||
PHPDBG_END_COMMAND
|
||||
};
|
||||
|
|
170
phpdbg_cmd.c
|
@ -143,6 +143,120 @@ PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */
|
|||
|
||||
} /* }}} */
|
||||
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
|
||||
case ADDR_PARAM:
|
||||
hash += param->addr;
|
||||
break;
|
||||
|
||||
case NUMERIC_PARAM:
|
||||
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 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]) {
|
||||
return (memcmp(
|
||||
l->file.name, r->file.name, lengths[0]) == SUCCESS);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
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;
|
||||
|
@ -242,35 +356,48 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
|||
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_write(phpdbg_get_prompt(TSRMLS_C)) ||
|
||||
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_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
|
||||
zend_bailout();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd = buf;
|
||||
#else
|
||||
cmd = readline(phpdbg_get_prompt(TSRMLS_C));
|
||||
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_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*) emalloc(sizeof(phpdbg_input_t));
|
||||
buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -296,7 +423,8 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
if (!buffered && cmd) {
|
||||
if (!buffered && cmd &&
|
||||
!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
|
||||
free(cmd);
|
||||
}
|
||||
#endif
|
||||
|
@ -307,6 +435,21 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
|||
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) {
|
||||
|
@ -314,17 +457,8 @@ PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
|
|||
efree((*input)->string);
|
||||
}
|
||||
|
||||
if ((*input)->argc > 0) {
|
||||
int arg;
|
||||
for (arg=0; arg<(*input)->argc; arg++) {
|
||||
phpdbg_destroy_input(
|
||||
&(*input)->argv[arg] TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
|
||||
if ((*input)->argv) {
|
||||
efree((*input)->argv);
|
||||
}
|
||||
phpdbg_destroy_argv(
|
||||
(*input)->argv, (*input)->argc TSRMLS_CC);
|
||||
|
||||
efree(*input);
|
||||
}
|
||||
|
@ -398,7 +532,7 @@ PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *in
|
|||
int arg;
|
||||
for (arg=1; arg<input->argc; arg++) {
|
||||
phpdbg_debug(
|
||||
"\t#%d: [%s=%d]",
|
||||
"\t#%d: [%s=%zu]",
|
||||
arg,
|
||||
input->argv[arg]->string,
|
||||
input->argv[arg]->length);
|
||||
|
|
12
phpdbg_cmd.h
|
@ -114,16 +114,24 @@ typedef struct {
|
|||
* Input Management
|
||||
*/
|
||||
PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
|
||||
PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc 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-1) == SUCCESS)
|
||||
(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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -258,6 +258,13 @@ PHPDBG_HELP(break) /* {{{ */
|
|||
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");
|
||||
|
@ -576,7 +583,19 @@ PHPDBG_HELP(options) /* {{{ */
|
|||
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_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;
|
||||
} /* }}} */
|
||||
|
|
|
@ -76,6 +76,11 @@ PHPDBG_INFO(vars) /* {{{ */
|
|||
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);
|
||||
|
||||
|
@ -223,6 +228,26 @@ PHPDBG_INFO(literal) /* {{{ */
|
|||
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(
|
||||
|
|
|
@ -31,15 +31,17 @@ 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, "lists included files", 'F', info_files, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(classes, "lists loaded classes", 'c', info_classes, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(funcs, "lists loaded classes", 'f', info_funcs, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(error, "show the last error", 'e', info_error, 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
|
||||
};
|
||||
|
||||
|
|
|
@ -573,6 +573,9 @@ PHPDBG_COMMAND(run) /* {{{ */
|
|||
zend_hash_clean(
|
||||
&PHPDBG_G(seek));
|
||||
|
||||
/* reset hit counters */
|
||||
phpdbg_reset_breakpoints(TSRMLS_C);
|
||||
|
||||
zend_try {
|
||||
php_output_activate(TSRMLS_C);
|
||||
PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
|
||||
|
@ -766,10 +769,6 @@ PHPDBG_COMMAND(print) /* {{{ */
|
|||
phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
|
||||
phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
|
||||
phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
|
||||
phpdbg_writeln(
|
||||
"Memory\t\t%.3f/%.3f (kB)",
|
||||
(float) (zend_memory_usage(1 TSRMLS_CC)/1024),
|
||||
(float) (zend_memory_usage(0 TSRMLS_CC)/1024));
|
||||
|
||||
phpdbg_writeln(SEPARATE);
|
||||
} break;
|
||||
|
@ -957,6 +956,9 @@ PHPDBG_COMMAND(clear) /* {{{ */
|
|||
phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]));
|
||||
phpdbg_writeln("Methods\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]));
|
||||
phpdbg_writeln("Oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]));
|
||||
phpdbg_writeln("File oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]));
|
||||
phpdbg_writeln("Function oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]));
|
||||
phpdbg_writeln("Method oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]));
|
||||
phpdbg_writeln("Conditionals\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]));
|
||||
|
||||
phpdbg_clear_breakpoints(TSRMLS_C);
|
||||
|
@ -1400,10 +1402,17 @@ zend_vm_enter:
|
|||
phpdbg_print_opline_ex(
|
||||
execute_data, &vars, 0 TSRMLS_CC);
|
||||
|
||||
if (PHPDBG_G(flags) & PHPDBG_BP_MASK
|
||||
&& phpdbg_find_breakpoint(execute_data TSRMLS_CC) == SUCCESS) {
|
||||
/* search for breakpoints */
|
||||
{
|
||||
phpdbg_breakbase_t *brake;
|
||||
|
||||
if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) &&
|
||||
(brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) {
|
||||
phpdbg_hit_breakpoint(
|
||||
brake, 1 TSRMLS_CC);
|
||||
DO_INTERACTIVE();
|
||||
}
|
||||
}
|
||||
|
||||
if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) {
|
||||
DO_INTERACTIVE();
|
||||
|
|
56
phpdbg_set.c
|
@ -21,6 +21,7 @@
|
|||
#include "phpdbg_cmd.h"
|
||||
#include "phpdbg_set.h"
|
||||
#include "phpdbg_utils.h"
|
||||
#include "phpdbg_bp.h"
|
||||
|
||||
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
||||
|
||||
|
@ -51,13 +52,33 @@ PHPDBG_SET(break) /* {{{ */
|
|||
|
||||
case STR_PARAM:
|
||||
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
|
||||
PHPDBG_G(flags) |= PHPDBG_IS_BP_ENABLED;
|
||||
phpdbg_enable_breakpoints(TSRMLS_C);
|
||||
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
|
||||
PHPDBG_G(flags) &= ~PHPDBG_IS_BP_ENABLED;
|
||||
phpdbg_disable_breakpoints(TSRMLS_C);
|
||||
}
|
||||
break;
|
||||
|
||||
phpdbg_default_switch_case();
|
||||
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;
|
||||
|
@ -71,6 +92,7 @@ PHPDBG_SET(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(
|
||||
|
@ -105,6 +127,34 @@ usage:
|
|||
}
|
||||
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) /* {{{ */
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
PHPDBG_SET(prompt);
|
||||
#ifndef _WIN32
|
||||
PHPDBG_SET(color);
|
||||
PHPDBG_SET(colors);
|
||||
#endif
|
||||
PHPDBG_SET(oplog);
|
||||
PHPDBG_SET(break);
|
||||
|
@ -35,9 +36,10 @@ 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 <on|off>", 'b', set_break, NULL, 0),
|
||||
PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
|
||||
PHPDBG_END_COMMAND
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "zend.h"
|
||||
#include "php.h"
|
||||
#include "spprintf.h"
|
||||
|
@ -100,6 +101,9 @@ PHPDBG_API int phpdbg_is_class_method(const char *str, size_t len, char **class,
|
|||
{
|
||||
char *sep = NULL;
|
||||
|
||||
if (strstr(str, "#") != NULL)
|
||||
return 0;
|
||||
|
||||
if (strstr(str, " ") != NULL)
|
||||
return 0;
|
||||
|
||||
|
@ -242,6 +246,33 @@ PHPDBG_API int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *format, ..
|
|||
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;
|
||||
|
|
|
@ -32,7 +32,7 @@ PHPDBG_API char *phpdbg_resolve_path(const char* TSRMLS_DC);
|
|||
PHPDBG_API char *phpdbg_trim(const char*, size_t, size_t*);
|
||||
|
||||
/**
|
||||
* Error/notice/formatting helper
|
||||
* Error/notice/formatting helpers
|
||||
*/
|
||||
enum {
|
||||
P_ERROR = 1,
|
||||
|
@ -48,6 +48,8 @@ PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUT
|
|||
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__)
|
||||
|
|
BIN
tutorials/break-at.png
Normal file
After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 202 KiB |
BIN
tutorials/java-example.png
Normal file
After Width: | Height: | Size: 43 KiB |
73
tutorials/java/build.xml
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="phpdbg-ui" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project phpdbg-ui.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar: JAR building
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="phpdbg-ui-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
</project>
|
32
tutorials/java/dist/README.TXT
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
========================
|
||||
BUILD OUTPUT DESCRIPTION
|
||||
========================
|
||||
|
||||
When you build an Java application project that has a main class, the IDE
|
||||
automatically copies all of the JAR
|
||||
files on the projects classpath to your projects dist/lib folder. The IDE
|
||||
also adds each of the JAR files to the Class-Path element in the application
|
||||
JAR files manifest file (MANIFEST.MF).
|
||||
|
||||
To run the project from the command line, go to the dist folder and
|
||||
type the following:
|
||||
|
||||
java -jar "phpdbg-ui.jar"
|
||||
|
||||
To distribute this project, zip up the dist folder (including the lib folder)
|
||||
and distribute the ZIP file.
|
||||
|
||||
Notes:
|
||||
|
||||
* If two JAR files on the project classpath have the same name, only the first
|
||||
JAR file is copied to the lib folder.
|
||||
* Only JAR files are copied to the lib folder.
|
||||
If the classpath contains other types of files or folders, these files (folders)
|
||||
are not copied.
|
||||
* If a library on the projects classpath also has a Class-Path element
|
||||
specified in the manifest,the content of the Class-Path element has to be on
|
||||
the projects runtime path.
|
||||
* To set a main class in a standard Java project, right-click the project node
|
||||
in the Projects window and choose Properties. Then click Run and enter the
|
||||
class name in the Main Class field. Alternatively, you can manually type the
|
||||
class name in the manifest Main-Class element.
|
BIN
tutorials/java/dist/phpdbg-ui.jar
vendored
Normal file
3
tutorials/java/manifest.mf
Normal file
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
X-COMMENT: Main-Class will be added automatically by build
|
||||
|
1407
tutorials/java/nbproject/build-impl.xml
Normal file
8
tutorials/java/nbproject/genfiles.properties
Normal file
|
@ -0,0 +1,8 @@
|
|||
build.xml.data.CRC32=ed67686a
|
||||
build.xml.script.CRC32=8c03bdb5
|
||||
build.xml.stylesheet.CRC32=8064a381@1.68.1.46
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=ed67686a
|
||||
nbproject/build-impl.xml.script.CRC32=ea01fe36
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=5a01deb7@1.68.1.46
|
0
tutorials/java/nbproject/private/config.properties
Normal file
6
tutorials/java/nbproject/private/private.properties
Normal file
|
@ -0,0 +1,6 @@
|
|||
compile.on.save=true
|
||||
do.depend=false
|
||||
do.jar=true
|
||||
javac.debug=true
|
||||
javadoc.preview=true
|
||||
user.properties.file=/home/joe/.netbeans/7.4/build.properties
|
76
tutorials/java/nbproject/project.properties
Normal file
|
@ -0,0 +1,76 @@
|
|||
annotation.processing.enabled=true
|
||||
annotation.processing.enabled.in.editor=false
|
||||
annotation.processing.processors.list=
|
||||
annotation.processing.run.all.processors=true
|
||||
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
|
||||
application.title=phpdbg-ui
|
||||
application.vendor=joe
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form
|
||||
# This directory is removed when the project is cleaned:
|
||||
build.dir=build
|
||||
build.generated.dir=${build.dir}/generated
|
||||
build.generated.sources.dir=${build.dir}/generated-sources
|
||||
# Only compile against the classpath explicitly listed here:
|
||||
build.sysclasspath=ignore
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
build.test.results.dir=${build.dir}/test/results
|
||||
# Uncomment to specify the preferred debugger connection transport:
|
||||
#debug.transport=dt_socket
|
||||
debug.classpath=\
|
||||
${run.classpath}
|
||||
debug.test.classpath=\
|
||||
${run.test.classpath}
|
||||
# Files in build.classes.dir which should be excluded from distribution jar
|
||||
dist.archive.excludes=
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
dist.jar=${dist.dir}/phpdbg-ui.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=
|
||||
includes=**
|
||||
jar.compress=false
|
||||
javac.classpath=\
|
||||
${libs.eclipselink.classpath}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
javac.processorpath=\
|
||||
${javac.classpath}
|
||||
javac.source=1.7
|
||||
javac.target=1.7
|
||||
javac.test.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
javac.test.processorpath=\
|
||||
${javac.test.classpath}
|
||||
javadoc.additionalparam=
|
||||
javadoc.author=false
|
||||
javadoc.encoding=${source.encoding}
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
main.class=phpdbg.ui.JConsole
|
||||
manifest.file=manifest.mf
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
mkdist.disabled=false
|
||||
platform.active=default_platform
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
# Space-separated list of JVM arguments used when running the project.
|
||||
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
|
||||
# To set system properties for unit tests define test-sys-prop.name=value:
|
||||
run.jvmargs=
|
||||
run.test.classpath=\
|
||||
${javac.test.classpath}:\
|
||||
${build.test.classes.dir}
|
||||
source.encoding=UTF-8
|
||||
src.dir=src
|
||||
test.src.dir=test
|
15
tutorials/java/nbproject/project.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>phpdbg-ui</name>
|
||||
<source-roots>
|
||||
<root id="src.dir"/>
|
||||
</source-roots>
|
||||
<test-roots>
|
||||
<root id="test.src.dir"/>
|
||||
</test-roots>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
49
tutorials/java/src/phpdbg/ui/CommandHistory.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
package phpdbg.ui;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implement a simple history list for command input
|
||||
* @author krakjoe
|
||||
*/
|
||||
public class CommandHistory extends ArrayList<String> {
|
||||
private Integer position = new Integer(0);
|
||||
|
||||
public CommandHistory() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override public boolean add(String text) {
|
||||
String last = last();
|
||||
if (text != null) {
|
||||
if (last == null || !last.equals(text)) {
|
||||
if (super.add(text)) {
|
||||
position = size();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String last() {
|
||||
if (position >= 1) {
|
||||
position--;
|
||||
return get(position);
|
||||
} else return new String();
|
||||
}
|
||||
|
||||
public String next() {
|
||||
if (position+1 < size()) {
|
||||
position++;
|
||||
return get(position);
|
||||
} else return new String();
|
||||
}
|
||||
}
|
157
tutorials/java/src/phpdbg/ui/DebugSocket.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
package phpdbg.ui;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import static java.nio.channels.SelectionKey.OP_READ;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import phpdbg.ui.JConsole.MessageType;
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manage input and output data
|
||||
* @author krakjoe
|
||||
*/
|
||||
public class DebugSocket implements Runnable {
|
||||
private final String host;
|
||||
private final Integer port;
|
||||
private final Boolean reader;
|
||||
private final JConsole main;
|
||||
private final Thread thread;
|
||||
|
||||
private volatile Boolean quit;
|
||||
private volatile Boolean started;
|
||||
|
||||
public DebugSocket(final String host, final Integer port, final JConsole main, Boolean reader) throws IOException {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.main = main;
|
||||
this.reader = reader;
|
||||
this.quit = false;
|
||||
this.started = false;
|
||||
this.thread = new Thread(this);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
synchronized(this) {
|
||||
if (!started) {
|
||||
quit = false;
|
||||
started = true;
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void quit() {
|
||||
synchronized(this) {
|
||||
quit = true;
|
||||
started = false;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
try {
|
||||
synchronized(main) {
|
||||
if (!main.isConnected()) {
|
||||
main.setConnected(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
/* The reader thread will wait() until there is input */
|
||||
Socket socket = new Socket(this.host, this.port);
|
||||
String command;
|
||||
OutputStream output = socket.getOutputStream();
|
||||
|
||||
do {
|
||||
synchronized(this) {
|
||||
wait();
|
||||
|
||||
if (!quit) {
|
||||
command = main.getInputField().getText();
|
||||
/* send command to stdin socket */
|
||||
if (command != null) {
|
||||
if (main.isEchoing()) {
|
||||
main.getOutputField()
|
||||
.appendANSI(
|
||||
String.format("remote> %s\n", command));
|
||||
}
|
||||
output.write(
|
||||
command.getBytes());
|
||||
output.write("\n".getBytes());
|
||||
output.flush();
|
||||
}
|
||||
main.getInputField().setText(null);
|
||||
}
|
||||
}
|
||||
} while (!quit);
|
||||
|
||||
socket.close();
|
||||
} else {
|
||||
/*
|
||||
* The writer thread will use non-blocking i/o consuming
|
||||
* resources only when there is data to read
|
||||
*/
|
||||
Selector selector = Selector.open();
|
||||
SocketChannel channel = SocketChannel.open();
|
||||
|
||||
channel.connect(
|
||||
new InetSocketAddress(this.host, this.port));
|
||||
channel.configureBlocking(false);
|
||||
channel.register(selector, OP_READ);
|
||||
|
||||
while (!quit) {
|
||||
selector.select();
|
||||
|
||||
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
if (!quit) {
|
||||
SocketChannel ready = (SocketChannel) (iter.next().channel());
|
||||
ByteBuffer bytes = ByteBuffer.allocate(128);
|
||||
|
||||
if (ready != null) {
|
||||
if (ready.read(bytes) != -1) {
|
||||
bytes.flip();
|
||||
|
||||
main.getOutputField()
|
||||
.appendANSI(new String(bytes.array()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
channel.close();
|
||||
}
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
if (!quit) {
|
||||
main.messageBox(ex.getMessage(), MessageType.ERROR);
|
||||
}
|
||||
} finally {
|
||||
synchronized(main) {
|
||||
if (main.isConnected()) {
|
||||
main.setConnected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
225
tutorials/java/src/phpdbg/ui/JConsole.form
Normal file
|
@ -0,0 +1,225 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Container class="javax.swing.JPopupMenu" name="stdoutPopupMenu">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignAbsoluteLayout">
|
||||
<Property name="useNullLayout" type="boolean" value="true"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<MenuItem class="javax.swing.JMenuItem" name="resetStdout">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Clear"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetStdoutActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Menu class="java.awt.PopupMenu" name="systrayMenu">
|
||||
<Properties>
|
||||
<Property name="label" type="java.lang.String" value="phpdbg"/>
|
||||
<Property name="name" type="java.lang.String" value=""/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="systrayMenuActionPerformed"/>
|
||||
</Events>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="10"/>
|
||||
</AuxValues>
|
||||
<SubComponents>
|
||||
<MenuItem class="java.awt.MenuItem" name="systrayExitMenuItem">
|
||||
<Properties>
|
||||
<Property name="label" type="java.lang.String" value="Exit"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="systrayExitMenuItemActionPerformed"/>
|
||||
</Events>
|
||||
</MenuItem>
|
||||
</SubComponents>
|
||||
</Menu>
|
||||
</NonVisualComponents>
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" value="phpdbg jui"/>
|
||||
<Property name="iconImage" type="java.awt.Image" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="default"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="outputScrollPane" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="hostnameLabel" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="commandLabel" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="input" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="echoCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="host" pref="345" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="stdinCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="stdinPort" min="-2" pref="60" max="-2" attributes="0"/>
|
||||
<EmptySpace type="separate" max="-2" attributes="0"/>
|
||||
<Component id="stdoutCheckBox" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="stdoutPort" min="-2" pref="60" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="openButton" min="-2" pref="100" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="outputScrollPane" pref="403" max="32767" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="input" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="echoCheckBox" alignment="3" max="-2" attributes="0"/>
|
||||
<Component id="commandLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="stdoutPort" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="stdinCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="stdoutCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="stdinPort" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="hostnameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="host" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="openButton" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextField" name="host">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="127.0.0.1"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Set the hostname, or IPv4 address of the machine running the phpdbg remote console server"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="stdoutPort">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="8000"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="By default, stdin * 2"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="stdinCheckBox">
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" value="stdin:"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Set the port for stdin, or uncheck to disable stdin"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="stdoutCheckBox">
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="text" type="java.lang.String" value="stdout:"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Set the port for stdout, or unset to disable stdout"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="openButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Connect"/>
|
||||
<Property name="actionCommand" type="java.lang.String" value="open"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="stdinPort">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="4000"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="The listen port passed to phpdbg (-l option)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="hostnameLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Hostname:"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="input">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Enter phpdbg commands here !"/>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="inputKeyReleased"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="outputScrollPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="phpdbg.ui.JTerminalPane" name="output">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="DialogInput" size="12" style="0"/>
|
||||
</Property>
|
||||
<Property name="componentPopupMenu" type="javax.swing.JPopupMenu" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="stdoutPopupMenu"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JCheckBox" name="echoCheckBox">
|
||||
<Properties>
|
||||
<Property name="selected" type="boolean" value="true"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Check to echo sent commands in output"/>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
<Property name="horizontalAlignment" type="int" value="2"/>
|
||||
<Property name="label" type="java.lang.String" value="echo"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="commandLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Command:"/>
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
471
tutorials/java/src/phpdbg/ui/JConsole.java
Normal file
|
@ -0,0 +1,471 @@
|
|||
package phpdbg.ui;
|
||||
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Image;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.TrayIcon;
|
||||
import static java.awt.event.KeyEvent.VK_DOWN;
|
||||
import static java.awt.event.KeyEvent.VK_ENTER;
|
||||
import static java.awt.event.KeyEvent.VK_UP;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.UnsupportedLookAndFeelException;
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author krakjoe
|
||||
*/
|
||||
public class JConsole extends javax.swing.JDialog {
|
||||
/**
|
||||
* Creates user interface
|
||||
* @param parent
|
||||
* @param modal
|
||||
*/
|
||||
public JConsole(java.awt.Frame parent, boolean modal) {
|
||||
super(parent, modal);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
stdoutPopupMenu = new javax.swing.JPopupMenu();
|
||||
resetStdout = new javax.swing.JMenuItem();
|
||||
systrayMenu = new java.awt.PopupMenu();
|
||||
systrayExitMenuItem = new java.awt.MenuItem();
|
||||
host = new javax.swing.JTextField();
|
||||
stdoutPort = new javax.swing.JTextField();
|
||||
stdinCheckBox = new javax.swing.JCheckBox();
|
||||
stdoutCheckBox = new javax.swing.JCheckBox();
|
||||
openButton = new javax.swing.JButton();
|
||||
stdinPort = new javax.swing.JTextField();
|
||||
hostnameLabel = new javax.swing.JLabel();
|
||||
input = new javax.swing.JTextField();
|
||||
outputScrollPane = new javax.swing.JScrollPane();
|
||||
output = new phpdbg.ui.JTerminalPane();
|
||||
echoCheckBox = new javax.swing.JCheckBox();
|
||||
commandLabel = new javax.swing.JLabel();
|
||||
|
||||
resetStdout.setText("Clear");
|
||||
resetStdout.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
resetStdoutActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
stdoutPopupMenu.add(resetStdout);
|
||||
|
||||
systrayMenu.setLabel("phpdbg");
|
||||
systrayMenu.setName("");
|
||||
systrayMenu.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
systrayMenuActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
systrayExitMenuItem.setLabel("Exit");
|
||||
systrayExitMenuItem.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
systrayExitMenuItemActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
systrayMenu.add(systrayExitMenuItem);
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("phpdbg jui");
|
||||
|
||||
host.setText("127.0.0.1");
|
||||
host.setToolTipText("Set the hostname, or IPv4 address of the machine running the phpdbg remote console server");
|
||||
|
||||
stdoutPort.setText("8000");
|
||||
stdoutPort.setToolTipText("By default, stdin * 2");
|
||||
|
||||
stdinCheckBox.setSelected(true);
|
||||
stdinCheckBox.setText("stdin:");
|
||||
stdinCheckBox.setToolTipText("Set the port for stdin, or uncheck to disable stdin");
|
||||
|
||||
stdoutCheckBox.setSelected(true);
|
||||
stdoutCheckBox.setText("stdout:");
|
||||
stdoutCheckBox.setToolTipText("Set the port for stdout, or unset to disable stdout");
|
||||
|
||||
openButton.setText("Connect");
|
||||
openButton.setActionCommand("open");
|
||||
openButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
openButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
stdinPort.setText("4000");
|
||||
stdinPort.setToolTipText("The listen port passed to phpdbg (-l option)");
|
||||
|
||||
hostnameLabel.setText("Hostname:");
|
||||
|
||||
input.setToolTipText("Enter phpdbg commands here !");
|
||||
input.setEnabled(false);
|
||||
input.addKeyListener(new java.awt.event.KeyAdapter() {
|
||||
public void keyReleased(java.awt.event.KeyEvent evt) {
|
||||
inputKeyReleased(evt);
|
||||
}
|
||||
});
|
||||
|
||||
output.setFont(new java.awt.Font("DialogInput", 0, 12)); // NOI18N
|
||||
output.setComponentPopupMenu(stdoutPopupMenu);
|
||||
outputScrollPane.setViewportView(output);
|
||||
|
||||
echoCheckBox.setSelected(true);
|
||||
echoCheckBox.setToolTipText("Check to echo sent commands in output");
|
||||
echoCheckBox.setEnabled(false);
|
||||
echoCheckBox.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||
echoCheckBox.setLabel("echo");
|
||||
|
||||
commandLabel.setText("Command:");
|
||||
commandLabel.setEnabled(false);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(outputScrollPane)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(hostnameLabel)
|
||||
.addComponent(commandLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(input)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(echoCheckBox))
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(host, javax.swing.GroupLayout.DEFAULT_SIZE, 345, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(stdinCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(stdinPort, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(18, 18, 18)
|
||||
.addComponent(stdoutCheckBox)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(stdoutPort, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(openButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(outputScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 403, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(input, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(echoCheckBox)
|
||||
.addComponent(commandLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(stdoutPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(stdinCheckBox)
|
||||
.addComponent(stdoutCheckBox)
|
||||
.addComponent(stdinPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(hostnameLabel)
|
||||
.addComponent(host, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(openButton))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void inputKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_inputKeyReleased
|
||||
switch (evt.getKeyCode()) {
|
||||
case VK_ENTER: {
|
||||
if (in != null) {
|
||||
history.add(input.getText());
|
||||
synchronized(in) {
|
||||
in.notifyAll();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case VK_UP: {
|
||||
String last = history.last();
|
||||
if (last.length() > 0) {
|
||||
input.setText(last);
|
||||
}
|
||||
} break;
|
||||
|
||||
case VK_DOWN: {
|
||||
String next = history.next();
|
||||
if (next.length() > 0) {
|
||||
input.setText(next);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}//GEN-LAST:event_inputKeyReleased
|
||||
|
||||
private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
|
||||
try {
|
||||
if (!connected) {
|
||||
connect();
|
||||
} else {
|
||||
disconnect();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
messageBox(ex.getMessage(), MessageType.ERROR);
|
||||
}
|
||||
}//GEN-LAST:event_openButtonActionPerformed
|
||||
|
||||
private void resetStdoutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetStdoutActionPerformed
|
||||
// TODO add your handling code here:
|
||||
output.setText(null);
|
||||
}//GEN-LAST:event_resetStdoutActionPerformed
|
||||
|
||||
private void systrayExitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_systrayExitMenuItemActionPerformed
|
||||
// TODO add your handling code here:
|
||||
dialog.disconnect();
|
||||
System.exit(0);
|
||||
}//GEN-LAST:event_systrayExitMenuItemActionPerformed
|
||||
|
||||
private void systrayMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_systrayMenuActionPerformed
|
||||
// TODO add your handling code here:
|
||||
}//GEN-LAST:event_systrayMenuActionPerformed
|
||||
|
||||
private void disconnect() {
|
||||
if (in != null) {
|
||||
in.quit();
|
||||
}
|
||||
if (out != null) {
|
||||
out.quit();
|
||||
}
|
||||
}
|
||||
|
||||
private void connect() throws IOException {
|
||||
Integer ports[] = new Integer[2];
|
||||
String address = getHost();
|
||||
|
||||
if (address != null) {
|
||||
ports[0] = stdinCheckBox.isSelected() ? getStdinPort() : -1;
|
||||
ports[1] = stdoutCheckBox.isSelected() ? getStdoutPort() : -1;
|
||||
|
||||
if (ports[0] != 0 && ports[1] != 0) {
|
||||
if (stdinCheckBox.isSelected()) {
|
||||
if (ports[0] > 0) {
|
||||
in = new DebugSocket(
|
||||
address, ports[0], this, true);
|
||||
in.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (stdoutCheckBox.isSelected()) {
|
||||
if (ports[1] > 0) {
|
||||
out = new DebugSocket(
|
||||
address, ports[1], this, false);
|
||||
out.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
public Boolean isEchoing() {
|
||||
return echoCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public void setConnected(Boolean isConnected) {
|
||||
synchronized(this) {
|
||||
if (isConnected) {
|
||||
connected = true;
|
||||
openButton.setText("Disconnect");
|
||||
host.setEnabled(false);
|
||||
stdinPort.setEnabled(false);
|
||||
stdinCheckBox.setEnabled(false);
|
||||
if (stdinCheckBox.isSelected()) {
|
||||
input.setEnabled(true);
|
||||
commandLabel.setEnabled(true);
|
||||
echoCheckBox.setEnabled(true);
|
||||
} else {
|
||||
input.setEnabled(false);
|
||||
commandLabel.setEnabled(false);
|
||||
echoCheckBox.setEnabled(false);
|
||||
}
|
||||
stdoutPort.setEnabled(false);
|
||||
stdoutCheckBox.setEnabled(false);
|
||||
hostnameLabel.setEnabled(false);
|
||||
commandLabel.setEnabled(true);
|
||||
} else {
|
||||
connected = false;
|
||||
openButton.setText("Connect");
|
||||
host.setEnabled(true);
|
||||
stdinPort.setEnabled(true);
|
||||
input.setEnabled(false);
|
||||
commandLabel.setEnabled(false);
|
||||
echoCheckBox.setEnabled(false);
|
||||
stdinCheckBox.setEnabled(true);
|
||||
stdoutPort.setEnabled(true);
|
||||
stdoutCheckBox.setEnabled(true);
|
||||
hostnameLabel.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JTextField getInputField() { return input; }
|
||||
public JTerminalPane getOutputField() { return output; }
|
||||
|
||||
public String getHost() {
|
||||
String address = host.getText();
|
||||
if (address != null && address.length() > 0) {
|
||||
return address;
|
||||
} else {
|
||||
messageBox("Invalid hostname provided !", MessageType.WARN);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getStdinPort() {
|
||||
try {
|
||||
return Integer.parseInt(stdinPort.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
messageBox("Invalid stdin port provided !", MessageType.WARN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Integer getStdoutPort() {
|
||||
try {
|
||||
return Integer.parseInt(stdoutPort.getText());
|
||||
} catch (NumberFormatException ex) {
|
||||
messageBox("Invalid stdout port provided !", MessageType.WARN);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public JScrollPane getOutputScrollPane() {
|
||||
return outputScrollPane;
|
||||
}
|
||||
|
||||
public synchronized void messageBox(String message) {
|
||||
messageBox(message, MessageType.INFO);
|
||||
}
|
||||
|
||||
public synchronized void messageBox(String message, MessageType type) {
|
||||
JOptionPane.showMessageDialog(this, message, "phpdbg jui", type.getType());
|
||||
}
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(final String args[]) {
|
||||
|
||||
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
|
||||
if (info.getName().equals("Nimbus")) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(
|
||||
info.getClassName());
|
||||
break;
|
||||
} catch (ClassNotFoundException |
|
||||
InstantiationException |
|
||||
IllegalAccessException |
|
||||
UnsupportedLookAndFeelException ex) {
|
||||
Logger.getLogger(JConsole.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and display the dialog */
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
@Override public void run() {
|
||||
dialog = new JConsole(new javax.swing.JFrame(), true);
|
||||
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(java.awt.event.WindowEvent e) {
|
||||
dialog.disconnect();
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
try {
|
||||
if (tray == null) {
|
||||
Image trayIconImage = ImageIO.read(
|
||||
JConsole.class.getResource("logo-dbg.png"));
|
||||
dialog.setIconImage(trayIconImage);
|
||||
|
||||
tray = new TrayIcon(trayIconImage);
|
||||
tray.setPopupMenu(systrayMenu);
|
||||
tray.setToolTip("phpdbg - The Interactive PHP Debugger");
|
||||
|
||||
SystemTray.getSystemTray().add(tray);
|
||||
}
|
||||
} catch ( AWTException | IOException ex) {
|
||||
dialog.messageBox(ex.getMessage(), MessageType.ERROR);
|
||||
}
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static DebugSocket in;
|
||||
private static DebugSocket out;
|
||||
private static JConsole dialog;
|
||||
private static Boolean connected = false;
|
||||
private static CommandHistory history = new CommandHistory();
|
||||
private static TrayIcon tray;
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel commandLabel;
|
||||
private javax.swing.JCheckBox echoCheckBox;
|
||||
private javax.swing.JTextField host;
|
||||
private javax.swing.JLabel hostnameLabel;
|
||||
private javax.swing.JTextField input;
|
||||
private javax.swing.JButton openButton;
|
||||
private phpdbg.ui.JTerminalPane output;
|
||||
private javax.swing.JScrollPane outputScrollPane;
|
||||
private javax.swing.JMenuItem resetStdout;
|
||||
private javax.swing.JCheckBox stdinCheckBox;
|
||||
private javax.swing.JTextField stdinPort;
|
||||
private javax.swing.JCheckBox stdoutCheckBox;
|
||||
private javax.swing.JPopupMenu stdoutPopupMenu;
|
||||
private javax.swing.JTextField stdoutPort;
|
||||
private java.awt.MenuItem systrayExitMenuItem;
|
||||
private static java.awt.PopupMenu systrayMenu;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
public enum MessageType {
|
||||
INFO (JOptionPane.INFORMATION_MESSAGE),
|
||||
WARN (JOptionPane.WARNING_MESSAGE),
|
||||
ERROR (JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
private final Integer type;
|
||||
private MessageType(Integer type) {
|
||||
this.type = type;
|
||||
}
|
||||
public Integer getType() { return this.type; }
|
||||
public Boolean equals(Integer other) { return this.type.equals(other); }
|
||||
public Boolean equals(MessageType other) { return this.type.equals(other.getType()); }
|
||||
}
|
||||
}
|
182
tutorials/java/src/phpdbg/ui/JTerminalPane.java
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
package phpdbg.ui;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author krakjoe
|
||||
*/
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class JTerminalPane extends JTextPane {
|
||||
private static class JTerminalColor {
|
||||
private final Color color;
|
||||
private final Boolean bold;
|
||||
private final Boolean underline;
|
||||
|
||||
public JTerminalColor(Float h, Float s, Float b, Boolean bold, Boolean underline) {
|
||||
this.color = Color.getHSBColor(h, s, b);
|
||||
this.bold = bold;
|
||||
this.underline = underline;
|
||||
}
|
||||
|
||||
public JTerminalColor(Float h, Float s, Float b, Boolean bold) {
|
||||
this(h, s, b, bold, false);
|
||||
}
|
||||
|
||||
public JTerminalColor(Float h, Float s, Float b) {
|
||||
this(h, s, b, false, false);
|
||||
}
|
||||
|
||||
public Boolean isUnderlined() { return this.underline; }
|
||||
public Boolean isBold() { return this.bold; }
|
||||
public Color getColor() { return this.color; }
|
||||
}
|
||||
|
||||
private static class JTerminalColors extends HashMap<String, JTerminalColor> {
|
||||
public JTerminalColors() {
|
||||
put("\u001B[0;30m", new JTerminalColor(0.000f, 0.000f, 0.502f));
|
||||
put("\u001B[0;31m", new JTerminalColor(0.000f, 1.000f, 1.000f));
|
||||
put("\u001B[0;32m", new JTerminalColor(0.333f, 1.000f, 1.000f));
|
||||
put("\u001B[0;33m", new JTerminalColor(0.167f, 1.000f, 1.000f));
|
||||
put("\u001B[0;34m", new JTerminalColor(0.667f, 1.000f, 1.000f));
|
||||
put("\u001B[0;35m", new JTerminalColor(0.833f, 1.000f, 1.000f));
|
||||
put("\u001B[0;36m", new JTerminalColor(0.500f, 1.000f, 1.000f));
|
||||
put("\u001B[0;37m", new JTerminalColor(0.000f, 0.000f, 1.000f));
|
||||
put("\u001B[0;64m", new JTerminalColor(0.000f, 0.000f, 1.000f));
|
||||
put("\u001B[1;30m", new JTerminalColor(0.000f, 0.000f, 0.502f, true));
|
||||
put("\u001B[1;31m", new JTerminalColor(0.000f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;32m", new JTerminalColor(0.333f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;33m", new JTerminalColor(0.167f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;34m", new JTerminalColor(0.667f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;35m", new JTerminalColor(0.833f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;36m", new JTerminalColor(0.500f, 1.000f, 1.000f, true));
|
||||
put("\u001B[1;37m", new JTerminalColor(0.000f, 0.000f, 1.000f, true));
|
||||
put("\u001B[1;64m", new JTerminalColor(0.000f, 0.000f, 1.000f, true));
|
||||
put("\u001B[4;30m", new JTerminalColor(0.000f, 0.000f, 0.502f, false, true));
|
||||
put("\u001B[4;31m", new JTerminalColor(0.000f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;32m", new JTerminalColor(0.333f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;33m", new JTerminalColor(0.167f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;34m", new JTerminalColor(0.667f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;35m", new JTerminalColor(0.833f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;36m", new JTerminalColor(0.500f, 1.000f, 1.000f, false, true));
|
||||
put("\u001B[4;37m", new JTerminalColor(0.000f, 0.000f, 1.000f, false, true));
|
||||
put("\u001B[4;64m", new JTerminalColor(0.000f, 0.000f, 1.000f, false, true));
|
||||
|
||||
put("reset", new JTerminalColor(0.000f, 0.000f, 1.000f));
|
||||
}
|
||||
|
||||
public JTerminalColor find(String ANSIColor) {
|
||||
if (containsKey(ANSIColor)) {
|
||||
return get(ANSIColor);
|
||||
} else return get("reset");
|
||||
}
|
||||
}
|
||||
|
||||
public JTerminalPane() {
|
||||
super();
|
||||
setOpaque(false);
|
||||
setBackground(new Color(0, 0, 0, 0));
|
||||
colorCurrent = colors.find("reset");
|
||||
}
|
||||
|
||||
@Override public void paintComponent(Graphics g) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
try {
|
||||
Image image = ImageIO.read(
|
||||
JTerminalPane.class.getResource("logo-small.png"));
|
||||
|
||||
g.drawImage(
|
||||
image,
|
||||
getWidth() - image.getWidth(this) - 10,
|
||||
getHeight() - image.getHeight(this) - 10,
|
||||
image.getWidth(this), image.getHeight(this), this);
|
||||
|
||||
} catch (IOException | NullPointerException | IllegalArgumentException ex) {}
|
||||
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
private void append(JTerminalColor c, String s) {
|
||||
StyleContext sc = StyleContext.getDefaultStyleContext();
|
||||
AttributeSet aset = sc.addAttribute(
|
||||
SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c.getColor());
|
||||
|
||||
aset = sc.addAttribute(aset, StyleConstants.Underline, c.isUnderlined());
|
||||
aset = sc.addAttribute(aset, StyleConstants.Bold, c.isBold());
|
||||
|
||||
setCharacterAttributes(aset, false);
|
||||
replaceSelection(s);
|
||||
setCaretPosition(getDocument().getLength());
|
||||
}
|
||||
|
||||
public synchronized void appendANSI(String s) {
|
||||
int aPos = 0;
|
||||
int aIndex;
|
||||
int mIndex;
|
||||
String tmpString;
|
||||
boolean stillSearching;
|
||||
|
||||
String addString = remaining + s;
|
||||
|
||||
remaining = "";
|
||||
|
||||
if (addString.length() > 0) {
|
||||
aIndex = addString.indexOf("\u001B");
|
||||
if (aIndex == -1) {
|
||||
append(colorCurrent,addString);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aIndex > 0) {
|
||||
tmpString = addString.substring(0,aIndex);
|
||||
append(colorCurrent, tmpString);
|
||||
aPos = aIndex;
|
||||
}
|
||||
|
||||
stillSearching = true;
|
||||
while (stillSearching) {
|
||||
mIndex = addString.indexOf("m",aPos);
|
||||
if (mIndex < 0) {
|
||||
remaining = addString.substring(aPos,addString.length());
|
||||
stillSearching = false;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
tmpString = addString.substring(aPos,mIndex+1);
|
||||
colorCurrent = colors.find(tmpString);
|
||||
}
|
||||
aPos = mIndex + 1;
|
||||
|
||||
aIndex = addString.indexOf("\u001B", aPos);
|
||||
|
||||
if (aIndex == -1) {
|
||||
tmpString = addString.substring(aPos,addString.length());
|
||||
append(colorCurrent, tmpString);
|
||||
stillSearching = false;
|
||||
continue;
|
||||
}
|
||||
tmpString = addString.substring(aPos,aIndex);
|
||||
aPos = aIndex;
|
||||
append(colorCurrent, tmpString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JTerminalColors colors = new JTerminalColors();
|
||||
private JTerminalColor colorCurrent;
|
||||
private String remaining = "";
|
||||
}
|
BIN
tutorials/java/src/phpdbg/ui/logo-dbg.png
Normal file
After Width: | Height: | Size: 248 B |
BIN
tutorials/java/src/phpdbg/ui/logo-small.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 180 KiB |
66
tutorials/remote.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
Remote Debugging
|
||||
================
|
||||
*Using phpdbg across the network*
|
||||
|
||||
It is sometimes useful to execute some code in the environment that exhibits some bugs; while there is a pretty good chance of being able to mock the environment that exhibited the bug on another machine, it is sometimes impossible or impractical.
|
||||
|
||||
Remote debugging allows you to start an instance of phpdbg on any machine, and connect to it from any machine, bundled is a remote client implemented in Java:
|
||||
|
||||

|
||||
|
||||
|
||||
Starting phpdbg in Remote Console Mode
|
||||
======================================
|
||||
*Starting the server ...*
|
||||
|
||||
Simply open a terminal on the target machine and issue the command
|
||||
|
||||
```
|
||||
./phpdbg -l4000
|
||||
```
|
||||
|
||||
This will initialize phpdbg in Remote Console Mode, using port 4000 for stdin and port 8000 for stdout.
|
||||
|
||||
To stop the service, send *SIGINT* to the phpdbg process, this will force it to bailout, as gracefully as it ever does.
|
||||
|
||||
Specific Ports
|
||||
--------------
|
||||
|
||||
To listen on specific ports, the -l flag will accept a pair of ports in the following way:
|
||||
|
||||
```
|
||||
./phpdbg -l4000/8000
|
||||
```
|
||||
|
||||
Specific Interfaces
|
||||
-------------------
|
||||
|
||||
To bind to a specific interface, or all interfaces use the -a flag:
|
||||
|
||||
```
|
||||
./phpdbg -l4000 -a192.168.0.3
|
||||
```
|
||||
|
||||
Will bind the Remote Console to the interface with the specified address, while:
|
||||
|
||||
```
|
||||
./phpdbg -l4000 -a
|
||||
```
|
||||
|
||||
Will bind the Remote Console to all available addresses.
|
||||
|
||||
Starting phpdbg-jui Anywhere
|
||||
============================
|
||||
*Java is everywhere, so is phpdbg ...*
|
||||
|
||||
A JRE is needed for the bundled remote client, given any operating system with a working installation of Java:
|
||||
|
||||
```
|
||||
java -jar /path/to/phpdbg-ui.jar
|
||||
```
|
||||
|
||||
Will initialize the bundled client, simply configure the settings accordingly and press *Connect*
|
||||
|
||||
If disconnection is normal, the environment persists - another team member can pick up the session where it is left off.
|
||||
|
||||
Debugging remotely is no different to debugging locally, all of the same functionality is provided remotely.
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 338 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |