Adapted opline_num breakpoints to master branch
4
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
||||||
.libs/
|
.libs/
|
||||||
phpdbg
|
./phpdbg
|
||||||
*.lo
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
|
build
|
||||||
|
|
13
Changelog.md
|
@ -1,15 +1,24 @@
|
||||||
ChangeLog for phpdbg
|
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
|
1. Added "break delete <id>" command
|
||||||
2. Added "break opcode <opcode>" command
|
2. Added "break opcode <opcode>" command
|
||||||
3. Added "set" command - control prompt and console colors
|
3. Added "set" command - control prompt and console colors
|
||||||
4. .phpdbginit now searched in (additional) ini dirs
|
4. .phpdbginit now searched in (additional) ini dirs
|
||||||
5. Added source command - load additional .phpdbginit script during session
|
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
|
Version 0.1.0 2013-11-23
|
||||||
------------------------
|
------------------------
|
||||||
|
|
|
@ -17,6 +17,7 @@ Features
|
||||||
- PHP Configuration File Support
|
- PHP Configuration File Support
|
||||||
- JIT Super Globals - Set Your Own !!
|
- JIT Super Globals - Set Your Own !!
|
||||||
- Optional readline Support - Comfortable Terminal Operation
|
- Optional readline Support - Comfortable Terminal Operation
|
||||||
|
- Remote Debugging Support - Bundled Java GUI
|
||||||
- Easy Operation - See Help :)
|
- Easy Operation - See Help :)
|
||||||
|
|
||||||
Planned
|
Planned
|
||||||
|
@ -63,6 +64,9 @@ The following switches change the default behaviour of phpdbg:
|
||||||
- -q do not print banner on startup
|
- -q do not print banner on startup
|
||||||
- -r jump straight to run
|
- -r jump straight to run
|
||||||
- -E enable step through eval()
|
- -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*
|
*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, [ ])
|
AC_DEFINE(PHPDBG_DEBUG, 0, [ ])
|
||||||
fi
|
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_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)
|
PHP_SUBST(PHP_PHPDBG_CFLAGS)
|
||||||
|
|
368
phpdbg.c
|
@ -27,6 +27,16 @@
|
||||||
#include "phpdbg_utils.h"
|
#include "phpdbg_utils.h"
|
||||||
#include "phpdbg_set.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);
|
ZEND_DECLARE_MODULE_GLOBALS(phpdbg);
|
||||||
|
|
||||||
#if PHP_VERSION_ID >= 50500
|
#if PHP_VERSION_ID >= 50500
|
||||||
|
@ -84,7 +94,7 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
|
||||||
|
|
||||||
static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */
|
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) /* {{{ */
|
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);
|
brake->ops TSRMLS_CC);
|
||||||
efree(brake->ops);
|
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();
|
TSRMLS_FETCH();
|
||||||
|
|
||||||
|
@ -127,7 +137,7 @@ static void php_phpdbg_destroy_registered(void *data)
|
||||||
|
|
||||||
destroy_zend_function(
|
destroy_zend_function(
|
||||||
function TSRMLS_CC);
|
function TSRMLS_CC);
|
||||||
}
|
} /* }}} */
|
||||||
|
|
||||||
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
|
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_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_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_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(seek), 8, NULL, NULL, 0);
|
||||||
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 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_OPCODE]);
|
||||||
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
|
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_COND]);
|
||||||
|
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
|
||||||
zend_hash_destroy(&PHPDBG_G(seek));
|
zend_hash_destroy(&PHPDBG_G(seek));
|
||||||
zend_hash_destroy(&PHPDBG_G(registered));
|
zend_hash_destroy(&PHPDBG_G(registered));
|
||||||
|
|
||||||
|
@ -514,6 +527,11 @@ const opt_struct OPTIONS[] = { /* {{{ */
|
||||||
{'O', 1, "opline log"},
|
{'O', 1, "opline log"},
|
||||||
{'r', 0, "run"},
|
{'r', 0, "run"},
|
||||||
{'E', 0, "step-through-eval"},
|
{'E', 0, "step-through-eval"},
|
||||||
|
{'S', 1, "sapi-name"},
|
||||||
|
#ifndef _WIN32
|
||||||
|
{'l', 1, "listen"},
|
||||||
|
{'a', 1, "address-or-any"},
|
||||||
|
#endif
|
||||||
{'-', 0, NULL}
|
{'-', 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) /* {{{ */
|
int main(int argc, char **argv) /* {{{ */
|
||||||
{
|
{
|
||||||
sapi_module_struct *phpdbg = &phpdbg_sapi_module;
|
sapi_module_struct *phpdbg = &phpdbg_sapi_module;
|
||||||
|
char *sapi_name;
|
||||||
char *ini_entries;
|
char *ini_entries;
|
||||||
int ini_entries_len;
|
int ini_entries_len;
|
||||||
|
char **zend_extensions = NULL;
|
||||||
|
zend_ulong zend_extensions_len = 0L;
|
||||||
zend_bool ini_ignore;
|
zend_bool ini_ignore;
|
||||||
char *ini_override;
|
char *ini_override;
|
||||||
char *exec;
|
char *exec;
|
||||||
|
@ -590,14 +757,34 @@ int main(int argc, char **argv) /* {{{ */
|
||||||
char *php_optarg;
|
char *php_optarg;
|
||||||
int php_optind, opt, show_banner = 1;
|
int php_optind, opt, show_banner = 1;
|
||||||
long cleaning = 0;
|
long cleaning = 0;
|
||||||
|
zend_bool remote = 0;
|
||||||
int run = 0;
|
int run = 0;
|
||||||
int step = 0;
|
int step = 0;
|
||||||
char *bp_tmp_file;
|
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
|
#ifdef ZTS
|
||||||
void ***tsrm_ls;
|
void ***tsrm_ls;
|
||||||
#endif
|
#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
|
#ifdef PHP_WIN32
|
||||||
_fmode = _O_BINARY; /* sets default for file streams to binary */
|
_fmode = _O_BINARY; /* sets default for file streams to binary */
|
||||||
setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
|
setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
|
||||||
|
@ -623,6 +810,8 @@ phpdbg_main:
|
||||||
ini_entries_len = 0;
|
ini_entries_len = 0;
|
||||||
ini_ignore = 0;
|
ini_ignore = 0;
|
||||||
ini_override = NULL;
|
ini_override = NULL;
|
||||||
|
zend_extensions = NULL;
|
||||||
|
zend_extensions_len = 0L;
|
||||||
exec = NULL;
|
exec = NULL;
|
||||||
exec_len = 0;
|
exec_len = 0;
|
||||||
init_file = NULL;
|
init_file = NULL;
|
||||||
|
@ -636,7 +825,9 @@ phpdbg_main:
|
||||||
opt = 0;
|
opt = 0;
|
||||||
run = 0;
|
run = 0;
|
||||||
step = 0;
|
step = 0;
|
||||||
|
sapi_name = NULL;
|
||||||
|
|
||||||
|
|
||||||
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
|
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'r':
|
case 'r':
|
||||||
|
@ -680,8 +871,13 @@ phpdbg_main:
|
||||||
ini_entries_len += len + sizeof("=1\n\0") - 2;
|
ini_entries_len += len + sizeof("=1\n\0") - 2;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 'z':
|
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;
|
break;
|
||||||
|
|
||||||
/* begin phpdbg options */
|
/* begin phpdbg options */
|
||||||
|
@ -689,9 +885,19 @@ phpdbg_main:
|
||||||
case 'e': { /* set execution context */
|
case 'e': { /* set execution context */
|
||||||
exec_len = strlen(php_optarg);
|
exec_len = strlen(php_optarg);
|
||||||
if (exec_len) {
|
if (exec_len) {
|
||||||
|
if (exec) {
|
||||||
|
free(exec);
|
||||||
|
}
|
||||||
exec = strdup(php_optarg);
|
exec = strdup(php_optarg);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case 'S': { /* set SAPI name */
|
||||||
|
if (sapi_name) {
|
||||||
|
free(sapi_name);
|
||||||
|
}
|
||||||
|
sapi_name = strdup(php_optarg);
|
||||||
|
} break;
|
||||||
|
|
||||||
case 'I': { /* ignore .phpdbginit */
|
case 'I': { /* ignore .phpdbginit */
|
||||||
init_file_default = 0;
|
init_file_default = 0;
|
||||||
|
@ -730,9 +936,50 @@ phpdbg_main:
|
||||||
case 'q': /* hide banner */
|
case 'q': /* hide banner */
|
||||||
show_banner = 0;
|
show_banner = 0;
|
||||||
break;
|
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->ini_defaults = phpdbg_ini_defaults;
|
||||||
phpdbg->phpinfo_as_text = 1;
|
phpdbg->phpinfo_as_text = 1;
|
||||||
phpdbg->php_ini_ignore_cwd = 1;
|
phpdbg->php_ini_ignore_cwd = 1;
|
||||||
|
@ -753,30 +1000,77 @@ phpdbg_main:
|
||||||
memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
|
memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
|
||||||
}
|
}
|
||||||
ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
|
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;
|
phpdbg->ini_entries = ini_entries;
|
||||||
|
|
||||||
if (phpdbg->startup(phpdbg) == SUCCESS) {
|
if (phpdbg->startup(phpdbg) == SUCCESS) {
|
||||||
zend_register_extension(&zend_extension_entry, NULL);
|
zend_register_extension(&zend_extension_entry, NULL);
|
||||||
|
|
||||||
zend_activate(TSRMLS_C);
|
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
|
#ifdef ZEND_SIGNALS
|
||||||
zend_try {
|
# ifndef _WIN32
|
||||||
zend_signal_activate(TSRMLS_C);
|
if (listen[0] < 0) {
|
||||||
zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
|
# endif
|
||||||
} zend_end_try();
|
zend_try {
|
||||||
|
zend_signal_activate(TSRMLS_C);
|
||||||
|
zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
|
||||||
|
} zend_end_try();
|
||||||
|
# ifndef _WIN32
|
||||||
|
}
|
||||||
|
# endif
|
||||||
#else
|
#else
|
||||||
signal(SIGINT, phpdbg_sigint_handler);
|
# ifndef _WIN32
|
||||||
|
if (listen[0] < 0) {
|
||||||
|
# endif
|
||||||
|
signal(SIGINT, phpdbg_sigint_handler);
|
||||||
|
#ifndef _WIN32
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PG(modules_activated) = 0;
|
PG(modules_activated) = 0;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
/* set up basic io here */
|
|
||||||
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
|
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
|
||||||
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
|
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
|
||||||
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
|
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
|
||||||
|
|
||||||
if (exec) { /* set execution context */
|
if (exec) { /* set execution context */
|
||||||
PHPDBG_G(exec) = phpdbg_resolve_path(
|
PHPDBG_G(exec) = phpdbg_resolve_path(
|
||||||
exec TSRMLS_CC);
|
exec TSRMLS_CC);
|
||||||
|
@ -794,9 +1088,6 @@ phpdbg_main:
|
||||||
free(oplog_file);
|
free(oplog_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set flags from command line */
|
|
||||||
PHPDBG_G(flags) = flags;
|
|
||||||
|
|
||||||
/* set default colors */
|
/* set default colors */
|
||||||
phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC);
|
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);
|
phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC);
|
||||||
|
@ -846,6 +1137,7 @@ phpdbg_main:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
phpdbg_interact:
|
||||||
/* phpdbg main() */
|
/* phpdbg main() */
|
||||||
do {
|
do {
|
||||||
zend_try {
|
zend_try {
|
||||||
|
@ -860,14 +1152,40 @@ phpdbg_main:
|
||||||
} else {
|
} else {
|
||||||
cleaning = 0;
|
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) {
|
if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) {
|
||||||
goto phpdbg_out;
|
goto phpdbg_out;
|
||||||
}
|
}
|
||||||
} zend_end_try();
|
} zend_end_try();
|
||||||
} while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
|
} while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
|
||||||
|
|
||||||
|
/* this must be forced */
|
||||||
|
CG(unclean_shutdown) = 0;
|
||||||
|
|
||||||
phpdbg_out:
|
phpdbg_out:
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED) {
|
||||||
|
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
|
||||||
|
goto phpdbg_interact;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ini_entries) {
|
if (ini_entries) {
|
||||||
free(ini_entries);
|
free(ini_entries);
|
||||||
}
|
}
|
||||||
|
@ -901,7 +1219,7 @@ phpdbg_out:
|
||||||
sapi_shutdown();
|
sapi_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cleaning) {
|
if (cleaning || remote) {
|
||||||
goto phpdbg_main;
|
goto phpdbg_main;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,6 +1227,16 @@ phpdbg_out:
|
||||||
/* bugggy */
|
/* bugggy */
|
||||||
/* tsrm_shutdown(); */
|
/* tsrm_shutdown(); */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (address) {
|
||||||
|
free(address);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (sapi_name) {
|
||||||
|
free(sapi_name);
|
||||||
|
}
|
||||||
|
|
||||||
free(bp_tmp_file);
|
free(bp_tmp_file);
|
||||||
|
|
||||||
|
|
16
phpdbg.h
|
@ -79,6 +79,10 @@
|
||||||
#define PHPDBG_FINISH 4
|
#define PHPDBG_FINISH 4
|
||||||
#define PHPDBG_LEAVE 5
|
#define PHPDBG_LEAVE 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE
|
||||||
|
*/
|
||||||
|
|
||||||
/* {{{ tables */
|
/* {{{ tables */
|
||||||
#define PHPDBG_BREAK_FILE 0
|
#define PHPDBG_BREAK_FILE 0
|
||||||
#define PHPDBG_BREAK_SYM 1
|
#define PHPDBG_BREAK_SYM 1
|
||||||
|
@ -89,7 +93,8 @@
|
||||||
#define PHPDBG_BREAK_FUNCTION_OPLINE 6
|
#define PHPDBG_BREAK_FUNCTION_OPLINE 6
|
||||||
#define PHPDBG_BREAK_METHOD_OPLINE 7
|
#define PHPDBG_BREAK_METHOD_OPLINE 7
|
||||||
#define PHPDBG_BREAK_FILE_OPLINE 8
|
#define PHPDBG_BREAK_FILE_OPLINE 8
|
||||||
#define PHPDBG_BREAK_TABLES 9 /* }}} */
|
#define PHPDBG_BREAK_MAP 9
|
||||||
|
#define PHPDBG_BREAK_TABLES 10 /* }}} */
|
||||||
|
|
||||||
/* {{{ flags */
|
/* {{{ flags */
|
||||||
#define PHPDBG_HAS_FILE_BP (1<<1)
|
#define PHPDBG_HAS_FILE_BP (1<<1)
|
||||||
|
@ -103,6 +108,10 @@
|
||||||
#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9)
|
#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)
|
#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_COND_BP (1<<8)
|
||||||
#define PHPDBG_IN_EVAL (1<<9)
|
#define PHPDBG_IN_EVAL (1<<9)
|
||||||
|
|
||||||
|
@ -123,6 +132,8 @@
|
||||||
#define PHPDBG_IS_SIGNALED (1<<21)
|
#define PHPDBG_IS_SIGNALED (1<<21)
|
||||||
#define PHPDBG_IS_INTERACTIVE (1<<22)
|
#define PHPDBG_IS_INTERACTIVE (1<<22)
|
||||||
#define PHPDBG_IS_BP_ENABLED (1<<23)
|
#define PHPDBG_IS_BP_ENABLED (1<<23)
|
||||||
|
#define PHPDBG_IS_REMOTE (1<<24)
|
||||||
|
#define PHPDBG_IS_DISCONNECTED (1<<25)
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED)
|
# 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_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
|
||||||
#define PHPDBG_URL "http://phpdbg.com"
|
#define PHPDBG_URL "http://phpdbg.com"
|
||||||
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
|
#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"
|
#define PHPDBG_INIT_FILENAME ".phpdbginit"
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
@ -157,7 +168,6 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
|
||||||
zend_op_array *ops; /* op_array */
|
zend_op_array *ops; /* op_array */
|
||||||
zval *retval; /* return value */
|
zval *retval; /* return value */
|
||||||
int bp_count; /* breakpoint count */
|
int bp_count; /* breakpoint count */
|
||||||
int del_bp_num; /* breakpoint to delete */
|
|
||||||
int vmret; /* return from last opcode handler execution */
|
int vmret; /* return from last opcode handler execution */
|
||||||
|
|
||||||
FILE *oplog; /* opline log */
|
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
|
1313
phpdbg_bp.c
101
phpdbg_bp.h
|
@ -23,102 +23,123 @@
|
||||||
/* {{{ */
|
/* {{{ */
|
||||||
typedef struct _zend_op *phpdbg_opline_ptr_t; /* }}} */
|
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
|
* Breakpoint file-based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakfile_t {
|
typedef struct _phpdbg_breakfile_t {
|
||||||
const char *filename;
|
phpdbg_breakbase(filename);
|
||||||
long line;
|
long line;
|
||||||
int id;
|
|
||||||
} phpdbg_breakfile_t;
|
} phpdbg_breakfile_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint symbol-based representation
|
* Breakpoint symbol-based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breaksymbol_t {
|
typedef struct _phpdbg_breaksymbol_t {
|
||||||
const char *symbol;
|
phpdbg_breakbase(symbol);
|
||||||
int id;
|
|
||||||
} phpdbg_breaksymbol_t;
|
} phpdbg_breaksymbol_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint method based representation
|
* Breakpoint method based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakmethod_t {
|
typedef struct _phpdbg_breakmethod_t {
|
||||||
const char *class_name;
|
phpdbg_breakbase(class_name);
|
||||||
size_t class_len;
|
size_t class_len;
|
||||||
const char *func_name;
|
const char *func_name;
|
||||||
size_t func_len;
|
size_t func_len;
|
||||||
int id;
|
|
||||||
} phpdbg_breakmethod_t;
|
} phpdbg_breakmethod_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint opline num based representation
|
* Breakpoint opline num based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakopline_t {
|
typedef struct _phpdbg_breakopline_t {
|
||||||
|
phpdbg_breakbase(func_name);
|
||||||
|
size_t func_len;
|
||||||
const char *class_name;
|
const char *class_name;
|
||||||
size_t class_len;
|
size_t class_len;
|
||||||
const char *func_name;
|
zend_ulong opline_num;
|
||||||
size_t func_len;
|
zend_ulong opline;
|
||||||
int opline;
|
|
||||||
int id;
|
|
||||||
} phpdbg_breakopline_t;
|
} phpdbg_breakopline_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint opline based representation
|
* Breakpoint opline based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakline_t {
|
typedef struct _phpdbg_breakline_t {
|
||||||
const char *name;
|
phpdbg_breakbase(name);
|
||||||
zend_ulong opline;
|
zend_ulong opline;
|
||||||
zend_uchar type;
|
phpdbg_breakopline_t *base;
|
||||||
int id;
|
|
||||||
} phpdbg_breakline_t;
|
} phpdbg_breakline_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint opcode based representation
|
* Breakpoint opcode based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakop_t {
|
typedef struct _phpdbg_breakop_t {
|
||||||
zend_ulong hash;
|
phpdbg_breakbase(name);
|
||||||
const char *name;
|
zend_ulong hash;
|
||||||
int id;
|
|
||||||
} phpdbg_breakop_t;
|
} phpdbg_breakop_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breakpoint condition based representation
|
* Breakpoint condition based representation
|
||||||
*/
|
*/
|
||||||
typedef struct _phpdbg_breakcond_t {
|
typedef struct _phpdbg_breakcond_t {
|
||||||
|
phpdbg_breakbase(code);
|
||||||
|
size_t code_len;
|
||||||
|
zend_bool paramed;
|
||||||
|
phpdbg_param_t param;
|
||||||
zend_ulong hash;
|
zend_ulong hash;
|
||||||
zval code;
|
|
||||||
zend_op_array *ops;
|
zend_op_array *ops;
|
||||||
int id;
|
|
||||||
} phpdbg_breakcond_t;
|
} phpdbg_breakcond_t;
|
||||||
|
|
||||||
|
/* {{{ Opline breaks API */
|
||||||
PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC);
|
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_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);
|
/* {{{ Breakpoint Creation API */
|
||||||
PHPDBG_API void phpdbg_set_breakpoint_symbol(const char*, size_t TSRMLS_DC);
|
PHPDBG_API void phpdbg_set_breakpoint_file(const char* filename, long lineno TSRMLS_DC);
|
||||||
PHPDBG_API void phpdbg_set_breakpoint_method(const char*, const char* 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_opcode(const char*, size_t 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_opline(zend_ulong 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_ex(phpdbg_opline_ptr_t TSRMLS_DC);
|
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline 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_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC);
|
||||||
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int 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_file_opline(const char *file, int 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_expression(const char*, size_t 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);
|
/* {{{ Breakpoint Detection API */
|
||||||
int phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC);
|
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* 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);
|
|
||||||
|
|
||||||
PHPDBG_API void phpdbg_delete_breakpoint(zend_ulong num TSRMLS_DC);
|
/* {{{ Misc Breakpoint API */
|
||||||
PHPDBG_API void phpdbg_clear_breakpoints(TSRMLS_D);
|
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_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 */
|
#endif /* PHPDBG_BP_H */
|
||||||
|
|
|
@ -48,7 +48,7 @@ PHPDBG_BREAK(method) /* {{{ */
|
||||||
|
|
||||||
phpdbg_default_switch_case();
|
phpdbg_default_switch_case();
|
||||||
}
|
}
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
} /* }}} */
|
} /* }}} */
|
||||||
|
|
||||||
|
@ -90,6 +90,13 @@ PHPDBG_BREAK(on) /* {{{ */
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
} /* }}} */
|
} /* }}} */
|
||||||
|
|
||||||
|
PHPDBG_BREAK(at) /* {{{ */
|
||||||
|
{
|
||||||
|
phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
} /* }}} */
|
||||||
|
|
||||||
PHPDBG_BREAK(lineno) /* {{{ */
|
PHPDBG_BREAK(lineno) /* {{{ */
|
||||||
{
|
{
|
||||||
switch (param->type) {
|
switch (param->type) {
|
||||||
|
|
|
@ -29,12 +29,13 @@
|
||||||
* Printer Forward Declarations
|
* Printer Forward Declarations
|
||||||
*/
|
*/
|
||||||
PHPDBG_BREAK(file);
|
PHPDBG_BREAK(file);
|
||||||
|
PHPDBG_BREAK(func);
|
||||||
PHPDBG_BREAK(method);
|
PHPDBG_BREAK(method);
|
||||||
PHPDBG_BREAK(address);
|
PHPDBG_BREAK(address);
|
||||||
|
PHPDBG_BREAK(at);
|
||||||
PHPDBG_BREAK(op);
|
PHPDBG_BREAK(op);
|
||||||
PHPDBG_BREAK(on);
|
PHPDBG_BREAK(on);
|
||||||
PHPDBG_BREAK(lineno);
|
PHPDBG_BREAK(lineno);
|
||||||
PHPDBG_BREAK(func);
|
|
||||||
PHPDBG_BREAK(del);
|
PHPDBG_BREAK(del);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,12 +43,13 @@ PHPDBG_BREAK(del);
|
||||||
*/
|
*/
|
||||||
static const phpdbg_command_t phpdbg_break_commands[] = {
|
static const phpdbg_command_t phpdbg_break_commands[] = {
|
||||||
PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
|
PHPDBG_COMMAND_D_EX(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(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(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(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(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_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
|
||||||
PHPDBG_END_COMMAND
|
PHPDBG_END_COMMAND
|
||||||
};
|
};
|
||||||
|
|
176
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) /* {{{ */
|
PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
|
@ -242,35 +356,48 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
||||||
char *cmd = NULL;
|
char *cmd = NULL;
|
||||||
|
|
||||||
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
|
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
|
||||||
if (buffered == NULL) {
|
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
|
||||||
|
(buffered == NULL)) {
|
||||||
|
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffered == NULL) {
|
||||||
#ifndef HAVE_LIBREADLINE
|
#ifndef HAVE_LIBREADLINE
|
||||||
char buf[PHPDBG_MAX_CMD];
|
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])) {
|
!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) {
|
||||||
/* the user has gone away */
|
/* the user has gone away */
|
||||||
phpdbg_error("Failed to read console !");
|
phpdbg_error("Failed to read console !");
|
||||||
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
|
PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
|
||||||
zend_bailout();
|
zend_bailout();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = buf;
|
cmd = buf;
|
||||||
#else
|
#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) {
|
if (!cmd) {
|
||||||
/* the user has gone away */
|
/* the user has gone away */
|
||||||
phpdbg_error("Failed to read console !");
|
phpdbg_error("Failed to read console !");
|
||||||
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
|
PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED);
|
||||||
zend_bailout();
|
zend_bailout();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_history(cmd);
|
if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
|
||||||
|
add_history(cmd);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} else cmd = buffered;
|
} else cmd = buffered;
|
||||||
|
|
||||||
/* allocate and sanitize buffer */
|
/* 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) {
|
if (!buffer) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -296,7 +423,8 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBREADLINE
|
#ifdef HAVE_LIBREADLINE
|
||||||
if (!buffered && cmd) {
|
if (!buffered && cmd &&
|
||||||
|
!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) {
|
||||||
free(cmd);
|
free(cmd);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -307,6 +435,21 @@ PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
|
||||||
return NULL;
|
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) /*{{{ */
|
PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
|
||||||
{
|
{
|
||||||
if (*input) {
|
if (*input) {
|
||||||
|
@ -314,17 +457,8 @@ PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
|
||||||
efree((*input)->string);
|
efree((*input)->string);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*input)->argc > 0) {
|
phpdbg_destroy_argv(
|
||||||
int arg;
|
(*input)->argv, (*input)->argc TSRMLS_CC);
|
||||||
for (arg=0; arg<(*input)->argc; arg++) {
|
|
||||||
phpdbg_destroy_input(
|
|
||||||
&(*input)->argv[arg] TSRMLS_CC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*input)->argv) {
|
|
||||||
efree((*input)->argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
efree(*input);
|
efree(*input);
|
||||||
}
|
}
|
||||||
|
@ -398,7 +532,7 @@ PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *in
|
||||||
int arg;
|
int arg;
|
||||||
for (arg=1; arg<input->argc; arg++) {
|
for (arg=1; arg<input->argc; arg++) {
|
||||||
phpdbg_debug(
|
phpdbg_debug(
|
||||||
"\t#%d: [%s=%d]",
|
"\t#%d: [%s=%zu]",
|
||||||
arg,
|
arg,
|
||||||
input->argv[arg]->string,
|
input->argv[arg]->string,
|
||||||
input->argv[arg]->length);
|
input->argv[arg]->length);
|
||||||
|
|
12
phpdbg_cmd.h
|
@ -114,16 +114,24 @@ typedef struct {
|
||||||
* Input Management
|
* Input Management
|
||||||
*/
|
*/
|
||||||
PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
|
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);
|
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) \
|
#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
|
* Parameter Management
|
||||||
*/
|
*/
|
||||||
PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char*, size_t, phpdbg_param_t* TSRMLS_DC);
|
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_clear_param(phpdbg_param_t* TSRMLS_DC);
|
||||||
|
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t*, phpdbg_param_t* TSRMLS_DC);
|
||||||
|
PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_param_t * TSRMLS_DC);
|
||||||
|
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
|
||||||
PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
|
PHPDBG_API 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("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
|
||||||
phpdbg_writeln("\tWill break when the condition evaluates to true");
|
phpdbg_writeln("\tWill break when the condition evaluates to true");
|
||||||
phpdbg_writeln(EMPTY);
|
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%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
|
||||||
phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
|
phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
|
||||||
phpdbg_writeln("\tWill break on every occurence of the opcode provided");
|
phpdbg_writeln("\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(" -O\t-Omy.oplog\t\tSets oplog output file");
|
||||||
phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
|
phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
|
||||||
phpdbg_writeln(" -E\tN/A\t\t\tEnable step through eval, careful !");
|
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");
|
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();
|
phpdbg_help_footer();
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
} /* }}} */
|
} /* }}} */
|
||||||
|
|
|
@ -76,6 +76,11 @@ PHPDBG_INFO(vars) /* {{{ */
|
||||||
char *var;
|
char *var;
|
||||||
zval **data;
|
zval **data;
|
||||||
|
|
||||||
|
if (!EG(active_op_array)) {
|
||||||
|
phpdbg_error("No active op array!");
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
if (!EG(active_symbol_table)) {
|
if (!EG(active_symbol_table)) {
|
||||||
zend_rebuild_symbol_table(TSRMLS_C);
|
zend_rebuild_symbol_table(TSRMLS_C);
|
||||||
|
|
||||||
|
@ -223,6 +228,26 @@ PHPDBG_INFO(literal) /* {{{ */
|
||||||
return SUCCESS;
|
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) /* {{{ */
|
static inline void phpdbg_print_class_name(zend_class_entry **ce TSRMLS_DC) /* {{{ */
|
||||||
{
|
{
|
||||||
phpdbg_write(
|
phpdbg_write(
|
||||||
|
|
|
@ -31,15 +31,17 @@ PHPDBG_INFO(funcs);
|
||||||
PHPDBG_INFO(error);
|
PHPDBG_INFO(error);
|
||||||
PHPDBG_INFO(vars);
|
PHPDBG_INFO(vars);
|
||||||
PHPDBG_INFO(literal);
|
PHPDBG_INFO(literal);
|
||||||
|
PHPDBG_INFO(memory);
|
||||||
|
|
||||||
static const phpdbg_command_t phpdbg_info_commands[] = {
|
static const phpdbg_command_t phpdbg_info_commands[] = {
|
||||||
PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
|
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(files, "show included files", 'F', info_files, NULL, 0),
|
||||||
PHPDBG_COMMAND_D_EX(classes, "lists loaded classes", 'c', info_classes, NULL, 0),
|
PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
|
||||||
PHPDBG_COMMAND_D_EX(funcs, "lists loaded classes", 'f', info_funcs, NULL, 0),
|
PHPDBG_COMMAND_D_EX(funcs, "show 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(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(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(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
|
PHPDBG_END_COMMAND
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -573,6 +573,9 @@ PHPDBG_COMMAND(run) /* {{{ */
|
||||||
zend_hash_clean(
|
zend_hash_clean(
|
||||||
&PHPDBG_G(seek));
|
&PHPDBG_G(seek));
|
||||||
|
|
||||||
|
/* reset hit counters */
|
||||||
|
phpdbg_reset_breakpoints(TSRMLS_C);
|
||||||
|
|
||||||
zend_try {
|
zend_try {
|
||||||
php_output_activate(TSRMLS_C);
|
php_output_activate(TSRMLS_C);
|
||||||
PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
|
PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
|
||||||
|
@ -766,11 +769,7 @@ PHPDBG_COMMAND(print) /* {{{ */
|
||||||
phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
|
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("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
|
||||||
phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
|
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);
|
phpdbg_writeln(SEPARATE);
|
||||||
} break;
|
} 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("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("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("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_writeln("Conditionals\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]));
|
||||||
|
|
||||||
phpdbg_clear_breakpoints(TSRMLS_C);
|
phpdbg_clear_breakpoints(TSRMLS_C);
|
||||||
|
@ -1400,9 +1402,16 @@ zend_vm_enter:
|
||||||
phpdbg_print_opline_ex(
|
phpdbg_print_opline_ex(
|
||||||
execute_data, &vars, 0 TSRMLS_CC);
|
execute_data, &vars, 0 TSRMLS_CC);
|
||||||
|
|
||||||
if (PHPDBG_G(flags) & PHPDBG_BP_MASK
|
/* search for breakpoints */
|
||||||
&& phpdbg_find_breakpoint(execute_data TSRMLS_CC) == SUCCESS) {
|
{
|
||||||
DO_INTERACTIVE();
|
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) {
|
if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) {
|
||||||
|
|
56
phpdbg_set.c
|
@ -21,6 +21,7 @@
|
||||||
#include "phpdbg_cmd.h"
|
#include "phpdbg_cmd.h"
|
||||||
#include "phpdbg_set.h"
|
#include "phpdbg_set.h"
|
||||||
#include "phpdbg_utils.h"
|
#include "phpdbg_utils.h"
|
||||||
|
#include "phpdbg_bp.h"
|
||||||
|
|
||||||
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
|
||||||
|
|
||||||
|
@ -51,13 +52,33 @@ PHPDBG_SET(break) /* {{{ */
|
||||||
|
|
||||||
case STR_PARAM:
|
case STR_PARAM:
|
||||||
if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
|
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) {
|
} else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
|
||||||
PHPDBG_G(flags) &= ~PHPDBG_IS_BP_ENABLED;
|
phpdbg_disable_breakpoints(TSRMLS_C);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NUMERIC_PARAM: {
|
||||||
|
if (input->argc > 2) {
|
||||||
|
if (phpdbg_argv_is(2, "on")) {
|
||||||
|
phpdbg_enable_breakpoint(param->num TSRMLS_CC);
|
||||||
|
} else if (phpdbg_argv_is(2, "off")) {
|
||||||
|
phpdbg_disable_breakpoint(param->num TSRMLS_CC);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
|
||||||
|
if (brake) {
|
||||||
|
phpdbg_writeln(
|
||||||
|
"%s", brake->disabled ? "off" : "on");
|
||||||
|
} else {
|
||||||
|
phpdbg_error("Failed to find breakpoint #%lx", param->num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
phpdbg_default_switch_case();
|
default:
|
||||||
|
phpdbg_error(
|
||||||
|
"set break used incorrectly: set break [id] <on|off>");
|
||||||
}
|
}
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
@ -71,6 +92,7 @@ PHPDBG_SET(color) /* {{{ */
|
||||||
input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
|
input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
|
||||||
int element = PHPDBG_COLOR_INVALID;
|
int element = PHPDBG_COLOR_INVALID;
|
||||||
|
|
||||||
|
/* @TODO(anyone) make this consistent with other set commands */
|
||||||
if (color) {
|
if (color) {
|
||||||
if (phpdbg_argv_is(1, "prompt")) {
|
if (phpdbg_argv_is(1, "prompt")) {
|
||||||
phpdbg_notice(
|
phpdbg_notice(
|
||||||
|
@ -105,6 +127,34 @@ usage:
|
||||||
}
|
}
|
||||||
return SUCCESS;
|
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
|
#endif
|
||||||
|
|
||||||
PHPDBG_SET(oplog) /* {{{ */
|
PHPDBG_SET(oplog) /* {{{ */
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
PHPDBG_SET(prompt);
|
PHPDBG_SET(prompt);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
PHPDBG_SET(color);
|
PHPDBG_SET(color);
|
||||||
|
PHPDBG_SET(colors);
|
||||||
#endif
|
#endif
|
||||||
PHPDBG_SET(oplog);
|
PHPDBG_SET(oplog);
|
||||||
PHPDBG_SET(break);
|
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),
|
PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
|
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
|
#endif
|
||||||
PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
|
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
|
PHPDBG_END_COMMAND
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
#include "zend.h"
|
#include "zend.h"
|
||||||
#include "php.h"
|
#include "php.h"
|
||||||
#include "spprintf.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;
|
char *sep = NULL;
|
||||||
|
|
||||||
|
if (strstr(str, "#") != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (strstr(str, " ") != NULL)
|
if (strstr(str, " ") != NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -242,6 +246,33 @@ PHPDBG_API int phpdbg_print(int type TSRMLS_DC, FILE *fp, const char *format, ..
|
||||||
return rc;
|
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) /* {{{ */
|
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC) /* {{{ */
|
||||||
{
|
{
|
||||||
const phpdbg_color_t *color = colors;
|
const phpdbg_color_t *color = colors;
|
||||||
|
@ -299,7 +330,7 @@ PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
|
||||||
} /* }}} */
|
} /* }}} */
|
||||||
|
|
||||||
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
|
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
|
||||||
{
|
{
|
||||||
/* find cached prompt */
|
/* find cached prompt */
|
||||||
if (PHPDBG_G(prompt)[1]) {
|
if (PHPDBG_G(prompt)[1]) {
|
||||||
return PHPDBG_G(prompt)[1];
|
return PHPDBG_G(prompt)[1];
|
||||||
|
|
|
@ -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*);
|
PHPDBG_API char *phpdbg_trim(const char*, size_t, size_t*);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error/notice/formatting helper
|
* Error/notice/formatting helpers
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
P_ERROR = 1,
|
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);
|
PHPDBG_API int phpdbg_print(int TSRMLS_DC, FILE*, const char*, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4);
|
||||||
#endif
|
#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_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_notice(fmt, ...) phpdbg_print(P_NOTICE TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
||||||
#define phpdbg_writeln(fmt, ...) phpdbg_print(P_WRITELN TSRMLS_CC, PHPDBG_G(io)[PHPDBG_STDOUT], fmt, ##__VA_ARGS__)
|
#define phpdbg_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 |