diff options
Diffstat (limited to 'sapi/phpdbg/phpdbg_prompt.c')
-rw-r--r-- | sapi/phpdbg/phpdbg_prompt.c | 1744 |
1 files changed, 1055 insertions, 689 deletions
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index b63018b1ee..6f73d3087a 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -1,6 +1,6 @@ /* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2016 The PHP Group | +----------------------------------------------------------------------+ @@ -22,12 +22,16 @@ #include <string.h> #include "zend.h" #include "zend_compile.h" +#include "zend_exceptions.h" +#include "zend_vm.h" +#include "zend_generators.h" +#include "zend_interfaces.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" @@ -37,253 +41,301 @@ #include "phpdbg_frame.h" #include "phpdbg_lexer.h" #include "phpdbg_parser.h" +#include "phpdbg_wait.h" +#include "phpdbg_eol.h" + +#if ZEND_VM_KIND != ZEND_VM_KIND_CALL +#error "phpdbg can only be built with CALL zend vm kind" +#endif + +ZEND_EXTERN_MODULE_GLOBALS(phpdbg) +extern int phpdbg_startup_run; + +#ifdef HAVE_LIBDL +#ifdef PHP_WIN32 +#include "win32/param.h" +#include "win32/winutil.h" +#define GET_DL_ERROR() php_win_err() +#elif defined(NETWARE) +#include <sys/param.h> +#define GET_DL_ERROR() dlerror() +#else +#include <sys/param.h> +#define GET_DL_ERROR() DL_ERROR() +#endif +#endif /* {{{ command declarations */ const phpdbg_command_t phpdbg_prompt_commands[] = { - PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"), - PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0), - PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0), - PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"), - PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"), - 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, 0), - PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"), - PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"), - PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"), - PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"), - PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"), - 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, "|s"), - PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"), - PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"), - PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"), - PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"), - PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"), - PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0), - PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"), + PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0), + PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0), + PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */ + PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0), + PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0), + PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0), + PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, "|*c", 0), + PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0), + PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "|s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0), + PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0), + PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s", 0), + PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0), + PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0), + PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(wait, "wait for other process", 'W', NULL, 0, 0), + PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0), + PHPDBG_COMMAND_D(next, "step over next line", 'n', NULL, 0, PHPDBG_ASYNC_SAFE), + PHPDBG_COMMAND_D(eol, "set EOL", 'E', NULL, "|s", 0), PHPDBG_END_COMMAND }; /* }}} */ -ZEND_EXTERN_MODULE_GLOBALS(phpdbg); - -static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */ +static inline int phpdbg_call_register(phpdbg_param_t *stack) /* {{{ */ { phpdbg_param_t *name = NULL; if (stack->type == STACK_PARAM) { + char *lc_name; + name = stack->next; - + if (!name || name->type != STR_PARAM) { return FAILURE; } - - if (zend_hash_exists( - &PHPDBG_G(registered), name->str, name->len+1)) { - zval fname, *fretval; - zend_fcall_info fci; + lc_name = zend_str_tolower_dup(name->str, name->len); - ZVAL_STRINGL(&fname, name->str, name->len, 1); + if (zend_hash_str_exists(&PHPDBG_G(registered), lc_name, name->len)) { + zval fretval; + zend_fcall_info fci; memset(&fci, 0, sizeof(zend_fcall_info)); + ZVAL_STRINGL(&fci.function_name, lc_name, name->len); 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.symbol_table = zend_rebuild_symbol_table(); + fci.symbol_table = NULL; + fci.object = NULL; + fci.retval = &fretval; fci.no_separation = 1; if (name->next) { zval params; phpdbg_param_t *next = name->next; - + array_init(¶ms); while (next) { char *buffered = NULL; - + switch (next->type) { case OP_PARAM: case COND_PARAM: case STR_PARAM: - add_next_index_stringl( - ¶ms, - next->str, - next->len, 1); + add_next_index_stringl(¶ms, next->str, next->len); break; - + case NUMERIC_PARAM: add_next_index_long(¶ms, next->num); break; - + case METHOD_PARAM: - spprintf(&buffered, 0, "%s::%s", - next->method.class, next->method.name); - add_next_index_string(¶ms, buffered, 0); + spprintf(&buffered, 0, "%s::%s", next->method.class, next->method.name); + add_next_index_string(¶ms, buffered); break; - + case NUMERIC_METHOD_PARAM: - spprintf(&buffered, 0, "%s::%s#%ld", - next->method.class, next->method.name, next->num); - add_next_index_string(¶ms, buffered, 0); + spprintf(&buffered, 0, "%s::%s#%ld", next->method.class, next->method.name, next->num); + add_next_index_string(¶ms, buffered); break; - + case NUMERIC_FUNCTION_PARAM: - spprintf(&buffered, 0, "%s#%ld", - next->str, next->num); - add_next_index_string(¶ms, buffered, 0); + spprintf(&buffered, 0, "%s#%ld", next->str, next->num); + add_next_index_string(¶ms, buffered); break; - + case FILE_PARAM: - spprintf(&buffered, 0, "%s:%ld", - next->file.name, next->file.line); - add_next_index_string(¶ms, buffered, 0); + spprintf(&buffered, 0, "%s:%ld", next->file.name, next->file.line); + add_next_index_string(¶ms, buffered); break; - + case NUMERIC_FILE_PARAM: - spprintf(&buffered, 0, "%s:#%ld", - next->file.name, next->file.line); - add_next_index_string(¶ms, buffered, 0); + spprintf(&buffered, 0, "%s:#%ld", next->file.name, next->file.line); + add_next_index_string(¶ms, buffered); break; - + default: { /* not yet */ } } - - next = next->next; + + next = next->next; } - zend_fcall_info_args(&fci, ¶ms TSRMLS_CC); + zend_fcall_info_args(&fci, ¶ms); } else { fci.params = NULL; fci.param_count = 0; } - phpdbg_debug( - "created %d params from arguments", - fci.param_count); + phpdbg_activate_err_buf(0); + phpdbg_free_err_buf(); - zend_call_function(&fci, NULL TSRMLS_CC); + phpdbg_debug("created %d params from arguments", fci.param_count); - if (fretval) { - zend_print_zval_r( - fretval, 0 TSRMLS_CC); - phpdbg_writeln(EMPTY); + if (zend_call_function(&fci, NULL) == SUCCESS) { + zend_print_zval_r(&fretval, 0); + phpdbg_out("\n"); + zval_ptr_dtor(&fretval); } - zval_dtor(&fname); + zval_dtor(&fci.function_name); + efree(lc_name); return SUCCESS; - } + } + + efree(lc_name); } 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; +struct phpdbg_init_state { + int line; + zend_bool in_code; + char *code; + size_t code_len; + const char *init_file; +}; - char cmd[PHPDBG_MAX_CMD]; - size_t cmd_len = 0L; - char *code = NULL; - size_t code_len = 0L; - zend_bool in_code = 0; +static void phpdbg_line_init(char *cmd, struct phpdbg_init_state *state) { + size_t cmd_len = strlen(cmd); - 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; - } - } - } + state->line++; - if (in_code) { - if (code == NULL) { - code = malloc(cmd_len + 1); - } else code = realloc(code, code_len + cmd_len + 1); + while (cmd_len > 0L && isspace(cmd[cmd_len-1])) { + cmd_len--; + } - if (code) { - memcpy( - &code[code_len], cmd, cmd_len); - code_len += cmd_len; - } - goto next_line; - } + cmd[cmd_len] = '\0'; + + if (*cmd && cmd_len > 0L && cmd[0] != '#') { + if (cmd_len == 2) { + if (memcmp(cmd, "<:", sizeof("<:")-1) == SUCCESS) { + state->in_code = 1; + return; + } else { + if (memcmp(cmd, ":>", sizeof(":>")-1) == SUCCESS) { + state->in_code = 0; + state->code[state->code_len] = '\0'; + zend_eval_stringl(state->code, state->code_len, NULL, "phpdbginit code"); + free(state->code); + state->code = NULL; + return; + } + } + } - { - char *why = NULL; - char *input = phpdbg_read_input(cmd TSRMLS_CC); - phpdbg_param_t stack; - - phpdbg_init_param(&stack, STACK_PARAM); - - if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) { - switch (phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { - case FAILURE: -// if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { - phpdbg_error( - "Unrecognized command in %s:%d: %s, %s!", - init_file, line, input, why); - } -// } - break; + if (state->in_code) { + if (state->code == NULL) { + state->code = malloc(cmd_len + 1); + } else { + state->code = realloc(state->code, state->code_len + cmd_len + 1); + } + + if (state->code) { + memcpy(&state->code[state->code_len], cmd, cmd_len); + state->code_len += cmd_len; + } + + return; + } + + zend_try { + char *input = phpdbg_read_input(cmd); + phpdbg_param_t stack; + + phpdbg_init_param(&stack, STACK_PARAM); + + phpdbg_activate_err_buf(1); + + if (phpdbg_do_parse(&stack, input) <= 0) { + switch (phpdbg_stack_execute(&stack, 1 /* allow_async_unsafe == 1 */)) { + case FAILURE: + phpdbg_activate_err_buf(0); + if (phpdbg_call_register(&stack) == FAILURE) { + if (state->init_file) { + phpdbg_output_err_buf("initfailure", "%b file=\"%s\" line=\"%d\" input=\"%s\"", "Unrecognized command in %s:%d: %s, %b!", state->init_file, state->line, input); + } else { + phpdbg_output_err_buf("initfailure", "%b line=\"%d\" input=\"%s\"", "Unrecognized command on line %d: %s, %b!", state->line, input); } } + break; + } + } - if (why) { - free(why); - why = NULL; - } + phpdbg_activate_err_buf(0); + phpdbg_free_err_buf(); - phpdbg_stack_free(&stack); - phpdbg_destroy_input(&input TSRMLS_CC); - } - } -next_line: - line++; + phpdbg_stack_free(&stack); + phpdbg_destroy_input(&input); + } zend_catch { + PHPDBG_G(flags) &= ~(PHPDBG_IS_RUNNING | PHPDBG_IS_CLEANING); + if (PHPDBG_G(flags) & PHPDBG_IS_QUITTING) { + zend_bailout(); } + } zend_end_try(); + } + +} + +void phpdbg_string_init(char *buffer) { + struct phpdbg_init_state state = {0}; + char *str = strtok(buffer, "\n"); + + while (str) { + phpdbg_line_init(str, &state); - if (code) { - free(code); + str = strtok(NULL, "\n"); + } + + if (state.code) { + free(state.code); + } +} + +void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init) /* {{{ */ +{ + zend_stat_t sb; + + if (init_file && VCWD_STAT(init_file, &sb) != -1) { + FILE *fp = fopen(init_file, "r"); + if (fp) { + char cmd[PHPDBG_MAX_CMD]; + struct phpdbg_init_state state = {0}; + + state.init_file = init_file; + + while (fgets(cmd, PHPDBG_MAX_CMD, fp) != NULL) { + phpdbg_line_init(cmd, &state); + } + + if (state.code) { + free(state.code); } fclose(fp); } else { - phpdbg_error( - "Failed to open %s for initialization", init_file); + phpdbg_error("initfailure", "type=\"openfile\" file=\"%s\"", "Failed to open %s for initialization", init_file); } if (free_init) { @@ -292,13 +344,16 @@ next_line: } } /* }}} */ -void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TSRMLS_DC) /* {{{ */ +void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default) /* {{{ */ { if (!init_file && use_default) { char *scan_dir = getenv("PHP_INI_SCAN_DIR"); + char *sys_ini; int i; - phpdbg_try_file_init(PHPDBG_STRL(PHP_CONFIG_FILE_PATH "/" PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + ZEND_IGNORE_VALUE(asprintf(&sys_ini, "%s/" PHPDBG_INIT_FILENAME, PHP_CONFIG_FILE_PATH)); + phpdbg_try_file_init(sys_ini, strlen(sys_ini), 0); + free(sys_ini); if (!scan_dir) { scan_dir = PHP_CONFIG_FILE_SCAN_DIR; @@ -315,93 +370,135 @@ void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TS 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); + ZEND_IGNORE_VALUE(asprintf(&init_file, "%s/%s", scan_dir, PHPDBG_INIT_FILENAME)); + phpdbg_try_file_init(init_file, strlen(init_file), 1); if (i == -1) { break; } scan_dir += i + 1; } - phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0 TSRMLS_CC); + phpdbg_try_file_init(PHPDBG_STRL(PHPDBG_INIT_FILENAME), 0); } else { - phpdbg_try_file_init(init_file, init_file_len, 1 TSRMLS_CC); + phpdbg_try_file_init(init_file, init_file_len, 1); } } +/* }}} */ + +void phpdbg_clean(zend_bool full) /* {{{ */ +{ + /* this is implicitly required */ + if (PHPDBG_G(ops)) { + destroy_op_array(PHPDBG_G(ops)); + efree(PHPDBG_G(ops)); + PHPDBG_G(ops) = NULL; + } + + if (full) { + PHPDBG_G(flags) |= PHPDBG_IS_CLEANING; + } +} /* }}} */ PHPDBG_COMMAND(exec) /* {{{ */ { - struct stat sb; + zend_stat_t 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); + char *res = phpdbg_resolve_path(param->str); size_t res_len = strlen(res); if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { + if (PHPDBG_G(in_execution)) { + if (phpdbg_ask_user_permission("Do you really want to stop execution to set a new execution context?") == FAILURE) { + return FAILURE; + } + } if (PHPDBG_G(exec)) { - phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec)); + phpdbg_notice("exec", "type=\"unset\" context=\"%s\"", "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_notice("exec", "type=\"unsetops\"", "Destroying compiled opcodes"); + phpdbg_clean(0); } PHPDBG_G(exec) = res; PHPDBG_G(exec_len) = res_len; - + + VCWD_CHDIR_FILE(res); + *SG(request_info).argv = PHPDBG_G(exec); - php_hash_environment(TSRMLS_C); + php_build_argv(NULL, &PG(http_globals)[TRACK_VARS_SERVER]); - phpdbg_notice("Set execution context: %s", PHPDBG_G(exec)); + phpdbg_notice("exec", "type=\"set\" context=\"%s\"", "Set execution context: %s", PHPDBG_G(exec)); - if (phpdbg_compile(TSRMLS_C) == FAILURE) { - phpdbg_error("Failed to compile %s", PHPDBG_G(exec)); + if (PHPDBG_G(in_execution)) { + phpdbg_clean(1); + return SUCCESS; } + + phpdbg_compile(); } else { - phpdbg_notice("Execution context not changed"); + phpdbg_notice("exec", "type=\"unchanged\"", "Execution context not changed"); } } else { - phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str); + phpdbg_error("exec", "type=\"invalid\" context=\"%s\"", "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); + phpdbg_error("exec", "type=\"notfound\" context=\"%s\"", "Cannot stat %s, ensure the file exists", param->str); } return SUCCESS; } /* }}} */ -int phpdbg_compile(TSRMLS_D) /* {{{ */ +int phpdbg_compile(void) /* {{{ */ { zend_file_handle fh; + char *buf; + size_t len; if (!PHPDBG_G(exec)) { - phpdbg_error("No execution context"); - return SUCCESS; - } - - if (EG(in_execution)) { - phpdbg_error("Cannot compile while in execution"); + phpdbg_error("inactive", "type=\"nocontext\"", "No execution context"); 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) == SUCCESS && zend_stream_fixup(&fh, &buf, &len) == SUCCESS) { + /* Skip #! line */ + if (len >= 3 && buf[0] == '#' && buf[1] == '!') { + char *end = buf + len; + do { + switch (fh.handle.stream.mmap.buf++[0]) { + case '\r': + if (fh.handle.stream.mmap.buf[0] == '\n') { + fh.handle.stream.mmap.buf++; + } + case '\n': + CG(start_lineno) = 2; + fh.handle.stream.mmap.len -= fh.handle.stream.mmap.buf - buf; + end = fh.handle.stream.mmap.buf; + } + } while (fh.handle.stream.mmap.buf + 1 < end); + } + + PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE); - if (php_stream_open_for_zend_ex(PHPDBG_G(exec), &fh, - USE_PATH|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC) == SUCCESS) { + fh.handle.stream.mmap.buf = buf; + fh.handle.stream.mmap.len = len; + zend_destroy_file_handle(&fh); + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + zend_bailout(); + } - PHPDBG_G(ops) = zend_compile_file(&fh, ZEND_INCLUDE TSRMLS_CC); - zend_destroy_file_handle(&fh TSRMLS_CC); + phpdbg_notice("compile", "context=\"%s\"", "Successful compilation of %s", PHPDBG_G(exec)); - phpdbg_notice("Success"); return SUCCESS; } else { - phpdbg_error("Could not open file %s", PHPDBG_G(exec)); + phpdbg_error("compile", "type=\"openfailure\" context=\"%s\"", "Could not open file %s", PHPDBG_G(exec)); } return FAILURE; @@ -409,7 +506,7 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */ PHPDBG_COMMAND(step) /* {{{ */ { - if (EG(in_execution)) { + if (PHPDBG_G(in_execution)) { PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; } @@ -421,62 +518,83 @@ PHPDBG_COMMAND(continue) /* {{{ */ return PHPDBG_NEXT; } /* }}} */ +int phpdbg_skip_line_helper() /* {{{ */ { + zend_execute_data *ex = phpdbg_user_execute_data(EG(current_execute_data)); + const zend_op_array *op_array = &ex->func->op_array; + const zend_op *opline = op_array->opcodes; + + PHPDBG_G(flags) |= PHPDBG_IN_UNTIL; + PHPDBG_G(seek_ex) = ex; + do { + if (opline->lineno != ex->opline->lineno + || opline->opcode == ZEND_RETURN + || opline->opcode == ZEND_FAST_RET + || opline->opcode == ZEND_GENERATOR_RETURN + || opline->opcode == ZEND_EXIT + || opline->opcode == ZEND_YIELD + || opline->opcode == ZEND_YIELD_FROM + ) { + zend_hash_index_update_ptr(&PHPDBG_G(seek), (zend_ulong) opline, (void *) opline); + } + } while (++opline < op_array->opcodes + op_array->last); + + return PHPDBG_UNTIL; +} +/* }}} */ + PHPDBG_COMMAND(until) /* {{{ */ { - if (!EG(in_execution)) { - phpdbg_error("Not executing"); + if (!PHPDBG_G(in_execution)) { + phpdbg_error("inactive", "type=\"noexec\"", "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_skip_line_helper(); +} /* }}} */ + +PHPDBG_COMMAND(next) /* {{{ */ +{ + if (!PHPDBG_G(in_execution)) { + phpdbg_error("inactive", "type=\"noexec\"", "Not executing"); + return SUCCESS; } - return PHPDBG_UNTIL; + PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; + return phpdbg_skip_line_helper(); } /* }}} */ +static void phpdbg_seek_to_end(void) /* {{{ */ { + zend_execute_data *ex = phpdbg_user_execute_data(EG(current_execute_data)); + const zend_op_array *op_array = &ex->func->op_array; + const zend_op *opline = op_array->opcodes; + + PHPDBG_G(seek_ex) = ex; + do { + switch (opline->opcode) { + case ZEND_RETURN: + case ZEND_FAST_RET: + case ZEND_GENERATOR_RETURN: + case ZEND_EXIT: + case ZEND_YIELD: + case ZEND_YIELD_FROM: + zend_hash_index_update_ptr(&PHPDBG_G(seek), (zend_ulong) opline, (void *) opline); + } + } while (++opline < op_array->opcodes + op_array->last); +} +/* }}} */ + PHPDBG_COMMAND(finish) /* {{{ */ { - if (!EG(in_execution)) { - phpdbg_error("Not executing"); + if (!PHPDBG_G(in_execution)) { + phpdbg_error("inactive", "type=\"noexec\"", "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; - } - } + phpdbg_seek_to_end(); + if (zend_hash_index_exists(&PHPDBG_G(seek), (zend_ulong) phpdbg_user_execute_data(EG(current_execute_data))->opline)) { + zend_hash_clean(&PHPDBG_G(seek)); + } else { + PHPDBG_G(flags) |= PHPDBG_IN_FINISH; } return PHPDBG_FINISH; @@ -484,142 +602,114 @@ PHPDBG_COMMAND(finish) /* {{{ */ PHPDBG_COMMAND(leave) /* {{{ */ { - if (!EG(in_execution)) { - phpdbg_error("Not executing"); + if (!PHPDBG_G(in_execution)) { + phpdbg_error("inactive", "type=\"noexec\"", "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; - } - } + phpdbg_seek_to_end(); + if (zend_hash_index_exists(&PHPDBG_G(seek), (zend_ulong) phpdbg_user_execute_data(EG(current_execute_data))->opline)) { + zend_hash_clean(&PHPDBG_G(seek)); + phpdbg_notice("leave", "type=\"end\"", "Already at the end of the function"); + return SUCCESS; + } else { + PHPDBG_G(flags) |= PHPDBG_IN_LEAVE; + return PHPDBG_LEAVE; } - - return PHPDBG_LEAVE; } /* }}} */ PHPDBG_COMMAND(frame) /* {{{ */ { if (!param) { - phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num); - } else phpdbg_switch_frame(param->num TSRMLS_CC); + phpdbg_notice("frame", "id=\"%d\"", "Currently in frame #%d", PHPDBG_G(frame).num); + } else { + phpdbg_switch_frame(param->num); + } return SUCCESS; } /* }}} */ -static inline void phpdbg_handle_exception(TSRMLS_D) /* }}} */ +static inline void phpdbg_handle_exception(void) /* {{{ */ { - 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); + zend_object *ex = EG(exception); + zend_string *msg, *file; + zend_long line; + zval zv, rv, tmp; - /* 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); + ZVAL_OBJ(&zv, ex); + zend_call_method_with_0_params(&zv, ex->ce, NULL, "__tostring", &tmp); + file = zval_get_string(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("file"), 1, &rv)); + line = zval_get_long(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("line"), 1, &rv)); + + if (EG(exception)) { + EG(exception) = NULL; + msg = ZSTR_EMPTY_ALLOC(); + } else { + zend_update_property_string(zend_get_exception_base(&zv), &zv, ZEND_STRL("string"), Z_STRVAL(tmp)); + zval_ptr_dtor(&tmp); + msg = zval_get_string(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("string"), 1, &rv)); } - /* output useful information about address */ - phpdbg_writeln( - "Stacked entered at %p in %s on line %u", - EG(active_op_array)->opcodes, filename, lineno); + phpdbg_error("exception", "name=\"%s\" file=\"%s\" line=\"" ZEND_LONG_FMT "\"", "Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(ex->ce->name), ZSTR_VAL(file), line); + zend_string_release(file); + phpdbg_writeln("exceptionmsg", "msg=\"%s\"", ZSTR_VAL(msg)); + zend_string_release(msg); - zval_dtor(&fname); - zval_dtor(&exception); + if (EG(prev_exception)) { + OBJ_RELEASE(EG(prev_exception)); + EG(prev_exception) = 0; + } + OBJ_RELEASE(ex); + EG(opline_before_exception) = NULL; + + EG(exit_status) = 255; } /* }}} */ 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; zend_execute_data *ex = EG(current_execute_data); - - if (!PHPDBG_G(ops)) { - if (phpdbg_compile(TSRMLS_C) == FAILURE) { - phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec)); - goto out; + zend_bool restore = 1; + + if (PHPDBG_G(in_execution)) { + if (phpdbg_ask_user_permission("Do you really want to restart execution?") == SUCCESS) { + phpdbg_startup_run++; + phpdbg_clean(1); } + return SUCCESS; } - 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); + if (!PHPDBG_G(ops)) { + if (phpdbg_compile() == FAILURE) { + phpdbg_error("compile", "type=\"compilefailure\" context=\"%s\"", "Failed to compile %s, cannot run", PHPDBG_G(exec)); + goto out; + } } /* clean up from last execution */ if (ex && ex->symbol_table) { zend_hash_clean(ex->symbol_table); + } else { + zend_rebuild_symbol_table(); } + PHPDBG_G(handled_exception) = NULL; /* clean seek state */ PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK; - zend_hash_clean( - &PHPDBG_G(seek)); + zend_hash_clean(&PHPDBG_G(seek)); /* reset hit counters */ - phpdbg_reset_breakpoints(TSRMLS_C); + phpdbg_reset_breakpoints(); if (param && param->type != EMPTY_PARAM && param->len != 0) { char **argv = emalloc(5 * sizeof(char *)); int argc = 0; int i; + /* TODO allow proper escaping with \, "" and '' here */ char *argv_str = strtok(param->str, " "); - + while (argv_str) { if (argc >= 4 && argc == (argc & -argc)) { argv = erealloc(argv, (argc * 2 + 1) * sizeof(char *)); @@ -635,38 +725,53 @@ PHPDBG_COMMAND(run) /* {{{ */ efree(SG(request_info).argv); SG(request_info).argv = erealloc(argv, ++argc * sizeof(char *)); SG(request_info).argc = argc; - - php_hash_environment(TSRMLS_C); + + php_build_argv(NULL, &PG(http_globals)[TRACK_VARS_SERVER]); } 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_RUNNING; + zend_execute(PHPDBG_G(ops), &PHPDBG_G(retval)); 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; + PHPDBG_G(in_execution) = 0; - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - phpdbg_error("Caught exit/error from VM"); + if (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)) { restore = 0; + } else { + zend_bailout(); } } zend_end_try(); + if (PHPDBG_G(socket_fd) != -1) { + close(PHPDBG_G(socket_fd)); + PHPDBG_G(socket_fd) = -1; + } + if (restore) { + zend_exception_restore(); + zend_try { + zend_try_exception_handler(); + PHPDBG_G(in_execution) = 1; + } zend_catch { + PHPDBG_G(in_execution) = 0; + + if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) { + zend_bailout(); + } + } zend_end_try(); + if (EG(exception)) { - phpdbg_handle_exception(TSRMLS_C); + phpdbg_handle_exception(); } - - EG(active_op_array) = orig_op_array; - EG(opline_ptr) = orig_opline; - EG(return_value_ptr_ptr) = orig_retval_ptr; } + + PHPDBG_G(flags) &= ~PHPDBG_IS_RUNNING; + + phpdbg_clean(1); } else { - phpdbg_error("Nothing to execute!"); + phpdbg_error("inactive", "type=\"nocontext\"", "Nothing to execute!"); } out: @@ -674,50 +779,106 @@ out: return SUCCESS; } /* }}} */ +int phpdbg_output_ev_variable(char *name, size_t len, char *keyname, size_t keylen, HashTable *parent, zval *zv) /* {{{ */ { + phpdbg_notice("eval", "variable=\"%.*s\"", "Printing variable %.*s", (int) len, name); + phpdbg_xml("<eval %r>"); + zend_print_zval_r(zv, 0); + phpdbg_xml("</eval>"); + phpdbg_out("\n"); + + efree(name); + efree(keyname); + + return SUCCESS; +} +/* }}} */ + PHPDBG_COMMAND(ev) /* {{{ */ { - zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING); + zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING) == PHPDBG_IS_STEPPING); zval retval; + zend_execute_data *original_execute_data = EG(current_execute_data); + zend_class_entry *original_scope = EG(scope); + zend_vm_stack original_stack = EG(vm_stack); + zend_object *ex = NULL; + + PHPDBG_OUTPUT_BACKUP(); + + original_stack->top = EG(vm_stack_top); + + if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) { + phpdbg_try_access { + phpdbg_parse_variable(param->str, param->len, &EG(symbol_table), 0, phpdbg_output_ev_variable, 0); + } phpdbg_catch_access { + phpdbg_error("signalsegv", "", "Could not fetch data, invalid data source"); + } phpdbg_end_try_access(); + + PHPDBG_OUTPUT_BACKUP_RESTORE(); + return SUCCESS; + } + if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { - PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING; + 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); + if (zend_eval_stringl(param->str, param->len, &retval, "eval()'d code") == SUCCESS) { + if (EG(exception)) { + ex = EG(exception); + zend_exception_error(EG(exception), E_ERROR); + } else { + phpdbg_xml("<eval %r>"); + if (PHPDBG_G(flags) & PHPDBG_WRITE_XML) { + zval *zvp = &retval; + phpdbg_xml_var_dump(zvp); + } + zend_print_zval_r(&retval, 0); + phpdbg_xml("</eval>"); + phpdbg_out("\n"); + zval_ptr_dtor(&retval); + } } + } zend_catch { + PHPDBG_G(unclean_eval) = 1; + if (ex) { + OBJ_RELEASE(ex); + } + EG(current_execute_data) = original_execute_data; + EG(scope) = original_scope; + EG(vm_stack_top) = original_stack->top; + EG(vm_stack_end) = original_stack->end; + EG(vm_stack) = original_stack; + EG(exit_status) = 0; } zend_end_try(); + PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL; /* switch stepping back on */ - if (stepping && - !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { + if (stepping && !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) { PHPDBG_G(flags) |= PHPDBG_IS_STEPPING; } CG(unclean_shutdown) = 0; + PHPDBG_OUTPUT_BACKUP_RESTORE(); + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(back) /* {{{ */ { - if (!EG(in_execution)) { - phpdbg_error("Not executing!"); + if (!PHPDBG_G(in_execution)) { + phpdbg_error("inactive", "type=\"noexec\"", "Not executing!"); return SUCCESS; } if (!param) { - phpdbg_dump_backtrace(0 TSRMLS_CC); + phpdbg_dump_backtrace(0); } else { - phpdbg_dump_backtrace(param->num TSRMLS_CC); + phpdbg_dump_backtrace(param->num); } return SUCCESS; @@ -725,62 +886,62 @@ PHPDBG_COMMAND(back) /* {{{ */ PHPDBG_COMMAND(print) /* {{{ */ { - phpdbg_writeln(SEPARATE); - phpdbg_notice("Execution Context Information"); + if (!param || param->type == EMPTY_PARAM) { + return phpdbg_do_print_stack(param); + } else switch (param->type) { + case STR_PARAM: + return phpdbg_do_print_func(param); + case METHOD_PARAM: + return phpdbg_do_print_method(param); + default: + phpdbg_error("print", "type=\"invalidarg\"", "Invalid arguments to print, expected nothing, function name or method name"); + return SUCCESS; + } +} /* }}} */ + +PHPDBG_COMMAND(info) /* {{{ */ +{ + phpdbg_out("Execution Context Information\n\n"); + phpdbg_xml("<printinfo %r>"); #ifdef HAVE_LIBREADLINE - phpdbg_writeln("Readline\tyes"); + phpdbg_writeln("info", "readline=\"yes\"", "Readline yes"); #else - phpdbg_writeln("Readline\tno"); + phpdbg_writeln("info", "readline=\"no\"", "Readline no"); #endif #ifdef HAVE_LIBEDIT - phpdbg_writeln("Libedit\t\tyes"); + phpdbg_writeln("info", "libedit=\"yes\"", "Libedit yes"); #else - phpdbg_writeln("Libedit\t\tno"); + phpdbg_writeln("info", "libedit=\"no\"", "Libedit no"); #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"); + phpdbg_writeln("info", "context=\"%s\"", "Exec %s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none"); + phpdbg_writeln("info", "compiled=\"%s\"", "Compiled %s", PHPDBG_G(ops) ? "yes" : "no"); + phpdbg_writeln("info", "stepping=\"%s\"", "Stepping %s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off"); + phpdbg_writeln("info", "quiet=\"%s\"", "Quietness %s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off"); + phpdbg_writeln("info", "oplog=\"%s\"", "Oplog %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("info", "ops=\"%d\"", "Opcodes %d", PHPDBG_G(ops)->last); + phpdbg_writeln("info", "vars=\"%d\"", "Variables %d", PHPDBG_G(ops)->last_var ? PHPDBG_G(ops)->last_var - 1 : 0); } - 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("info", "executing=\"%d\"", "Executing %s", PHPDBG_G(in_execution) ? "yes" : "no"); + if (PHPDBG_G(in_execution)) { + phpdbg_writeln("info", "vmret=\"%d\"", "VM Return %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); - - return SUCCESS; -} /* }}} */ - -PHPDBG_COMMAND(info) /* {{{ */ -{ - phpdbg_error( - "No information command selected!"); + phpdbg_writeln("info", "classes=\"%d\"", "Classes %d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("info", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("info", "constants=\"%d\"", "Constants %d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("info", "includes=\"%d\"", "Included %d", zend_hash_num_elements(&EG(included_files))); + phpdbg_xml("</printinfo>"); return SUCCESS; } /* }}} */ PHPDBG_COMMAND(set) /* {{{ */ { - phpdbg_error( - "No set command selected!"); + phpdbg_error("set", "type=\"toofewargs\" expected=\"1\"", "No set command selected!"); return SUCCESS; } /* }}} */ @@ -789,42 +950,42 @@ PHPDBG_COMMAND(break) /* {{{ */ { if (!param) { phpdbg_set_breakpoint_file( - zend_get_executed_filename(TSRMLS_C), - zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC); + zend_get_executed_filename(), + zend_get_executed_lineno()); } else switch (param->type) { case ADDR_PARAM: - phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); + phpdbg_set_breakpoint_opline(param->addr); break; case NUMERIC_PARAM: if (PHPDBG_G(exec)) { - phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC); + phpdbg_set_breakpoint_file(phpdbg_current_file(), param->num); } else { - phpdbg_error("Execution context not set!"); + phpdbg_error("inactive", "type=\"noexec\"", "Execution context not set!"); } break; case METHOD_PARAM: - phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC); + phpdbg_set_breakpoint_method(param->method.class, param->method.name); break; case NUMERIC_METHOD_PARAM: - phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC); + phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num); break; case NUMERIC_FUNCTION_PARAM: - phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC); + phpdbg_set_breakpoint_function_opline(param->str, param->num); break; case FILE_PARAM: - phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC); + phpdbg_set_breakpoint_file(param->file.name, param->file.line); break; case NUMERIC_FILE_PARAM: - phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC); + phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line); break; case COND_PARAM: - phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC); + phpdbg_set_breakpoint_expression(param->str, param->len); break; case STR_PARAM: - phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC); + phpdbg_set_breakpoint_symbol(param->str, param->len); break; case OP_PARAM: - phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC); + phpdbg_set_breakpoint_opcode(param->str, param->len); break; phpdbg_default_switch_case(); @@ -837,42 +998,229 @@ PHPDBG_COMMAND(sh) /* {{{ */ { FILE *fd = NULL; if ((fd=VCWD_POPEN((char*)param->str, "w"))) { - /* do something perhaps ?? do we want input ?? */ - fclose(fd); + /* TODO: do something perhaps ?? do we want input ?? */ + pclose(fd); } else { - phpdbg_error( - "Failed to execute %s", param->str); + phpdbg_error("sh", "type=\"failure\" smd=\"%s\"", "Failed to execute %s", param->str); } - + + return SUCCESS; +} /* }}} */ + +static int add_module_info(zend_module_entry *module) /* {{{ */ { + phpdbg_write("module", "name=\"%s\"", "%s\n", module->name); + return 0; +} +/* }}} */ + +static int add_zendext_info(zend_extension *ext) /* {{{ */ { + phpdbg_write("extension", "name=\"%s\"", "%s\n", ext->name); + return 0; +} +/* }}} */ + +PHPDBG_API const char *phpdbg_load_module_or_extension(char **path, char **name) /* {{{ */ { + DL_HANDLE handle; + char *extension_dir; + + extension_dir = INI_STR("extension_dir"); + + if (strchr(*path, '/') != NULL || strchr(*path, DEFAULT_SLASH) != NULL) { + /* path is fine */ + } else if (extension_dir && extension_dir[0]) { + char *libpath; + int extension_dir_len = strlen(extension_dir); + if (IS_SLASH(extension_dir[extension_dir_len-1])) { + spprintf(&libpath, 0, "%s%s", extension_dir, *path); /* SAFE */ + } else { + spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, *path); /* SAFE */ + } + efree(*path); + *path = libpath; + } else { + phpdbg_error("dl", "type=\"relpath\"", "Not a full path given or extension_dir ini setting is not set"); + + return NULL; + } + + handle = DL_LOAD(*path); + + if (!handle) { +#if PHP_WIN32 + char *err = GET_DL_ERROR(); + if (err && err[0]) { + phpdbg_error("dl", "type=\"unknown\"", "%s", err); + LocalFree(err); + } else { + phpdbg_error("dl", "type=\"unknown\"", "Unknown reason"); + } +#else + phpdbg_error("dl", "type=\"unknown\"", "%s", GET_DL_ERROR()); +#endif + return NULL; + } + +#if ZEND_EXTENSIONS_SUPPORT + do { + zend_extension *new_extension; + zend_extension_version_info *extension_version_info; + + extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info"); + if (!extension_version_info) { + extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "_extension_version_info"); + } + new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "zend_extension_entry"); + if (!new_extension) { + new_extension = (zend_extension *) DL_FETCH_SYMBOL(handle, "_zend_extension_entry"); + } + if (!extension_version_info || !new_extension) { + break; + } + if (extension_version_info->zend_extension_api_no != ZEND_EXTENSION_API_NO &&(!new_extension->api_no_check || new_extension->api_no_check(ZEND_EXTENSION_API_NO) != SUCCESS)) { + phpdbg_error("dl", "type=\"wrongapi\" extension=\"%s\" apineeded=\"%d\" apiinstalled=\"%d\"", "%s requires Zend Engine API version %d, which does not match the installed Zend Engine API version %d", new_extension->name, extension_version_info->zend_extension_api_no, ZEND_EXTENSION_API_NO); + + goto quit; + } else if (strcmp(ZEND_EXTENSION_BUILD_ID, extension_version_info->build_id) && (!new_extension->build_id_check || new_extension->build_id_check(ZEND_EXTENSION_BUILD_ID) != SUCCESS)) { + phpdbg_error("dl", "type=\"wrongbuild\" extension=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", new_extension->name, extension_version_info->build_id, ZEND_EXTENSION_BUILD_ID); + + goto quit; + } + + *name = new_extension->name; + + zend_register_extension(new_extension, handle); + + if (new_extension->startup) { + if (new_extension->startup(new_extension) != SUCCESS) { + phpdbg_error("dl", "type=\"startupfailure\" extension=\"%s\"", "Unable to startup Zend extension %s", new_extension->name); + + goto quit; + } + zend_append_version_info(new_extension); + } + + return "Zend extension"; + } while (0); +#endif + + do { + zend_module_entry *module_entry; + zend_module_entry *(*get_module)(void); + + get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module"); + if (!get_module) { + get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module"); + } + + if (!get_module) { + break; + } + + module_entry = get_module(); + *name = (char *) module_entry->name; + + if (strcmp(ZEND_EXTENSION_BUILD_ID, module_entry->build_id)) { + phpdbg_error("dl", "type=\"wrongbuild\" module=\"%s\" buildneeded=\"%s\" buildinstalled=\"%s\"", "%s was built with configuration %s, whereas running engine is %s", module_entry->name, module_entry->build_id, ZEND_EXTENSION_BUILD_ID); + + goto quit; + } + + module_entry->type = MODULE_PERSISTENT; + module_entry->module_number = zend_next_free_module(); + module_entry->handle = handle; + + if ((module_entry = zend_register_module_ex(module_entry)) == NULL) { + phpdbg_error("dl", "type=\"registerfailure\" module=\"%s\"", "Unable to register module %s", module_entry->name); + + goto quit; + } + + if (zend_startup_module_ex(module_entry) == FAILURE) { + phpdbg_error("dl", "type=\"startupfailure\" module=\"%s\"", "Unable to startup module %s", module_entry->name); + + goto quit; + } + + if (module_entry->request_startup_func) { + if (module_entry->request_startup_func(MODULE_PERSISTENT, module_entry->module_number) == FAILURE) { + phpdbg_error("dl", "type=\"initfailure\" module=\"%s\"", "Unable to initialize module %s", module_entry->name); + + goto quit; + } + } + + return "module"; + } while (0); + + phpdbg_error("dl", "type=\"nophpso\"", "This shared object is nor a Zend extension nor a module"); + +quit: + DL_UNLOAD(handle); + return NULL; +} +/* }}} */ + +PHPDBG_COMMAND(dl) /* {{{ */ +{ + const char *type; + char *name, *path; + + if (!param || param->type == EMPTY_PARAM) { + phpdbg_notice("dl", "extensiontype=\"Zend extension\"", "Zend extensions"); + zend_llist_apply(&zend_extensions, (llist_apply_func_t) add_zendext_info); + phpdbg_out("\n"); + phpdbg_notice("dl", "extensiontype=\"module\"", "Modules"); + zend_hash_apply(&module_registry, (apply_func_t) add_module_info); + } else switch (param->type) { + case STR_PARAM: +#ifdef HAVE_LIBDL + path = estrndup(param->str, param->len); + + phpdbg_activate_err_buf(1); + if ((type = phpdbg_load_module_or_extension(&path, &name)) == NULL) { + phpdbg_error("dl", "path=\"%s\" %b", "Could not load %s, not found or invalid zend extension / module: %b", path); + efree(name); + } else { + phpdbg_notice("dl", "extensiontype=\"%s\" name=\"%s\" path=\"%s\"", "Successfully loaded the %s %s at path %s", type, name, path); + } + phpdbg_activate_err_buf(0); + phpdbg_free_err_buf(); + efree(path); +#else + phpdbg_error("dl", "type=\"unsupported\" path=\"%.*s\"", "Cannot dynamically load %.*s - dynamic modules are not supported", (int) param->len, param->str); +#endif + break; + + phpdbg_default_switch_case(); + } + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(source) /* {{{ */ { - struct stat sb; - + zend_stat_t sb; + if (VCWD_STAT(param->str, &sb) != -1) { - phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC); + phpdbg_try_file_init(param->str, param->len, 0); } else { - phpdbg_error( - "Failed to stat %s, file does not exist", param->str); + phpdbg_error("source", "type=\"notfound\" file=\"%s\"", "Failed to stat %s, file does not exist", param->str); } - + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(export) /* {{{ */ { FILE *handle = VCWD_FOPEN(param->str, "w+"); - + if (handle) { - phpdbg_export_breakpoints(handle TSRMLS_CC); + phpdbg_export_breakpoints(handle); fclose(handle); } else { - phpdbg_error( - "Failed to open or create %s, check path and permissions", param->str); + phpdbg_error("export", "type=\"openfailure\" file=\"%s\"", "Failed to open or create %s, check path and permissions", param->str); } - + return SUCCESS; } /* }}} */ @@ -882,20 +1230,17 @@ PHPDBG_COMMAND(register) /* {{{ */ 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); + if (!zend_hash_str_exists(&PHPDBG_G(registered), lcname, lcname_len)) { + if ((function = zend_hash_str_find_ptr(EG(function_table), lcname, lcname_len))) { + zend_hash_str_update_ptr(&PHPDBG_G(registered), lcname, lcname_len, function); function_add_ref(function); - phpdbg_notice( - "Registered %s", lcname); + phpdbg_notice("register", "function=\"%s\"", "Registered %s", lcname); } else { - phpdbg_error("The requested function (%s) could not be found", param->str); + phpdbg_error("register", "type=\"notfound\" function=\"%s\"", "The requested function (%s) could not be found", param->str); } } else { - phpdbg_error( - "The requested name (%s) is already in use", lcname); + phpdbg_error("register", "type=\"inuse\" function=\"%s\"", "The requested name (%s) is already in use", lcname); } efree(lcname); @@ -904,48 +1249,52 @@ PHPDBG_COMMAND(register) /* {{{ */ PHPDBG_COMMAND(quit) /* {{{ */ { - /* don't allow this to loop, ever ... */ - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; - zend_bailout(); - } + PHPDBG_G(flags) |= PHPDBG_IS_QUITTING; + PHPDBG_G(flags) &= ~PHPDBG_IS_CLEANING; - return PHPDBG_NEXT; + return SUCCESS; } /* }}} */ PHPDBG_COMMAND(clean) /* {{{ */ { - if (EG(in_execution)) { - phpdbg_error("Cannot clean environment while executing"); - return SUCCESS; + if (PHPDBG_G(in_execution)) { + if (phpdbg_ask_user_permission("Do you really want to clean your current environment?") == FAILURE) { + return SUCCESS; + } } - phpdbg_notice("Cleaning Execution Environment"); + phpdbg_out("Cleaning Execution Environment\n"); + phpdbg_xml("<cleaninfo %r>"); - 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_writeln("clean", "classes=\"%d\"", "Classes %d", zend_hash_num_elements(EG(class_table))); + phpdbg_writeln("clean", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(EG(function_table))); + phpdbg_writeln("clean", "constants=\"%d\"", "Constants %d", zend_hash_num_elements(EG(zend_constants))); + phpdbg_writeln("clean", "includes=\"%d\"", "Includes %d", zend_hash_num_elements(&EG(included_files))); - phpdbg_clean(1 TSRMLS_CC); + phpdbg_clean(1); + + phpdbg_xml("</cleaninfo>"); return SUCCESS; } /* }}} */ PHPDBG_COMMAND(clear) /* {{{ */ { - phpdbg_notice("Clearing Breakpoints"); + phpdbg_out("Clearing Breakpoints\n"); + phpdbg_xml("<clearinfo %r>"); + + phpdbg_writeln("clear", "files=\"%d\"", "File %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE])); + phpdbg_writeln("clear", "functions=\"%d\"", "Functions %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM])); + phpdbg_writeln("clear", "methods=\"%d\"", "Methods %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD])); + phpdbg_writeln("clear", "oplines=\"%d\"", "Oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE])); + phpdbg_writeln("clear", "fileoplines=\"%d\"", "File oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE])); + phpdbg_writeln("clear", "functionoplines=\"%d\"", "Function oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE])); + phpdbg_writeln("clear", "methodoplines=\"%d\"", "Method oplines %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE])); + phpdbg_writeln("clear", "eval=\"%d\"", "Conditionals %d", zend_hash_num_elements(&PHPDBG_G(bp)[PHPDBG_BREAK_COND])); - 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(); - phpdbg_clear_breakpoints(TSRMLS_C); + phpdbg_xml("</clearinfo>"); return SUCCESS; } /* }}} */ @@ -962,7 +1311,7 @@ PHPDBG_COMMAND(list) /* {{{ */ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS); case STR_PARAM: - phpdbg_list_function_byname(param->str, param->len TSRMLS_CC); + phpdbg_list_function_byname(param->str, param->len); break; case METHOD_PARAM: @@ -977,11 +1326,11 @@ PHPDBG_COMMAND(list) /* {{{ */ PHPDBG_COMMAND(watch) /* {{{ */ { if (!param || param->type == EMPTY_PARAM) { - phpdbg_list_watchpoints(TSRMLS_C); + phpdbg_list_watchpoints(); } else switch (param->type) { case STR_PARAM: - if (phpdbg_create_var_watchpoint(param->str, param->len TSRMLS_CC) != FAILURE) { - phpdbg_notice("Set watchpoint on %.*s", (int)param->len, param->str); + if (phpdbg_create_var_watchpoint(param->str, param->len) != FAILURE) { + phpdbg_notice("watch", "variable=\"%.*s\"", "Set watchpoint on %.*s", (int) param->len, param->str); } break; @@ -991,249 +1340,221 @@ PHPDBG_COMMAND(watch) /* {{{ */ return SUCCESS; } /* }}} */ -int phpdbg_interactive(TSRMLS_D) /* {{{ */ +int phpdbg_interactive(zend_bool allow_async_unsafe) /* {{{ */ { int ret = SUCCESS; - char *why = NULL; char *input = NULL; phpdbg_param_t stack; PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE; - input = phpdbg_read_input(NULL TSRMLS_CC); + while (ret == SUCCESS || ret == FAILURE) { + if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) { + zend_bailout(); + } - if (input) { - do { - phpdbg_init_param(&stack, STACK_PARAM); + if (!(input = phpdbg_read_input(NULL))) { + break; + } - if (phpdbg_do_parse(&stack, input TSRMLS_CC) <= 0) { - switch (ret = phpdbg_stack_execute(&stack, &why TSRMLS_CC)) { - case FAILURE: - if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) { - if (why) { - phpdbg_error("%s", why); - } - } - } - if (why) { - free(why); - why = NULL; - } - break; + phpdbg_init_param(&stack, STACK_PARAM); - case PHPDBG_LEAVE: - case PHPDBG_FINISH: - case PHPDBG_UNTIL: - case PHPDBG_NEXT: { - if (!EG(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) { - phpdbg_error("Not running"); + if (phpdbg_do_parse(&stack, input) <= 0) { + phpdbg_activate_err_buf(1); + +#ifdef PHP_WIN32 +#define PARA ((phpdbg_param_t *)stack.next)->type + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE && (RUN_PARAM == PARA || EVAL_PARAM == PARA)) { + sigio_watcher_start(); + } +#endif + switch (ret = phpdbg_stack_execute(&stack, allow_async_unsafe)) { + case FAILURE: + if (!(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)) { + if (!allow_async_unsafe || phpdbg_call_register(&stack) == FAILURE) { + phpdbg_output_err_buf(NULL, "%b", "%b"); } - goto out; } + break; + + case PHPDBG_LEAVE: + case PHPDBG_FINISH: + case PHPDBG_UNTIL: + case PHPDBG_NEXT: { + phpdbg_activate_err_buf(0); + phpdbg_free_err_buf(); + if (!PHPDBG_G(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_STOPPING)) { + phpdbg_error("command", "type=\"noexec\"", "Not running"); + } + break; } } - if (why) { - free(why); - why = NULL; + phpdbg_activate_err_buf(0); + phpdbg_free_err_buf(); +#ifdef PHP_WIN32 + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE && (RUN_PARAM == PARA || EVAL_PARAM == PARA)) { + sigio_watcher_stop(); } +#undef PARA +#endif + } - phpdbg_stack_free(&stack); - phpdbg_destroy_input(&input TSRMLS_CC); - - } while ((input = phpdbg_read_input(NULL TSRMLS_CC))); + phpdbg_stack_free(&stack); + phpdbg_destroy_input(&input); + PHPDBG_G(req_id) = 0; + input = NULL; } -out: if (input) { phpdbg_stack_free(&stack); - phpdbg_destroy_input(&input TSRMLS_CC); + phpdbg_destroy_input(&input); + PHPDBG_G(req_id) = 0; } - if (why) { - free(why); - } - - if (EG(in_execution)) { - phpdbg_restore_frame(TSRMLS_C); + if (PHPDBG_G(in_execution)) { + phpdbg_restore_frame(); } PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE; - phpdbg_print_changed_zvals(TSRMLS_C); + phpdbg_print_changed_zvals(); 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; - } +/* code may behave weirdly if EG(exception) is set; thus backup it */ +#define DO_INTERACTIVE(allow_async_unsafe) do { \ + const zend_op *backup_opline; \ + const zend_op *before_ex; \ + if (exception) { \ + if (EG(current_execute_data) && EG(current_execute_data)->func && ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) { \ + backup_opline = EG(current_execute_data)->opline; \ + } \ + before_ex = EG(opline_before_exception); \ + ++GC_REFCOUNT(exception); \ + zend_clear_exception(); \ + } \ + if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \ + 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); \ + } \ + \ + switch (phpdbg_interactive(allow_async_unsafe)) { \ + zval zv; \ + case PHPDBG_LEAVE: \ + case PHPDBG_FINISH: \ + case PHPDBG_UNTIL: \ + case PHPDBG_NEXT: \ + if (exception) { \ + if (EG(current_execute_data) && EG(current_execute_data)->func && ZEND_USER_CODE(EG(current_execute_data)->func->common.type) \ + && (backup_opline->opcode == ZEND_HANDLE_EXCEPTION || backup_opline->opcode == ZEND_CATCH)) { \ + EG(current_execute_data)->opline = backup_opline; \ + EG(exception) = exception; \ + } else { \ + Z_OBJ(zv) = exception; \ + zend_throw_exception_internal(&zv); \ + } \ + EG(opline_before_exception) = before_ex; \ + } \ + /* fallthrough */ \ + default: \ + goto next; \ + } \ +} while (0) - if (full) { - PHPDBG_G(flags) |= PHPDBG_IS_CLEANING; +void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ +{ + zend_bool original_in_execution = PHPDBG_G(in_execution); + if ((PHPDBG_G(flags) & PHPDBG_IS_STOPPING) && !(PHPDBG_G(flags) & PHPDBG_IS_RUNNING)) { 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 + PHPDBG_G(in_execution) = 1; -#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*)); - } + while (1) { + zend_object *exception = EG(exception); - 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)); - } + if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) { + /* resolve nth opline breakpoints */ + phpdbg_resolve_op_array_breaks(&execute_data->func->op_array); } - } - 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); +#ifdef ZEND_WIN32 + if (EG(timed_out)) { + zend_timeout(0); + } +#endif - EX(function_state).function = (zend_function *) op_array; - EX(function_state).arguments = NULL; + if (PHPDBG_G(flags) & PHPDBG_PREVENT_INTERACTIVE) { + phpdbg_print_opline_ex(execute_data, 0); + goto next; + } - return execute_data; -#endif -} /* }}} */ + /* check for uncaught exceptions */ + if (exception && PHPDBG_G(handled_exception) != exception && !(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { + zend_execute_data *prev_ex = execute_data; + zval zv, rv; + zend_string *file, *msg; + zend_long line; + + do { + prev_ex = zend_generator_check_placeholder_frame(prev_ex); + /* assuming that no internal functions will silently swallow exceptions ... */ + if (!prev_ex->func || !ZEND_USER_CODE(prev_ex->func->common.type)) { + continue; + } -#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 (phpdbg_check_caught_ex(prev_ex, exception)) { + goto ex_is_caught; + } + } while ((prev_ex = prev_ex->prev_execute_data)); -#if PHP_VERSION_ID < 50500 - if (EG(exception)) { - return; - } -#endif + PHPDBG_G(handled_exception) = exception; - EG(in_execution) = 1; + ZVAL_OBJ(&zv, exception); + file = zval_get_string(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("file"), 1, &rv)); + line = zval_get_long(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("line"), 1, &rv)); + msg = zval_get_string(zend_read_property(zend_get_exception_base(&zv), &zv, ZEND_STRL("message"), 1, &rv)); -#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 + phpdbg_error("exception", + "name=\"%s\" file=\"%s\" line=\"" ZEND_LONG_FMT "\"", + "Uncaught %s in %s on line " ZEND_LONG_FMT ": %.*s", + ZSTR_VAL(exception->ce->name), ZSTR_VAL(file), line, + ZSTR_LEN(msg) < 80 ? (int) ZSTR_LEN(msg) : 80, ZSTR_VAL(msg)); + zend_string_release(msg); + zend_string_release(file); - 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); + DO_INTERACTIVE(1); } - -#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) +ex_is_caught: - /* allow conditional breakpoints and - initialization to access the vm uninterrupted */ - if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) || - (PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) { + /* allow conditional breakpoints and initialization to access the vm uninterrupted */ + if (PHPDBG_G(flags) & (PHPDBG_IN_COND_BP | PHPDBG_IS_INITIALIZING)) { /* skip possible breakpoints */ goto next; } /* perform seek operation */ - if (PHPDBG_G(flags) & PHPDBG_SEEK_MASK) { + if ((PHPDBG_G(flags) & PHPDBG_SEEK_MASK) && !(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { /* current address */ zend_ulong address = (zend_ulong) execute_data->opline; + if (PHPDBG_G(seek_ex) != execute_data) { + goto next; + } + +#define INDEX_EXISTS_CHECK (zend_hash_index_exists(&PHPDBG_G(seek), address) || (exception && phpdbg_check_caught_ex(execute_data, exception) == 0)) + /* run to next line */ if (PHPDBG_G(flags) & PHPDBG_IN_UNTIL) { - if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + if (INDEX_EXISTS_CHECK) { PHPDBG_G(flags) &= ~PHPDBG_IN_UNTIL; - zend_hash_clean( - &PHPDBG_G(seek)); + zend_hash_clean(&PHPDBG_G(seek)); } else { /* skip possible breakpoints */ goto next; @@ -1242,10 +1563,9 @@ zend_vm_enter: /* run to finish */ if (PHPDBG_G(flags) & PHPDBG_IN_FINISH) { - if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + if (INDEX_EXISTS_CHECK) { PHPDBG_G(flags) &= ~PHPDBG_IN_FINISH; - zend_hash_clean( - &PHPDBG_G(seek)); + zend_hash_clean(&PHPDBG_G(seek)); } /* skip possible breakpoints */ goto next; @@ -1253,16 +1573,14 @@ zend_vm_enter: /* break for leave */ if (PHPDBG_G(flags) & PHPDBG_IN_LEAVE) { - if (zend_hash_index_exists(&PHPDBG_G(seek), address)) { + if (INDEX_EXISTS_CHECK) { 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) + zend_hash_clean(&PHPDBG_G(seek)); + phpdbg_notice("breakpoint", "id=\"leave\" file=\"%s\" line=\"%u\"", "Breaking for leave at %s:%u", + zend_get_executed_filename(), + zend_get_executed_lineno() ); - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } else { /* skip possible breakpoints */ goto next; @@ -1271,18 +1589,17 @@ zend_vm_enter: } /* not while in conditionals */ - phpdbg_print_opline_ex( - execute_data, &vars, 0 TSRMLS_CC); + phpdbg_print_opline_ex(execute_data, 0); if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) { PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING; - DO_INTERACTIVE(); + DO_INTERACTIVE(1); } /* check if some watchpoint was hit */ { - if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) { - DO_INTERACTIVE(); + if (phpdbg_print_changed_zvals() == SUCCESS) { + DO_INTERACTIVE(1); } } @@ -1291,45 +1608,94 @@ zend_vm_enter: phpdbg_breakbase_t *brake; if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) - && (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC)) + && (brake = phpdbg_find_breakpoint(execute_data)) && (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) { - phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC); - DO_INTERACTIVE(); + phpdbg_hit_breakpoint(brake, 1); + DO_INTERACTIVE(1); } } -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_out("\n"); + phpdbg_notice("signal", "type=\"SIGINT\"", "Program received signal SIGINT"); + DO_INTERACTIVE(1); } +next: + PHPDBG_G(last_line) = execute_data->opline->lineno; - PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC); + /* stupid hack to make zend_do_fcall_common_helper return ZEND_VM_ENTER() instead of recursively calling zend_execute() and eventually segfaulting */ + if ((execute_data->opline->opcode == ZEND_DO_FCALL || + execute_data->opline->opcode == ZEND_DO_UCALL || + execute_data->opline->opcode == ZEND_DO_FCALL_BY_NAME) && + execute_data->call->func->type == ZEND_USER_FUNCTION) { + zend_execute_ex = execute_ex; + } + PHPDBG_G(vmret) = zend_vm_call_opcode_handler(execute_data); + zend_execute_ex = phpdbg_execute_ex; - 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; + if (PHPDBG_G(vmret) != 0) { + if (PHPDBG_G(vmret) < 0) { + PHPDBG_G(in_execution) = original_in_execution; + return; + } else { + execute_data = EG(current_execute_data); } } } zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); } /* }}} */ + +/* only if *not* interactive and while executing */ +void phpdbg_force_interruption(void) /* {{{ */ { + zend_object *exception = EG(exception); + zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */ + + PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER; + + if (data) { + if (data->func) { + if (ZEND_USER_CODE(data->func->type)) { + phpdbg_notice("hardinterrupt", "opline=\"%p\" num=\"%lu\" file=\"%s\" line=\"%u\"", "Current opline: %p (op #%lu) in %s:%u", data->opline, (data->opline - data->func->op_array.opcodes) / sizeof(data->opline), data->func->op_array.filename->val, data->opline->lineno); + } else if (data->func->internal_function.function_name) { + phpdbg_notice("hardinterrupt", "func=\"%s\"", "Current opline: in internal function %s", data->func->internal_function.function_name->val); + } else { + phpdbg_notice("hardinterrupt", "", "Current opline: executing internal code"); + } + } else { + phpdbg_notice("hardinterrupt", "opline=\"%p\"", "Current opline: %p (op_array information unavailable)", data->opline); + } + } else { + phpdbg_notice("hardinterrupt", "", "No information available about executing context"); + } + + DO_INTERACTIVE(0); + +next: + PHPDBG_G(flags) &= ~PHPDBG_IN_SIGNAL_HANDLER; + + if (PHPDBG_G(flags) & PHPDBG_IS_STOPPING) { + zend_bailout(); + } +} +/* }}} */ + +PHPDBG_COMMAND(eol) /* {{{ */ +{ + if (!param || param->type == EMPTY_PARAM) { + phpdbg_notice("eol", "argument required", "argument required"); + } else switch (param->type) { + case STR_PARAM: + if (FAILURE == phpdbg_eol_global_update(param->str)) { + phpdbg_notice("eol", "unknown EOL name '%s', give crlf, lf, cr", "unknown EOL name '%s', give crlf, lf, cr", param->str); + } + break; + + phpdbg_default_switch_case(); + } + + return SUCCESS; +} /* }}} */ |