com php-src: Merge branch 'phpdbg' into PHP-5.6: sapi/phpdbg/phpdbg.c sapi/phpdbg/phpdbg.h sapi/phpdbg/phpdbg_cmd.c sapi/phpdbg /phpdbg_opcode.c
sapi/phpdbg/phpdbg_prompt.c
| From: | Johannes Schlüter | Date: | Sat, 18 Jan 2014 12:52:27 +0000 |
| Subject: | com php-src: Merge branch 'phpdbg' into PHP-5.6: sapi/phpdbg/phpdbg.c sapi/phpdbg/phpdbg.h sapi/phpdbg/phpdbg_cmd.c sapi/phpdbg /phpdbg_opcode.c sapi/phpdbg/phpdbg_prompt.c |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-75683@lists.php.net to get a copy of this message | ||
Commit: 8ff4d61f0864fbca7b2905f14de76fd400d9b3df
Author: Johannes Schlüter <johannes@php.net> Sat, 18 Jan 2014 13:52:27 +0100
Parents: 43a8f4f81439455d22471822bfa5f61708a2dc17 c205a6f3de9d8923c2be6e16b1edc332981313a3
Branches: PHP-5.6 master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=8ff4d61f0864fbca7b2905f14de76fd400d9b3df
Log:
Merge branch 'phpdbg' into PHP-5.6
Changed paths:
MA sapi/phpdbg/phpdbg.c
MA sapi/phpdbg/phpdbg.h
MA sapi/phpdbg/phpdbg_cmd.c
MA sapi/phpdbg/phpdbg_opcode.c
MA sapi/phpdbg/phpdbg_prompt.c
diff --cc sapi/phpdbg/phpdbg.c index deb7be6,0000000..e7f841c mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@@ -1,1338 -1,0 +1,1348 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SIGNALS +# include <signal.h> +#endif +#include "phpdbg.h" +#include "phpdbg_prompt.h" +#include "phpdbg_bp.h" +#include "phpdbg_break.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" +#include "phpdbg_set.h" + +/* {{{ remote console headers */ +#ifndef _WIN32 +# include <sys/socket.h> +# include <sys/select.h> +# include <sys/time.h> +# include <sys/types.h> +# include <unistd.h> +# include <arpa/inet.h> +#endif /* }}} */ + +ZEND_DECLARE_MODULE_GLOBALS(phpdbg); + +static zend_bool phpdbg_booted = 0; + +#if PHP_VERSION_ID >= 50500 +void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC); +#else +void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC); +#endif + +static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ +{ + pg->prompt[0] = NULL; + pg->prompt[1] = NULL; + + pg->colors[0] = NULL; + pg->colors[1] = NULL; + pg->colors[2] = NULL; + + pg->exec = NULL; + pg->exec_len = 0; + pg->ops = NULL; + pg->vmret = 0; + pg->bp_count = 0; + pg->lcmd = NULL; + pg->flags = PHPDBG_DEFAULT_FLAGS; + pg->oplog = NULL; + pg->io[PHPDBG_STDIN] = NULL; + pg->io[PHPDBG_STDOUT] = NULL; + pg->io[PHPDBG_STDERR] = NULL; + memset(&pg->lparam, 0, sizeof(phpdbg_param_t)); + pg->frame.num = 0; +} /* }}} */ + +static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ +{ + ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL); +#if PHP_VERSION_ID >= 50500 + zend_execute_old = zend_execute_ex; + zend_execute_ex = phpdbg_execute_ex; +#else + zend_execute_old = zend_execute; + zend_execute = phpdbg_execute_ex; +#endif + + REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT); + + return SUCCESS; +} /* }}} */ + +static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol); +} /* }}} */ + +static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breakop_t*)brake)->name); +} /* }}} */ + + +static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +{ + phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data; + + if (brake) { + if (brake->ops) { + TSRMLS_FETCH(); + + destroy_op_array( + brake->ops TSRMLS_CC); + efree(brake->ops); + } + efree((char*)brake->code); + } +} /* }}} */ + +static void php_phpdbg_destroy_registered(void *data) /* {{{ */ +{ + zend_function *function = (zend_function*) data; + TSRMLS_FETCH(); + + destroy_zend_function( + function TSRMLS_CC); +} /* }}} */ + +static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 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_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0); + + zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); + zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); + + return SUCCESS; +} /* }}} */ + +static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); + zend_hash_destroy(&PHPDBG_G(seek)); + zend_hash_destroy(&PHPDBG_G(registered)); + + if (PHPDBG_G(exec)) { + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + } + + if (PHPDBG_G(prompt)[0]) { + free(PHPDBG_G(prompt)[0]); + } + if (PHPDBG_G(prompt)[1]) { + free(PHPDBG_G(prompt)[1]); + } + + PHPDBG_G(prompt)[0] = NULL; + PHPDBG_G(prompt)[1] = NULL; + + if (PHPDBG_G(oplog)) { + fclose( + PHPDBG_G(oplog)); + PHPDBG_G(oplog) = NULL; + } + + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + return SUCCESS; +} /* }}} */ + +/* {{{ proto mixed phpdbg_exec(string context) + Attempt to set the execution context for phpdbg + If the execution context was set previously it is returned + If the execution context was not set previously boolean true is returned + If the request to set the context fails, boolean false is returned, and an E_WARNING raised */ +static PHP_FUNCTION(phpdbg_exec) +{ + char *exec = NULL; + zend_ulong exec_len = 0L; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { + return; + } + + { + struct stat sb; + zend_bool result = 1; + + if (VCWD_STAT(exec, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + if (PHPDBG_G(exec)) { + ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1); + efree(PHPDBG_G(exec)); + result = 0; + } + + PHPDBG_G(exec) = estrndup(exec, exec_len); + PHPDBG_G(exec_len) = exec_len; + + if (result) + ZVAL_BOOL(return_value, 1); + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); + ZVAL_BOOL(return_value, 0); + } + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); + + ZVAL_BOOL(return_value, 0); + } + } +} /* }}} */ + +/* {{{ proto void phpdbg_break([integer type, string expression]) + instructs phpdbg to insert a breakpoint at the next opcode */ +static PHP_FUNCTION(phpdbg_break) +{ + if (ZEND_NUM_ARGS() > 0) { + long type; + char *expr = NULL; + zend_uint expr_len = 0; + phpdbg_param_t param; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) { + return; + } + + phpdbg_parse_param(expr, expr_len, ¶m TSRMLS_CC); + + switch (type) { + case METHOD_PARAM: + phpdbg_do_break_method(¶m, NULL TSRMLS_CC); + break; + + case FILE_PARAM: + phpdbg_do_break_file(¶m, NULL TSRMLS_CC); + break; + + case NUMERIC_PARAM: + phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC); + break; + + case STR_PARAM: + phpdbg_do_break_func(¶m, NULL TSRMLS_CC); + break; + + default: zend_error( + E_WARNING, "unrecognized parameter type %ld", type); + } + + phpdbg_clear_param(¶m TSRMLS_CC); + + } else if (EG(current_execute_data) && EG(active_op_array)) { + zend_ulong opline_num = (EG(current_execute_data)->opline - + EG(active_op_array)->opcodes); + + phpdbg_set_breakpoint_opline_ex( + &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC); + } +} /* }}} */ + +/* {{{ proto void phpdbg_clear(void) + instructs phpdbg to clear breakpoints */ +static PHP_FUNCTION(phpdbg_clear) +{ + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); +} /* }}} */ + +/* {{{ proto void phpdbg_color(integer element, string color) */ +static PHP_FUNCTION(phpdbg_color) +{ + long element; + char *color; + zend_uint color_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) { + return; + } + + switch (element) { + case PHPDBG_COLOR_NOTICE: + case PHPDBG_COLOR_ERROR: + case PHPDBG_COLOR_PROMPT: + phpdbg_set_color_ex(element, color, color_len TSRMLS_CC); + break; + + default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant"); + } +} /* }}} */ + +/* {{{ proto void phpdbg_prompt(string prompt) */ +static PHP_FUNCTION(phpdbg_prompt) +{ + char *prompt; + zend_uint prompt_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) { + return; + } + + phpdbg_set_prompt(prompt TSRMLS_CC); +} /* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, expression) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, element) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, string) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, context) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +zend_function_entry phpdbg_user_functions[] = { + PHP_FE(phpdbg_clear, phpdbg_clear_arginfo) + PHP_FE(phpdbg_break, phpdbg_break_arginfo) + PHP_FE(phpdbg_exec, phpdbg_exec_arginfo) + PHP_FE(phpdbg_color, phpdbg_color_arginfo) + PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo) +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL,NULL,NULL} +#endif +}; + +static zend_module_entry sapi_phpdbg_module_entry = { + STANDARD_MODULE_HEADER, + PHPDBG_NAME, + phpdbg_user_functions, + PHP_MINIT(phpdbg), + NULL, + PHP_RINIT(phpdbg), + PHP_RSHUTDOWN(phpdbg), + NULL, + PHPDBG_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */ +{ + if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) { + return FAILURE; + } + + phpdbg_booted=1; + + return SUCCESS; +} /* }}} */ + +static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */ +{ + return NULL; +} /* }}} */ + +static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */ +{ + return 0; +} +/* }}} */ + +static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */ +{ + /* We do nothing here, this function is needed to prevent that the fallback + * header handling is called. */ + return SAPI_HEADER_SENT_SUCCESSFULLY; +} +/* }}} */ + +static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */ +{ +} +/* }}} */ + +static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ +{ + /* + * We must not request TSRM before being boot + */ + if (phpdbg_booted) { + phpdbg_error("%s", message); + + switch (PG(last_error_type)) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + case E_RECOVERABLE_ERROR: + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { + phpdbg_list_file( + zend_get_executed_filename(TSRMLS_C), + 3, + zend_get_executed_lineno(TSRMLS_C)-1, + zend_get_executed_lineno(TSRMLS_C) + TSRMLS_CC + ); + } + + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: + return; + } + } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + } + } else fprintf(stdout, "%s\n", message); +} +/* }}} */ + +static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ +{ + fflush(stdout); + if(SG(request_info).argv0) { + free(SG(request_info).argv0); + SG(request_info).argv0 = NULL; + } + return SUCCESS; +} +/* }}} */ + +static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */ +{ + unsigned int len; + char *docroot = ""; + + /* In phpdbg mode, we consider the environment to be a part of the server variables + */ + php_import_environment_variables(track_vars_array TSRMLS_CC); + + if (PHPDBG_G(exec)) { + len = PHPDBG_G(exec_len); + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PHP_SELF", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + } + + /* any old docroot will doo */ + len = 0U; + if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", + &docroot, len, &len TSRMLS_CC)) { + php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC); + } +} +/* }}} */ + +static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */ +{ + return phpdbg_write("%s", message); +} /* }}} */ + +#if PHP_VERSION_ID >= 50700 +static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */ +{ +#else +static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ +{ + TSRMLS_FETCH(); +#endif + + fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); +} /* }}} */ + +/* {{{ sapi_module_struct phpdbg_sapi_module +*/ +static sapi_module_struct phpdbg_sapi_module = { + "phpdbg", /* name */ + "phpdbg", /* pretty name */ + + php_sapi_phpdbg_module_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + php_sapi_phpdbg_deactivate, /* deactivate */ + + php_sapi_phpdbg_ub_write, /* unbuffered write */ + php_sapi_phpdbg_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + php_sapi_phpdbg_header_handler, /* header handler */ + php_sapi_phpdbg_send_headers, /* send headers handler */ + php_sapi_phpdbg_send_header, /* send header handler */ + + NULL, /* read POST data */ + php_sapi_phpdbg_read_cookies, /* read Cookies */ + + php_sapi_phpdbg_register_vars, /* register server variables */ + php_sapi_phpdbg_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + STANDARD_SAPI_MODULE_PROPERTIES +}; +/* }}} */ + +const opt_struct OPTIONS[] = { /* {{{ */ + {'c', 1, "ini path override"}, + {'d', 1, "define ini entry on command line"}, + {'n', 0, "no php.ini"}, + {'z', 1, "load zend_extension"}, + /* phpdbg options */ + {'q', 0, "no banner"}, + {'e', 1, "exec"}, + {'v', 0, "disable quietness"}, + {'s', 0, "enable stepping"}, + {'b', 0, "boring colours"}, + {'i', 1, "specify init"}, + {'I', 0, "ignore init"}, + {'O', 1, "opline log"}, + {'r', 0, "run"}, + {'E', 0, "step-through-eval"}, + {'S', 1, "sapi-name"}, +#ifndef _WIN32 + {'l', 1, "listen"}, + {'a', 1, "address-or-any"}, +#endif + {'V', 0, "version"}, + {'-', 0, NULL} +}; /* }}} */ + +const char phpdbg_ini_hardcoded[] = +"html_errors=Off\n" +"register_argc_argv=On\n" +"implicit_flush=On\n" +"display_errors=Off\n" +"log_errors=On\n" +"max_execution_time=0\n" +"max_input_time=-1\n" +"error_log=\n\0"; + +/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ +#define INI_DEFAULT(name, value) \ + Z_SET_REFCOUNT(tmp, 0); \ + Z_UNSET_ISREF(tmp); \ + ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \ + zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL); + +void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ +{ + zval tmp; + INI_DEFAULT("report_zend_debug", "0"); +} /* }}} */ + +static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */ +{ + /* print blurb */ + if (!cleaning) { + phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", + PHPDBG_VERSION); + phpdbg_writeln("To get help using phpdbg type \"help\" and press enter"); + phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES); + } else { + phpdbg_notice("Clean Execution Environment"); + + phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files))); + } +} /* }}} */ + +static inline void phpdbg_sigint_handler(int signo) /* {{{ */ +{ + TSRMLS_FETCH(); + + if (EG(in_execution)) { + /* set signalled only when not interactive */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; + } + } else { - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; - zend_bailout(); ++ /* we quit remote consoles on recv SIGINT */ ++ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { ++ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; ++ zend_bailout(); ++ } + } +} /* }}} */ + +#ifndef _WIN32 +int phpdbg_open_socket(const char *interface, short port) /* {{{ */ +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + + switch (fd) { + case -1: + return -1; + + default: { + int reuse = 1; + + switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) { + case -1: + close(fd); + return -2; + + default: { + struct sockaddr_in address; + + memset(&address, 0, sizeof(address)); + + address.sin_port = htons(port); + address.sin_family = AF_INET; + + if ((*interface == '*')) { + address.sin_addr.s_addr = htonl(INADDR_ANY); + } else if (!inet_pton(AF_INET, interface, &address.sin_addr)) { + close(fd); + return -3; + } + + switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) { + case -1: + close(fd); + return -4; + + default: { + listen(fd, 5); + } + } + } + } + } + } + + return fd; +} /* }}} */ + +static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */ +{ + if ((*socket)[0] >= 0) { + shutdown( + (*socket)[0], SHUT_RDWR); + close((*socket)[0]); + } + + if (streams[0]) { + fclose(streams[0]); + } + + if ((*socket)[1] >= 0) { + shutdown( + (*socket)[1], SHUT_RDWR); + close((*socket)[1]); + } + + if (streams[1]) { + fclose(streams[1]); + } +} /* }}} */ + +/* don't inline this, want to debug it easily, will inline when done */ + +int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */ +{ + if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) { + ((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]); + ((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]); + } + + streams[0] = NULL; + streams[1] = NULL; + + if ((*listen)[0] < 0 || (*listen)[1] < 0) { + if ((*listen)[0] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdin) on %s:%d", address, port[0]); + } + + if ((*listen)[1] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdout) on %s:%d", address, port[1]); + } + + if ((*listen)[0] >= 0) { + close((*listen)[0]); + } + + if ((*listen)[1] >= 0) { + close((*listen)[1]); + } + + return FAILURE; + } + + phpdbg_close_sockets(socket, streams); + + phpdbg_rlog(stderr, + "accepting connections on %s:%d/%d", address, port[0], port[1]); + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + char buffer[20] = {0}; + + { + memset(&address, 0, size); + (*socket)[0] = accept( + (*listen)[0], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdin) from %s", buffer); + } + + { + memset(&address, 0, size); + (*socket)[1] = accept( + (*listen)[1], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdout) from %s", buffer); + } + } + + dup2((*socket)[0], fileno(stdin)); + dup2((*socket)[1], fileno(stdout)); + + setbuf(stdout, NULL); + + streams[0] = fdopen((*socket)[0], "r"); + streams[1] = fdopen((*socket)[1], "w"); + + return SUCCESS; +} /* }}} */ +#endif + +int main(int argc, char **argv) /* {{{ */ +{ + sapi_module_struct *phpdbg = &phpdbg_sapi_module; + char *sapi_name; + char *ini_entries; + int ini_entries_len; + char **zend_extensions = NULL; + zend_ulong zend_extensions_len = 0L; + zend_bool ini_ignore; + char *ini_override; + char *exec; + size_t exec_len; + char *init_file; + size_t init_file_len; + zend_bool init_file_default; + char *oplog_file; + size_t oplog_file_len; + zend_ulong flags; + char *php_optarg; + int php_optind, opt, show_banner = 1; + long cleaning = 0; + zend_bool remote = 0; + int run = 0; + int step = 0; + char *bp_tmp_file; +#ifndef _WIN32 + char *address; + int listen[2]; + int server[2]; + int socket[2]; + FILE* streams[2] = {NULL, NULL}; +#endif + +#ifdef ZTS + void ***tsrm_ls; +#endif + +#ifndef _WIN32 + address = strdup("127.0.0.1"); + socket[0] = -1; + socket[1] = -1; + listen[0] = -1; + listen[1] = -1; + server[0] = -1; + server[1] = -1; + streams[0] = NULL; + streams[1] = NULL; +#endif + +#ifdef PHP_WIN32 + _fmode = _O_BINARY; /* sets default for file streams to binary */ + setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ +#endif + +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); + + tsrm_ls = ts_resource(0); +#endif + +phpdbg_main: + if (!cleaning) { + bp_tmp_file = malloc(L_tmpnam); + tmpnam(bp_tmp_file); + if (bp_tmp_file == NULL) { + phpdbg_error("Unable to create temporary file"); + } + } + ini_entries = NULL; + ini_entries_len = 0; + ini_ignore = 0; + ini_override = NULL; + zend_extensions = NULL; + zend_extensions_len = 0L; + exec = NULL; + exec_len = 0; + init_file = NULL; + init_file_len = 0; + init_file_default = 1; + oplog_file = NULL; + oplog_file_len = 0; + flags = PHPDBG_DEFAULT_FLAGS; + php_optarg = NULL; + php_optind = 1; + opt = 0; + run = 0; + step = 0; + sapi_name = NULL; + + + while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { + switch (opt) { + case 'r': + run++; + break; + case 'n': + ini_ignore = 1; + break; + case 'c': + if (ini_override) { + free(ini_override); + } + ini_override = strdup(php_optarg); + break; + case 'd': { + int len = strlen(php_optarg); + char *val; + + if ((val = strchr(php_optarg, '='))) { + val++; + if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); + ini_entries_len += (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"", 1); + ini_entries_len++; + memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg)); + ini_entries_len += len - (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); + ini_entries_len += sizeof("\n\0\"") - 2; + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); + ini_entries_len += len + sizeof("\n\0") - 2; + } + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); + ini_entries_len += len + sizeof("=1\n\0") - 2; + } + } break; + + case 'z': + zend_extensions_len++; + if (zend_extensions) { + zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len); + } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len); + zend_extensions[zend_extensions_len-1] = strdup(php_optarg); + break; + + /* begin phpdbg options */ + + case 'e': { /* set execution context */ + exec_len = strlen(php_optarg); + if (exec_len) { + if (exec) { + free(exec); + } + exec = strdup(php_optarg); + } + } break; + + case 'S': { /* set SAPI name */ + if (sapi_name) { + free(sapi_name); + } + sapi_name = strdup(php_optarg); + } break; + + case 'I': { /* ignore .phpdbginit */ + init_file_default = 0; + } break; + + case 'i': { /* set init file */ + if (init_file) { + free(init_file); + } + + init_file_len = strlen(php_optarg); + if (init_file_len) { + init_file = strdup(php_optarg); + } + } break; + + case 'O': { /* set oplog output */ + oplog_file_len = strlen(php_optarg); + if (oplog_file_len) { + oplog_file = strdup(php_optarg); + } + } break; + + case 'v': /* set quietness off */ + flags &= ~PHPDBG_IS_QUIET; + break; + + case 's': /* set stepping on */ + step = 1; + break; + + case 'E': /* stepping through eval on */ + flags |= PHPDBG_IS_STEPONEVAL; + break; + + case 'b': /* set colours off */ + flags &= ~PHPDBG_IS_COLOURED; + break; + + case 'q': /* hide banner */ + show_banner = 0; + break; + +#ifndef _WIN32 + /* if you pass a listen port, we will accept input on listen port */ + /* and write output to listen port * 2 */ + + case 'l': { /* set listen ports */ + if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) { + if (sscanf(php_optarg, "%d", &listen[0]) != 1) { + /* default to hardcoded ports */ + listen[0] = 4000; + listen[1] = 8000; + } else { + listen[1] = (listen[0] * 2); + } + } + } break; + + case 'a': { /* set bind address */ + free(address); + if (!php_optarg) { + address = strdup("*"); + } else address = strdup(php_optarg); + } break; +#endif + + case 'V': { + sapi_startup(phpdbg); + phpdbg->startup(phpdbg); + printf( + "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2014 The PHP Group\n%s", + PHPDBG_VERSION, + __DATE__, + __TIME__, + PHP_VERSION, + get_zend_version() + ); + sapi_deactivate(TSRMLS_C); + sapi_shutdown(); + return 0; + } break; + } + } + +#ifndef _WIN32 + /* setup remote server if necessary */ + if (!cleaning && + (listen[0] > 0 && listen[1] > 0)) { + if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) { + remote = 0; + exit(0); + } + /* set remote flag to stop service shutting down upon quit */ + remote = 1; + } +#endif + + if (sapi_name) { + phpdbg->name = sapi_name; + } + + phpdbg->ini_defaults = phpdbg_ini_defaults; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore_cwd = 1; + + sapi_startup(phpdbg); + + phpdbg->executable_location = argv[0]; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore = ini_ignore; + phpdbg->php_ini_path_override = ini_override; + + if (ini_entries) { + ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded)); + memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2); + } else { + ini_entries = malloc(sizeof(phpdbg_ini_hardcoded)); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded)); + } + ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2; + + if (zend_extensions_len) { + zend_ulong zend_extension = 0L; + + while (zend_extension < zend_extensions_len) { + const char *ze = zend_extensions[zend_extension]; + size_t ze_len = strlen(ze); + + ini_entries = realloc( + ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n")))); + memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1)); + ini_entries_len += (sizeof("zend_extension=")-1); + memcpy(&ini_entries[ini_entries_len], ze, ze_len); + ini_entries_len += ze_len; + memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1)); + + free(zend_extensions[zend_extension]); + zend_extension++; + } + + free(zend_extensions); + } + + phpdbg->ini_entries = ini_entries; + + if (phpdbg->startup(phpdbg) == SUCCESS) { + + zend_activate(TSRMLS_C); + + /* do not install sigint handlers for remote consoles */ + /* sending SIGINT then provides a decent way of shutting down the server */ +#ifdef ZEND_SIGNALS +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + zend_try { + zend_signal_activate(TSRMLS_C); + zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); + } zend_end_try(); +# ifndef _WIN32 + } +# endif +#else +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + signal(SIGINT, phpdbg_sigint_handler); +#ifndef _WIN32 + } +#endif +#endif + + PG(modules_activated) = 0; + + /* set flags from command line */ + PHPDBG_G(flags) = flags; + +#ifndef _WIN32 + /* setup io here */ + if (streams[0] && streams[1]) { + PHPDBG_G(flags) |= PHPDBG_IS_REMOTE; + + signal(SIGPIPE, SIG_IGN); + } +#endif + + PHPDBG_G(io)[PHPDBG_STDIN] = stdin; + PHPDBG_G(io)[PHPDBG_STDOUT] = stdout; + PHPDBG_G(io)[PHPDBG_STDERR] = stderr; + + if (exec) { /* set execution context */ + PHPDBG_G(exec) = phpdbg_resolve_path( + exec TSRMLS_CC); + PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec)); + + free(exec); + } + + if (oplog_file) { /* open oplog */ + PHPDBG_G(oplog) = fopen(oplog_file, "w+"); + if (!PHPDBG_G(oplog)) { + phpdbg_error( + "Failed to open oplog %s", oplog_file); + } + free(oplog_file); + } + + /* set default colors */ + phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC); + + /* set default prompt */ + phpdbg_set_prompt(PROMPT TSRMLS_CC); + + zend_try { + zend_activate_modules(TSRMLS_C); + } zend_end_try(); + + if (show_banner) { + /* print blurb */ + phpdbg_welcome((cleaning > 0) TSRMLS_CC); + } + + zend_try { + /* activate globals, they can be overwritten */ + zend_activate_auto_globals(TSRMLS_C); + } zend_end_try(); + + /* initialize from file */ ++ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; + zend_try { - PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; + phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC); + phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC); - PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; - } zend_catch { - PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; - if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { - goto phpdbg_out; - } + } zend_end_try(); ++ PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; ++ ++ /* quit if init says so */ ++ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { ++ goto phpdbg_out; ++ } + + /* step from here, not through init */ + if (step) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } + + if (run) { + /* no need to try{}, run does it ... */ + PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC); + if (run > 1) { + /* if -r is on the command line more than once just quit */ + goto phpdbg_out; + } + } + +phpdbg_interact: + /* phpdbg main() */ + do { + zend_try { + phpdbg_interactive(TSRMLS_C); + } zend_catch { + if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { + FILE *bp_tmp_fp = fopen(bp_tmp_file, "w"); + phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC); + fclose(bp_tmp_fp); + cleaning = 1; - goto phpdbg_out; + } else { + cleaning = 0; + } - #ifndef _WIN32 - /* remote client disconnected */ - if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + - /* renegociate connections */ - phpdbg_open_sockets( - address, listen, &server, &socket, streams); ++#ifndef _WIN32 ++ if (!cleaning) { ++ /* remote client disconnected */ ++ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + - /* set streams */ - if (streams[0] && streams[1]) { - PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING; ++ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { ++ /* 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; ++ } else { ++ /* local consoles cannot disconnect, ignore EOF */ ++ PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; ++ } + } - - /* this must be forced */ - CG(unclean_shutdown) = 0; + } +#endif - if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { - goto phpdbg_out; - } + } zend_end_try(); - } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); ++ } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + /* this must be forced */ + CG(unclean_shutdown) = 0; + ++ /* this is just helpful */ ++ PG(report_memleaks) = 0; ++ +phpdbg_out: +#ifndef _WIN32 - if (PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED) { ++ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; + goto phpdbg_interact; + } +#endif + +#ifndef ZTS + /* force cleanup of auto and core globals */ + zend_hash_clean(CG(auto_globals)); + memset( + &core_globals, 0, sizeof(php_core_globals)); +#endif + + if (ini_entries) { + free(ini_entries); + } + + if (ini_override) { + free(ini_override); + } + + if (PG(modules_activated)) { + zend_try { + zend_deactivate_modules(TSRMLS_C); + } zend_end_try(); + } + + zend_deactivate(TSRMLS_C); + + zend_try { + zend_post_deactivate_modules(TSRMLS_C); + } zend_end_try(); + +#ifdef ZEND_SIGNALS + zend_try { + zend_signal_deactivate(TSRMLS_C); + } zend_end_try(); +#endif + + zend_try { + php_module_shutdown(TSRMLS_C); + } zend_end_try(); + + sapi_shutdown(); + } + + if (cleaning || remote) { + goto phpdbg_main; + } - ++ +#ifdef ZTS + /* bugggy */ + /* tsrm_shutdown(); */ +#endif + +#ifndef _WIN32 + if (address) { + free(address); + } +#endif + + if (sapi_name) { + free(sapi_name); + } + + free(bp_tmp_file); + + return 0; +} /* }}} */ diff --cc sapi/phpdbg/phpdbg.h index 66b4f69,0000000..6b7afb2 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg.h +++ b/sapi/phpdbg/phpdbg.h @@@ -1,188 -1,0 +1,188 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_H +#define PHPDBG_H + +#ifdef PHP_WIN32 +# define PHPDBG_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHPDBG_API __attribute__ ((visibility("default"))) +#else +# define PHPDBG_API +#endif + +#include "php.h" +#include "php_globals.h" +#include "php_variables.h" +#include "php_getopt.h" +#include "zend_builtin_functions.h" +#include "zend_extensions.h" +#include "zend_modules.h" +#include "zend_globals.h" +#include "zend_ini_scanner.h" +#include "zend_stream.h" +#include "SAPI.h" +#include <fcntl.h> +#include <sys/types.h> +#if defined(_WIN32) && !defined(__MINGW32__) +# include <windows.h> +# include "config.w32.h" +# undef strcasecmp +# undef strncasecmp +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +#else +# include "php_config.h" +#endif +#ifndef O_BINARY +# define O_BINARY 0 +#endif +#include "php_main.h" + +#ifdef ZTS +# include "TSRM.h" +#endif + +#ifdef HAVE_LIBREADLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "phpdbg_cmd.h" +#include "phpdbg_utils.h" + +#ifdef ZTS +# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v) +#else +# define PHPDBG_G(v) (phpdbg_globals.v) +#endif + +#define PHPDBG_NEXT 2 +#define PHPDBG_UNTIL 3 +#define PHPDBG_FINISH 4 +#define PHPDBG_LEAVE 5 + +/* + BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE +*/ + +/* {{{ tables */ +#define PHPDBG_BREAK_FILE 0 +#define PHPDBG_BREAK_SYM 1 +#define PHPDBG_BREAK_OPLINE 2 +#define PHPDBG_BREAK_METHOD 3 +#define PHPDBG_BREAK_COND 4 +#define PHPDBG_BREAK_OPCODE 5 +#define PHPDBG_BREAK_FUNCTION_OPLINE 6 +#define PHPDBG_BREAK_METHOD_OPLINE 7 +#define PHPDBG_BREAK_FILE_OPLINE 8 +#define PHPDBG_BREAK_MAP 9 +#define PHPDBG_BREAK_TABLES 10 /* }}} */ + +/* {{{ flags */ +#define PHPDBG_HAS_FILE_BP (1<<1) +#define PHPDBG_HAS_SYM_BP (1<<2) +#define PHPDBG_HAS_OPLINE_BP (1<<3) +#define PHPDBG_HAS_METHOD_BP (1<<4) +#define PHPDBG_HAS_COND_BP (1<<5) +#define PHPDBG_HAS_OPCODE_BP (1<<6) +#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7) +#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8) +#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) /* }}} */ + +/* + END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE +*/ + +#define PHPDBG_IN_COND_BP (1<<10) +#define PHPDBG_IN_EVAL (1<<11) + +#define PHPDBG_IS_STEPPING (1<<12) +#define PHPDBG_IS_QUIET (1<<13) +#define PHPDBG_IS_QUITTING (1<<14) +#define PHPDBG_IS_COLOURED (1<<15) +#define PHPDBG_IS_CLEANING (1<<16) + +#define PHPDBG_IN_UNTIL (1<<17) +#define PHPDBG_IN_FINISH (1<<18) +#define PHPDBG_IN_LEAVE (1<<19) + +#define PHPDBG_IS_REGISTERED (1<<20) +#define PHPDBG_IS_STEPONEVAL (1<<21) +#define PHPDBG_IS_INITIALIZING (1<<22) +#define PHPDBG_IS_SIGNALED (1<<23) +#define PHPDBG_IS_INTERACTIVE (1<<24) +#define PHPDBG_IS_BP_ENABLED (1<<25) +#define PHPDBG_IS_REMOTE (1<<26) +#define PHPDBG_IS_DISCONNECTED (1<<27) + +#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) +#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) +#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) + +#ifndef _WIN32 +# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED) +#else +# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_BP_ENABLED) +#endif /* }}} */ + +/* {{{ strings */ +#define PHPDBG_NAME "phpdbg" +#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */ +#define PHPDBG_URL "http://phpdbg.com" +#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues" - #define PHPDBG_VERSION "0.3.0" ++#define PHPDBG_VERSION "0.3.1" +#define PHPDBG_INIT_FILENAME ".phpdbginit" +/* }}} */ + +/* {{{ output descriptors */ +#define PHPDBG_STDIN 0 +#define PHPDBG_STDOUT 1 +#define PHPDBG_STDERR 2 +#define PHPDBG_IO_FDS 3 /* }}} */ + +/* {{{ structs */ +ZEND_BEGIN_MODULE_GLOBALS(phpdbg) + HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */ + HashTable registered; /* registered */ + HashTable seek; /* seek oplines */ + phpdbg_frame_t frame; /* frame */ + + char *exec; /* file to execute */ + size_t exec_len; /* size of exec */ + zend_op_array *ops; /* op_array */ + zval *retval; /* return value */ + int bp_count; /* breakpoint count */ + int vmret; /* return from last opcode handler execution */ + + FILE *oplog; /* opline log */ + FILE *io[PHPDBG_IO_FDS]; /* io */ + + char *prompt[2]; /* prompt */ + const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */ + + phpdbg_command_t *lcmd; /* last command */ + phpdbg_param_t lparam; /* last param */ + + zend_ulong flags; /* phpdbg flags */ +ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */ + +#endif /* PHPDBG_H */ diff --cc sapi/phpdbg/phpdbg_cmd.c index 36a9d26,0000000..29424d0 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_cmd.c +++ b/sapi/phpdbg/phpdbg_cmd.c @@@ -1,669 -1,0 +1,680 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg.h" +#include "phpdbg_cmd.h" +#include "phpdbg_utils.h" +#include "phpdbg_set.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + return "empty"; + case ADDR_PARAM: + return "address"; + case NUMERIC_PARAM: + return "numeric"; + case METHOD_PARAM: + return "method"; + case NUMERIC_FUNCTION_PARAM: + return "function opline"; + case NUMERIC_METHOD_PARAM: + return "method opline"; + case FILE_PARAM: + return "file or file opline"; + case STR_PARAM: + return "string"; + default: /* this is bad */ + return "unknown"; + } +} + +PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + char *class_name, *func_name; + + if (len == 0) { + param->type = EMPTY_PARAM; + goto parsed; + } + + if (phpdbg_is_addr(str)) { + param->addr = strtoul(str, 0, 16); + param->type = ADDR_PARAM; + goto parsed; + + } else if (phpdbg_is_numeric(str)) { + param->num = strtol(str, NULL, 0); + param->type = NUMERIC_PARAM; + goto parsed; + + } else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) { + param->method.class = class_name; + param->method.name = func_name; + param->type = METHOD_PARAM; + goto parsed; + } else { + char *line_pos = strrchr(str, ':'); + + if (line_pos && phpdbg_is_numeric(line_pos+1)) { + if (strchr(str, ':') == line_pos) { + char path[MAXPATHLEN]; + + memcpy(path, str, line_pos - str); + path[line_pos - str] = 0; + *line_pos = 0; + param->file.name = phpdbg_resolve_path(path TSRMLS_CC); + param->file.line = strtol(line_pos+1, NULL, 0); + param->type = FILE_PARAM; + + goto parsed; + } + } + + line_pos = strrchr(str, '#'); + + if (line_pos && phpdbg_is_numeric(line_pos+1)) { + if (strchr(str, '#') == line_pos) { + param->num = strtol(line_pos + 1, NULL, 0); + + if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) { + param->method.class = class_name; + param->method.name = func_name; + param->type = NUMERIC_METHOD_PARAM; + } else { + param->len = line_pos - str; + param->str = estrndup(str, param->len); + param->type = NUMERIC_FUNCTION_PARAM; + } + + goto parsed; + } + } + } + + param->str = estrndup(str, len); + param->len = len; + param->type = STR_PARAM; + +parsed: + phpdbg_debug("phpdbg_parse_param(\"%s\", %lu): %s", + str, len, phpdbg_get_param_type(param TSRMLS_CC)); + return param->type; +} /* }}} */ + +PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + if (param) { + switch (param->type) { + case FILE_PARAM: + efree(param->file.name); + break; + case METHOD_PARAM: + efree(param->method.class); + efree(param->method.name); + break; + case STR_PARAM: + efree(param->str); + break; + default: + break; + } + } + +} /* }}} */ + +PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: + asprintf(pointer, + "%s", param->str); + break; + + case ADDR_PARAM: + asprintf(pointer, + "%#lx", param->addr); + break; + + case NUMERIC_PARAM: + asprintf(pointer, + "%li", + param->num); + break; + + case METHOD_PARAM: + asprintf(pointer, + "%s::%s", + param->method.class, + param->method.name); + break; + + case FILE_PARAM: + if (param->num) { + asprintf(pointer, + "%s:%lu#%lu", + param->file.name, + param->file.line, + param->num); + } else { + asprintf(pointer, + "%s:%lu", + param->file.name, + param->file.line); + } + break; + + case NUMERIC_FUNCTION_PARAM: + asprintf(pointer, + "%s#%lu", param->str, param->num); + break; + + case NUMERIC_METHOD_PARAM: + asprintf(pointer, + "%s::%s#%lu", + param->method.class, + param->method.name, + param->num); + break; + + default: + asprintf(pointer, + "%s", "unknown"); + } + + return *pointer; +} /* }}} */ + +PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */ +{ + switch ((dest->type = src->type)) { + case STR_PARAM: + dest->str = estrndup(src->str, src->len); + dest->len = src->len; + break; + + case ADDR_PARAM: + dest->addr = src->addr; + break; + + case NUMERIC_PARAM: + dest->num = src->num; + break; + + case METHOD_PARAM: + dest->method.class = estrdup(src->method.class); + dest->method.name = estrdup(src->method.name); + break; + + case FILE_PARAM: + dest->file.name = estrdup(src->file.name); + dest->file.line = src->file.line; + if (src->num) + dest->num = src->num; + break; + + case NUMERIC_FUNCTION_PARAM: + dest->str = estrndup(src->str, src->len); + dest->num = src->num; + dest->len = src->len; + break; + + case NUMERIC_METHOD_PARAM: + dest->method.class = estrdup(src->method.class); + dest->method.name = estrdup(src->method.name); + dest->num = src->num; + break; + + case EMPTY_PARAM: { /* do nothing */ } break; + } +} /* }}} */ + +PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + zend_ulong hash = param->type; + + switch (param->type) { + case STR_PARAM: + hash += zend_inline_hash_func(param->str, param->len); + break; + + case METHOD_PARAM: + hash += zend_inline_hash_func(param->method.class, strlen(param->method.class)); + hash += zend_inline_hash_func(param->method.name, strlen(param->method.name)); + break; + + case FILE_PARAM: + hash += zend_inline_hash_func(param->file.name, strlen(param->file.name)); + hash += param->file.line; + if (param->num) + hash += param->num; + break; + + case ADDR_PARAM: + hash += param->addr; + break; + + case NUMERIC_PARAM: + hash += param->num; + break; + + case NUMERIC_FUNCTION_PARAM: + hash += zend_inline_hash_func(param->str, param->len); + hash += param->num; + break; + + case NUMERIC_METHOD_PARAM: + hash += zend_inline_hash_func(param->method.class, strlen(param->method.class)); + hash += zend_inline_hash_func(param->method.name, strlen(param->method.name)); + if (param->num) + hash+= param->num; + break; + + case EMPTY_PARAM: { /* do nothing */ } break; + } + + return hash; +} /* }}} */ + +PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */ +{ + if (l && r) { + if (l->type == r->type) { + switch (l->type) { + + case NUMERIC_FUNCTION_PARAM: + if (l->num != r->num) { + break; + } + /* break intentionally omitted */ + + case STR_PARAM: + return (l->len == r->len) && + (memcmp(l->str, r->str, l->len) == SUCCESS); + + case NUMERIC_PARAM: + return (l->num == r->num); + + case ADDR_PARAM: + return (l->addr == r->addr); + + case FILE_PARAM: { + if (l->file.line == r->file.line) { + size_t lengths[2] = { + strlen(l->file.name), strlen(r->file.name)}; + + if (lengths[0] == lengths[1]) { + if ((!l->num && !r->num) || (l->num == r->num)) { + return (memcmp( + l->file.name, r->file.name, lengths[0]) == SUCCESS); + } + } + } + } break; + + case NUMERIC_METHOD_PARAM: + if (l->num != r->num) { + break; + } + /* break intentionally omitted */ + + case METHOD_PARAM: { + size_t lengths[2] = { + strlen(l->method.class), strlen(r->method.class)}; + if (lengths[0] == lengths[1]) { + if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) { + lengths[0] = strlen(l->method.name); + lengths[1] = strlen(r->method.name); + + if (lengths[0] == lengths[1]) { + return (memcmp( + l->method.name, r->method.name, lengths[0]) == SUCCESS); + } + } + } + } break; + + case EMPTY_PARAM: + return 1; + } + } + } + return 0; +} /* }}} */ + +PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */ +{ + char *p; + char b[PHPDBG_MAX_CMD]; + int l=0; + enum states { + IN_BETWEEN, + IN_WORD, + IN_STRING + } state = IN_BETWEEN; + phpdbg_input_t **argv = NULL; + + argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*)); + (*argc) = 0; + +#define RESET_STATE() do { \ + phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \ + if (arg) { \ + b[l]=0; \ + arg->length = l; \ + arg->string = estrndup(b, arg->length); \ + arg->argv = NULL; \ + arg->argc = 0; \ + argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \ + argv[(*argc)++] = arg; \ + l = 0; \ + } \ + state = IN_BETWEEN; \ +} while (0) + + for (p = buffer; *p != '\0'; p++) { + int c = (unsigned char) *p; + switch (state) { + case IN_BETWEEN: + if (isspace(c)) { + continue; + } + if (c == '"') { + state = IN_STRING; + continue; + } + state = IN_WORD; + b[l++]=c; + continue; + + case IN_STRING: + if (c == '"') { + if (buffer[(p - buffer)-1] == '\\') { + b[l-1]=c; + continue; + } + RESET_STATE(); + } else { + b[l++]=c; + } + continue; + + case IN_WORD: + if (isspace(c)) { + RESET_STATE(); + } else { + b[l++]=c; + } + continue; + } + } + + switch (state) { + case IN_WORD: { + RESET_STATE(); + } break; + + case IN_STRING: + phpdbg_error( + "Malformed command line (unclosed quote) @ %ld: %s!", + (p - buffer)-1, &buffer[(p - buffer)-1]); + break; + + case IN_BETWEEN: + break; + } + + if ((*argc) == 0) { + /* not needed */ + efree(argv); + + /* to be sure */ + return NULL; + } + + return argv; +} /* }}} */ + +PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ +{ + phpdbg_input_t *buffer = NULL; + char *cmd = NULL; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && + (buffered == NULL)) { + fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); + } + + if (buffered == NULL) { - #ifndef HAVE_LIBREADLINE - char buf[PHPDBG_MAX_CMD]; - if ((!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && !phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) || - !fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { - /* the user has gone away */ - phpdbg_error("Failed to read console!"); ++disconnect: ++ if (0) { + PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED); + zend_bailout(); + return NULL; + } + ++#ifndef HAVE_LIBREADLINE ++ char buf[PHPDBG_MAX_CMD]; ++ if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { ++ if (!phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) { ++ goto disconnect; ++ } ++ } ++ ++ /* note: EOF is ignored */ ++readline: ++ if (!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { ++ /* the user has gone away */ ++ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { ++ goto disconnect; ++ } else goto readline; ++ } ++ + cmd = buf; +#else ++ /* note: EOF makes readline write prompt again in local console mode */ ++readline: + if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + char buf[PHPDBG_MAX_CMD]; + if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { + cmd = buf; - } else cmd = NULL; ++ } else goto disconnect; + } else cmd = readline(phpdbg_get_prompt(TSRMLS_C)); + + if (!cmd) { - /* the user has gone away */ - phpdbg_error("Failed to read console!"); - PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED); - zend_bailout(); - return NULL; ++ goto readline; + } + + if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + add_history(cmd); + } +#endif + } else cmd = buffered; + + /* allocate and sanitize buffer */ + buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t)); + if (!buffer) { + return NULL; + } + + buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length); + + /* store constant pointer to start of buffer */ + buffer->start = (char* const*) buffer->string; + + buffer->argv = phpdbg_read_argv( + buffer->string, &buffer->argc TSRMLS_CC); + +#ifdef PHPDBG_DEBUG + if (buffer->argc) { + int arg = 0; + + while (arg < buffer->argc) { + phpdbg_debug( + "argv %d=%s", arg, buffer->argv[arg]->string); + arg++; + } + } +#endif + +#ifdef HAVE_LIBREADLINE + if (!buffered && cmd && + !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + free(cmd); + } +#endif + + return buffer; + } + + return NULL; +} /* }}} */ + +PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */ +{ + if (argv) { + if (argc) { + int arg; + for (arg=0; arg<argc; arg++) { + phpdbg_destroy_input( + &argv[arg] TSRMLS_CC); + } + } + efree(argv); + } + +} /* }}} */ + +PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */ +{ + if (*input) { + if ((*input)->string) { + efree((*input)->string); + } + + phpdbg_destroy_argv( + (*input)->argv, (*input)->argc TSRMLS_CC); + + efree(*input); + } +} /* }}} */ + +PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +{ + int rc = FAILURE; + + if (input->argc > 0) { + while (command && command->name && command->handler) { + if (((command->name_len == input->argv[0]->length) && + (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) || + (command->alias && + (input->argv[0]->length == 1) && + (command->alias == *input->argv[0]->string))) { + + phpdbg_param_t param; + + param.type = EMPTY_PARAM; + + if (input->argc > 1) { + if (command->subs) { + phpdbg_input_t sub = *input; + + sub.string += input->argv[0]->length; + sub.length -= input->argv[0]->length; + + sub.string = phpdbg_trim( + sub.string, sub.length, &sub.length); + + sub.argc--; + sub.argv++; + + phpdbg_debug( + "trying sub commands in \"%s\" for \"%s\" with %d arguments", + command->name, sub.argv[0]->string, sub.argc-1); + + if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) { + efree(sub.string); + return SUCCESS; + } + + efree(sub.string); + } + + /* no sub command found */ + { + char *store = input->string; + + input->string += input->argv[0]->length; + input->length -= input->argv[0]->length; + + input->string = phpdbg_trim( + input->string, input->length, &input->length); + + efree(store); + } + + /* pass parameter on */ + phpdbg_parse_param( + input->string, + input->length, + ¶m TSRMLS_CC); + } + + phpdbg_debug( + "found command %s for %s with %d arguments", + command->name, input->argv[0]->string, input->argc-1); + { + int arg; + for (arg=1; arg<input->argc; arg++) { + phpdbg_debug( + "\t#%d: [%s=%zu]", + arg, + input->argv[arg]->string, + input->argv[arg]->length); + } + } + + rc = command->handler(¶m, input TSRMLS_CC); + + /* only set last command when it is worth it! */ + if ((rc != FAILURE) && + !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { + PHPDBG_G(lcmd) = (phpdbg_command_t*) command; + phpdbg_clear_param( + &PHPDBG_G(lparam) TSRMLS_CC); + PHPDBG_G(lparam) = param; + } + break; + } + command++; + } + } else { + /* this should NEVER happen */ + phpdbg_error( + "No function executed!!"); + } + + return rc; +} /* }}} */ + diff --cc sapi/phpdbg/phpdbg_opcode.c index 7e64d16,0000000..50073eb mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@@ -1,361 -1,0 +1,363 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg.h" +#include "zend_vm_opcodes.h" +#include "zend_compile.h" +#include "phpdbg_opcode.h" +#include "phpdbg_utils.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */ +{ + int iter = 0; + + while (iter < ops->last_literal) { + if (literal == &ops->literals[iter]) { + return iter; + } + iter++; + } + + return 0; +} /* }}} */ + +static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */ +{ + char *decode = NULL; + + switch (type &~ EXT_TYPE_UNUSED) { + case IS_CV: + asprintf(&decode, "$%s", ops->vars[op->var].name); + break; + + case IS_VAR: + case IS_TMP_VAR: { + zend_ulong id = 0, *pid = NULL; - if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) { - id = zend_hash_num_elements(vars); - zend_hash_index_update( - vars, (zend_ulong) ops->vars - op->var, - (void**) &id, - sizeof(zend_ulong), NULL); - } else id = *pid; ++ if (vars != NULL) { ++ if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) { ++ id = zend_hash_num_elements(vars); ++ zend_hash_index_update( ++ vars, (zend_ulong) ops->vars - op->var, ++ (void**) &id, ++ sizeof(zend_ulong), NULL); ++ } else id = *pid; ++ } + asprintf(&decode, "@%lu", id); + } break; + + case IS_CONST: + asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC)); + break; + + case IS_UNUSED: + asprintf(&decode, "<unused>"); + break; + } + return decode; +} /* }}} */ + +char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */ +{ + char *decode[4] = {NULL, NULL, NULL, NULL}; + + switch (op->opcode) { + case ZEND_JMP: +#ifdef ZEND_GOTO + case ZEND_GOTO: +#endif +#ifdef ZEND_FAST_CALL + case ZEND_FAST_CALL: +#endif + asprintf(&decode[1], "J%ld", op->op1.jmp_addr - ops->opcodes); + goto format; + + case ZEND_JMPZNZ: + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + asprintf( + &decode[2], "J%u or J%lu", op->op2.opline_num, op->extended_value); + goto result; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + +#ifdef ZEND_JMP_SET + case ZEND_JMP_SET: +#endif +#ifdef ZEND_JMP_SET_VAR + case ZEND_JMP_SET_VAR: +#endif + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + asprintf( + &decode[2], "J%ld", op->op2.jmp_addr - ops->opcodes); + goto result; + + case ZEND_RECV_INIT: + goto result; + + default: { + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC); +result: + decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC); +format: + asprintf( + &decode[0], + "%-20s %-20s %-20s", + decode[1] ? decode[1] : "", + decode[2] ? decode[2] : "", + decode[3] ? decode[3] : ""); + } + } + + if (decode[1]) + free(decode[1]); + if (decode[2]) + free(decode[2]); + if (decode[3]) + free(decode[3]); + + return decode[0]; +} /* }}} */ + +void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */ +{ + /* force out a line while stepping so the user knows what is happening */ + if (ignore_flags || + (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) || + (PHPDBG_G(oplog)))) { + + zend_op *opline = execute_data->opline; + char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC); + + if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { + /* output line info */ + phpdbg_notice("L%-5u %16p %-30s %s %s", + opline->lineno, + opline, + phpdbg_decode_opcode(opline->opcode), + decode, + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } + + if (!ignore_flags && PHPDBG_G(oplog)) { + phpdbg_log_ex(PHPDBG_G(oplog), "L%-5u %16p %-30s %s %s", + opline->lineno, + opline, + phpdbg_decode_opcode(opline->opcode), + decode, + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } + + if (decode) { + free(decode); + } + } +} /* }}} */ + +void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */ +{ + phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC); +} /* }}} */ + +const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */ +{ +#define CASE(s) case s: return #s + switch (opcode) { + CASE(ZEND_NOP); + CASE(ZEND_ADD); + CASE(ZEND_SUB); + CASE(ZEND_MUL); + CASE(ZEND_DIV); + CASE(ZEND_MOD); + CASE(ZEND_SL); + CASE(ZEND_SR); + CASE(ZEND_CONCAT); + CASE(ZEND_BW_OR); + CASE(ZEND_BW_AND); + CASE(ZEND_BW_XOR); + CASE(ZEND_BW_NOT); + CASE(ZEND_BOOL_NOT); + CASE(ZEND_BOOL_XOR); + CASE(ZEND_IS_IDENTICAL); + CASE(ZEND_IS_NOT_IDENTICAL); + CASE(ZEND_IS_EQUAL); + CASE(ZEND_IS_NOT_EQUAL); + CASE(ZEND_IS_SMALLER); + CASE(ZEND_IS_SMALLER_OR_EQUAL); + CASE(ZEND_CAST); + CASE(ZEND_QM_ASSIGN); + CASE(ZEND_ASSIGN_ADD); + CASE(ZEND_ASSIGN_SUB); + CASE(ZEND_ASSIGN_MUL); + CASE(ZEND_ASSIGN_DIV); + CASE(ZEND_ASSIGN_MOD); + CASE(ZEND_ASSIGN_SL); + CASE(ZEND_ASSIGN_SR); + CASE(ZEND_ASSIGN_CONCAT); + CASE(ZEND_ASSIGN_BW_OR); + CASE(ZEND_ASSIGN_BW_AND); + CASE(ZEND_ASSIGN_BW_XOR); + CASE(ZEND_PRE_INC); + CASE(ZEND_PRE_DEC); + CASE(ZEND_POST_INC); + CASE(ZEND_POST_DEC); + CASE(ZEND_ASSIGN); + CASE(ZEND_ASSIGN_REF); + CASE(ZEND_ECHO); + CASE(ZEND_PRINT); + CASE(ZEND_JMP); + CASE(ZEND_JMPZ); + CASE(ZEND_JMPNZ); + CASE(ZEND_JMPZNZ); + CASE(ZEND_JMPZ_EX); + CASE(ZEND_JMPNZ_EX); + CASE(ZEND_CASE); + CASE(ZEND_SWITCH_FREE); + CASE(ZEND_BRK); + CASE(ZEND_CONT); + CASE(ZEND_BOOL); + CASE(ZEND_INIT_STRING); + CASE(ZEND_ADD_CHAR); + CASE(ZEND_ADD_STRING); + CASE(ZEND_ADD_VAR); + CASE(ZEND_BEGIN_SILENCE); + CASE(ZEND_END_SILENCE); + CASE(ZEND_INIT_FCALL_BY_NAME); + CASE(ZEND_DO_FCALL); + CASE(ZEND_DO_FCALL_BY_NAME); + CASE(ZEND_RETURN); + CASE(ZEND_RECV); + CASE(ZEND_RECV_INIT); + CASE(ZEND_SEND_VAL); + CASE(ZEND_SEND_VAR); + CASE(ZEND_SEND_REF); + CASE(ZEND_NEW); + CASE(ZEND_INIT_NS_FCALL_BY_NAME); + CASE(ZEND_FREE); + CASE(ZEND_INIT_ARRAY); + CASE(ZEND_ADD_ARRAY_ELEMENT); + CASE(ZEND_INCLUDE_OR_EVAL); + CASE(ZEND_UNSET_VAR); + CASE(ZEND_UNSET_DIM); + CASE(ZEND_UNSET_OBJ); + CASE(ZEND_FE_RESET); + CASE(ZEND_FE_FETCH); + CASE(ZEND_EXIT); + CASE(ZEND_FETCH_R); + CASE(ZEND_FETCH_DIM_R); + CASE(ZEND_FETCH_OBJ_R); + CASE(ZEND_FETCH_W); + CASE(ZEND_FETCH_DIM_W); + CASE(ZEND_FETCH_OBJ_W); + CASE(ZEND_FETCH_RW); + CASE(ZEND_FETCH_DIM_RW); + CASE(ZEND_FETCH_OBJ_RW); + CASE(ZEND_FETCH_IS); + CASE(ZEND_FETCH_DIM_IS); + CASE(ZEND_FETCH_OBJ_IS); + CASE(ZEND_FETCH_FUNC_ARG); + CASE(ZEND_FETCH_DIM_FUNC_ARG); + CASE(ZEND_FETCH_OBJ_FUNC_ARG); + CASE(ZEND_FETCH_UNSET); + CASE(ZEND_FETCH_DIM_UNSET); + CASE(ZEND_FETCH_OBJ_UNSET); + CASE(ZEND_FETCH_DIM_TMP_VAR); + CASE(ZEND_FETCH_CONSTANT); + CASE(ZEND_GOTO); + CASE(ZEND_EXT_STMT); + CASE(ZEND_EXT_FCALL_BEGIN); + CASE(ZEND_EXT_FCALL_END); + CASE(ZEND_EXT_NOP); + CASE(ZEND_TICKS); + CASE(ZEND_SEND_VAR_NO_REF); + CASE(ZEND_CATCH); + CASE(ZEND_THROW); + CASE(ZEND_FETCH_CLASS); + CASE(ZEND_CLONE); + CASE(ZEND_RETURN_BY_REF); + CASE(ZEND_INIT_METHOD_CALL); + CASE(ZEND_INIT_STATIC_METHOD_CALL); + CASE(ZEND_ISSET_ISEMPTY_VAR); + CASE(ZEND_ISSET_ISEMPTY_DIM_OBJ); + CASE(ZEND_PRE_INC_OBJ); + CASE(ZEND_PRE_DEC_OBJ); + CASE(ZEND_POST_INC_OBJ); + CASE(ZEND_POST_DEC_OBJ); + CASE(ZEND_ASSIGN_OBJ); + CASE(ZEND_INSTANCEOF); + CASE(ZEND_DECLARE_CLASS); + CASE(ZEND_DECLARE_INHERITED_CLASS); + CASE(ZEND_DECLARE_FUNCTION); + CASE(ZEND_RAISE_ABSTRACT_ERROR); + CASE(ZEND_DECLARE_CONST); + CASE(ZEND_ADD_INTERFACE); + CASE(ZEND_DECLARE_INHERITED_CLASS_DELAYED); + CASE(ZEND_VERIFY_ABSTRACT_CLASS); + CASE(ZEND_ASSIGN_DIM); + CASE(ZEND_ISSET_ISEMPTY_PROP_OBJ); + CASE(ZEND_HANDLE_EXCEPTION); + CASE(ZEND_USER_OPCODE); +#ifdef ZEND_JMP_SET + CASE(ZEND_JMP_SET); +#endif + CASE(ZEND_DECLARE_LAMBDA_FUNCTION); +#ifdef ZEND_ADD_TRAIT + CASE(ZEND_ADD_TRAIT); +#endif +#ifdef ZEND_BIND_TRAITS + CASE(ZEND_BIND_TRAITS); +#endif +#ifdef ZEND_SEPARATE + CASE(ZEND_SEPARATE); +#endif +#ifdef ZEND_QM_ASSIGN_VAR + CASE(ZEND_QM_ASSIGN_VAR); +#endif +#ifdef ZEND_JMP_SET_VAR + CASE(ZEND_JMP_SET_VAR); +#endif +#ifdef ZEND_DISCARD_EXCEPTION + CASE(ZEND_DISCARD_EXCEPTION); +#endif +#ifdef ZEND_YIELD + CASE(ZEND_YIELD); +#endif +#ifdef ZEND_GENERATOR_RETURN + CASE(ZEND_GENERATOR_RETURN); +#endif +#ifdef ZEND_FAST_CALL + CASE(ZEND_FAST_CALL); +#endif +#ifdef ZEND_FAST_RET + CASE(ZEND_FAST_RET); +#endif +#ifdef ZEND_RECV_VARIADIC + CASE(ZEND_RECV_VARIADIC); +#endif + CASE(ZEND_OP_DATA); + default: + return "UNKNOWN"; + } +} /* }}} */ diff --cc sapi/phpdbg/phpdbg_prompt.c index bb26556,0000000..cb46407 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@@ -1,1328 -1,0 +1,1330 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include <stdio.h> +#include <string.h> +#include "zend.h" +#include "zend_compile.h" +#include "phpdbg.h" +#include "phpdbg_help.h" +#include "phpdbg_print.h" +#include "phpdbg_info.h" +#include "phpdbg_break.h" +#include "phpdbg_bp.h" +#include "phpdbg_opcode.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" +#include "phpdbg_prompt.h" +#include "phpdbg_cmd.h" +#include "phpdbg_set.h" +#include "phpdbg_frame.h" + +/* {{{ command declarations */ +const phpdbg_command_t phpdbg_prompt_commands[] = { + PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1), + PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0), + PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1), + PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0), + PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0), + PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1), + PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0), + PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0), + PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0), + PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2), + PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1), + PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1), + PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2), + PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1), + PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0), + PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0), + PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2), + PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1), + PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0), + PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1), + PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1), + PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1), + PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1), + PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0), + PHPDBG_END_COMMAND +}; /* }}} */ + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +{ + phpdbg_input_t *function = input->argv[0]; + + if (zend_hash_exists( + &PHPDBG_G(registered), function->string, function->length+1)) { + + zval fname, *fretval; + zend_fcall_info fci; + + ZVAL_STRINGL(&fname, function->string, function->length, 1); + + memset(&fci, 0, sizeof(zend_fcall_info)); + + fci.size = sizeof(zend_fcall_info); + fci.function_table = &PHPDBG_G(registered); + fci.function_name = &fname; + fci.symbol_table = EG(active_symbol_table); + fci.object_ptr = NULL; + fci.retval_ptr_ptr = &fretval; + fci.no_separation = 1; + + if (input->argc > 1) { + int param; + zval params; + + array_init(¶ms); + + for (param = 0; param < (input->argc-1); param++) { + add_next_index_stringl( + ¶ms, + input->argv[param+1]->string, + input->argv[param+1]->length, 1); + + phpdbg_debug( + "created param[%d] from argv[%d]: %s", + param, param+1, input->argv[param+1]->string); + } + + zend_fcall_info_args(&fci, ¶ms TSRMLS_CC); + } else { + fci.params = NULL; + fci.param_count = 0; + } + + phpdbg_debug( + "created %d params from %d arguments", + fci.param_count, input->argc); + + zend_call_function(&fci, NULL TSRMLS_CC); + + if (fretval) { + zend_print_zval_r( + fretval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + } + + zval_dtor(&fname); + + return SUCCESS; + } + + return FAILURE; +} /* }}} */ + +void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */ +{ + struct stat sb; + + if (init_file && VCWD_STAT(init_file, &sb) != -1) { + FILE *fp = fopen(init_file, "r"); + if (fp) { + int line = 1; + + char cmd[PHPDBG_MAX_CMD]; + size_t cmd_len = 0L; + char *code = NULL; + size_t code_len = 0L; + zend_bool in_code = 0; + + while (fgets(cmd, PHPDBG_MAX_CMD, fp) != NULL) { + cmd_len = strlen(cmd)-1; + + while (cmd_len > 0L && isspace(cmd[cmd_len-1])) + cmd_len--; + + cmd[cmd_len] = '\0'; + + if (*cmd && cmd_len > 0L && cmd[0] != '#') { + if (cmd_len == 2) { + if (memcmp(cmd, "<:", sizeof("<:")-1) == SUCCESS) { + in_code = 1; + goto next_line; + } else { + if (memcmp(cmd, ":>", sizeof(":>")-1) == SUCCESS) { + in_code = 0; + code[code_len] = '\0'; + { + zend_eval_stringl( + code, code_len, NULL, "phpdbginit code" TSRMLS_CC); + } + free(code); + code = NULL; + goto next_line; + } + } + } + + if (in_code) { + if (code == NULL) { + code = malloc(cmd_len + 1); + } else code = realloc(code, code_len + cmd_len + 1); + + if (code) { + memcpy( + &code[code_len], cmd, cmd_len); + code_len += cmd_len; + } + goto next_line; + } + + { + phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC); + switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { + phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string); + } + } + break; + } + phpdbg_destroy_input(&input TSRMLS_CC); + } + + } +next_line: + line++; + } + + if (code) { + free(code); + } + + fclose(fp); + } else { + phpdbg_error( + "Failed to open %s for initialization", init_file); + } + + if (free_init) { + free(init_file); + } + } +} /* }}} */ + +void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC) /* {{{ */ +{ + if (!init_file && use_default) { + char *scan_dir = getenv("PHP_INI_SCAN_DIR"); + int i; + + phpdbg_try_file_init(PHPDBG_STRL(PHP_CONFIG_FILE_PATH "/" PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + + if (!scan_dir) { + scan_dir = PHP_CONFIG_FILE_SCAN_DIR; + } + while (*scan_dir != 0) { + i = 0; + while (scan_dir[i] != ':') { + if (scan_dir[i++] == 0) { + i = -1; + break; + } + } + if (i != -1) { + scan_dir[i] = 0; + } + + asprintf( + &init_file, "%s/%s", scan_dir, PHPDBG_INIT_FILENAME); + phpdbg_try_file_init(init_file, strlen(init_file), 1 TSRMLS_CC); + if (i == -1) { + break; + } + scan_dir += i + 1; + } + + phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + } else { + phpdbg_try_file_init(init_file, init_file_len, 1 TSRMLS_CC); + } +} + +PHPDBG_COMMAND(exec) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + struct stat sb; + + if (VCWD_STAT(param->str, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + char *res = phpdbg_resolve_path(param->str TSRMLS_CC); + size_t res_len = strlen(res); + + if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { + + if (PHPDBG_G(exec)) { + phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec)); + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + PHPDBG_G(exec_len) = 0L; + } + + if (PHPDBG_G(ops)) { + phpdbg_notice("Destroying compiled opcodes"); + phpdbg_clean(0 TSRMLS_CC); + } + + PHPDBG_G(exec) = res; + PHPDBG_G(exec_len) = res_len; + + phpdbg_notice("Set execution context: %s", PHPDBG_G(exec)); + } else { + phpdbg_notice("Execution context not changed"); + } + } else { + phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str); + } + } else { + phpdbg_error("Cannot stat %s, ensure the file exists", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +int phpdbg_compile(TSRMLS_D) /* {{{ */ +{ + zend_file_handle fh; + + if (EG(in_execution)) { + phpdbg_error("Cannot compile while in execution"); + return FAILURE; + } + + phpdbg_notice("Attempting compilation of %s", PHPDBG_G(exec)); + + if (php_stream_open_for_zend_ex(PHPDBG_G(exec), &fh, + USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) { + + PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE TSRMLS_CC); + zend_destroy_file_handle(&fh TSRMLS_CC); + + phpdbg_notice("Success"); + return SUCCESS; + } else { + phpdbg_error("Could not open file %s", PHPDBG_G(exec)); + } + + return FAILURE; +} /* }}} */ + +PHPDBG_COMMAND(compile) /* {{{ */ +{ + if (!PHPDBG_G(exec)) { + phpdbg_error("No execution context"); + return SUCCESS; + } + + if (!EG(in_execution)) { + if (PHPDBG_G(ops)) { + phpdbg_error("Destroying previously compiled opcodes"); + phpdbg_clean(0 TSRMLS_CC); + } + } + + phpdbg_compile(TSRMLS_C); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(step) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + case NUMERIC_PARAM: { + if (param->type == NUMERIC_PARAM && param->num) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } else { + PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; + } + + phpdbg_notice("Stepping %s", + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(next) /* {{{ */ +{ + return PHPDBG_NEXT; +} /* }}} */ + +PHPDBG_COMMAND(until) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_UNTIL; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + zend_op *opline = &EG(active_op_array)->opcodes[self]; + + for (next = self; next < EG(active_op_array)->last; next++) { + if (EG(active_op_array)->opcodes[next].lineno != opline->lineno) { + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_UNTIL; +} /* }}} */ + +PHPDBG_COMMAND(finish) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_FINISH; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + + for (next = self; next < EG(active_op_array)->last; next++) { + switch (EG(active_op_array)->opcodes[next].opcode) { + case ZEND_RETURN: + case ZEND_THROW: + case ZEND_EXIT: +#ifdef ZEND_YIELD + case ZEND_YIELD: +#endif + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_FINISH; +} /* }}} */ + +PHPDBG_COMMAND(leave) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_LEAVE; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + + for (next = self; next < EG(active_op_array)->last; next++) { + switch (EG(active_op_array)->opcodes[next].opcode) { + case ZEND_RETURN: + case ZEND_THROW: + case ZEND_EXIT: +#ifdef ZEND_YIELD + case ZEND_YIELD: +#endif + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_LEAVE; +} /* }}} */ + +PHPDBG_COMMAND(frame) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: + phpdbg_switch_frame(param->num TSRMLS_CC); + break; + + case EMPTY_PARAM: + phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */ +{ + zend_fcall_info fci; + + zval fname, + *trace, + exception; + + /* get filename and linenumber before unsetting exception */ + const char *filename = zend_get_executed_filename(TSRMLS_C); + zend_uint lineno = zend_get_executed_lineno(TSRMLS_C); + + /* copy exception */ + exception = *EG(exception); + zval_copy_ctor(&exception); + EG(exception) = NULL; + + phpdbg_error( + "Uncaught %s!", + Z_OBJCE(exception)->name); + + /* call __toString */ + ZVAL_STRINGL(&fname, "__tostring", sizeof("__tostring")-1, 1); + fci.size = sizeof(fci); + fci.function_table = &Z_OBJCE(exception)->function_table; + fci.function_name = &fname; + fci.symbol_table = NULL; + fci.object_ptr = &exception; + fci.retval_ptr_ptr = &trace; + fci.param_count = 0; + fci.params = NULL; + fci.no_separation = 1; + zend_call_function(&fci, NULL TSRMLS_CC); + + if (trace) { + phpdbg_writeln( + "Uncaught %s", Z_STRVAL_P(trace)); + /* remember to dtor trace */ + zval_ptr_dtor(&trace); + } + + /* output useful information about address */ + phpdbg_writeln( + "Stacked entered at %p in %s on line %u", + EG(active_op_array)->opcodes, filename, lineno); + + zval_dtor(&fname); + zval_dtor(&exception); +} /* }}} */ + +PHPDBG_COMMAND(run) /* {{{ */ +{ + if (EG(in_execution)) { + phpdbg_error("Cannot start another execution while one is in progress"); + return SUCCESS; + } + + if (PHPDBG_G(ops) || PHPDBG_G(exec)) { + zend_op **orig_opline = EG(opline_ptr); + zend_op_array *orig_op_array = EG(active_op_array); + zval **orig_retval_ptr = EG(return_value_ptr_ptr); - ++ zend_bool restore = 1; ++ + if (!PHPDBG_G(ops)) { + if (phpdbg_compile(TSRMLS_C) == FAILURE) { + phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec)); + goto out; + } + } + + EG(active_op_array) = PHPDBG_G(ops); + EG(return_value_ptr_ptr) = &PHPDBG_G(retval); + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + + /* clean seek state */ + PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK; + zend_hash_clean( + &PHPDBG_G(seek)); + + /* reset hit counters */ + phpdbg_reset_breakpoints(TSRMLS_C); + + zend_try { + php_output_activate(TSRMLS_C); + PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE; + zend_execute(EG(active_op_array) TSRMLS_CC); + PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE; + php_output_deactivate(TSRMLS_C); + } zend_catch { + EG(active_op_array) = orig_op_array; + EG(opline_ptr) = orig_opline; + EG(return_value_ptr_ptr) = orig_retval_ptr; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + phpdbg_error("Caught exit/error from VM"); - goto out; ++ restore = 0; + } + } zend_end_try(); + - if (EG(exception)) { - phpdbg_handle_exception(TSRMLS_C); - } - - EG(active_op_array) = orig_op_array; - EG(opline_ptr) = orig_opline; - EG(return_value_ptr_ptr) = orig_retval_ptr; ++ if (restore) { ++ if (EG(exception)) { ++ phpdbg_handle_exception(TSRMLS_C); ++ } + ++ EG(active_op_array) = orig_op_array; ++ EG(opline_ptr) = orig_opline; ++ EG(return_value_ptr_ptr) = orig_retval_ptr; ++ } + } else { + phpdbg_error("Nothing to execute!"); + } + +out: + PHPDBG_FRAME(num) = 0; + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(eval) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING); + zval retval; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING; + } + + /* disable stepping while eval() in progress */ + PHPDBG_G(flags) |= PHPDBG_IN_EVAL; + zend_try { + if (zend_eval_stringl(param->str, param->len, + &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) { + zend_print_zval_r( + &retval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + zval_dtor(&retval); + } + } zend_end_try(); + PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL; + + /* switch stepping back on */ + if (stepping && + !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } + + CG(unclean_shutdown) = 0; + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(back) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing!"); + return SUCCESS; + } + + switch (param->type) { + case EMPTY_PARAM: + case NUMERIC_PARAM: + phpdbg_dump_backtrace( + (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(print) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: { + phpdbg_writeln(SEPARATE); + phpdbg_notice("Execution Context Information"); +#ifdef HAVE_LIBREADLINE + phpdbg_writeln("Readline\tyes"); +#else + phpdbg_writeln("Readline\tno"); +#endif + + phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); + phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no"); + phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off"); + phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off"); + + if (PHPDBG_G(ops)) { + phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last); + + if (PHPDBG_G(ops)->last_var) { + phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1); + } else { + phpdbg_writeln("Variables\tNone"); + } + } + + phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no"); + if (EG(in_execution)) { + phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret)); + } + + phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_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("Included\t%d", zend_hash_num_elements(&EG(included_files))); + + phpdbg_writeln(SEPARATE); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(info) /* {{{ */ +{ + phpdbg_error( + "No information command selected!"); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(set) /* {{{ */ +{ + phpdbg_error( + "No information command selected!"); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(break) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + phpdbg_set_breakpoint_file( + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC); + break; + case ADDR_PARAM: + phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); + break; + case NUMERIC_PARAM: + if (PHPDBG_G(exec)) { + phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC); + } else { + phpdbg_error("Execution context not set!"); + } + break; + case METHOD_PARAM: + phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC); + break; + case NUMERIC_METHOD_PARAM: + phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC); + break; + case NUMERIC_FUNCTION_PARAM: + phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC); + break; + case FILE_PARAM: + phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC); + break; + case STR_PARAM: + phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(shell) /* {{{ */ +{ + /* don't allow this to loop, ever ... */ + switch (param->type) { + case STR_PARAM: { + FILE *fd = NULL; + if ((fd=VCWD_POPEN((char*)param->str, "w"))) { + /* do something perhaps ?? do we want input ?? */ + fclose(fd); + } else { + phpdbg_error( + "Failed to execute %s", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(source) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + if (input->argc > 2) { + if (phpdbg_argv_is(1, "export")) { + FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+"); + if (h) { + phpdbg_export_breakpoints(h TSRMLS_CC); + fclose(h); + } else phpdbg_error("Failed to open %s", input->argv[1]->string); + } else { + phpdbg_error( + "Incorrect usage of source command, see help"); + } + } else { + struct stat sb; + if (VCWD_STAT(param->str, &sb) != -1) { + phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC); + } else phpdbg_error("Cannot stat %s", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(register) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + zend_function *function; + char *lcname = zend_str_tolower_dup(param->str, param->len); + size_t lcname_len = strlen(lcname); + + if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) { + if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) { + zend_hash_update( + &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL); + function_add_ref(function); + + phpdbg_notice( + "Registered %s", lcname); + } else { + phpdbg_error("The requested function (%s) could not be found", param->str); + } + } else { + phpdbg_error( + "The requested name (%s) is already in use", lcname); + } + + efree(lcname); + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(quit) /* {{{ */ +{ + /* don't allow this to loop, ever ... */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + + phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC); + + PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + zend_bailout(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(clean) /* {{{ */ +{ + if (EG(in_execution)) { + phpdbg_error("Cannot clean environment while executing"); + return SUCCESS; + } + + phpdbg_notice("Cleaning Execution Environment"); + + phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files))); + + phpdbg_clean(1 TSRMLS_CC); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(clear) /* {{{ */ +{ + phpdbg_notice("Clearing Breakpoints"); + + phpdbg_writeln("File\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE])); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM])); + phpdbg_writeln("Methods\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD])); + phpdbg_writeln("Oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE])); + phpdbg_writeln("File oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE])); + phpdbg_writeln("Function oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE])); + phpdbg_writeln("Method oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE])); + phpdbg_writeln("Conditionals\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND])); + + phpdbg_clear_breakpoints(TSRMLS_C); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(aliases) /* {{{ */ +{ + const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; + + phpdbg_help_header(); + phpdbg_writeln("Below are the aliased, short versions of all supported commands"); + while (prompt_command && prompt_command->name) { + if (prompt_command->alias) { + if (prompt_command->subs) { + const phpdbg_command_t *sub_command = prompt_command->subs; + phpdbg_writeln(EMPTY); + phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name); + while (sub_command && sub_command->name) { + if (sub_command->alias) { + phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias, + sub_command->name, sub_command->tip); + } + ++sub_command; + } + phpdbg_writeln(EMPTY); + } else { + phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias, + prompt_command->name, prompt_command->tip); + } + } + + ++prompt_command; + } + phpdbg_help_footer(); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(help) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: { + const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; + const phpdbg_command_t *help_command = phpdbg_help_commands; + + phpdbg_help_header(); + phpdbg_writeln("To get help regarding a specific command type \"help command\""); + + phpdbg_notice("Commands"); + + while (prompt_command && prompt_command->name) { + phpdbg_writeln( + " %10s\t%s", prompt_command->name, prompt_command->tip); + ++prompt_command; + } + + phpdbg_notice("Help Commands"); + + while (help_command && help_command->name) { + phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip); + ++help_command; + } + + phpdbg_help_footer(); + } break; + + default: { + phpdbg_error( + "No help can be found for the subject \"%s\"", param->str); + } + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(quiet) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: { + if (param->num) { + PHPDBG_G(flags) |= PHPDBG_IS_QUIET; + } else { + PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET; + } + phpdbg_notice("Quietness %s", + (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled"); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(list) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: + case EMPTY_PARAM: + return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); + + case FILE_PARAM: + return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); + + case STR_PARAM: + phpdbg_list_function_byname(param->str, param->len TSRMLS_CC); + break; + + case METHOD_PARAM: + return PHPDBG_LIST_HANDLER(method)(PHPDBG_COMMAND_ARGS); + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +int phpdbg_interactive(TSRMLS_D) /* {{{ */ +{ + int ret = SUCCESS; + phpdbg_input_t *input; + + PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE; + + input = phpdbg_read_input(NULL TSRMLS_CC); + + if (input && input->length > 0L) { + do { + switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { + phpdbg_error("Failed to execute %s!", input->string); + } + } + break; + + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: { + if (!EG(in_execution)) { + phpdbg_error("Not running"); + } + goto out; + } + } + + phpdbg_destroy_input(&input TSRMLS_CC); + } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L)); + + if (input && !input->length) + goto last; + + } else { +last: + if (PHPDBG_G(lcmd)) { + ret = PHPDBG_G(lcmd)->handler( + &PHPDBG_G(lparam), input TSRMLS_CC); + goto out; + } + } + +out: + phpdbg_destroy_input(&input TSRMLS_CC); + + if (EG(in_execution)) { + phpdbg_restore_frame(TSRMLS_C); + } + + PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE; + + return ret; +} /* }}} */ + +void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */ +{ + /* this is implicitly required */ + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + if (full) { + PHPDBG_G(flags) |= PHPDBG_IS_CLEANING; + + zend_bailout(); + } +} /* }}} */ + +static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_array, zend_bool nested TSRMLS_DC) /* {{{ */ +{ +#if PHP_VERSION_ID >= 50500 + return zend_create_execute_data_from_op_array(op_array, nested TSRMLS_CC); +#else + +#undef EX +#define EX(element) execute_data->element +#undef EX_CV +#define EX_CV(var) EX(CVs)[var] +#undef EX_CVs +#define EX_CVs() EX(CVs) +#undef EX_T +#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) +#undef EX_Ts +#define EX_Ts() EX(Ts) + + zend_execute_data *execute_data = (zend_execute_data *)zend_vm_stack_alloc( + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + + EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); + memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); + EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2))); + EX(fbc) = NULL; + EX(called_scope) = NULL; + EX(object) = NULL; + EX(old_error_reporting) = NULL; + EX(op_array) = op_array; + EX(symbol_table) = EG(active_symbol_table); + EX(prev_execute_data) = EG(current_execute_data); + EG(current_execute_data) = execute_data; + EX(nested) = nested; + + if (!op_array->run_time_cache && op_array->last_cache_slot) { + op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*)); + } + + if (op_array->this_var != -1 && EG(This)) { + Z_ADDREF_P(EG(This)); /* For $this pointer */ + if (!EG(active_symbol_table)) { + EX_CV(op_array->this_var) = (zval**)EX_CVs() + (op_array->last_var + op_array->this_var); + *EX_CV(op_array->this_var) = EG(This); + } else { + if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX_CV(op_array->this_var))==FAILURE) { + Z_DELREF_P(EG(This)); + } + } + } + + EX(opline) = UNEXPECTED((op_array->fn_flags & ZEND_ACC_INTERACTIVE) != 0) && EG(start_op) ? EG(start_op) : op_array->opcodes; + EG(opline_ptr) = &EX(opline); + + EX(function_state).function = (zend_function *) op_array; + EX(function_state).arguments = NULL; + + return execute_data; +#endif +} /* }}} */ + +#if PHP_VERSION_ID >= 50500 +void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */ +{ +#else +void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC) /* {{{ */ +{ + long long flags = 0; + zend_ulong address = 0L; + zend_execute_data *execute_data; + zend_bool nested = 0; +#endif + zend_bool original_in_execution = EG(in_execution); + HashTable vars; + +#if PHP_VERSION_ID < 50500 + if (EG(exception)) { + return; + } +#endif + + EG(in_execution) = 1; + +#if PHP_VERSION_ID >= 50500 + if (0) { +zend_vm_enter: + execute_data = phpdbg_create_execute_data(EG(active_op_array), 1 TSRMLS_CC); + } + zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0); +#else +zend_vm_enter: + execute_data = phpdbg_create_execute_data(op_array, nested TSRMLS_CC); + nested = 1; + zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0); +#endif + + while (1) { + + if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) { + /* resolve nth opline breakpoints */ + phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC); + } + +#ifdef ZEND_WIN32 + if (EG(timed_out)) { + zend_timeout(0); + } +#endif + +#define DO_INTERACTIVE() do { \ + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ + phpdbg_list_file( \ + zend_get_executed_filename(TSRMLS_C), \ + 3, \ + zend_get_executed_lineno(TSRMLS_C)-1, \ + zend_get_executed_lineno(TSRMLS_C) \ + TSRMLS_CC \ + ); \ + } \ + \ + do { \ + switch (phpdbg_interactive(TSRMLS_C)) { \ + case PHPDBG_LEAVE: \ + case PHPDBG_FINISH: \ + case PHPDBG_UNTIL: \ + case PHPDBG_NEXT:{ \ + goto next; \ + } \ + } \ + } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \ +} while (0) + + /* allow conditional breakpoints and + initialization to access the vm uninterrupted */ + if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) || + (PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { + /* skip possible breakpoints */ + goto next; + } + + /* perform seek operation */ + if (PHPDBG_G(flags) & PHPDBG_SEEK_MASK) { + /* current address */ + zend_ulong address = (zend_ulong) execute_data->opline; + + /* run to next line */ + if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_UNTIL; + zend_hash_clean( + &PHPDBG_G(seek)); + } else { + /* skip possible breakpoints */ + goto next; + } + } + + /* run to finish */ + if (PHPDBG_G(flags) & PHPDBG_IN_FINISH) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_FINISH; + zend_hash_clean( + &PHPDBG_G(seek)); + } + /* skip possible breakpoints */ + goto next; + } + + /* break for leave */ + if (PHPDBG_G(flags) & PHPDBG_IN_LEAVE) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_LEAVE; + zend_hash_clean( + &PHPDBG_G(seek)); + phpdbg_notice( + "Breaking for leave at %s:%u", + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C) + ); + DO_INTERACTIVE(); + } else { + /* skip possible breakpoints */ + goto next; + } + } + } + + /* not while in conditionals */ + phpdbg_print_opline_ex( + execute_data, &vars, 0 TSRMLS_CC); + + /* search for breakpoints */ + { + phpdbg_breakbase_t *brake; + + if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) && + (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) { + phpdbg_hit_breakpoint( + brake, 1 TSRMLS_CC); + DO_INTERACTIVE(); + } + } + + if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) { + DO_INTERACTIVE(); + } + +next: + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + phpdbg_writeln(EMPTY); + phpdbg_notice("Program received signal SIGINT"); + PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED; + DO_INTERACTIVE(); + } + + PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); + + if (PHPDBG_G(vmret) > 0) { + switch (PHPDBG_G(vmret)) { + case 1: + EG(in_execution) = original_in_execution; + zend_hash_destroy(&vars); + return; + case 2: +#if PHP_VERSION_ID < 50500 + op_array = EG(active_op_array); +#endif + zend_hash_destroy(&vars); + goto zend_vm_enter; + break; + case 3: + execute_data = EG(current_execute_data); + break; + default: + break; + } + } + } + zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); +} /* }}} */
diff --cc sapi/phpdbg/phpdbg.c index deb7be6,0000000..e7f841c mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@@ -1,1338 -1,0 +1,1348 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SIGNALS +# include <signal.h> +#endif +#include "phpdbg.h" +#include "phpdbg_prompt.h" +#include "phpdbg_bp.h" +#include "phpdbg_break.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" +#include "phpdbg_set.h" + +/* {{{ remote console headers */ +#ifndef _WIN32 +# include <sys/socket.h> +# include <sys/select.h> +# include <sys/time.h> +# include <sys/types.h> +# include <unistd.h> +# include <arpa/inet.h> +#endif /* }}} */ + +ZEND_DECLARE_MODULE_GLOBALS(phpdbg); + +static zend_bool phpdbg_booted = 0; + +#if PHP_VERSION_ID >= 50500 +void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC); +#else +void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC); +#endif + +static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ +{ + pg->prompt[0] = NULL; + pg->prompt[1] = NULL; + + pg->colors[0] = NULL; + pg->colors[1] = NULL; + pg->colors[2] = NULL; + + pg->exec = NULL; + pg->exec_len = 0; + pg->ops = NULL; + pg->vmret = 0; + pg->bp_count = 0; + pg->lcmd = NULL; + pg->flags = PHPDBG_DEFAULT_FLAGS; + pg->oplog = NULL; + pg->io[PHPDBG_STDIN] = NULL; + pg->io[PHPDBG_STDOUT] = NULL; + pg->io[PHPDBG_STDERR] = NULL; + memset(&pg->lparam, 0, sizeof(phpdbg_param_t)); + pg->frame.num = 0; +} /* }}} */ + +static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ +{ + ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL); +#if PHP_VERSION_ID >= 50500 + zend_execute_old = zend_execute_ex; + zend_execute_ex = phpdbg_execute_ex; +#else + zend_execute_old = zend_execute; + zend_execute = phpdbg_execute_ex; +#endif + + REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT); + + return SUCCESS; +} /* }}} */ + +static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol); +} /* }}} */ + +static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */ +{ + efree((char*)((phpdbg_breakop_t*)brake)->name); +} /* }}} */ + + +static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +{ + zend_hash_destroy((HashTable*)brake); +} /* }}} */ + +static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +{ + phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data; + + if (brake) { + if (brake->ops) { + TSRMLS_FETCH(); + + destroy_op_array( + brake->ops TSRMLS_CC); + efree(brake->ops); + } + efree((char*)brake->code); + } +} /* }}} */ + +static void php_phpdbg_destroy_registered(void *data) /* {{{ */ +{ + zend_function *function = (zend_function*) data; + TSRMLS_FETCH(); + + destroy_zend_function( + function TSRMLS_CC); +} /* }}} */ + +static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 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_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0); + + zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); + zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); + + return SUCCESS; +} /* }}} */ + +static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ +{ + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); + zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); + zend_hash_destroy(&PHPDBG_G(seek)); + zend_hash_destroy(&PHPDBG_G(registered)); + + if (PHPDBG_G(exec)) { + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + } + + if (PHPDBG_G(prompt)[0]) { + free(PHPDBG_G(prompt)[0]); + } + if (PHPDBG_G(prompt)[1]) { + free(PHPDBG_G(prompt)[1]); + } + + PHPDBG_G(prompt)[0] = NULL; + PHPDBG_G(prompt)[1] = NULL; + + if (PHPDBG_G(oplog)) { + fclose( + PHPDBG_G(oplog)); + PHPDBG_G(oplog) = NULL; + } + + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + return SUCCESS; +} /* }}} */ + +/* {{{ proto mixed phpdbg_exec(string context) + Attempt to set the execution context for phpdbg + If the execution context was set previously it is returned + If the execution context was not set previously boolean true is returned + If the request to set the context fails, boolean false is returned, and an E_WARNING raised */ +static PHP_FUNCTION(phpdbg_exec) +{ + char *exec = NULL; + zend_ulong exec_len = 0L; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { + return; + } + + { + struct stat sb; + zend_bool result = 1; + + if (VCWD_STAT(exec, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + if (PHPDBG_G(exec)) { + ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len), 1); + efree(PHPDBG_G(exec)); + result = 0; + } + + PHPDBG_G(exec) = estrndup(exec, exec_len); + PHPDBG_G(exec_len) = exec_len; + + if (result) + ZVAL_BOOL(return_value, 1); + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); + ZVAL_BOOL(return_value, 0); + } + } else { + zend_error( + E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); + + ZVAL_BOOL(return_value, 0); + } + } +} /* }}} */ + +/* {{{ proto void phpdbg_break([integer type, string expression]) + instructs phpdbg to insert a breakpoint at the next opcode */ +static PHP_FUNCTION(phpdbg_break) +{ + if (ZEND_NUM_ARGS() > 0) { + long type; + char *expr = NULL; + zend_uint expr_len = 0; + phpdbg_param_t param; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) { + return; + } + + phpdbg_parse_param(expr, expr_len, ¶m TSRMLS_CC); + + switch (type) { + case METHOD_PARAM: + phpdbg_do_break_method(¶m, NULL TSRMLS_CC); + break; + + case FILE_PARAM: + phpdbg_do_break_file(¶m, NULL TSRMLS_CC); + break; + + case NUMERIC_PARAM: + phpdbg_do_break_lineno(¶m, NULL TSRMLS_CC); + break; + + case STR_PARAM: + phpdbg_do_break_func(¶m, NULL TSRMLS_CC); + break; + + default: zend_error( + E_WARNING, "unrecognized parameter type %ld", type); + } + + phpdbg_clear_param(¶m TSRMLS_CC); + + } else if (EG(current_execute_data) && EG(active_op_array)) { + zend_ulong opline_num = (EG(current_execute_data)->opline - + EG(active_op_array)->opcodes); + + phpdbg_set_breakpoint_opline_ex( + &EG(active_op_array)->opcodes[opline_num+1] TSRMLS_CC); + } +} /* }}} */ + +/* {{{ proto void phpdbg_clear(void) + instructs phpdbg to clear breakpoints */ +static PHP_FUNCTION(phpdbg_clear) +{ + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); +} /* }}} */ + +/* {{{ proto void phpdbg_color(integer element, string color) */ +static PHP_FUNCTION(phpdbg_color) +{ + long element; + char *color; + zend_uint color_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) { + return; + } + + switch (element) { + case PHPDBG_COLOR_NOTICE: + case PHPDBG_COLOR_ERROR: + case PHPDBG_COLOR_PROMPT: + phpdbg_set_color_ex(element, color, color_len TSRMLS_CC); + break; + + default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant"); + } +} /* }}} */ + +/* {{{ proto void phpdbg_prompt(string prompt) */ +static PHP_FUNCTION(phpdbg_prompt) +{ + char *prompt; + zend_uint prompt_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) { + return; + } + + phpdbg_set_prompt(prompt TSRMLS_CC); +} /* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, expression) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, element) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_prompt_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, string) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_exec_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, context) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +zend_function_entry phpdbg_user_functions[] = { + PHP_FE(phpdbg_clear, phpdbg_clear_arginfo) + PHP_FE(phpdbg_break, phpdbg_break_arginfo) + PHP_FE(phpdbg_exec, phpdbg_exec_arginfo) + PHP_FE(phpdbg_color, phpdbg_color_arginfo) + PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo) +#ifdef PHP_FE_END + PHP_FE_END +#else + {NULL,NULL,NULL} +#endif +}; + +static zend_module_entry sapi_phpdbg_module_entry = { + STANDARD_MODULE_HEADER, + PHPDBG_NAME, + phpdbg_user_functions, + PHP_MINIT(phpdbg), + NULL, + PHP_RINIT(phpdbg), + PHP_RSHUTDOWN(phpdbg), + NULL, + PHPDBG_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */ +{ + if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) { + return FAILURE; + } + + phpdbg_booted=1; + + return SUCCESS; +} /* }}} */ + +static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */ +{ + return NULL; +} /* }}} */ + +static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */ +{ + return 0; +} +/* }}} */ + +static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */ +{ + /* We do nothing here, this function is needed to prevent that the fallback + * header handling is called. */ + return SAPI_HEADER_SENT_SUCCESSFULLY; +} +/* }}} */ + +static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */ +{ +} +/* }}} */ + +static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ +{ + /* + * We must not request TSRM before being boot + */ + if (phpdbg_booted) { + phpdbg_error("%s", message); + + switch (PG(last_error_type)) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + case E_RECOVERABLE_ERROR: + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { + phpdbg_list_file( + zend_get_executed_filename(TSRMLS_C), + 3, + zend_get_executed_lineno(TSRMLS_C)-1, + zend_get_executed_lineno(TSRMLS_C) + TSRMLS_CC + ); + } + + do { + switch (phpdbg_interactive(TSRMLS_C)) { + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: + return; + } + } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + } + } else fprintf(stdout, "%s\n", message); +} +/* }}} */ + +static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ +{ + fflush(stdout); + if(SG(request_info).argv0) { + free(SG(request_info).argv0); + SG(request_info).argv0 = NULL; + } + return SUCCESS; +} +/* }}} */ + +static void php_sapi_phpdbg_register_vars(zval *track_vars_array TSRMLS_DC) /* {{{ */ +{ + unsigned int len; + char *docroot = ""; + + /* In phpdbg mode, we consider the environment to be a part of the server variables + */ + php_import_environment_variables(track_vars_array TSRMLS_CC); + + if (PHPDBG_G(exec)) { + len = PHPDBG_G(exec_len); + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PHP_SELF", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", + &PHPDBG_G(exec), PHPDBG_G(exec_len), &len TSRMLS_CC)) { + php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), + track_vars_array TSRMLS_CC); + } + } + + /* any old docroot will doo */ + len = 0U; + if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", + &docroot, len, &len TSRMLS_CC)) { + php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC); + } +} +/* }}} */ + +static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */ +{ + return phpdbg_write("%s", message); +} /* }}} */ + +#if PHP_VERSION_ID >= 50700 +static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */ +{ +#else +static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ +{ + TSRMLS_FETCH(); +#endif + + fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); +} /* }}} */ + +/* {{{ sapi_module_struct phpdbg_sapi_module +*/ +static sapi_module_struct phpdbg_sapi_module = { + "phpdbg", /* name */ + "phpdbg", /* pretty name */ + + php_sapi_phpdbg_module_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + php_sapi_phpdbg_deactivate, /* deactivate */ + + php_sapi_phpdbg_ub_write, /* unbuffered write */ + php_sapi_phpdbg_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + php_sapi_phpdbg_header_handler, /* header handler */ + php_sapi_phpdbg_send_headers, /* send headers handler */ + php_sapi_phpdbg_send_header, /* send header handler */ + + NULL, /* read POST data */ + php_sapi_phpdbg_read_cookies, /* read Cookies */ + + php_sapi_phpdbg_register_vars, /* register server variables */ + php_sapi_phpdbg_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + STANDARD_SAPI_MODULE_PROPERTIES +}; +/* }}} */ + +const opt_struct OPTIONS[] = { /* {{{ */ + {'c', 1, "ini path override"}, + {'d', 1, "define ini entry on command line"}, + {'n', 0, "no php.ini"}, + {'z', 1, "load zend_extension"}, + /* phpdbg options */ + {'q', 0, "no banner"}, + {'e', 1, "exec"}, + {'v', 0, "disable quietness"}, + {'s', 0, "enable stepping"}, + {'b', 0, "boring colours"}, + {'i', 1, "specify init"}, + {'I', 0, "ignore init"}, + {'O', 1, "opline log"}, + {'r', 0, "run"}, + {'E', 0, "step-through-eval"}, + {'S', 1, "sapi-name"}, +#ifndef _WIN32 + {'l', 1, "listen"}, + {'a', 1, "address-or-any"}, +#endif + {'V', 0, "version"}, + {'-', 0, NULL} +}; /* }}} */ + +const char phpdbg_ini_hardcoded[] = +"html_errors=Off\n" +"register_argc_argv=On\n" +"implicit_flush=On\n" +"display_errors=Off\n" +"log_errors=On\n" +"max_execution_time=0\n" +"max_input_time=-1\n" +"error_log=\n\0"; + +/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ +#define INI_DEFAULT(name, value) \ + Z_SET_REFCOUNT(tmp, 0); \ + Z_UNSET_ISREF(tmp); \ + ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \ + zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL); + +void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ +{ + zval tmp; + INI_DEFAULT("report_zend_debug", "0"); +} /* }}} */ + +static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */ +{ + /* print blurb */ + if (!cleaning) { + phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", + PHPDBG_VERSION); + phpdbg_writeln("To get help using phpdbg type \"help\" and press enter"); + phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES); + } else { + phpdbg_notice("Clean Execution Environment"); + + phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files))); + } +} /* }}} */ + +static inline void phpdbg_sigint_handler(int signo) /* {{{ */ +{ + TSRMLS_FETCH(); + + if (EG(in_execution)) { + /* set signalled only when not interactive */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; + } + } else { - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; - zend_bailout(); ++ /* we quit remote consoles on recv SIGINT */ ++ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { ++ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; ++ zend_bailout(); ++ } + } +} /* }}} */ + +#ifndef _WIN32 +int phpdbg_open_socket(const char *interface, short port) /* {{{ */ +{ + int fd = socket(AF_INET, SOCK_STREAM, 0); + + switch (fd) { + case -1: + return -1; + + default: { + int reuse = 1; + + switch (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse))) { + case -1: + close(fd); + return -2; + + default: { + struct sockaddr_in address; + + memset(&address, 0, sizeof(address)); + + address.sin_port = htons(port); + address.sin_family = AF_INET; + + if ((*interface == '*')) { + address.sin_addr.s_addr = htonl(INADDR_ANY); + } else if (!inet_pton(AF_INET, interface, &address.sin_addr)) { + close(fd); + return -3; + } + + switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) { + case -1: + close(fd); + return -4; + + default: { + listen(fd, 5); + } + } + } + } + } + } + + return fd; +} /* }}} */ + +static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */ +{ + if ((*socket)[0] >= 0) { + shutdown( + (*socket)[0], SHUT_RDWR); + close((*socket)[0]); + } + + if (streams[0]) { + fclose(streams[0]); + } + + if ((*socket)[1] >= 0) { + shutdown( + (*socket)[1], SHUT_RDWR); + close((*socket)[1]); + } + + if (streams[1]) { + fclose(streams[1]); + } +} /* }}} */ + +/* don't inline this, want to debug it easily, will inline when done */ + +int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*socket)[2], FILE* streams[2]) /* {{{ */ +{ + if (((*listen)[0]) < 0 && ((*listen)[1]) < 0) { + ((*listen)[0]) = phpdbg_open_socket(address, (short)port[0]); + ((*listen)[1]) = phpdbg_open_socket(address, (short)port[1]); + } + + streams[0] = NULL; + streams[1] = NULL; + + if ((*listen)[0] < 0 || (*listen)[1] < 0) { + if ((*listen)[0] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdin) on %s:%d", address, port[0]); + } + + if ((*listen)[1] < 0) { + phpdbg_rlog(stderr, + "console failed to initialize (stdout) on %s:%d", address, port[1]); + } + + if ((*listen)[0] >= 0) { + close((*listen)[0]); + } + + if ((*listen)[1] >= 0) { + close((*listen)[1]); + } + + return FAILURE; + } + + phpdbg_close_sockets(socket, streams); + + phpdbg_rlog(stderr, + "accepting connections on %s:%d/%d", address, port[0], port[1]); + { + struct sockaddr_in address; + socklen_t size = sizeof(address); + char buffer[20] = {0}; + + { + memset(&address, 0, size); + (*socket)[0] = accept( + (*listen)[0], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdin) from %s", buffer); + } + + { + memset(&address, 0, size); + (*socket)[1] = accept( + (*listen)[1], (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + + phpdbg_rlog(stderr, "connection (stdout) from %s", buffer); + } + } + + dup2((*socket)[0], fileno(stdin)); + dup2((*socket)[1], fileno(stdout)); + + setbuf(stdout, NULL); + + streams[0] = fdopen((*socket)[0], "r"); + streams[1] = fdopen((*socket)[1], "w"); + + return SUCCESS; +} /* }}} */ +#endif + +int main(int argc, char **argv) /* {{{ */ +{ + sapi_module_struct *phpdbg = &phpdbg_sapi_module; + char *sapi_name; + char *ini_entries; + int ini_entries_len; + char **zend_extensions = NULL; + zend_ulong zend_extensions_len = 0L; + zend_bool ini_ignore; + char *ini_override; + char *exec; + size_t exec_len; + char *init_file; + size_t init_file_len; + zend_bool init_file_default; + char *oplog_file; + size_t oplog_file_len; + zend_ulong flags; + char *php_optarg; + int php_optind, opt, show_banner = 1; + long cleaning = 0; + zend_bool remote = 0; + int run = 0; + int step = 0; + char *bp_tmp_file; +#ifndef _WIN32 + char *address; + int listen[2]; + int server[2]; + int socket[2]; + FILE* streams[2] = {NULL, NULL}; +#endif + +#ifdef ZTS + void ***tsrm_ls; +#endif + +#ifndef _WIN32 + address = strdup("127.0.0.1"); + socket[0] = -1; + socket[1] = -1; + listen[0] = -1; + listen[1] = -1; + server[0] = -1; + server[1] = -1; + streams[0] = NULL; + streams[1] = NULL; +#endif + +#ifdef PHP_WIN32 + _fmode = _O_BINARY; /* sets default for file streams to binary */ + setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ +#endif + +#ifdef ZTS + tsrm_startup(1, 1, 0, NULL); + + tsrm_ls = ts_resource(0); +#endif + +phpdbg_main: + if (!cleaning) { + bp_tmp_file = malloc(L_tmpnam); + tmpnam(bp_tmp_file); + if (bp_tmp_file == NULL) { + phpdbg_error("Unable to create temporary file"); + } + } + ini_entries = NULL; + ini_entries_len = 0; + ini_ignore = 0; + ini_override = NULL; + zend_extensions = NULL; + zend_extensions_len = 0L; + exec = NULL; + exec_len = 0; + init_file = NULL; + init_file_len = 0; + init_file_default = 1; + oplog_file = NULL; + oplog_file_len = 0; + flags = PHPDBG_DEFAULT_FLAGS; + php_optarg = NULL; + php_optind = 1; + opt = 0; + run = 0; + step = 0; + sapi_name = NULL; + + + while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { + switch (opt) { + case 'r': + run++; + break; + case 'n': + ini_ignore = 1; + break; + case 'c': + if (ini_override) { + free(ini_override); + } + ini_override = strdup(php_optarg); + break; + case 'd': { + int len = strlen(php_optarg); + char *val; + + if ((val = strchr(php_optarg, '='))) { + val++; + if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); + ini_entries_len += (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"", 1); + ini_entries_len++; + memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg)); + ini_entries_len += len - (val - php_optarg); + memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); + ini_entries_len += sizeof("\n\0\"") - 2; + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); + ini_entries_len += len + sizeof("\n\0") - 2; + } + } else { + ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0")); + memcpy(ini_entries + ini_entries_len, php_optarg, len); + memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); + ini_entries_len += len + sizeof("=1\n\0") - 2; + } + } break; + + case 'z': + zend_extensions_len++; + if (zend_extensions) { + zend_extensions = realloc(zend_extensions, sizeof(char*) * zend_extensions_len); + } else zend_extensions = malloc(sizeof(char*) * zend_extensions_len); + zend_extensions[zend_extensions_len-1] = strdup(php_optarg); + break; + + /* begin phpdbg options */ + + case 'e': { /* set execution context */ + exec_len = strlen(php_optarg); + if (exec_len) { + if (exec) { + free(exec); + } + exec = strdup(php_optarg); + } + } break; + + case 'S': { /* set SAPI name */ + if (sapi_name) { + free(sapi_name); + } + sapi_name = strdup(php_optarg); + } break; + + case 'I': { /* ignore .phpdbginit */ + init_file_default = 0; + } break; + + case 'i': { /* set init file */ + if (init_file) { + free(init_file); + } + + init_file_len = strlen(php_optarg); + if (init_file_len) { + init_file = strdup(php_optarg); + } + } break; + + case 'O': { /* set oplog output */ + oplog_file_len = strlen(php_optarg); + if (oplog_file_len) { + oplog_file = strdup(php_optarg); + } + } break; + + case 'v': /* set quietness off */ + flags &= ~PHPDBG_IS_QUIET; + break; + + case 's': /* set stepping on */ + step = 1; + break; + + case 'E': /* stepping through eval on */ + flags |= PHPDBG_IS_STEPONEVAL; + break; + + case 'b': /* set colours off */ + flags &= ~PHPDBG_IS_COLOURED; + break; + + case 'q': /* hide banner */ + show_banner = 0; + break; + +#ifndef _WIN32 + /* if you pass a listen port, we will accept input on listen port */ + /* and write output to listen port * 2 */ + + case 'l': { /* set listen ports */ + if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) { + if (sscanf(php_optarg, "%d", &listen[0]) != 1) { + /* default to hardcoded ports */ + listen[0] = 4000; + listen[1] = 8000; + } else { + listen[1] = (listen[0] * 2); + } + } + } break; + + case 'a': { /* set bind address */ + free(address); + if (!php_optarg) { + address = strdup("*"); + } else address = strdup(php_optarg); + } break; +#endif + + case 'V': { + sapi_startup(phpdbg); + phpdbg->startup(phpdbg); + printf( + "phpdbg %s (built: %s %s)\nPHP %s, Copyright (c) 1997-2014 The PHP Group\n%s", + PHPDBG_VERSION, + __DATE__, + __TIME__, + PHP_VERSION, + get_zend_version() + ); + sapi_deactivate(TSRMLS_C); + sapi_shutdown(); + return 0; + } break; + } + } + +#ifndef _WIN32 + /* setup remote server if necessary */ + if (!cleaning && + (listen[0] > 0 && listen[1] > 0)) { + if (phpdbg_open_sockets(address, listen, &server, &socket, streams) == FAILURE) { + remote = 0; + exit(0); + } + /* set remote flag to stop service shutting down upon quit */ + remote = 1; + } +#endif + + if (sapi_name) { + phpdbg->name = sapi_name; + } + + phpdbg->ini_defaults = phpdbg_ini_defaults; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore_cwd = 1; + + sapi_startup(phpdbg); + + phpdbg->executable_location = argv[0]; + phpdbg->phpinfo_as_text = 1; + phpdbg->php_ini_ignore = ini_ignore; + phpdbg->php_ini_path_override = ini_override; + + if (ini_entries) { + ini_entries = realloc(ini_entries, ini_entries_len + sizeof(phpdbg_ini_hardcoded)); + memmove(ini_entries + sizeof(phpdbg_ini_hardcoded) - 2, ini_entries, ini_entries_len + 1); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded) - 2); + } else { + ini_entries = malloc(sizeof(phpdbg_ini_hardcoded)); + memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded)); + } + ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2; + + if (zend_extensions_len) { + zend_ulong zend_extension = 0L; + + while (zend_extension < zend_extensions_len) { + const char *ze = zend_extensions[zend_extension]; + size_t ze_len = strlen(ze); + + ini_entries = realloc( + ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n")))); + memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1)); + ini_entries_len += (sizeof("zend_extension=")-1); + memcpy(&ini_entries[ini_entries_len], ze, ze_len); + ini_entries_len += ze_len; + memcpy(&ini_entries[ini_entries_len], "\n", (sizeof("\n") - 1)); + + free(zend_extensions[zend_extension]); + zend_extension++; + } + + free(zend_extensions); + } + + phpdbg->ini_entries = ini_entries; + + if (phpdbg->startup(phpdbg) == SUCCESS) { + + zend_activate(TSRMLS_C); + + /* do not install sigint handlers for remote consoles */ + /* sending SIGINT then provides a decent way of shutting down the server */ +#ifdef ZEND_SIGNALS +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + zend_try { + zend_signal_activate(TSRMLS_C); + zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); + } zend_end_try(); +# ifndef _WIN32 + } +# endif +#else +# ifndef _WIN32 + if (listen[0] < 0) { +# endif + signal(SIGINT, phpdbg_sigint_handler); +#ifndef _WIN32 + } +#endif +#endif + + PG(modules_activated) = 0; + + /* set flags from command line */ + PHPDBG_G(flags) = flags; + +#ifndef _WIN32 + /* setup io here */ + if (streams[0] && streams[1]) { + PHPDBG_G(flags) |= PHPDBG_IS_REMOTE; + + signal(SIGPIPE, SIG_IGN); + } +#endif + + PHPDBG_G(io)[PHPDBG_STDIN] = stdin; + PHPDBG_G(io)[PHPDBG_STDOUT] = stdout; + PHPDBG_G(io)[PHPDBG_STDERR] = stderr; + + if (exec) { /* set execution context */ + PHPDBG_G(exec) = phpdbg_resolve_path( + exec TSRMLS_CC); + PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec)); + + free(exec); + } + + if (oplog_file) { /* open oplog */ + PHPDBG_G(oplog) = fopen(oplog_file, "w+"); + if (!PHPDBG_G(oplog)) { + phpdbg_error( + "Failed to open oplog %s", oplog_file); + } + free(oplog_file); + } + + /* set default colors */ + phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold") TSRMLS_CC); + phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green") TSRMLS_CC); + + /* set default prompt */ + phpdbg_set_prompt(PROMPT TSRMLS_CC); + + zend_try { + zend_activate_modules(TSRMLS_C); + } zend_end_try(); + + if (show_banner) { + /* print blurb */ + phpdbg_welcome((cleaning > 0) TSRMLS_CC); + } + + zend_try { + /* activate globals, they can be overwritten */ + zend_activate_auto_globals(TSRMLS_C); + } zend_end_try(); + + /* initialize from file */ ++ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; + zend_try { - PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; + phpdbg_init(init_file, init_file_len, init_file_default TSRMLS_CC); + phpdbg_try_file_init(bp_tmp_file, strlen(bp_tmp_file), 0 TSRMLS_CC); - PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; - } zend_catch { - PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; - if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { - goto phpdbg_out; - } + } zend_end_try(); ++ PHPDBG_G(flags) &= ~PHPDBG_IS_INITIALIZING; ++ ++ /* quit if init says so */ ++ if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { ++ goto phpdbg_out; ++ } + + /* step from here, not through init */ + if (step) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } + + if (run) { + /* no need to try{}, run does it ... */ + PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC); + if (run > 1) { + /* if -r is on the command line more than once just quit */ + goto phpdbg_out; + } + } + +phpdbg_interact: + /* phpdbg main() */ + do { + zend_try { + phpdbg_interactive(TSRMLS_C); + } zend_catch { + if ((PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { + FILE *bp_tmp_fp = fopen(bp_tmp_file, "w"); + phpdbg_export_breakpoints(bp_tmp_fp TSRMLS_CC); + fclose(bp_tmp_fp); + cleaning = 1; - goto phpdbg_out; + } else { + cleaning = 0; + } - #ifndef _WIN32 - /* remote client disconnected */ - if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + - /* renegociate connections */ - phpdbg_open_sockets( - address, listen, &server, &socket, streams); ++#ifndef _WIN32 ++ if (!cleaning) { ++ /* remote client disconnected */ ++ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + - /* set streams */ - if (streams[0] && streams[1]) { - PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING; ++ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { ++ /* 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; ++ } else { ++ /* local consoles cannot disconnect, ignore EOF */ ++ PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; ++ } + } - - /* this must be forced */ - CG(unclean_shutdown) = 0; + } +#endif - if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { - goto phpdbg_out; - } + } zend_end_try(); - } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); ++ } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); + + /* this must be forced */ + CG(unclean_shutdown) = 0; + ++ /* this is just helpful */ ++ PG(report_memleaks) = 0; ++ +phpdbg_out: +#ifndef _WIN32 - if (PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED) { ++ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { + PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED; + goto phpdbg_interact; + } +#endif + +#ifndef ZTS + /* force cleanup of auto and core globals */ + zend_hash_clean(CG(auto_globals)); + memset( + &core_globals, 0, sizeof(php_core_globals)); +#endif + + if (ini_entries) { + free(ini_entries); + } + + if (ini_override) { + free(ini_override); + } + + if (PG(modules_activated)) { + zend_try { + zend_deactivate_modules(TSRMLS_C); + } zend_end_try(); + } + + zend_deactivate(TSRMLS_C); + + zend_try { + zend_post_deactivate_modules(TSRMLS_C); + } zend_end_try(); + +#ifdef ZEND_SIGNALS + zend_try { + zend_signal_deactivate(TSRMLS_C); + } zend_end_try(); +#endif + + zend_try { + php_module_shutdown(TSRMLS_C); + } zend_end_try(); + + sapi_shutdown(); + } + + if (cleaning || remote) { + goto phpdbg_main; + } - ++ +#ifdef ZTS + /* bugggy */ + /* tsrm_shutdown(); */ +#endif + +#ifndef _WIN32 + if (address) { + free(address); + } +#endif + + if (sapi_name) { + free(sapi_name); + } + + free(bp_tmp_file); + + return 0; +} /* }}} */ diff --cc sapi/phpdbg/phpdbg.h index 66b4f69,0000000..6b7afb2 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg.h +++ b/sapi/phpdbg/phpdbg.h @@@ -1,188 -1,0 +1,188 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHPDBG_H +#define PHPDBG_H + +#ifdef PHP_WIN32 +# define PHPDBG_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHPDBG_API __attribute__ ((visibility("default"))) +#else +# define PHPDBG_API +#endif + +#include "php.h" +#include "php_globals.h" +#include "php_variables.h" +#include "php_getopt.h" +#include "zend_builtin_functions.h" +#include "zend_extensions.h" +#include "zend_modules.h" +#include "zend_globals.h" +#include "zend_ini_scanner.h" +#include "zend_stream.h" +#include "SAPI.h" +#include <fcntl.h> +#include <sys/types.h> +#if defined(_WIN32) && !defined(__MINGW32__) +# include <windows.h> +# include "config.w32.h" +# undef strcasecmp +# undef strncasecmp +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +#else +# include "php_config.h" +#endif +#ifndef O_BINARY +# define O_BINARY 0 +#endif +#include "php_main.h" + +#ifdef ZTS +# include "TSRM.h" +#endif + +#ifdef HAVE_LIBREADLINE +# include <readline/readline.h> +# include <readline/history.h> +#endif + +#include "phpdbg_cmd.h" +#include "phpdbg_utils.h" + +#ifdef ZTS +# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v) +#else +# define PHPDBG_G(v) (phpdbg_globals.v) +#endif + +#define PHPDBG_NEXT 2 +#define PHPDBG_UNTIL 3 +#define PHPDBG_FINISH 4 +#define PHPDBG_LEAVE 5 + +/* + BEGIN: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE +*/ + +/* {{{ tables */ +#define PHPDBG_BREAK_FILE 0 +#define PHPDBG_BREAK_SYM 1 +#define PHPDBG_BREAK_OPLINE 2 +#define PHPDBG_BREAK_METHOD 3 +#define PHPDBG_BREAK_COND 4 +#define PHPDBG_BREAK_OPCODE 5 +#define PHPDBG_BREAK_FUNCTION_OPLINE 6 +#define PHPDBG_BREAK_METHOD_OPLINE 7 +#define PHPDBG_BREAK_FILE_OPLINE 8 +#define PHPDBG_BREAK_MAP 9 +#define PHPDBG_BREAK_TABLES 10 /* }}} */ + +/* {{{ flags */ +#define PHPDBG_HAS_FILE_BP (1<<1) +#define PHPDBG_HAS_SYM_BP (1<<2) +#define PHPDBG_HAS_OPLINE_BP (1<<3) +#define PHPDBG_HAS_METHOD_BP (1<<4) +#define PHPDBG_HAS_COND_BP (1<<5) +#define PHPDBG_HAS_OPCODE_BP (1<<6) +#define PHPDBG_HAS_FUNCTION_OPLINE_BP (1<<7) +#define PHPDBG_HAS_METHOD_OPLINE_BP (1<<8) +#define PHPDBG_HAS_FILE_OPLINE_BP (1<<9) /* }}} */ + +/* + END: DO NOT CHANGE DO NOT CHANGE DO NOT CHANGE +*/ + +#define PHPDBG_IN_COND_BP (1<<10) +#define PHPDBG_IN_EVAL (1<<11) + +#define PHPDBG_IS_STEPPING (1<<12) +#define PHPDBG_IS_QUIET (1<<13) +#define PHPDBG_IS_QUITTING (1<<14) +#define PHPDBG_IS_COLOURED (1<<15) +#define PHPDBG_IS_CLEANING (1<<16) + +#define PHPDBG_IN_UNTIL (1<<17) +#define PHPDBG_IN_FINISH (1<<18) +#define PHPDBG_IN_LEAVE (1<<19) + +#define PHPDBG_IS_REGISTERED (1<<20) +#define PHPDBG_IS_STEPONEVAL (1<<21) +#define PHPDBG_IS_INITIALIZING (1<<22) +#define PHPDBG_IS_SIGNALED (1<<23) +#define PHPDBG_IS_INTERACTIVE (1<<24) +#define PHPDBG_IS_BP_ENABLED (1<<25) +#define PHPDBG_IS_REMOTE (1<<26) +#define PHPDBG_IS_DISCONNECTED (1<<27) + +#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE) +#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) +#define PHPDBG_BP_MASK (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP|PHPDBG_HAS_OPCODE_BP|PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP) + +#ifndef _WIN32 +# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED|PHPDBG_IS_BP_ENABLED) +#else +# define PHPDBG_DEFAULT_FLAGS (PHPDBG_IS_QUIET|PHPDBG_IS_BP_ENABLED) +#endif /* }}} */ + +/* {{{ strings */ +#define PHPDBG_NAME "phpdbg" +#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */ +#define PHPDBG_URL "http://phpdbg.com" +#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues" - #define PHPDBG_VERSION "0.3.0" ++#define PHPDBG_VERSION "0.3.1" +#define PHPDBG_INIT_FILENAME ".phpdbginit" +/* }}} */ + +/* {{{ output descriptors */ +#define PHPDBG_STDIN 0 +#define PHPDBG_STDOUT 1 +#define PHPDBG_STDERR 2 +#define PHPDBG_IO_FDS 3 /* }}} */ + +/* {{{ structs */ +ZEND_BEGIN_MODULE_GLOBALS(phpdbg) + HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */ + HashTable registered; /* registered */ + HashTable seek; /* seek oplines */ + phpdbg_frame_t frame; /* frame */ + + char *exec; /* file to execute */ + size_t exec_len; /* size of exec */ + zend_op_array *ops; /* op_array */ + zval *retval; /* return value */ + int bp_count; /* breakpoint count */ + int vmret; /* return from last opcode handler execution */ + + FILE *oplog; /* opline log */ + FILE *io[PHPDBG_IO_FDS]; /* io */ + + char *prompt[2]; /* prompt */ + const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */ + + phpdbg_command_t *lcmd; /* last command */ + phpdbg_param_t lparam; /* last param */ + + zend_ulong flags; /* phpdbg flags */ +ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */ + +#endif /* PHPDBG_H */ diff --cc sapi/phpdbg/phpdbg_cmd.c index 36a9d26,0000000..29424d0 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_cmd.c +++ b/sapi/phpdbg/phpdbg_cmd.c @@@ -1,669 -1,0 +1,680 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg.h" +#include "phpdbg_cmd.h" +#include "phpdbg_utils.h" +#include "phpdbg_set.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + return "empty"; + case ADDR_PARAM: + return "address"; + case NUMERIC_PARAM: + return "numeric"; + case METHOD_PARAM: + return "method"; + case NUMERIC_FUNCTION_PARAM: + return "function opline"; + case NUMERIC_METHOD_PARAM: + return "method opline"; + case FILE_PARAM: + return "file or file opline"; + case STR_PARAM: + return "string"; + default: /* this is bad */ + return "unknown"; + } +} + +PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + char *class_name, *func_name; + + if (len == 0) { + param->type = EMPTY_PARAM; + goto parsed; + } + + if (phpdbg_is_addr(str)) { + param->addr = strtoul(str, 0, 16); + param->type = ADDR_PARAM; + goto parsed; + + } else if (phpdbg_is_numeric(str)) { + param->num = strtol(str, NULL, 0); + param->type = NUMERIC_PARAM; + goto parsed; + + } else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) { + param->method.class = class_name; + param->method.name = func_name; + param->type = METHOD_PARAM; + goto parsed; + } else { + char *line_pos = strrchr(str, ':'); + + if (line_pos && phpdbg_is_numeric(line_pos+1)) { + if (strchr(str, ':') == line_pos) { + char path[MAXPATHLEN]; + + memcpy(path, str, line_pos - str); + path[line_pos - str] = 0; + *line_pos = 0; + param->file.name = phpdbg_resolve_path(path TSRMLS_CC); + param->file.line = strtol(line_pos+1, NULL, 0); + param->type = FILE_PARAM; + + goto parsed; + } + } + + line_pos = strrchr(str, '#'); + + if (line_pos && phpdbg_is_numeric(line_pos+1)) { + if (strchr(str, '#') == line_pos) { + param->num = strtol(line_pos + 1, NULL, 0); + + if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) { + param->method.class = class_name; + param->method.name = func_name; + param->type = NUMERIC_METHOD_PARAM; + } else { + param->len = line_pos - str; + param->str = estrndup(str, param->len); + param->type = NUMERIC_FUNCTION_PARAM; + } + + goto parsed; + } + } + } + + param->str = estrndup(str, len); + param->len = len; + param->type = STR_PARAM; + +parsed: + phpdbg_debug("phpdbg_parse_param(\"%s\", %lu): %s", + str, len, phpdbg_get_param_type(param TSRMLS_CC)); + return param->type; +} /* }}} */ + +PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + if (param) { + switch (param->type) { + case FILE_PARAM: + efree(param->file.name); + break; + case METHOD_PARAM: + efree(param->method.class); + efree(param->method.name); + break; + case STR_PARAM: + efree(param->str); + break; + default: + break; + } + } + +} /* }}} */ + +PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: + asprintf(pointer, + "%s", param->str); + break; + + case ADDR_PARAM: + asprintf(pointer, + "%#lx", param->addr); + break; + + case NUMERIC_PARAM: + asprintf(pointer, + "%li", + param->num); + break; + + case METHOD_PARAM: + asprintf(pointer, + "%s::%s", + param->method.class, + param->method.name); + break; + + case FILE_PARAM: + if (param->num) { + asprintf(pointer, + "%s:%lu#%lu", + param->file.name, + param->file.line, + param->num); + } else { + asprintf(pointer, + "%s:%lu", + param->file.name, + param->file.line); + } + break; + + case NUMERIC_FUNCTION_PARAM: + asprintf(pointer, + "%s#%lu", param->str, param->num); + break; + + case NUMERIC_METHOD_PARAM: + asprintf(pointer, + "%s::%s#%lu", + param->method.class, + param->method.name, + param->num); + break; + + default: + asprintf(pointer, + "%s", "unknown"); + } + + return *pointer; +} /* }}} */ + +PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */ +{ + switch ((dest->type = src->type)) { + case STR_PARAM: + dest->str = estrndup(src->str, src->len); + dest->len = src->len; + break; + + case ADDR_PARAM: + dest->addr = src->addr; + break; + + case NUMERIC_PARAM: + dest->num = src->num; + break; + + case METHOD_PARAM: + dest->method.class = estrdup(src->method.class); + dest->method.name = estrdup(src->method.name); + break; + + case FILE_PARAM: + dest->file.name = estrdup(src->file.name); + dest->file.line = src->file.line; + if (src->num) + dest->num = src->num; + break; + + case NUMERIC_FUNCTION_PARAM: + dest->str = estrndup(src->str, src->len); + dest->num = src->num; + dest->len = src->len; + break; + + case NUMERIC_METHOD_PARAM: + dest->method.class = estrdup(src->method.class); + dest->method.name = estrdup(src->method.name); + dest->num = src->num; + break; + + case EMPTY_PARAM: { /* do nothing */ } break; + } +} /* }}} */ + +PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */ +{ + zend_ulong hash = param->type; + + switch (param->type) { + case STR_PARAM: + hash += zend_inline_hash_func(param->str, param->len); + break; + + case METHOD_PARAM: + hash += zend_inline_hash_func(param->method.class, strlen(param->method.class)); + hash += zend_inline_hash_func(param->method.name, strlen(param->method.name)); + break; + + case FILE_PARAM: + hash += zend_inline_hash_func(param->file.name, strlen(param->file.name)); + hash += param->file.line; + if (param->num) + hash += param->num; + break; + + case ADDR_PARAM: + hash += param->addr; + break; + + case NUMERIC_PARAM: + hash += param->num; + break; + + case NUMERIC_FUNCTION_PARAM: + hash += zend_inline_hash_func(param->str, param->len); + hash += param->num; + break; + + case NUMERIC_METHOD_PARAM: + hash += zend_inline_hash_func(param->method.class, strlen(param->method.class)); + hash += zend_inline_hash_func(param->method.name, strlen(param->method.name)); + if (param->num) + hash+= param->num; + break; + + case EMPTY_PARAM: { /* do nothing */ } break; + } + + return hash; +} /* }}} */ + +PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */ +{ + if (l && r) { + if (l->type == r->type) { + switch (l->type) { + + case NUMERIC_FUNCTION_PARAM: + if (l->num != r->num) { + break; + } + /* break intentionally omitted */ + + case STR_PARAM: + return (l->len == r->len) && + (memcmp(l->str, r->str, l->len) == SUCCESS); + + case NUMERIC_PARAM: + return (l->num == r->num); + + case ADDR_PARAM: + return (l->addr == r->addr); + + case FILE_PARAM: { + if (l->file.line == r->file.line) { + size_t lengths[2] = { + strlen(l->file.name), strlen(r->file.name)}; + + if (lengths[0] == lengths[1]) { + if ((!l->num && !r->num) || (l->num == r->num)) { + return (memcmp( + l->file.name, r->file.name, lengths[0]) == SUCCESS); + } + } + } + } break; + + case NUMERIC_METHOD_PARAM: + if (l->num != r->num) { + break; + } + /* break intentionally omitted */ + + case METHOD_PARAM: { + size_t lengths[2] = { + strlen(l->method.class), strlen(r->method.class)}; + if (lengths[0] == lengths[1]) { + if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) { + lengths[0] = strlen(l->method.name); + lengths[1] = strlen(r->method.name); + + if (lengths[0] == lengths[1]) { + return (memcmp( + l->method.name, r->method.name, lengths[0]) == SUCCESS); + } + } + } + } break; + + case EMPTY_PARAM: + return 1; + } + } + } + return 0; +} /* }}} */ + +PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */ +{ + char *p; + char b[PHPDBG_MAX_CMD]; + int l=0; + enum states { + IN_BETWEEN, + IN_WORD, + IN_STRING + } state = IN_BETWEEN; + phpdbg_input_t **argv = NULL; + + argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*)); + (*argc) = 0; + +#define RESET_STATE() do { \ + phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \ + if (arg) { \ + b[l]=0; \ + arg->length = l; \ + arg->string = estrndup(b, arg->length); \ + arg->argv = NULL; \ + arg->argc = 0; \ + argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \ + argv[(*argc)++] = arg; \ + l = 0; \ + } \ + state = IN_BETWEEN; \ +} while (0) + + for (p = buffer; *p != '\0'; p++) { + int c = (unsigned char) *p; + switch (state) { + case IN_BETWEEN: + if (isspace(c)) { + continue; + } + if (c == '"') { + state = IN_STRING; + continue; + } + state = IN_WORD; + b[l++]=c; + continue; + + case IN_STRING: + if (c == '"') { + if (buffer[(p - buffer)-1] == '\\') { + b[l-1]=c; + continue; + } + RESET_STATE(); + } else { + b[l++]=c; + } + continue; + + case IN_WORD: + if (isspace(c)) { + RESET_STATE(); + } else { + b[l++]=c; + } + continue; + } + } + + switch (state) { + case IN_WORD: { + RESET_STATE(); + } break; + + case IN_STRING: + phpdbg_error( + "Malformed command line (unclosed quote) @ %ld: %s!", + (p - buffer)-1, &buffer[(p - buffer)-1]); + break; + + case IN_BETWEEN: + break; + } + + if ((*argc) == 0) { + /* not needed */ + efree(argv); + + /* to be sure */ + return NULL; + } + + return argv; +} /* }}} */ + +PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */ +{ + phpdbg_input_t *buffer = NULL; + char *cmd = NULL; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && + (buffered == NULL)) { + fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); + } + + if (buffered == NULL) { - #ifndef HAVE_LIBREADLINE - char buf[PHPDBG_MAX_CMD]; - if ((!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE) && !phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) || - !fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { - /* the user has gone away */ - phpdbg_error("Failed to read console!"); ++disconnect: ++ if (0) { + PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED); + zend_bailout(); + return NULL; + } + ++#ifndef HAVE_LIBREADLINE ++ char buf[PHPDBG_MAX_CMD]; ++ if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { ++ if (!phpdbg_write(phpdbg_get_prompt(TSRMLS_C))) { ++ goto disconnect; ++ } ++ } ++ ++ /* note: EOF is ignored */ ++readline: ++ if (!fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { ++ /* the user has gone away */ ++ if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { ++ goto disconnect; ++ } else goto readline; ++ } ++ + cmd = buf; +#else ++ /* note: EOF makes readline write prompt again in local console mode */ ++readline: + if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + char buf[PHPDBG_MAX_CMD]; + if (fgets(buf, PHPDBG_MAX_CMD, PHPDBG_G(io)[PHPDBG_STDIN])) { + cmd = buf; - } else cmd = NULL; ++ } else goto disconnect; + } else cmd = readline(phpdbg_get_prompt(TSRMLS_C)); + + if (!cmd) { - /* the user has gone away */ - phpdbg_error("Failed to read console!"); - PHPDBG_G(flags) |= (PHPDBG_IS_QUITTING|PHPDBG_IS_DISCONNECTED); - zend_bailout(); - return NULL; ++ goto readline; + } + + if (!(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + add_history(cmd); + } +#endif + } else cmd = buffered; + + /* allocate and sanitize buffer */ + buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t)); + if (!buffer) { + return NULL; + } + + buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length); + + /* store constant pointer to start of buffer */ + buffer->start = (char* const*) buffer->string; + + buffer->argv = phpdbg_read_argv( + buffer->string, &buffer->argc TSRMLS_CC); + +#ifdef PHPDBG_DEBUG + if (buffer->argc) { + int arg = 0; + + while (arg < buffer->argc) { + phpdbg_debug( + "argv %d=%s", arg, buffer->argv[arg]->string); + arg++; + } + } +#endif + +#ifdef HAVE_LIBREADLINE + if (!buffered && cmd && + !(PHPDBG_G(flags) & PHPDBG_IS_REMOTE)) { + free(cmd); + } +#endif + + return buffer; + } + + return NULL; +} /* }}} */ + +PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */ +{ + if (argv) { + if (argc) { + int arg; + for (arg=0; arg<argc; arg++) { + phpdbg_destroy_input( + &argv[arg] TSRMLS_CC); + } + } + efree(argv); + } + +} /* }}} */ + +PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */ +{ + if (*input) { + if ((*input)->string) { + efree((*input)->string); + } + + phpdbg_destroy_argv( + (*input)->argv, (*input)->argc TSRMLS_CC); + + efree(*input); + } +} /* }}} */ + +PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +{ + int rc = FAILURE; + + if (input->argc > 0) { + while (command && command->name && command->handler) { + if (((command->name_len == input->argv[0]->length) && + (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) || + (command->alias && + (input->argv[0]->length == 1) && + (command->alias == *input->argv[0]->string))) { + + phpdbg_param_t param; + + param.type = EMPTY_PARAM; + + if (input->argc > 1) { + if (command->subs) { + phpdbg_input_t sub = *input; + + sub.string += input->argv[0]->length; + sub.length -= input->argv[0]->length; + + sub.string = phpdbg_trim( + sub.string, sub.length, &sub.length); + + sub.argc--; + sub.argv++; + + phpdbg_debug( + "trying sub commands in \"%s\" for \"%s\" with %d arguments", + command->name, sub.argv[0]->string, sub.argc-1); + + if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) { + efree(sub.string); + return SUCCESS; + } + + efree(sub.string); + } + + /* no sub command found */ + { + char *store = input->string; + + input->string += input->argv[0]->length; + input->length -= input->argv[0]->length; + + input->string = phpdbg_trim( + input->string, input->length, &input->length); + + efree(store); + } + + /* pass parameter on */ + phpdbg_parse_param( + input->string, + input->length, + ¶m TSRMLS_CC); + } + + phpdbg_debug( + "found command %s for %s with %d arguments", + command->name, input->argv[0]->string, input->argc-1); + { + int arg; + for (arg=1; arg<input->argc; arg++) { + phpdbg_debug( + "\t#%d: [%s=%zu]", + arg, + input->argv[arg]->string, + input->argv[arg]->length); + } + } + + rc = command->handler(¶m, input TSRMLS_CC); + + /* only set last command when it is worth it! */ + if ((rc != FAILURE) && + !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { + PHPDBG_G(lcmd) = (phpdbg_command_t*) command; + phpdbg_clear_param( + &PHPDBG_G(lparam) TSRMLS_CC); + PHPDBG_G(lparam) = param; + } + break; + } + command++; + } + } else { + /* this should NEVER happen */ + phpdbg_error( + "No function executed!!"); + } + + return rc; +} /* }}} */ + diff --cc sapi/phpdbg/phpdbg_opcode.c index 7e64d16,0000000..50073eb mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@@ -1,361 -1,0 +1,363 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include "phpdbg.h" +#include "zend_vm_opcodes.h" +#include "zend_compile.h" +#include "phpdbg_opcode.h" +#include "phpdbg_utils.h" + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +static inline zend_uint phpdbg_decode_literal(zend_op_array *ops, zend_literal *literal TSRMLS_DC) /* {{{ */ +{ + int iter = 0; + + while (iter < ops->last_literal) { + if (literal == &ops->literals[iter]) { + return iter; + } + iter++; + } + + return 0; +} /* }}} */ + +static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, zend_uint type, HashTable *vars TSRMLS_DC) /* {{{ */ +{ + char *decode = NULL; + + switch (type &~ EXT_TYPE_UNUSED) { + case IS_CV: + asprintf(&decode, "$%s", ops->vars[op->var].name); + break; + + case IS_VAR: + case IS_TMP_VAR: { + zend_ulong id = 0, *pid = NULL; - if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) { - id = zend_hash_num_elements(vars); - zend_hash_index_update( - vars, (zend_ulong) ops->vars - op->var, - (void**) &id, - sizeof(zend_ulong), NULL); - } else id = *pid; ++ if (vars != NULL) { ++ if (zend_hash_index_find(vars, (zend_ulong) ops->vars - op->var, (void**) &pid) != SUCCESS) { ++ id = zend_hash_num_elements(vars); ++ zend_hash_index_update( ++ vars, (zend_ulong) ops->vars - op->var, ++ (void**) &id, ++ sizeof(zend_ulong), NULL); ++ } else id = *pid; ++ } + asprintf(&decode, "@%lu", id); + } break; + + case IS_CONST: + asprintf(&decode, "C%u", phpdbg_decode_literal(ops, op->literal TSRMLS_CC)); + break; + + case IS_UNUSED: + asprintf(&decode, "<unused>"); + break; + } + return decode; +} /* }}} */ + +char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars TSRMLS_DC) /*{{{ */ +{ + char *decode[4] = {NULL, NULL, NULL, NULL}; + + switch (op->opcode) { + case ZEND_JMP: +#ifdef ZEND_GOTO + case ZEND_GOTO: +#endif +#ifdef ZEND_FAST_CALL + case ZEND_FAST_CALL: +#endif + asprintf(&decode[1], "J%ld", op->op1.jmp_addr - ops->opcodes); + goto format; + + case ZEND_JMPZNZ: + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + asprintf( + &decode[2], "J%u or J%lu", op->op2.opline_num, op->extended_value); + goto result; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + +#ifdef ZEND_JMP_SET + case ZEND_JMP_SET: +#endif +#ifdef ZEND_JMP_SET_VAR + case ZEND_JMP_SET_VAR: +#endif + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + asprintf( + &decode[2], "J%ld", op->op2.jmp_addr - ops->opcodes); + goto result; + + case ZEND_RECV_INIT: + goto result; + + default: { + decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars TSRMLS_CC); + decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars TSRMLS_CC); +result: + decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars TSRMLS_CC); +format: + asprintf( + &decode[0], + "%-20s %-20s %-20s", + decode[1] ? decode[1] : "", + decode[2] ? decode[2] : "", + decode[3] ? decode[3] : ""); + } + } + + if (decode[1]) + free(decode[1]); + if (decode[2]) + free(decode[2]); + if (decode[3]) + free(decode[3]); + + return decode[0]; +} /* }}} */ + +void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags TSRMLS_DC) /* {{{ */ +{ + /* force out a line while stepping so the user knows what is happening */ + if (ignore_flags || + (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) || + (PHPDBG_G(oplog)))) { + + zend_op *opline = execute_data->opline; + char *decode = phpdbg_decode_opline(execute_data->op_array, opline, vars TSRMLS_CC); + + if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) { + /* output line info */ + phpdbg_notice("L%-5u %16p %-30s %s %s", + opline->lineno, + opline, + phpdbg_decode_opcode(opline->opcode), + decode, + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } + + if (!ignore_flags && PHPDBG_G(oplog)) { + phpdbg_log_ex(PHPDBG_G(oplog), "L%-5u %16p %-30s %s %s", + opline->lineno, + opline, + phpdbg_decode_opcode(opline->opcode), + decode, + execute_data->op_array->filename ? execute_data->op_array->filename : "unknown"); + } + + if (decode) { + free(decode); + } + } +} /* }}} */ + +void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags TSRMLS_DC) /* {{{ */ +{ + phpdbg_print_opline_ex(execute_data, NULL, ignore_flags TSRMLS_CC); +} /* }}} */ + +const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */ +{ +#define CASE(s) case s: return #s + switch (opcode) { + CASE(ZEND_NOP); + CASE(ZEND_ADD); + CASE(ZEND_SUB); + CASE(ZEND_MUL); + CASE(ZEND_DIV); + CASE(ZEND_MOD); + CASE(ZEND_SL); + CASE(ZEND_SR); + CASE(ZEND_CONCAT); + CASE(ZEND_BW_OR); + CASE(ZEND_BW_AND); + CASE(ZEND_BW_XOR); + CASE(ZEND_BW_NOT); + CASE(ZEND_BOOL_NOT); + CASE(ZEND_BOOL_XOR); + CASE(ZEND_IS_IDENTICAL); + CASE(ZEND_IS_NOT_IDENTICAL); + CASE(ZEND_IS_EQUAL); + CASE(ZEND_IS_NOT_EQUAL); + CASE(ZEND_IS_SMALLER); + CASE(ZEND_IS_SMALLER_OR_EQUAL); + CASE(ZEND_CAST); + CASE(ZEND_QM_ASSIGN); + CASE(ZEND_ASSIGN_ADD); + CASE(ZEND_ASSIGN_SUB); + CASE(ZEND_ASSIGN_MUL); + CASE(ZEND_ASSIGN_DIV); + CASE(ZEND_ASSIGN_MOD); + CASE(ZEND_ASSIGN_SL); + CASE(ZEND_ASSIGN_SR); + CASE(ZEND_ASSIGN_CONCAT); + CASE(ZEND_ASSIGN_BW_OR); + CASE(ZEND_ASSIGN_BW_AND); + CASE(ZEND_ASSIGN_BW_XOR); + CASE(ZEND_PRE_INC); + CASE(ZEND_PRE_DEC); + CASE(ZEND_POST_INC); + CASE(ZEND_POST_DEC); + CASE(ZEND_ASSIGN); + CASE(ZEND_ASSIGN_REF); + CASE(ZEND_ECHO); + CASE(ZEND_PRINT); + CASE(ZEND_JMP); + CASE(ZEND_JMPZ); + CASE(ZEND_JMPNZ); + CASE(ZEND_JMPZNZ); + CASE(ZEND_JMPZ_EX); + CASE(ZEND_JMPNZ_EX); + CASE(ZEND_CASE); + CASE(ZEND_SWITCH_FREE); + CASE(ZEND_BRK); + CASE(ZEND_CONT); + CASE(ZEND_BOOL); + CASE(ZEND_INIT_STRING); + CASE(ZEND_ADD_CHAR); + CASE(ZEND_ADD_STRING); + CASE(ZEND_ADD_VAR); + CASE(ZEND_BEGIN_SILENCE); + CASE(ZEND_END_SILENCE); + CASE(ZEND_INIT_FCALL_BY_NAME); + CASE(ZEND_DO_FCALL); + CASE(ZEND_DO_FCALL_BY_NAME); + CASE(ZEND_RETURN); + CASE(ZEND_RECV); + CASE(ZEND_RECV_INIT); + CASE(ZEND_SEND_VAL); + CASE(ZEND_SEND_VAR); + CASE(ZEND_SEND_REF); + CASE(ZEND_NEW); + CASE(ZEND_INIT_NS_FCALL_BY_NAME); + CASE(ZEND_FREE); + CASE(ZEND_INIT_ARRAY); + CASE(ZEND_ADD_ARRAY_ELEMENT); + CASE(ZEND_INCLUDE_OR_EVAL); + CASE(ZEND_UNSET_VAR); + CASE(ZEND_UNSET_DIM); + CASE(ZEND_UNSET_OBJ); + CASE(ZEND_FE_RESET); + CASE(ZEND_FE_FETCH); + CASE(ZEND_EXIT); + CASE(ZEND_FETCH_R); + CASE(ZEND_FETCH_DIM_R); + CASE(ZEND_FETCH_OBJ_R); + CASE(ZEND_FETCH_W); + CASE(ZEND_FETCH_DIM_W); + CASE(ZEND_FETCH_OBJ_W); + CASE(ZEND_FETCH_RW); + CASE(ZEND_FETCH_DIM_RW); + CASE(ZEND_FETCH_OBJ_RW); + CASE(ZEND_FETCH_IS); + CASE(ZEND_FETCH_DIM_IS); + CASE(ZEND_FETCH_OBJ_IS); + CASE(ZEND_FETCH_FUNC_ARG); + CASE(ZEND_FETCH_DIM_FUNC_ARG); + CASE(ZEND_FETCH_OBJ_FUNC_ARG); + CASE(ZEND_FETCH_UNSET); + CASE(ZEND_FETCH_DIM_UNSET); + CASE(ZEND_FETCH_OBJ_UNSET); + CASE(ZEND_FETCH_DIM_TMP_VAR); + CASE(ZEND_FETCH_CONSTANT); + CASE(ZEND_GOTO); + CASE(ZEND_EXT_STMT); + CASE(ZEND_EXT_FCALL_BEGIN); + CASE(ZEND_EXT_FCALL_END); + CASE(ZEND_EXT_NOP); + CASE(ZEND_TICKS); + CASE(ZEND_SEND_VAR_NO_REF); + CASE(ZEND_CATCH); + CASE(ZEND_THROW); + CASE(ZEND_FETCH_CLASS); + CASE(ZEND_CLONE); + CASE(ZEND_RETURN_BY_REF); + CASE(ZEND_INIT_METHOD_CALL); + CASE(ZEND_INIT_STATIC_METHOD_CALL); + CASE(ZEND_ISSET_ISEMPTY_VAR); + CASE(ZEND_ISSET_ISEMPTY_DIM_OBJ); + CASE(ZEND_PRE_INC_OBJ); + CASE(ZEND_PRE_DEC_OBJ); + CASE(ZEND_POST_INC_OBJ); + CASE(ZEND_POST_DEC_OBJ); + CASE(ZEND_ASSIGN_OBJ); + CASE(ZEND_INSTANCEOF); + CASE(ZEND_DECLARE_CLASS); + CASE(ZEND_DECLARE_INHERITED_CLASS); + CASE(ZEND_DECLARE_FUNCTION); + CASE(ZEND_RAISE_ABSTRACT_ERROR); + CASE(ZEND_DECLARE_CONST); + CASE(ZEND_ADD_INTERFACE); + CASE(ZEND_DECLARE_INHERITED_CLASS_DELAYED); + CASE(ZEND_VERIFY_ABSTRACT_CLASS); + CASE(ZEND_ASSIGN_DIM); + CASE(ZEND_ISSET_ISEMPTY_PROP_OBJ); + CASE(ZEND_HANDLE_EXCEPTION); + CASE(ZEND_USER_OPCODE); +#ifdef ZEND_JMP_SET + CASE(ZEND_JMP_SET); +#endif + CASE(ZEND_DECLARE_LAMBDA_FUNCTION); +#ifdef ZEND_ADD_TRAIT + CASE(ZEND_ADD_TRAIT); +#endif +#ifdef ZEND_BIND_TRAITS + CASE(ZEND_BIND_TRAITS); +#endif +#ifdef ZEND_SEPARATE + CASE(ZEND_SEPARATE); +#endif +#ifdef ZEND_QM_ASSIGN_VAR + CASE(ZEND_QM_ASSIGN_VAR); +#endif +#ifdef ZEND_JMP_SET_VAR + CASE(ZEND_JMP_SET_VAR); +#endif +#ifdef ZEND_DISCARD_EXCEPTION + CASE(ZEND_DISCARD_EXCEPTION); +#endif +#ifdef ZEND_YIELD + CASE(ZEND_YIELD); +#endif +#ifdef ZEND_GENERATOR_RETURN + CASE(ZEND_GENERATOR_RETURN); +#endif +#ifdef ZEND_FAST_CALL + CASE(ZEND_FAST_CALL); +#endif +#ifdef ZEND_FAST_RET + CASE(ZEND_FAST_RET); +#endif +#ifdef ZEND_RECV_VARIADIC + CASE(ZEND_RECV_VARIADIC); +#endif + CASE(ZEND_OP_DATA); + default: + return "UNKNOWN"; + } +} /* }}} */ diff --cc sapi/phpdbg/phpdbg_prompt.c index bb26556,0000000..cb46407 mode 100644,000000..100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@@ -1,1328 -1,0 +1,1330 @@@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Felipe Pena <felipe@php.net> | + | Authors: Joe Watkins <joe.watkins@live.co.uk> | + | Authors: Bob Weinand <bwoebi@php.net> | + +----------------------------------------------------------------------+ +*/ + +#include <stdio.h> +#include <string.h> +#include "zend.h" +#include "zend_compile.h" +#include "phpdbg.h" +#include "phpdbg_help.h" +#include "phpdbg_print.h" +#include "phpdbg_info.h" +#include "phpdbg_break.h" +#include "phpdbg_bp.h" +#include "phpdbg_opcode.h" +#include "phpdbg_list.h" +#include "phpdbg_utils.h" +#include "phpdbg_prompt.h" +#include "phpdbg_cmd.h" +#include "phpdbg_set.h" +#include "phpdbg_frame.h" + +/* {{{ command declarations */ +const phpdbg_command_t phpdbg_prompt_commands[] = { + PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1), + PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0), + PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1), + PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0), + PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0), + PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1), + PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0), + PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0), + PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0), + PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2), + PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1), + PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1), + PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2), + PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1), + PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0), + PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0), + PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2), + PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1), + PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0), + PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1), + PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1), + PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1), + PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1), + PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0), + PHPDBG_END_COMMAND +}; /* }}} */ + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg); + +static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */ +{ + phpdbg_input_t *function = input->argv[0]; + + if (zend_hash_exists( + &PHPDBG_G(registered), function->string, function->length+1)) { + + zval fname, *fretval; + zend_fcall_info fci; + + ZVAL_STRINGL(&fname, function->string, function->length, 1); + + memset(&fci, 0, sizeof(zend_fcall_info)); + + fci.size = sizeof(zend_fcall_info); + fci.function_table = &PHPDBG_G(registered); + fci.function_name = &fname; + fci.symbol_table = EG(active_symbol_table); + fci.object_ptr = NULL; + fci.retval_ptr_ptr = &fretval; + fci.no_separation = 1; + + if (input->argc > 1) { + int param; + zval params; + + array_init(¶ms); + + for (param = 0; param < (input->argc-1); param++) { + add_next_index_stringl( + ¶ms, + input->argv[param+1]->string, + input->argv[param+1]->length, 1); + + phpdbg_debug( + "created param[%d] from argv[%d]: %s", + param, param+1, input->argv[param+1]->string); + } + + zend_fcall_info_args(&fci, ¶ms TSRMLS_CC); + } else { + fci.params = NULL; + fci.param_count = 0; + } + + phpdbg_debug( + "created %d params from %d arguments", + fci.param_count, input->argc); + + zend_call_function(&fci, NULL TSRMLS_CC); + + if (fretval) { + zend_print_zval_r( + fretval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + } + + zval_dtor(&fname); + + return SUCCESS; + } + + return FAILURE; +} /* }}} */ + +void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */ +{ + struct stat sb; + + if (init_file && VCWD_STAT(init_file, &sb) != -1) { + FILE *fp = fopen(init_file, "r"); + if (fp) { + int line = 1; + + char cmd[PHPDBG_MAX_CMD]; + size_t cmd_len = 0L; + char *code = NULL; + size_t code_len = 0L; + zend_bool in_code = 0; + + while (fgets(cmd, PHPDBG_MAX_CMD, fp) != NULL) { + cmd_len = strlen(cmd)-1; + + while (cmd_len > 0L && isspace(cmd[cmd_len-1])) + cmd_len--; + + cmd[cmd_len] = '\0'; + + if (*cmd && cmd_len > 0L && cmd[0] != '#') { + if (cmd_len == 2) { + if (memcmp(cmd, "<:", sizeof("<:")-1) == SUCCESS) { + in_code = 1; + goto next_line; + } else { + if (memcmp(cmd, ":>", sizeof(":>")-1) == SUCCESS) { + in_code = 0; + code[code_len] = '\0'; + { + zend_eval_stringl( + code, code_len, NULL, "phpdbginit code" TSRMLS_CC); + } + free(code); + code = NULL; + goto next_line; + } + } + } + + if (in_code) { + if (code == NULL) { + code = malloc(cmd_len + 1); + } else code = realloc(code, code_len + cmd_len + 1); + + if (code) { + memcpy( + &code[code_len], cmd, cmd_len); + code_len += cmd_len; + } + goto next_line; + } + + { + phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC); + switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { + phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string); + } + } + break; + } + phpdbg_destroy_input(&input TSRMLS_CC); + } + + } +next_line: + line++; + } + + if (code) { + free(code); + } + + fclose(fp); + } else { + phpdbg_error( + "Failed to open %s for initialization", init_file); + } + + if (free_init) { + free(init_file); + } + } +} /* }}} */ + +void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC) /* {{{ */ +{ + if (!init_file && use_default) { + char *scan_dir = getenv("PHP_INI_SCAN_DIR"); + int i; + + phpdbg_try_file_init(PHPDBG_STRL(PHP_CONFIG_FILE_PATH "/" PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + + if (!scan_dir) { + scan_dir = PHP_CONFIG_FILE_SCAN_DIR; + } + while (*scan_dir != 0) { + i = 0; + while (scan_dir[i] != ':') { + if (scan_dir[i++] == 0) { + i = -1; + break; + } + } + if (i != -1) { + scan_dir[i] = 0; + } + + asprintf( + &init_file, "%s/%s", scan_dir, PHPDBG_INIT_FILENAME); + phpdbg_try_file_init(init_file, strlen(init_file), 1 TSRMLS_CC); + if (i == -1) { + break; + } + scan_dir += i + 1; + } + + phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + } else { + phpdbg_try_file_init(init_file, init_file_len, 1 TSRMLS_CC); + } +} + +PHPDBG_COMMAND(exec) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + struct stat sb; + + if (VCWD_STAT(param->str, &sb) != FAILURE) { + if (sb.st_mode & (S_IFREG|S_IFLNK)) { + char *res = phpdbg_resolve_path(param->str TSRMLS_CC); + size_t res_len = strlen(res); + + if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { + + if (PHPDBG_G(exec)) { + phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec)); + efree(PHPDBG_G(exec)); + PHPDBG_G(exec) = NULL; + PHPDBG_G(exec_len) = 0L; + } + + if (PHPDBG_G(ops)) { + phpdbg_notice("Destroying compiled opcodes"); + phpdbg_clean(0 TSRMLS_CC); + } + + PHPDBG_G(exec) = res; + PHPDBG_G(exec_len) = res_len; + + phpdbg_notice("Set execution context: %s", PHPDBG_G(exec)); + } else { + phpdbg_notice("Execution context not changed"); + } + } else { + phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str); + } + } else { + phpdbg_error("Cannot stat %s, ensure the file exists", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +int phpdbg_compile(TSRMLS_D) /* {{{ */ +{ + zend_file_handle fh; + + if (EG(in_execution)) { + phpdbg_error("Cannot compile while in execution"); + return FAILURE; + } + + phpdbg_notice("Attempting compilation of %s", PHPDBG_G(exec)); + + if (php_stream_open_for_zend_ex(PHPDBG_G(exec), &fh, + USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) { + + PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE TSRMLS_CC); + zend_destroy_file_handle(&fh TSRMLS_CC); + + phpdbg_notice("Success"); + return SUCCESS; + } else { + phpdbg_error("Could not open file %s", PHPDBG_G(exec)); + } + + return FAILURE; +} /* }}} */ + +PHPDBG_COMMAND(compile) /* {{{ */ +{ + if (!PHPDBG_G(exec)) { + phpdbg_error("No execution context"); + return SUCCESS; + } + + if (!EG(in_execution)) { + if (PHPDBG_G(ops)) { + phpdbg_error("Destroying previously compiled opcodes"); + phpdbg_clean(0 TSRMLS_CC); + } + } + + phpdbg_compile(TSRMLS_C); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(step) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + case NUMERIC_PARAM: { + if (param->type == NUMERIC_PARAM && param->num) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } else { + PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; + } + + phpdbg_notice("Stepping %s", + (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(next) /* {{{ */ +{ + return PHPDBG_NEXT; +} /* }}} */ + +PHPDBG_COMMAND(until) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_UNTIL; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + zend_op *opline = &EG(active_op_array)->opcodes[self]; + + for (next = self; next < EG(active_op_array)->last; next++) { + if (EG(active_op_array)->opcodes[next].lineno != opline->lineno) { + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_UNTIL; +} /* }}} */ + +PHPDBG_COMMAND(finish) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_FINISH; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + + for (next = self; next < EG(active_op_array)->last; next++) { + switch (EG(active_op_array)->opcodes[next].opcode) { + case ZEND_RETURN: + case ZEND_THROW: + case ZEND_EXIT: +#ifdef ZEND_YIELD + case ZEND_YIELD: +#endif + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_FINISH; +} /* }}} */ + +PHPDBG_COMMAND(leave) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing"); + return SUCCESS; + } + + PHPDBG_G(flags) |= PHPDBG_IN_LEAVE; + { + zend_uint next = 0, + self = (EG(current_execute_data)->opline - EG(active_op_array)->opcodes); + + for (next = self; next < EG(active_op_array)->last; next++) { + switch (EG(active_op_array)->opcodes[next].opcode) { + case ZEND_RETURN: + case ZEND_THROW: + case ZEND_EXIT: +#ifdef ZEND_YIELD + case ZEND_YIELD: +#endif + zend_hash_index_update( + &PHPDBG_G(seek), + (zend_ulong) &EG(active_op_array)->opcodes[next], + &EG(active_op_array)->opcodes[next], + sizeof(zend_op), NULL); + break; + } + } + } + + return PHPDBG_LEAVE; +} /* }}} */ + +PHPDBG_COMMAND(frame) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: + phpdbg_switch_frame(param->num TSRMLS_CC); + break; + + case EMPTY_PARAM: + phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */ +{ + zend_fcall_info fci; + + zval fname, + *trace, + exception; + + /* get filename and linenumber before unsetting exception */ + const char *filename = zend_get_executed_filename(TSRMLS_C); + zend_uint lineno = zend_get_executed_lineno(TSRMLS_C); + + /* copy exception */ + exception = *EG(exception); + zval_copy_ctor(&exception); + EG(exception) = NULL; + + phpdbg_error( + "Uncaught %s!", + Z_OBJCE(exception)->name); + + /* call __toString */ + ZVAL_STRINGL(&fname, "__tostring", sizeof("__tostring")-1, 1); + fci.size = sizeof(fci); + fci.function_table = &Z_OBJCE(exception)->function_table; + fci.function_name = &fname; + fci.symbol_table = NULL; + fci.object_ptr = &exception; + fci.retval_ptr_ptr = &trace; + fci.param_count = 0; + fci.params = NULL; + fci.no_separation = 1; + zend_call_function(&fci, NULL TSRMLS_CC); + + if (trace) { + phpdbg_writeln( + "Uncaught %s", Z_STRVAL_P(trace)); + /* remember to dtor trace */ + zval_ptr_dtor(&trace); + } + + /* output useful information about address */ + phpdbg_writeln( + "Stacked entered at %p in %s on line %u", + EG(active_op_array)->opcodes, filename, lineno); + + zval_dtor(&fname); + zval_dtor(&exception); +} /* }}} */ + +PHPDBG_COMMAND(run) /* {{{ */ +{ + if (EG(in_execution)) { + phpdbg_error("Cannot start another execution while one is in progress"); + return SUCCESS; + } + + if (PHPDBG_G(ops) || PHPDBG_G(exec)) { + zend_op **orig_opline = EG(opline_ptr); + zend_op_array *orig_op_array = EG(active_op_array); + zval **orig_retval_ptr = EG(return_value_ptr_ptr); - ++ zend_bool restore = 1; ++ + if (!PHPDBG_G(ops)) { + if (phpdbg_compile(TSRMLS_C) == FAILURE) { + phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec)); + goto out; + } + } + + EG(active_op_array) = PHPDBG_G(ops); + EG(return_value_ptr_ptr) = &PHPDBG_G(retval); + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + + /* clean seek state */ + PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK; + zend_hash_clean( + &PHPDBG_G(seek)); + + /* reset hit counters */ + phpdbg_reset_breakpoints(TSRMLS_C); + + zend_try { + php_output_activate(TSRMLS_C); + PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE; + zend_execute(EG(active_op_array) TSRMLS_CC); + PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE; + php_output_deactivate(TSRMLS_C); + } zend_catch { + EG(active_op_array) = orig_op_array; + EG(opline_ptr) = orig_opline; + EG(return_value_ptr_ptr) = orig_retval_ptr; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + phpdbg_error("Caught exit/error from VM"); - goto out; ++ restore = 0; + } + } zend_end_try(); + - if (EG(exception)) { - phpdbg_handle_exception(TSRMLS_C); - } - - EG(active_op_array) = orig_op_array; - EG(opline_ptr) = orig_opline; - EG(return_value_ptr_ptr) = orig_retval_ptr; ++ if (restore) { ++ if (EG(exception)) { ++ phpdbg_handle_exception(TSRMLS_C); ++ } + ++ EG(active_op_array) = orig_op_array; ++ EG(opline_ptr) = orig_opline; ++ EG(return_value_ptr_ptr) = orig_retval_ptr; ++ } + } else { + phpdbg_error("Nothing to execute!"); + } + +out: + PHPDBG_FRAME(num) = 0; + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(eval) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING); + zval retval; + + if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING; + } + + /* disable stepping while eval() in progress */ + PHPDBG_G(flags) |= PHPDBG_IN_EVAL; + zend_try { + if (zend_eval_stringl(param->str, param->len, + &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) { + zend_print_zval_r( + &retval, 0 TSRMLS_CC); + phpdbg_writeln(EMPTY); + zval_dtor(&retval); + } + } zend_end_try(); + PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL; + + /* switch stepping back on */ + if (stepping && + !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + } + + CG(unclean_shutdown) = 0; + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(back) /* {{{ */ +{ + if (!EG(in_execution)) { + phpdbg_error("Not executing!"); + return SUCCESS; + } + + switch (param->type) { + case EMPTY_PARAM: + case NUMERIC_PARAM: + phpdbg_dump_backtrace( + (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(print) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: { + phpdbg_writeln(SEPARATE); + phpdbg_notice("Execution Context Information"); +#ifdef HAVE_LIBREADLINE + phpdbg_writeln("Readline\tyes"); +#else + phpdbg_writeln("Readline\tno"); +#endif + + phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); + phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no"); + phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off"); + phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off"); + + if (PHPDBG_G(ops)) { + phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last); + + if (PHPDBG_G(ops)->last_var) { + phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1); + } else { + phpdbg_writeln("Variables\tNone"); + } + } + + phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no"); + if (EG(in_execution)) { + phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret)); + } + + phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_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("Included\t%d", zend_hash_num_elements(&EG(included_files))); + + phpdbg_writeln(SEPARATE); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(info) /* {{{ */ +{ + phpdbg_error( + "No information command selected!"); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(set) /* {{{ */ +{ + phpdbg_error( + "No information command selected!"); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(break) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: + phpdbg_set_breakpoint_file( + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC); + break; + case ADDR_PARAM: + phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); + break; + case NUMERIC_PARAM: + if (PHPDBG_G(exec)) { + phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC); + } else { + phpdbg_error("Execution context not set!"); + } + break; + case METHOD_PARAM: + phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC); + break; + case NUMERIC_METHOD_PARAM: + phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC); + break; + case NUMERIC_FUNCTION_PARAM: + phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC); + break; + case FILE_PARAM: + phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC); + break; + case STR_PARAM: + phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC); + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(shell) /* {{{ */ +{ + /* don't allow this to loop, ever ... */ + switch (param->type) { + case STR_PARAM: { + FILE *fd = NULL; + if ((fd=VCWD_POPEN((char*)param->str, "w"))) { + /* do something perhaps ?? do we want input ?? */ + fclose(fd); + } else { + phpdbg_error( + "Failed to execute %s", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(source) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + if (input->argc > 2) { + if (phpdbg_argv_is(1, "export")) { + FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+"); + if (h) { + phpdbg_export_breakpoints(h TSRMLS_CC); + fclose(h); + } else phpdbg_error("Failed to open %s", input->argv[1]->string); + } else { + phpdbg_error( + "Incorrect usage of source command, see help"); + } + } else { + struct stat sb; + if (VCWD_STAT(param->str, &sb) != -1) { + phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC); + } else phpdbg_error("Cannot stat %s", param->str); + } + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(register) /* {{{ */ +{ + switch (param->type) { + case STR_PARAM: { + zend_function *function; + char *lcname = zend_str_tolower_dup(param->str, param->len); + size_t lcname_len = strlen(lcname); + + if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) { + if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) { + zend_hash_update( + &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL); + function_add_ref(function); + + phpdbg_notice( + "Registered %s", lcname); + } else { + phpdbg_error("The requested function (%s) could not be found", param->str); + } + } else { + phpdbg_error( + "The requested name (%s) is already in use", lcname); + } + + efree(lcname); + } break; + + phpdbg_default_switch_case(); + } + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(quit) /* {{{ */ +{ + /* don't allow this to loop, ever ... */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + + phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC); + + PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + zend_bailout(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(clean) /* {{{ */ +{ + if (EG(in_execution)) { + phpdbg_error("Cannot clean environment while executing"); + return SUCCESS; + } + + phpdbg_notice("Cleaning Execution Environment"); + + phpdbg_writeln("Classes\t\t\t%d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("Constants\t\t%d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("Includes\t\t%d", zend_hash_num_elements(&EG(included_files))); + + phpdbg_clean(1 TSRMLS_CC); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(clear) /* {{{ */ +{ + phpdbg_notice("Clearing Breakpoints"); + + phpdbg_writeln("File\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE])); + phpdbg_writeln("Functions\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM])); + phpdbg_writeln("Methods\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD])); + phpdbg_writeln("Oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE])); + phpdbg_writeln("File oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE])); + phpdbg_writeln("Function oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE])); + phpdbg_writeln("Method oplines\t\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE])); + phpdbg_writeln("Conditionals\t\t%d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND])); + + phpdbg_clear_breakpoints(TSRMLS_C); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(aliases) /* {{{ */ +{ + const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; + + phpdbg_help_header(); + phpdbg_writeln("Below are the aliased, short versions of all supported commands"); + while (prompt_command && prompt_command->name) { + if (prompt_command->alias) { + if (prompt_command->subs) { + const phpdbg_command_t *sub_command = prompt_command->subs; + phpdbg_writeln(EMPTY); + phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name); + while (sub_command && sub_command->name) { + if (sub_command->alias) { + phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias, + sub_command->name, sub_command->tip); + } + ++sub_command; + } + phpdbg_writeln(EMPTY); + } else { + phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias, + prompt_command->name, prompt_command->tip); + } + } + + ++prompt_command; + } + phpdbg_help_footer(); + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(help) /* {{{ */ +{ + switch (param->type) { + case EMPTY_PARAM: { + const phpdbg_command_t *prompt_command = phpdbg_prompt_commands; + const phpdbg_command_t *help_command = phpdbg_help_commands; + + phpdbg_help_header(); + phpdbg_writeln("To get help regarding a specific command type \"help command\""); + + phpdbg_notice("Commands"); + + while (prompt_command && prompt_command->name) { + phpdbg_writeln( + " %10s\t%s", prompt_command->name, prompt_command->tip); + ++prompt_command; + } + + phpdbg_notice("Help Commands"); + + while (help_command && help_command->name) { + phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip); + ++help_command; + } + + phpdbg_help_footer(); + } break; + + default: { + phpdbg_error( + "No help can be found for the subject \"%s\"", param->str); + } + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(quiet) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: { + if (param->num) { + PHPDBG_G(flags) |= PHPDBG_IS_QUIET; + } else { + PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET; + } + phpdbg_notice("Quietness %s", + (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled"); + } break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +PHPDBG_COMMAND(list) /* {{{ */ +{ + switch (param->type) { + case NUMERIC_PARAM: + case EMPTY_PARAM: + return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); + + case FILE_PARAM: + return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); + + case STR_PARAM: + phpdbg_list_function_byname(param->str, param->len TSRMLS_CC); + break; + + case METHOD_PARAM: + return PHPDBG_LIST_HANDLER(method)(PHPDBG_COMMAND_ARGS); + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ + +int phpdbg_interactive(TSRMLS_D) /* {{{ */ +{ + int ret = SUCCESS; + phpdbg_input_t *input; + + PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE; + + input = phpdbg_read_input(NULL TSRMLS_CC); + + if (input && input->length > 0L) { + do { + switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { + if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) { + phpdbg_error("Failed to execute %s!", input->string); + } + } + break; + + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: { + if (!EG(in_execution)) { + phpdbg_error("Not running"); + } + goto out; + } + } + + phpdbg_destroy_input(&input TSRMLS_CC); + } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L)); + + if (input && !input->length) + goto last; + + } else { +last: + if (PHPDBG_G(lcmd)) { + ret = PHPDBG_G(lcmd)->handler( + &PHPDBG_G(lparam), input TSRMLS_CC); + goto out; + } + } + +out: + phpdbg_destroy_input(&input TSRMLS_CC); + + if (EG(in_execution)) { + phpdbg_restore_frame(TSRMLS_C); + } + + PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE; + + return ret; +} /* }}} */ + +void phpdbg_clean(zend_bool full TSRMLS_DC) /* {{{ */ +{ + /* this is implicitly required */ + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + if (full) { + PHPDBG_G(flags) |= PHPDBG_IS_CLEANING; + + zend_bailout(); + } +} /* }}} */ + +static inline zend_execute_data *phpdbg_create_execute_data(zend_op_array *op_array, zend_bool nested TSRMLS_DC) /* {{{ */ +{ +#if PHP_VERSION_ID >= 50500 + return zend_create_execute_data_from_op_array(op_array, nested TSRMLS_CC); +#else + +#undef EX +#define EX(element) execute_data->element +#undef EX_CV +#define EX_CV(var) EX(CVs)[var] +#undef EX_CVs +#define EX_CVs() EX(CVs) +#undef EX_T +#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) +#undef EX_Ts +#define EX_Ts() EX(Ts) + + zend_execute_data *execute_data = (zend_execute_data *)zend_vm_stack_alloc( + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + + EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); + memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); + EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2))); + EX(fbc) = NULL; + EX(called_scope) = NULL; + EX(object) = NULL; + EX(old_error_reporting) = NULL; + EX(op_array) = op_array; + EX(symbol_table) = EG(active_symbol_table); + EX(prev_execute_data) = EG(current_execute_data); + EG(current_execute_data) = execute_data; + EX(nested) = nested; + + if (!op_array->run_time_cache && op_array->last_cache_slot) { + op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*)); + } + + if (op_array->this_var != -1 && EG(This)) { + Z_ADDREF_P(EG(This)); /* For $this pointer */ + if (!EG(active_symbol_table)) { + EX_CV(op_array->this_var) = (zval**)EX_CVs() + (op_array->last_var + op_array->this_var); + *EX_CV(op_array->this_var) = EG(This); + } else { + if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX_CV(op_array->this_var))==FAILURE) { + Z_DELREF_P(EG(This)); + } + } + } + + EX(opline) = UNEXPECTED((op_array->fn_flags & ZEND_ACC_INTERACTIVE) != 0) && EG(start_op) ? EG(start_op) : op_array->opcodes; + EG(opline_ptr) = &EX(opline); + + EX(function_state).function = (zend_function *) op_array; + EX(function_state).arguments = NULL; + + return execute_data; +#endif +} /* }}} */ + +#if PHP_VERSION_ID >= 50500 +void phpdbg_execute_ex(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */ +{ +#else +void phpdbg_execute_ex(zend_op_array *op_array TSRMLS_DC) /* {{{ */ +{ + long long flags = 0; + zend_ulong address = 0L; + zend_execute_data *execute_data; + zend_bool nested = 0; +#endif + zend_bool original_in_execution = EG(in_execution); + HashTable vars; + +#if PHP_VERSION_ID < 50500 + if (EG(exception)) { + return; + } +#endif + + EG(in_execution) = 1; + +#if PHP_VERSION_ID >= 50500 + if (0) { +zend_vm_enter: + execute_data = phpdbg_create_execute_data(EG(active_op_array), 1 TSRMLS_CC); + } + zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0); +#else +zend_vm_enter: + execute_data = phpdbg_create_execute_data(op_array, nested TSRMLS_CC); + nested = 1; + zend_hash_init(&vars, EG(active_op_array)->last, NULL, NULL, 0); +#endif + + while (1) { + + if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) { + /* resolve nth opline breakpoints */ + phpdbg_resolve_op_array_breaks(EG(active_op_array) TSRMLS_CC); + } + +#ifdef ZEND_WIN32 + if (EG(timed_out)) { + zend_timeout(0); + } +#endif + +#define DO_INTERACTIVE() do { \ + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ + phpdbg_list_file( \ + zend_get_executed_filename(TSRMLS_C), \ + 3, \ + zend_get_executed_lineno(TSRMLS_C)-1, \ + zend_get_executed_lineno(TSRMLS_C) \ + TSRMLS_CC \ + ); \ + } \ + \ + do { \ + switch (phpdbg_interactive(TSRMLS_C)) { \ + case PHPDBG_LEAVE: \ + case PHPDBG_FINISH: \ + case PHPDBG_UNTIL: \ + case PHPDBG_NEXT:{ \ + goto next; \ + } \ + } \ + } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \ +} while (0) + + /* allow conditional breakpoints and + initialization to access the vm uninterrupted */ + if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) || + (PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { + /* skip possible breakpoints */ + goto next; + } + + /* perform seek operation */ + if (PHPDBG_G(flags) & PHPDBG_SEEK_MASK) { + /* current address */ + zend_ulong address = (zend_ulong) execute_data->opline; + + /* run to next line */ + if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_UNTIL; + zend_hash_clean( + &PHPDBG_G(seek)); + } else { + /* skip possible breakpoints */ + goto next; + } + } + + /* run to finish */ + if (PHPDBG_G(flags) & PHPDBG_IN_FINISH) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_FINISH; + zend_hash_clean( + &PHPDBG_G(seek)); + } + /* skip possible breakpoints */ + goto next; + } + + /* break for leave */ + if (PHPDBG_G(flags) & PHPDBG_IN_LEAVE) { + if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + PHPDBG_G(flags) &= ~PHPDBG_IN_LEAVE; + zend_hash_clean( + &PHPDBG_G(seek)); + phpdbg_notice( + "Breaking for leave at %s:%u", + zend_get_executed_filename(TSRMLS_C), + zend_get_executed_lineno(TSRMLS_C) + ); + DO_INTERACTIVE(); + } else { + /* skip possible breakpoints */ + goto next; + } + } + } + + /* not while in conditionals */ + phpdbg_print_opline_ex( + execute_data, &vars, 0 TSRMLS_CC); + + /* search for breakpoints */ + { + phpdbg_breakbase_t *brake; + + if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) && + (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) { + phpdbg_hit_breakpoint( + brake, 1 TSRMLS_CC); + DO_INTERACTIVE(); + } + } + + if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) { + DO_INTERACTIVE(); + } + +next: + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + phpdbg_writeln(EMPTY); + phpdbg_notice("Program received signal SIGINT"); + PHPDBG_G(flags) &= ~PHPDBG_IS_SIGNALED; + DO_INTERACTIVE(); + } + + PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); + + if (PHPDBG_G(vmret) > 0) { + switch (PHPDBG_G(vmret)) { + case 1: + EG(in_execution) = original_in_execution; + zend_hash_destroy(&vars); + return; + case 2: +#if PHP_VERSION_ID < 50500 + op_array = EG(active_op_array); +#endif + zend_hash_destroy(&vars); + goto zend_vm_enter; + break; + case 3: + execute_data = EG(current_execute_data); + break; + default: + break; + } + } + } + zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); +} /* }}} */