diff options
Diffstat (limited to 'sapi/phpdbg/phpdbg.c')
-rw-r--r-- | sapi/phpdbg/phpdbg.c | 1457 |
1 files changed, 954 insertions, 503 deletions
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 6fce7f4a42..ecfe7a9249 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2015 The PHP Group | +----------------------------------------------------------------------+ @@ -21,6 +21,7 @@ #if !defined(ZEND_SIGNALS) || defined(_WIN32) # include <signal.h> #endif + #include "phpdbg.h" #include "phpdbg_prompt.h" #include "phpdbg_bp.h" @@ -28,7 +29,12 @@ #include "phpdbg_list.h" #include "phpdbg_utils.h" #include "phpdbg_set.h" +#include "phpdbg_io.h" #include "zend_alloc.h" +#include "phpdbg_eol.h" +#include "phpdbg_print.h" + +#include "ext/standard/basic_functions.h" /* {{{ remote console headers */ #ifndef _WIN32 @@ -36,19 +42,40 @@ # include <sys/select.h> # include <sys/time.h> # include <sys/types.h> +# include <sys/poll.h> # include <netinet/in.h> # include <unistd.h> # include <arpa/inet.h> #endif /* }}} */ +#if defined(PHP_WIN32) && defined(HAVE_OPENSSL) +# include "openssl/applink.c" +#endif + ZEND_DECLARE_MODULE_GLOBALS(phpdbg); +int phpdbg_startup_run = 0; + +static PHP_INI_MH(OnUpdateEol) +{ + if (!new_value) { + return FAILURE; + } + + return phpdbg_eol_global_update(ZSTR_VAL(new_value)); +} + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("phpdbg.path", "", PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString, socket_path, zend_phpdbg_globals, phpdbg_globals) + STD_PHP_INI_ENTRY("phpdbg.eol", "2", PHP_INI_ALL, OnUpdateEol, socket_path, zend_phpdbg_globals, phpdbg_globals) +PHP_INI_END() static zend_bool phpdbg_booted = 0; +static zend_bool phpdbg_fully_started = 0; #if PHP_VERSION_ID >= 50500 -void (*zend_execute_old)(zend_execute_data *execute_data TSRMLS_DC); +void (*zend_execute_old)(zend_execute_data *execute_data); #else -void (*zend_execute_old)(zend_op_array *op_array TSRMLS_DC); +void (*zend_execute_old)(zend_op_array *op_array); #endif static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ @@ -63,20 +90,42 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */ pg->exec = NULL; pg->exec_len = 0; pg->buffer = NULL; + pg->last_was_newline = 1; pg->ops = NULL; pg->vmret = 0; + pg->in_execution = 0; pg->bp_count = 0; 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->io, 0, sizeof(pg->io)); pg->frame.num = 0; + pg->sapi_name_ptr = NULL; + pg->socket_fd = -1; + pg->socket_server_fd = -1; + + pg->req_id = 0; + pg->err_buf.active = 0; + pg->err_buf.type = 0; + + pg->input_buflen = 0; + pg->sigsafe_mem.mem = NULL; + pg->sigsegv_bailout = NULL; + + pg->oplog_list = NULL; + +#ifdef PHP_WIN32 + pg->sigio_watcher_thread = INVALID_HANDLE_VALUE; + memset(&pg->swd, 0, sizeof(struct win32_sigio_watcher_data)); +#endif + + pg->eol = PHPDBG_EOL_LF; } /* }}} */ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ { ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL); + REGISTER_INI_ENTRIES(); + #if PHP_VERSION_ID >= 50500 zend_execute_old = zend_execute_ex; zend_execute_ex = phpdbg_execute_ex; @@ -86,7 +135,7 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ #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); @@ -99,56 +148,54 @@ static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */ return SUCCESS; } /* }}} */ -static void php_phpdbg_destroy_bp_file(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */ { - zend_hash_destroy((HashTable*)brake); + zend_hash_destroy(Z_ARRVAL_P(brake)); } /* }}} */ -static void php_phpdbg_destroy_bp_symbol(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */ { - efree((char*)((phpdbg_breaksymbol_t*)brake)->symbol); + efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol); } /* }}} */ -static void php_phpdbg_destroy_bp_opcode(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */ { - efree((char*)((phpdbg_breakop_t*)brake)->name); + efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name); } /* }}} */ -static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */ +static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */ { - zend_hash_destroy((HashTable*)brake); + zend_hash_destroy(Z_ARRVAL_P(brake)); } /* }}} */ -static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */ +static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */ { - phpdbg_breakcond_t *brake = (phpdbg_breakcond_t*) data; + phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data); if (brake) { if (brake->ops) { - TSRMLS_FETCH(); - destroy_op_array( - brake->ops TSRMLS_CC); + destroy_op_array(brake->ops); efree(brake->ops); } - efree((char*)brake->code); + efree((char*) brake->code); } } /* }}} */ -static void php_phpdbg_destroy_registered(void *data) /* {{{ */ +static void php_phpdbg_destroy_registered(zval *data) /* {{{ */ { - zend_function *function = (zend_function*) data; - TSRMLS_FETCH(); + zend_function *function = (zend_function *) Z_PTR_P(data); + - destroy_zend_function( - function TSRMLS_CC); + destroy_zend_function(function); } /* }}} */ 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_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 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); @@ -178,6 +225,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ 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(file_sources)); zend_hash_destroy(&PHPDBG_G(registered)); zend_hash_destroy(&PHPDBG_G(watchpoints)); zend_llist_destroy(&PHPDBG_G(watchlist_mem)); @@ -186,7 +234,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ efree(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL; } - + if (PHPDBG_G(exec)) { efree(PHPDBG_G(exec)); PHPDBG_G(exec) = NULL; @@ -203,17 +251,28 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ PHPDBG_G(prompt)[1] = NULL; if (PHPDBG_G(oplog)) { - fclose( - PHPDBG_G(oplog)); + fclose(PHPDBG_G(oplog)); PHPDBG_G(oplog) = NULL; } if (PHPDBG_G(ops)) { - destroy_op_array(PHPDBG_G(ops) TSRMLS_CC); + destroy_op_array(PHPDBG_G(ops)); efree(PHPDBG_G(ops)); PHPDBG_G(ops) = NULL; } + if (PHPDBG_G(oplog_list)) { + phpdbg_oplog_list *cur = PHPDBG_G(oplog_list); + do { + phpdbg_oplog_list *prev = cur->prev; + efree(cur); + cur = prev; + } while (cur != NULL); + + zend_arena_destroy(PHPDBG_G(oplog_arena)); + PHPDBG_G(oplog_list) = NULL; + } + return SUCCESS; } /* }}} */ @@ -224,99 +283,91 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ 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; - int exec_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) { + zend_string *exec; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &exec) == FAILURE) { return; } { - struct stat sb; + zend_stat_t sb; zend_bool result = 1; - if (VCWD_STAT(exec, &sb) != FAILURE) { + if (VCWD_STAT(ZSTR_VAL(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); + ZVAL_STRINGL(return_value, PHPDBG_G(exec), PHPDBG_G(exec_len)); efree(PHPDBG_G(exec)); result = 0; } - PHPDBG_G(exec) = estrndup(exec, exec_len); - PHPDBG_G(exec_len) = exec_len; + PHPDBG_G(exec) = estrndup(ZSTR_VAL(exec), ZSTR_LEN(exec)); + PHPDBG_G(exec_len) = ZSTR_LEN(exec); - if (result) - ZVAL_BOOL(return_value, 1); + if (result) { + ZVAL_TRUE(return_value); + } } else { - zend_error( - E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); - ZVAL_BOOL(return_value, 0); + zend_error(E_WARNING, "Failed to set execution context (%s), not a regular file or symlink", exec); + ZVAL_FALSE(return_value); } } else { - zend_error( - E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); + zend_error(E_WARNING, "Failed to set execution context (%s) the file does not exist", exec); - ZVAL_BOOL(return_value, 0); + ZVAL_FALSE(return_value); } } } /* }}} */ -/* {{{ proto void phpdbg_break_next() +/* {{{ proto void phpdbg_break() instructs phpdbg to insert a breakpoint at the next opcode */ static PHP_FUNCTION(phpdbg_break_next) { - if (zend_parse_parameters_none() != SUCCESS) { + if (zend_parse_parameters_none() == FAILURE && EG(current_execute_data)) { return; - } 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); } + + phpdbg_set_breakpoint_opline_ex((phpdbg_opline_ptr_t) EG(current_execute_data)->opline + 1); } /* }}} */ /* {{{ proto void phpdbg_break_file(string file, integer line) */ static PHP_FUNCTION(phpdbg_break_file) { - char *file = NULL; - int flen = 0; - long line; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &file, &flen, &line) == FAILURE) { - return; - } - - phpdbg_set_breakpoint_file(file, line TSRMLS_CC); + char *file = NULL; + size_t flen = 0; + long line; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &file, &flen, &line) == FAILURE) { + return; + } + + phpdbg_set_breakpoint_file(file, line); } /* }}} */ /* {{{ proto void phpdbg_break_method(string class, string method) */ static PHP_FUNCTION(phpdbg_break_method) { - char *class = NULL, - *method = NULL; - int clen = 0, - mlen = 0; + char *class = NULL, *method = NULL; + size_t clen = 0, mlen = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &class, &clen, &method, &mlen) == FAILURE) { - return; - } + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &class, &clen, &method, &mlen) == FAILURE) { + return; + } - phpdbg_set_breakpoint_method(class, method TSRMLS_CC); + phpdbg_set_breakpoint_method(class, method); } /* }}} */ /* {{{ proto void phpdbg_break_function(string function) */ static PHP_FUNCTION(phpdbg_break_function) { - char *function = NULL; - int function_len; + char *function = NULL; + size_t function_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &function, &function_len) == FAILURE) { - return; - } + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &function, &function_len) == FAILURE) { + return; + } - phpdbg_set_breakpoint_symbol(function, function_len TSRMLS_CC); + phpdbg_set_breakpoint_symbol(function, function_len); } /* }}} */ /* {{{ proto void phpdbg_clear(void) @@ -324,6 +375,7 @@ static PHP_FUNCTION(phpdbg_break_function) static PHP_FUNCTION(phpdbg_clear) { zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]); 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]); @@ -338,9 +390,9 @@ static PHP_FUNCTION(phpdbg_color) { long element = 0L; char *color = NULL; - int color_len = 0; + size_t color_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &element, &color, &color_len) == FAILURE) { return; } @@ -348,7 +400,7 @@ static PHP_FUNCTION(phpdbg_color) case PHPDBG_COLOR_NOTICE: case PHPDBG_COLOR_ERROR: case PHPDBG_COLOR_PROMPT: - phpdbg_set_color_ex(element, color, color_len TSRMLS_CC); + phpdbg_set_color_ex(element, color, color_len); break; default: zend_error(E_ERROR, "phpdbg detected an incorrect color constant"); @@ -359,30 +411,298 @@ static PHP_FUNCTION(phpdbg_color) static PHP_FUNCTION(phpdbg_prompt) { char *prompt = NULL; - int prompt_len = 0; + size_t prompt_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &prompt, &prompt_len) == FAILURE) { return; } - phpdbg_set_prompt(prompt TSRMLS_CC); + phpdbg_set_prompt(prompt); } /* }}} */ +/* {{{ proto void phpdbg_start_oplog() */ +static PHP_FUNCTION(phpdbg_start_oplog) +{ + phpdbg_oplog_list *prev; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + prev = PHPDBG_G(oplog_list); + + if (!prev) { + PHPDBG_G(oplog_arena) = zend_arena_create(64 * 1024); + + PHPDBG_G(oplog_cur) = ((phpdbg_oplog_entry *) zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry))) + 1; + PHPDBG_G(oplog_cur)->next = NULL; + } + + PHPDBG_G(oplog_list) = emalloc(sizeof(phpdbg_oplog_list)); + PHPDBG_G(oplog_list)->prev = prev; + PHPDBG_G(oplog_list)->start = PHPDBG_G(oplog_cur); +} + +static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, zend_bool by_opcode) { + /* ignore RECV_* opcodes */ + zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC); + zend_op *end = op_array->opcodes + op_array->last; + + zend_long insert_idx; + zval zero; + ZVAL_LONG(&zero, 0); + + /* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */ + if (op_array->last > 1 && (end - 1)->opcode == ZEND_RETURN && ((end - 2)->opcode == ZEND_RETURN || (end - 2)->opcode == ZEND_GENERATOR_RETURN || (end - 2)->opcode == ZEND_THROW)) { + end--; + } + + for (; cur < end; cur++) { + if (cur->opcode == ZEND_NOP || cur->opcode == ZEND_OP_DATA || cur->opcode == ZEND_FE_FREE || cur->opcode == ZEND_FREE || cur->opcode == ZEND_ASSERT_CHECK + || cur->opcode == ZEND_DECLARE_CONST || cur->opcode == ZEND_DECLARE_CLASS || cur->opcode == ZEND_DECLARE_INHERITED_CLASS || cur->opcode == ZEND_DECLARE_FUNCTION + || cur->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED || cur->opcode == ZEND_VERIFY_ABSTRACT_CLASS || cur->opcode == ZEND_ADD_TRAIT || cur->opcode == ZEND_BIND_TRAITS + || cur->opcode == ZEND_DECLARE_ANON_CLASS || cur->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS || cur->opcode == ZEND_FAST_RET || cur->opcode == ZEND_TICKS + || cur->opcode == ZEND_EXT_STMT || cur->opcode == ZEND_EXT_FCALL_BEGIN || cur->opcode == ZEND_EXT_FCALL_END || cur->opcode == ZEND_EXT_NOP || cur->opcode == ZEND_BIND_GLOBAL) { + continue; + } + + if (by_opcode) { + insert_idx = cur - op_array->opcodes; + } else { + insert_idx = cur->lineno; + } + + if (cur->opcode == ZEND_NEW && (cur + 1)->opcode == ZEND_DO_FCALL) { + cur++; + } + + zend_hash_index_update(insert_ht, insert_idx, &zero); + } +} + +static inline HashTable* phpdbg_add_empty_array(HashTable *ht, zend_string *name) { + zval *ht_zv = zend_hash_find(ht, name); + if (!ht_zv) { + zval zv; + array_init(&zv); + ht_zv = zend_hash_add_new(ht, name, &zv); + } + return Z_ARR_P(ht_zv); +} + +/* {{{ proto void phpdbg_end_oplog() */ +static PHP_FUNCTION(phpdbg_get_executable) +{ + HashTable *options = NULL; + zval *option_buffer; + zend_bool by_function = 0; + zend_bool by_opcode = 0; + HashTable *insert_ht; + + zend_function *func; + zend_class_entry *ce; + zend_string *name; + HashTable *files = &PHPDBG_G(file_sources); + HashTable files_tmp; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) { + return; + } + + if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) { + by_function = zend_is_true(option_buffer); + } + + if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) { + if (by_function) { + by_opcode = zend_is_true(option_buffer); + } + } + + if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("files")))) { + ZVAL_DEREF(option_buffer); + if (Z_TYPE_P(option_buffer) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(option_buffer)) > 0) { + zval *filename; + + files = &files_tmp; + zend_hash_init(files, 0, NULL, NULL, 0); + + ZEND_HASH_FOREACH_VAL(Z_ARR_P(option_buffer), filename) { + zend_hash_add_empty_element(files, zval_get_string(filename)); + } ZEND_HASH_FOREACH_END(); + } else { + GC_REFCOUNT(files)++; + } + } else { + GC_REFCOUNT(files)++; + } + + array_init(return_value); + + ZEND_HASH_FOREACH_STR_KEY_PTR(EG(function_table), name, func) { + if (func->type == ZEND_USER_FUNCTION) { + if (zend_hash_exists(files, func->op_array.filename)) { + insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename); + + if (by_function) { + insert_ht = phpdbg_add_empty_array(insert_ht, name); + } + + phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode); + } + } + } ZEND_HASH_FOREACH_END(); + + ZEND_HASH_FOREACH_STR_KEY_PTR(EG(class_table), name, ce) { + if (ce->type == ZEND_USER_CLASS) { + if (zend_hash_exists(files, ce->info.user.filename)) { + ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { + if (func->type == ZEND_USER_FUNCTION) { + insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), func->op_array.filename); + + if (by_function) { + zend_string *fn_name = strpprintf(ZSTR_LEN(name) + ZSTR_LEN(func->op_array.function_name) + 2, "%.*s::%.*s", ZSTR_LEN(name), ZSTR_VAL(name), ZSTR_LEN(func->op_array.function_name), ZSTR_VAL(func->op_array.function_name)); + insert_ht = phpdbg_add_empty_array(insert_ht, fn_name); + zend_string_release(fn_name); + } + + phpdbg_oplog_fill_executable(&func->op_array, insert_ht, by_opcode); + } + } ZEND_HASH_FOREACH_END(); + } + } + } ZEND_HASH_FOREACH_END(); + + ZEND_HASH_FOREACH_STR_KEY(files, name) { + phpdbg_file_source *source = zend_hash_find_ptr(&PHPDBG_G(file_sources), name); + if (source) { + phpdbg_oplog_fill_executable( + source->op_array, + phpdbg_add_empty_array(Z_ARR_P(return_value), source->op_array->filename), + by_opcode); + } + } ZEND_HASH_FOREACH_END(); + + if (!--GC_REFCOUNT(files)) { + zend_hash_destroy(files); + } +} + +/* {{{ proto void phpdbg_end_oplog() */ +static PHP_FUNCTION(phpdbg_end_oplog) +{ + phpdbg_oplog_entry *cur; + phpdbg_oplog_list *prev; + + HashTable *options = NULL; + zval *option_buffer; + zend_bool by_function = 0; + zend_bool by_opcode = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) { + return; + } + + if (!PHPDBG_G(oplog_list)) { + zend_error(E_WARNING, "Can not end an oplog without starting it"); + return; + } + + cur = PHPDBG_G(oplog_list)->start; + prev = PHPDBG_G(oplog_list)->prev; + + efree(PHPDBG_G(oplog_list)); + PHPDBG_G(oplog_list) = prev; + + if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("functions")))) { + by_function = zend_is_true(option_buffer); + } + + if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("opcodes")))) { + if (by_function) { + by_opcode = zend_is_true(option_buffer); + } + } + + array_init(return_value); + + { + zend_string *last_file = NULL; + HashTable *file_ht; + zend_string *last_function = (void *)~(uintptr_t)0; + zend_class_entry *last_scope = NULL; + + HashTable *insert_ht; + zend_long insert_idx; + + do { + zend_op_array *op_array = cur->op_array; + zval zero; + ZVAL_LONG(&zero, 0); + + if (op_array->filename != last_file) { + last_file = op_array->filename; + file_ht = insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), last_file); + } + + if (by_function) { + if (op_array->function_name == NULL) { + if (last_function != NULL) { + insert_ht = file_ht; + } + last_function = NULL; + } else if (op_array->function_name != last_function || op_array->scope != last_scope) { + zend_string *fn_name; + last_function = op_array->function_name; + last_scope = op_array->scope; + if (last_scope == NULL) { + fn_name = zend_string_copy(last_function); + } else { + fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name), ZSTR_LEN(last_function), ZSTR_VAL(last_function)); + } + insert_ht = phpdbg_add_empty_array(Z_ARR_P(return_value), fn_name); + zend_string_release(fn_name); + } + } + + if (by_opcode) { + insert_idx = cur->op - op_array->opcodes; + } else { + insert_idx = cur->op->lineno; + } + + { + zval *num = zend_hash_index_find(insert_ht, insert_idx); + if (!num) { + num = zend_hash_index_add_new(insert_ht, insert_idx, &zero); + } + Z_LVAL_P(num)++; + } + + cur = cur->next; + } while (cur != NULL); + } + + if (!prev) { + zend_arena_destroy(PHPDBG_G(oplog_arena)); + } +} + ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_next_arginfo, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_file_arginfo, 0, 0, 2) - ZEND_ARG_INFO(0, file) - ZEND_ARG_INFO(0, line) + ZEND_ARG_INFO(0, file) + ZEND_ARG_INFO(0, line) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_method_arginfo, 0, 0, 2) - ZEND_ARG_INFO(0, class) - ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, class) + ZEND_ARG_INFO(0, method) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_break_function_arginfo, 0, 0, 1) - ZEND_ARG_INFO(0, function) + ZEND_ARG_INFO(0, function) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_color_arginfo, 0, 0, 0) @@ -401,6 +721,17 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(phpdbg_clear_arginfo, 0, 0, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(phpdbg_start_oplog_arginfo, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_end_oplog_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(phpdbg_get_executable_arginfo, 0, 0, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + zend_function_entry phpdbg_user_functions[] = { PHP_FE(phpdbg_clear, phpdbg_clear_arginfo) PHP_FE(phpdbg_break_next, phpdbg_break_next_arginfo) @@ -410,6 +741,9 @@ zend_function_entry phpdbg_user_functions[] = { PHP_FE(phpdbg_exec, phpdbg_exec_arginfo) PHP_FE(phpdbg_color, phpdbg_color_arginfo) PHP_FE(phpdbg_prompt, phpdbg_prompt_arginfo) + PHP_FE(phpdbg_start_oplog, phpdbg_start_oplog_arginfo) + PHP_FE(phpdbg_end_oplog, phpdbg_end_oplog_arginfo) + PHP_FE(phpdbg_get_executable, phpdbg_get_executable_arginfo) #ifdef PHP_FE_END PHP_FE_END #else @@ -441,18 +775,18 @@ static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* return SUCCESS; } /* }}} */ -static char* php_sapi_phpdbg_read_cookies(TSRMLS_D) /* {{{ */ +static char* php_sapi_phpdbg_read_cookies(void) /* {{{ */ { return NULL; } /* }}} */ -static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */ +static int php_sapi_phpdbg_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s) /* {{{ */ { return 0; } /* }}} */ -static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */ +static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */ { /* We do nothing here, this function is needed to prevent that the fallback * header handling is called. */ @@ -460,18 +794,23 @@ static int php_sapi_phpdbg_send_headers(sapi_headers_struct *sapi_headers TSRMLS } /* }}} */ -static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */ +static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *server_context) /* {{{ */ { } /* }}} */ -static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ +static void php_sapi_phpdbg_log_message(char *message) /* {{{ */ { /* * We must not request TSRM before being boot */ if (phpdbg_booted) { - phpdbg_error("%s", message); + if (PHPDBG_G(flags) & PHPDBG_IN_EVAL) { + phpdbg_error("eval", "msg=\"%s\"", "%s", message); + return; + } + + phpdbg_error("php", "msg=\"%s\"", "%s", message); switch (PG(last_error_type)) { case E_ERROR: @@ -479,123 +818,160 @@ static void php_sapi_phpdbg_log_message(char *message TSRMLS_DC) /* {{{ */ 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 - ); + case E_RECOVERABLE_ERROR: { + const char *file_char = zend_get_executed_filename(); + zend_string *file = zend_string_init(file_char, strlen(file_char), 0); + phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno()); + efree(file); + + if (!phpdbg_fully_started) { + return; } do { - switch (phpdbg_interactive(TSRMLS_C)) { + switch (phpdbg_interactive(1)) { case PHPDBG_LEAVE: case PHPDBG_FINISH: case PHPDBG_UNTIL: case PHPDBG_NEXT: return; } - } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); - + } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)); + } } - } else fprintf(stdout, "%s\n", message); + } else { + fprintf(stdout, "%s\n", message); + } } /* }}} */ -static int php_sapi_phpdbg_deactivate(TSRMLS_D) /* {{{ */ +static int php_sapi_phpdbg_deactivate(void) /* {{{ */ { + if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { + zend_phpdbg_globals *pg = PHPDBG_G(backup) = calloc(1, sizeof(zend_phpdbg_globals)); + + php_phpdbg_globals_ctor(pg); + + pg->exec = zend_strndup(PHPDBG_G(exec), PHPDBG_G(exec_len)); + pg->exec_len = PHPDBG_G(exec_len); + pg->oplog = PHPDBG_G(oplog); + pg->prompt[0] = PHPDBG_G(prompt)[0]; + pg->prompt[1] = PHPDBG_G(prompt)[1]; + memcpy(pg->colors, PHPDBG_G(colors), sizeof(pg->colors)); + pg->eol = PHPDBG_G(eol); + pg->input_buflen = PHPDBG_G(input_buflen); + memcpy(pg->input_buffer, PHPDBG_G(input_buffer), pg->input_buflen); + pg->flags = PHPDBG_G(flags) & PHPDBG_PRESERVE_FLAGS_MASK; + } + 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) /* {{{ */ +static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */ { - unsigned int len; - char *docroot = ""; + size_t 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); + php_import_environment_variables(track_vars_array); 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, "PHP_SELF", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) { + php_register_variable("PHP_SELF", PHPDBG_G(exec), track_vars_array); } - 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_NAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) { + php_register_variable("SCRIPT_NAME", PHPDBG_G(exec), track_vars_array); } - 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, "SCRIPT_FILENAME", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) { + php_register_variable("SCRIPT_FILENAME", PHPDBG_G(exec), track_vars_array); } - 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); + if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &PHPDBG_G(exec), PHPDBG_G(exec_len), &len)) { + php_register_variable("PATH_TRANSLATED", PHPDBG_G(exec), track_vars_array); } } - /* 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); + /* any old docroot will do */ + len = 0; + if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) { + php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array); } } /* }}} */ -static inline int php_sapi_phpdbg_ub_write(const char *message, unsigned int length TSRMLS_DC) /* {{{ */ +static inline size_t php_sapi_phpdbg_ub_write(const char *message, size_t length) /* {{{ */ { - return phpdbg_write("%s", message); + if (PHPDBG_G(socket_fd) != -1 && !(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + send(PHPDBG_G(socket_fd), message, length, 0); + } + return phpdbg_script(P_STDOUT, "%.*s", length, message); } /* }}} */ +/* beginning of struct, see main/streams/plain_wrapper.c line 111 */ +typedef struct { + FILE *file; + int fd; +} php_stdio_stream_data; + +static size_t phpdbg_stdiop_write(php_stream *stream, const char *buf, size_t count) { + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + while (data->fd >= 0) { + struct stat stat[3]; + memset(stat, 0, sizeof(stat)); + if (((fstat(fileno(stderr), &stat[2]) < 0) & (fstat(fileno(stdout), &stat[0]) < 0)) | (fstat(data->fd, &stat[1]) < 0)) { + break; + } + + if (stat[0].st_dev == stat[1].st_dev && stat[0].st_ino == stat[1].st_ino) { + phpdbg_script(P_STDOUT, "%.*s", (int) count, buf); + return count; + } + if (stat[2].st_dev == stat[1].st_dev && stat[2].st_ino == stat[1].st_ino) { + phpdbg_script(P_STDERR, "%.*s", (int) count, buf); + return count; + } + break; + } + + return PHPDBG_G(php_stdiop_write)(stream, buf, count); +} + #if PHP_VERSION_ID >= 50700 -static inline void php_sapi_phpdbg_flush(void *context TSRMLS_DC) /* {{{ */ +static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ { #else static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */ { - TSRMLS_FETCH(); #endif - fflush(PHPDBG_G(io)[PHPDBG_STDOUT]); + if (!phpdbg_active_sigsafe_mem()) { + fflush(PHPDBG_G(io)[PHPDBG_STDOUT].ptr); + } } /* }}} */ /* copied from sapi/cli/php_cli.c cli_register_file_handles */ -static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */ +static void phpdbg_register_file_handles(void) /* {{{ */ { - zval *zin, *zout, *zerr; + zval zin, zout, zerr; php_stream *s_in, *s_out, *s_err; php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL; zend_constant ic, oc, ec; - MAKE_STD_ZVAL(zin); - MAKE_STD_ZVAL(zout); - MAKE_STD_ZVAL(zerr); - s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); if (s_in==NULL || s_out==NULL || s_err==NULL) { - FREE_ZVAL(zin); - FREE_ZVAL(zout); - FREE_ZVAL(zerr); if (s_in) php_stream_close(s_in); if (s_out) php_stream_close(s_out); if (s_err) php_stream_close(s_err); @@ -608,34 +984,27 @@ static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; #endif - php_stream_to_zval(s_in, zin); - php_stream_to_zval(s_out, zout); - php_stream_to_zval(s_err, zerr); + php_stream_to_zval(s_in, &zin); + php_stream_to_zval(s_out, &zout); + php_stream_to_zval(s_err, &zerr); - ic.value = *zin; + ic.value = zin; ic.flags = CONST_CS; - ic.name = zend_strndup(ZEND_STRL("STDIN")); - ic.name_len = sizeof("STDIN"); + ic.name = zend_string_init(ZEND_STRL("STDIN"), 0); ic.module_number = 0; - zend_register_constant(&ic TSRMLS_CC); + zend_register_constant(&ic); - oc.value = *zout; + oc.value = zout; oc.flags = CONST_CS; - oc.name = zend_strndup(ZEND_STRL("STDOUT")); - oc.name_len = sizeof("STDOUT"); + oc.name = zend_string_init(ZEND_STRL("STDOUT"), 0); oc.module_number = 0; - zend_register_constant(&oc TSRMLS_CC); + zend_register_constant(&oc); - ec.value = *zerr; + ec.value = zerr; ec.flags = CONST_CS; - ec.name = zend_strndup(ZEND_STRL("STDERR")); - ec.name_len = sizeof("STDERR"); + ec.name = zend_string_init(ZEND_STRL("STDERR"), 0); ec.module_number = 0; - zend_register_constant(&ec TSRMLS_CC); - - FREE_ZVAL(zin); - FREE_ZVAL(zout); - FREE_ZVAL(zerr); + zend_register_constant(&ec); } /* }}} */ @@ -681,18 +1050,21 @@ const opt_struct OPTIONS[] = { /* {{{ */ /* phpdbg options */ {'q', 0, "no banner"}, {'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, "generate ext_stmt opcodes"}, {'E', 0, "step-through-eval"}, {'S', 1, "sapi-name"}, #ifndef _WIN32 {'l', 1, "listen"}, {'a', 1, "address-or-any"}, #endif + {'x', 0, "xml output"}, + {'p', 2, "show opcodes"}, + {'h', 0, "help"}, {'V', 0, "version"}, {'-', 0, NULL} }; /* }}} */ @@ -706,14 +1078,13 @@ const char phpdbg_ini_hardcoded[] = "max_execution_time=0\n" "max_input_time=-1\n" "error_log=\n" -"output_buffering=off\0"; +"output_buffering=off\n\0"; /* overwriteable ini defaults must be set in phpdbg_ini_defaults() */ #define INI_DEFAULT(name, value) \ + ZVAL_STRINGL(&tmp, value, sizeof(value) - 1); \ 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); + zend_hash_str_update(configuration_hash, name, sizeof(name) - 1, &tmp); void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ { @@ -721,199 +1092,173 @@ void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */ INI_DEFAULT("report_zend_debug", "0"); } /* }}} */ -static void phpdbg_welcome(zend_bool cleaning TSRMLS_DC) /* {{{ */ +static void phpdbg_welcome(zend_bool cleaning) /* {{{ */ { /* 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_xml("<intros>"); + phpdbg_notice("intro", "version=\"%s\"", "Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION); + phpdbg_writeln("intro", "help=\"help\"", "To get help using phpdbg type \"help\" and press enter"); + phpdbg_notice("intro", "report=\"%s\"", "Please report bugs to <%s>", PHPDBG_ISSUES); + phpdbg_xml("</intros>"); + } else if (phpdbg_startup_run == 0) { + if (!(PHPDBG_G(flags) & PHPDBG_WRITE_XML)) { + phpdbg_notice(NULL, NULL, "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))); + phpdbg_write("cleaninfo", "classes=\"%d\" functions=\"%d\" constants=\"%d\" includes=\"%d\"", + "Classes %d\n" + "Functions %d\n" + "Constants %d\n" + "Includes %d\n", + zend_hash_num_elements(EG(class_table)), + zend_hash_num_elements(EG(function_table)), + zend_hash_num_elements(EG(zend_constants)), + 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 { + if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) { /* we quit remote consoles on recv SIGINT */ if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + PHPDBG_G(flags) |= PHPDBG_IS_STOPPING; 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); - } - } - } + } else { + /* set signalled only when not interactive */ + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1]; + + phpdbg_set_sigsafe_mem(mem); + zend_try { + phpdbg_force_interruption(); + } zend_end_try() + phpdbg_clear_sigsafe_mem(); + return; } + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; } } - - 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]); +static void phpdbg_remote_close(int socket, FILE *stream) { + if (socket >= 0) { + phpdbg_close_socket(socket); } - if (streams[0]) { - fclose(streams[0]); + if (stream) { + fclose(stream); } +} - if ((*socket)[1] >= 0) { - shutdown( - (*socket)[1], SHUT_RDWR); - close((*socket)[1]); - } +/* don't inline this, want to debug it easily, will inline when done */ +static int phpdbg_remote_init(const char* address, unsigned short port, int server, int *socket, FILE **stream) { + phpdbg_remote_close(*socket, *stream); + + if (server < 0) { + phpdbg_rlog(fileno(stderr), "Initializing connection on %s:%u failed", address, port); - if (streams[1]) { - fclose(streams[1]); + return FAILURE; } -} /* }}} */ -/* don't inline this, want to debug it easily, will inline when done */ + phpdbg_rlog(fileno(stderr), "accepting connections on %s:%u", address, port); + { + struct sockaddr_storage address; + socklen_t size = sizeof(address); + char buffer[20] = {0}; + /* XXX error checks */ + memset(&address, 0, size); + *socket = accept(server, (struct sockaddr *) &address, &size); + inet_ntop(AF_INET, &(((struct sockaddr_in *)&address)->sin_addr), buffer, sizeof(buffer)); -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]); + phpdbg_rlog(fileno(stderr), "connection established from %s", buffer); } - 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]); - } +#ifndef _WIN32 + dup2(*socket, fileno(stdout)); + dup2(*socket, fileno(stdin)); - if ((*listen)[1] < 0) { - phpdbg_rlog(stderr, - "console failed to initialize (stdout) on %s:%d", address, port[1]); - } + setbuf(stdout, NULL); - if ((*listen)[0] >= 0) { - close((*listen)[0]); - } + *stream = fdopen(*socket, "r+"); - if ((*listen)[1] >= 0) { - close((*listen)[1]); - } + phpdbg_set_async_io(*socket); +#endif + return SUCCESS; +} - return FAILURE; - } +#ifndef _WIN32 +/* This function *strictly* assumes that SIGIO is *only* used on the remote connection stream */ +void phpdbg_sigio_handler(int sig, siginfo_t *info, void *context) /* {{{ */ +{ + int flags; + size_t newlen; + size_t i/*, last_nl*/; - phpdbg_close_sockets(socket, streams); +// if (!(info->si_band & POLLIN)) { +// return; /* Not interested in writeablility etc., just interested in incoming data */ +// } - 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}; + /* only non-blocking reading, avoid non-blocking writing */ + flags = fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_GETFL, 0); + fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags | O_NONBLOCK); - { - memset(&address, 0, size); - (*socket)[0] = accept( - (*listen)[0], (struct sockaddr *) &address, &size); - inet_ntop(AF_INET, &address.sin_addr, buffer, sizeof(buffer)); + do { + char mem[PHPDBG_SIGSAFE_MEM_SIZE + 1]; + size_t off = 0; - phpdbg_rlog(stderr, "connection (stdin) from %s", buffer); + if ((newlen = recv(PHPDBG_G(io)[PHPDBG_STDIN].fd, mem, PHPDBG_SIGSAFE_MEM_SIZE, MSG_PEEK)) == (size_t) -1) { + break; } - - { - 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); + for (i = 0; i < newlen; i++) { + switch (mem[off + i]) { + case '\x03': /* ^C char */ + if (PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE) { + break; /* or quit ??? */ + } + if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) { + phpdbg_set_sigsafe_mem(mem); + zend_try { + phpdbg_force_interruption(); + } zend_end_try(); + phpdbg_clear_sigsafe_mem(); + break; + } + if (!(PHPDBG_G(flags) & PHPDBG_IS_INTERACTIVE)) { + PHPDBG_G(flags) |= PHPDBG_IS_SIGNALED; + } + break; +/* case '\n': + zend_llist_add_element(PHPDBG_G(stdin), strndup() + last_nl = PHPDBG_G(stdin_buf).len + i; + break; +*/ } } - } + off += i; + } while (0); - 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; + fcntl(PHPDBG_G(io)[PHPDBG_STDIN].fd, F_SETFL, flags); } /* }}} */ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */ { int is_handled = FAILURE; - TSRMLS_FETCH(); switch (sig) { case SIGBUS: case SIGSEGV: - is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC); + if (PHPDBG_G(sigsegv_bailout)) { + LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE); + } + is_handled = phpdbg_watchpoint_segfault_handler(info, context); if (is_handled == FAILURE) { #ifdef ZEND_SIGNALS - zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC); + zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL); #else sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL); #endif @@ -928,10 +1273,9 @@ static inline zend_mm_heap *phpdbg_mm_get_heap() /* {{{ */ { zend_mm_heap *mm_heap; - TSRMLS_FETCH(); - mm_heap = zend_mm_set_heap(NULL TSRMLS_CC); - zend_mm_set_heap(mm_heap TSRMLS_CC); + mm_heap = zend_mm_set_heap(NULL); + zend_mm_set_heap(mm_heap); return mm_heap; } /* }}} */ @@ -943,7 +1287,14 @@ void *phpdbg_malloc_wrapper(size_t size) /* {{{ */ void phpdbg_free_wrapper(void *p) /* {{{ */ { - zend_mm_free(phpdbg_mm_get_heap(), p); + zend_mm_heap *heap = phpdbg_mm_get_heap(); + if (UNEXPECTED(heap == p)) { + /* TODO: heap maybe allocated by mmap(zend_mm_init) or malloc(USE_ZEND_ALLOC=0) + * let's prevent it from segfault for now + */ + } else { + zend_mm_free(heap, p); + } } /* }}} */ void *phpdbg_realloc_wrapper(void *ptr, size_t size) /* {{{ */ @@ -961,8 +1312,7 @@ int main(int argc, char **argv) /* {{{ */ zend_ulong zend_extensions_len = 0L; zend_bool ini_ignore; char *ini_override; - char *exec; - size_t exec_len; + char *exec = NULL; char *init_file; size_t init_file_len; zend_bool init_file_default; @@ -971,43 +1321,34 @@ int main(int argc, char **argv) /* {{{ */ zend_ulong flags; char *php_optarg; int php_optind, opt, show_banner = 1; - long cleaning = 0; + long cleaning = -1; + volatile zend_bool quit_immediately = 0; /* somehow some gcc release builds will play a bit around with order in combination with setjmp..., hence volatile */ zend_bool remote = 0; - int run = 0; - int step = 0; - -#ifdef _WIN32 - char *bp_tmp_file = NULL; -#else - char bp_tmp_file[] = "/tmp/phpdbg.XXXXXX"; -#endif - -#ifndef _WIN32 + zend_phpdbg_globals *settings = NULL; + char *bp_tmp = NULL; char *address; - int listen[2]; - int server[2]; - int socket[2]; - FILE* streams[2] = {NULL, NULL}; -#endif + int listen = -1; + int server = -1; + int socket = -1; + FILE* stream = NULL; + char *print_opline_func; + zend_bool ext_stmt = 0; #ifdef ZTS void ***tsrm_ls; #endif #ifndef _WIN32 + struct sigaction sigio_struct; struct sigaction signal_struct; signal_struct.sa_sigaction = phpdbg_signal_handler; signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER; + sigemptyset(&signal_struct.sa_mask); + sigio_struct.sa_sigaction = phpdbg_sigio_handler; + sigio_struct.sa_flags = SA_SIGINFO; + sigemptyset(&sigio_struct.sa_mask); 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 @@ -1024,37 +1365,12 @@ int main(int argc, char **argv) /* {{{ */ #endif phpdbg_main: - if (!cleaning) { - -#ifdef _WIN32 - bp_tmp_file = malloc(L_tmpnam); - - if (bp_tmp_file) { - if (!tmpnam(bp_tmp_file)) { - free(bp_tmp_file); - bp_tmp_file = NULL; - } - } - - if (!bp_tmp_file) { - phpdbg_error("Unable to create temporary file"); - return 1; - } -#else - if (!mkstemp(bp_tmp_file)) { - memset(bp_tmp_file, 0, sizeof(bp_tmp_file)); - } -#endif - - } 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; @@ -1064,14 +1380,17 @@ phpdbg_main: php_optarg = NULL; php_optind = 1; opt = 0; - run = 0; - step = 0; sapi_name = NULL; + if (settings) { + exec = settings->exec; + } while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { switch (opt) { case 'r': - run++; + if (settings == NULL) { + phpdbg_startup_run++; + } break; case 'n': ini_ignore = 1; @@ -1155,8 +1474,8 @@ phpdbg_main: flags &= ~PHPDBG_IS_QUIET; break; - case 's': /* set stepping on */ - step = 1; + case 'e': + ext_stmt = 1; break; case 'E': /* stepping through eval on */ @@ -1172,20 +1491,12 @@ phpdbg_main: 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); - } + /* if you pass a listen port, we will read and write on listen port */ + case 'l': /* set listen ports */ + if (sscanf(php_optarg, "%d", &listen) != 1) { + listen = 8000; } - } break; + break; case 'a': { /* set bind address */ free(address); @@ -1195,6 +1506,28 @@ phpdbg_main: } break; #endif + case 'x': + flags |= PHPDBG_WRITE_XML; + break; + + + case 'p': { + print_opline_func = php_optarg; + show_banner = 0; + settings = (void *) 0x1; + } break; + + case 'h': { + sapi_startup(phpdbg); + phpdbg->startup(phpdbg); + PHPDBG_G(flags) = 0; + phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT); + phpdbg_do_help(NULL); + sapi_deactivate(); + sapi_shutdown(); + return 0; + } break; + case 'V': { sapi_startup(phpdbg); phpdbg->startup(phpdbg); @@ -1206,39 +1539,23 @@ phpdbg_main: PHP_VERSION, get_zend_version() ); - sapi_deactivate(TSRMLS_C); + sapi_deactivate(); sapi_shutdown(); return 0; } break; } + + php_optarg = NULL; } - + /* set exec if present on command line */ - if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS)) - { - exec_len = strlen(argv[php_optind]); - if (exec_len) { - if (exec) { - free(exec); - } + if (argc > php_optind && (strcmp(argv[php_optind-1], "--") != SUCCESS)) { + if (!exec && strlen(argv[php_optind])) { exec = strdup(argv[php_optind]); } php_optind++; } -#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; } @@ -1293,60 +1610,110 @@ phpdbg_main: EXCEPTION_POINTERS *xp; __try { #endif - zend_mm_heap *mm_heap = phpdbg_mm_get_heap(); + zend_mm_heap *mm_heap; + void* (*_malloc)(size_t); + void (*_free)(void*); + void* (*_realloc)(void*, size_t); + + /* set flags from command line */ + PHPDBG_G(flags) = flags; + + if (settings > (zend_phpdbg_globals *) 0x2) { +#ifdef ZTS + *((zend_phpdbg_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings; +#else + phpdbg_globals = *settings; +#endif + } - if (mm_heap->use_zend_alloc) { - mm_heap->_malloc = phpdbg_malloc_wrapper; - mm_heap->_realloc = phpdbg_realloc_wrapper; - mm_heap->_free = phpdbg_free_wrapper; - mm_heap->use_zend_alloc = 0; + /* setup remote server if necessary */ + if (cleaning <= 0 && listen > 0) { + server = phpdbg_open_socket(address, listen); + if (-1 > server || phpdbg_remote_init(address, listen, server, &socket, &stream) == FAILURE) { + exit(0); + } + +#ifndef _WIN32 + sigaction(SIGIO, &sigio_struct, NULL); +#endif + + /* set remote flag to stop service shutting down upon quit */ + remote = 1; } - zend_activate(TSRMLS_C); + mm_heap = phpdbg_mm_get_heap(); + zend_mm_get_custom_handlers(mm_heap, &_malloc, &_free, &_realloc); - PHPDBG_G(original_free_function) = mm_heap->_free; - mm_heap->_free = phpdbg_watch_efree; + if (!_malloc) { + _malloc = phpdbg_malloc_wrapper; + } + if (!_realloc) { + _realloc = phpdbg_realloc_wrapper; + } + if (!_free) { + _free = phpdbg_free_wrapper; + } + + zend_activate(); + + phpdbg_init_list(); + + PHPDBG_G(original_free_function) = _free; + _free = phpdbg_watch_efree; + + zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc); - phpdbg_setup_watchpoints(TSRMLS_C); + phpdbg_setup_watchpoints(); #if defined(ZEND_SIGNALS) && !defined(_WIN32) zend_try { - zend_signal_activate(TSRMLS_C); + zend_signal_activate(); } zend_end_try(); #endif #if defined(ZEND_SIGNALS) && !defined(_WIN32) - zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try(); - zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try(); + zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try(); + zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try(); #elif !defined(_WIN32) sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); #endif - if (php_request_startup(TSRMLS_C) == SUCCESS) { + PHPDBG_G(sapi_name_ptr) = sapi_name; + + if (exec) { /* set execution context */ + PHPDBG_G(exec) = phpdbg_resolve_path(exec); + PHPDBG_G(exec_len) = PHPDBG_G(exec) ? strlen(PHPDBG_G(exec)) : 0; + + free(exec); + exec = NULL; + } + + php_output_activate(); + php_output_deactivate(); + + php_output_activate(); + + if (php_request_startup() == SUCCESS) { int i; - - SG(request_info).argc = argc - php_optind + 1; + + SG(request_info).argc = argc - php_optind + 1; SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *)); for (i = SG(request_info).argc; --i;) { SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]); } - SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup(""); + SG(request_info).argv[0] = PHPDBG_G(exec) ? estrdup(PHPDBG_G(exec)) : estrdup(""); - php_hash_environment(TSRMLS_C); + php_hash_environment(); } - /* make sure to turn off buffer for ev command */ - php_output_activate(TSRMLS_C); - php_output_deactivate(TSRMLS_C); - /* do not install sigint handlers for remote consoles */ /* sending SIGINT then provides a decent way of shutting down the server */ #ifndef _WIN32 - if (listen[0] < 0) { + if (listen < 0) { #endif #if defined(ZEND_SIGNALS) && !defined(_WIN32) - zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try(); + zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try(); #else signal(SIGINT, phpdbg_sigint_handler); #endif @@ -1356,99 +1723,151 @@ phpdbg_main: PG(modules_activated) = 0; - /* set flags from command line */ - PHPDBG_G(flags) = flags; - #ifndef _WIN32 /* setup io here */ - if (streams[0] && streams[1]) { + if (remote) { 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); +#ifndef _WIN32 + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin); + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout); +#else + /* XXX this is a complete mess here with FILE/fd/SOCKET, + we should let only one to survive probably. Need + a clean separation whether it's a remote or local + prompt. And what is supposed to go as user interaction, + error log, etc. */ + if (remote) { + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = socket; + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = socket; + } else { + PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin; + PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin); + PHPDBG_G(io)[PHPDBG_STDOUT].ptr = stdout; + PHPDBG_G(io)[PHPDBG_STDOUT].fd = fileno(stdout); } +#endif + PHPDBG_G(io)[PHPDBG_STDERR].ptr = stderr; + PHPDBG_G(io)[PHPDBG_STDERR].fd = fileno(stderr); + +#ifndef _WIN32 + PHPDBG_G(php_stdiop_write) = php_stream_stdio_ops.write; + php_stream_stdio_ops.write = phpdbg_stdiop_write; +#endif 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); + phpdbg_error("oplog", "path=\"%s\"", "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); + phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold")); + phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold")); + phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green")); /* set default prompt */ - phpdbg_set_prompt(PROMPT TSRMLS_CC); + phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT); /* Make stdin, stdout and stderr accessible from PHP scripts */ - phpdbg_register_file_handles(TSRMLS_C); + phpdbg_register_file_handles(); - if (show_banner) { + if (show_banner && cleaning < 2) { /* print blurb */ - phpdbg_welcome((cleaning > 0) TSRMLS_CC); + phpdbg_welcome(cleaning == 1); } - /* auto compile */ - if (PHPDBG_G(exec)) { - phpdbg_compile(TSRMLS_C); + cleaning = -1; + + if (ext_stmt) { + CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; } /* initialize from file */ PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING; zend_try { - 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_init(init_file, init_file_len, init_file_default); + if (bp_tmp) { + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; + phpdbg_string_init(bp_tmp); + free(bp_tmp); + bp_tmp = NULL; + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; + } } 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; + /* auto compile */ + if (PHPDBG_G(exec)) { + if (settings || phpdbg_startup_run > 0) { + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; + } + + zend_try { + phpdbg_compile(); + } zend_end_try(); + + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; } - if (run) { - /* no need to try{}, run does it ... */ - PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC); - if (run > 1) { - /* if -r is on the command line more than once just quit */ - goto phpdbg_out; + if (settings == (void *) 0x1) { + if (PHPDBG_G(ops)) { + phpdbg_print_opcodes(print_opline_func); + } else { + write(PHPDBG_G(io)[PHPDBG_STDERR].fd, ZEND_STRL("No opcodes could be compiled | No file specified or compilation failed?\n")); } + goto phpdbg_out; } + PG(during_request_startup) = 0; + + phpdbg_fully_started = 1; + /* #ifndef for making compiler shutting up */ #ifndef _WIN32 phpdbg_interact: #endif + /* phpdbg main() */ do { zend_try { - phpdbg_interactive(TSRMLS_C); + if (phpdbg_startup_run) { + quit_immediately = phpdbg_startup_run > 1; + phpdbg_startup_run = 0; + PHPDBG_COMMAND_HANDLER(run)(NULL); + if (quit_immediately) { + /* if -r is on the command line more than once just quit */ + EG(bailout) = __orig_bailout; /* reset zend_try */ + break; + } + } + + phpdbg_interactive(1); } 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); + char *bp_tmp_str; + PHPDBG_G(flags) |= PHPDBG_DISCARD_OUTPUT; + phpdbg_export_breakpoints_to_string(&bp_tmp_str); + PHPDBG_G(flags) &= ~PHPDBG_DISCARD_OUTPUT; + if (bp_tmp_str) { + bp_tmp = strdup(bp_tmp_str); + efree(bp_tmp_str); + } cleaning = 1; } else { cleaning = 0; @@ -1458,17 +1877,16 @@ phpdbg_interact: if (!cleaning) { /* remote client disconnected */ if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { - + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { /* renegociate connections */ - phpdbg_open_sockets( - address, listen, &server, &socket, streams); - + phpdbg_remote_init(address, listen, server, &socket, &stream); + /* set streams */ - if (streams[0] && streams[1]) { + if (stream) { PHPDBG_G(flags) &= ~PHPDBG_IS_QUITTING; } - + /* this must be forced */ CG(unclean_shutdown) = 0; } else { @@ -1479,14 +1897,19 @@ phpdbg_interact: } #endif } zend_end_try(); - } while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); - + } while (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)); + + + if (PHPDBG_G(exec) && (PHPDBG_G(flags) & PHPDBG_IS_CLEANING)) { + exec = strdup(PHPDBG_G(exec)); /* preserve exec, don't reparse that from cmd */ + } + /* this must be forced */ CG(unclean_shutdown) = 0; - + /* this is just helpful */ PG(report_memleaks) = 0; - + #ifndef _WIN32 phpdbg_out: if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) { @@ -1497,25 +1920,34 @@ phpdbg_out: #ifdef _WIN32 } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) { - phpdbg_error("Access violation (Segmentation fault) encountered\ntrying to abort cleanly..."); + phpdbg_error("segfault", "", "Access violation (Segmentation fault) encountered\ntrying to abort cleanly..."); } phpdbg_out: #endif - + + if (cleaning <= 0) { + PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING; + cleaning = -1; + } + { int i; /* free argv */ - for (i = SG(request_info).argc; --i;) { + for (i = SG(request_info).argc; i--;) { efree(SG(request_info).argv[i]); } efree(SG(request_info).argv); } +#ifndef _WIN32 + /* reset it... else we risk a stack overflow upon next run (when clean'ing) */ + php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write); +#endif + #ifndef ZTS /* force cleanup of auto and core globals */ zend_hash_clean(CG(auto_globals)); - memset( - &core_globals, 0, sizeof(php_core_globals)); + memset( &core_globals, 0, sizeof(php_core_globals)); #endif if (ini_entries) { free(ini_entries); @@ -1524,27 +1956,52 @@ phpdbg_out: if (ini_override) { free(ini_override); } - + /* this must be forced */ CG(unclean_shutdown) = 0; - + /* this is just helpful */ PG(report_memleaks) = 0; - php_request_shutdown((void*)0); + if ((PHPDBG_G(flags) & (PHPDBG_IS_CLEANING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_CLEANING) { + php_free_shutdown_functions(); + zend_objects_store_mark_destructed(&EG(objects_store)); + } + + /* sapi_module.deactivate is where to backup things, last chance before mm_shutdown... */ + + zend_try { + php_request_shutdown(NULL); + } zend_end_try(); + + if ((PHPDBG_G(flags) & (PHPDBG_IS_QUITTING | PHPDBG_IS_RUNNING)) == PHPDBG_IS_RUNNING) { + if (!quit_immediately && !phpdbg_startup_run) { + phpdbg_notice("stop", "type=\"normal\"", "Script ended normally"); + } + cleaning++; + } + + if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) == PHPDBG_IS_CLEANING) { + settings = PHPDBG_G(backup); + } + + php_output_deactivate(); zend_try { - php_module_shutdown(TSRMLS_C); + php_module_shutdown(); } zend_end_try(); sapi_shutdown(); } - if (cleaning || remote) { + if ((cleaning > 0 || remote) && !quit_immediately) { + /* reset internal php_getopt state */ + php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0); + goto phpdbg_main; } - + #ifdef ZTS /* bugggy */ /* tsrm_shutdown(); */ @@ -1556,15 +2013,9 @@ phpdbg_out: } #endif - if (sapi_name) { - free(sapi_name); + if (PHPDBG_G(sapi_name_ptr)) { + free(PHPDBG_G(sapi_name_ptr)); } - -#ifdef _WIN32 - free(bp_tmp_file); -#else - unlink(bp_tmp_file); -#endif return 0; } /* }}} */ |