/* +----------------------------------------------------------------------+ | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2015 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Felipe Pena | | Authors: Joe Watkins | | Authors: Bob Weinand | +----------------------------------------------------------------------+ */ #include #include #include #ifndef _WIN32 # include # include #endif #include #include "phpdbg.h" #include "phpdbg_list.h" #include "phpdbg_utils.h" #include "phpdbg_prompt.h" #include "php_streams.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); #define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s, flags) \ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[12], flags) const phpdbg_command_t phpdbg_list_commands[] = { PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l", PHPDBG_ASYNC_SAFE), PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s", PHPDBG_ASYNC_SAFE), PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m", PHPDBG_ASYNC_SAFE), PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s", PHPDBG_ASYNC_SAFE), PHPDBG_END_COMMAND }; PHPDBG_LIST(lines) /* {{{ */ { if (!PHPDBG_G(exec) && !zend_is_executing()) { phpdbg_error("inactive", "type=\"execution\"", "Not executing, and execution context not set"); return SUCCESS; } switch (param->type) { case NUMERIC_PARAM: { const char *char_file = phpdbg_current_file(); zend_string *file = zend_string_init(char_file, strlen(char_file), 0); phpdbg_list_file(file, param->num < 0 ? 1 - param->num : param->num, (param->num < 0 ? param->num : 0) + zend_get_executed_lineno(), 0); efree(file); } break; case FILE_PARAM: { zend_string *file = zend_string_init(param->file.name, strlen(param->file.name), 0); phpdbg_list_file(file, param->file.line, 0, 0); efree(file); } break; phpdbg_default_switch_case(); } return SUCCESS; } /* }}} */ PHPDBG_LIST(func) /* {{{ */ { phpdbg_list_function_byname(param->str, param->len); return SUCCESS; } /* }}} */ PHPDBG_LIST(method) /* {{{ */ { zend_class_entry *ce; if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) { zend_function *function; char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name)); if ((function = zend_hash_str_find_ptr(&ce->function_table, lcname, strlen(lcname)))) { phpdbg_list_function(function); } else { phpdbg_error("list", "type=\"notfound\" method=\"%s::%s\"", "Could not find %s::%s", param->method.class, param->method.name); } efree(lcname); } else { phpdbg_error("list", "type=\"notfound\" class=\"%s\"", "Could not find the class %s", param->method.class); } return SUCCESS; } /* }}} */ PHPDBG_LIST(class) /* {{{ */ { zend_class_entry *ce; if (phpdbg_safe_class_lookup(param->str, param->len, &ce) == SUCCESS) { if (ce->type == ZEND_USER_CLASS) { if (ce->info.user.filename) { phpdbg_list_file(ce->info.user.filename, ce->info.user.line_end - ce->info.user.line_start + 1, ce->info.user.line_start, 0); } else { phpdbg_error("list", "type=\"nosource\" class=\"%s\"", "The source of the requested class (%s) cannot be found", ce->name); } } else { phpdbg_error("list", "type=\"internalclass\" class=\"%s\"", "The class requested (%s) is not user defined", ce->name); } } else { phpdbg_error("list", "type=\"notfound\" class=\"%s\"", "The requested class (%s) could not be found", param->str); } return SUCCESS; } /* }}} */ void phpdbg_list_file(zend_string *filename, uint count, int offset, uint highlight) /* {{{ */ { uint line, lastline; phpdbg_file_source *data; char resolved_path_buf[MAXPATHLEN]; const char *abspath; if (VCWD_REALPATH(filename->val, resolved_path_buf)) { abspath = resolved_path_buf; } else { abspath = filename->val; } if (!(data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), abspath, strlen(abspath)))) { phpdbg_error("list", "type=\"unknownfile\"", "Could not find information about included file..."); return; } if (offset < 0) { count += offset; offset = 0; } lastline = offset + count; if (lastline > data->lines) { lastline = data->lines; } phpdbg_xml("", filename); for (line = offset; line < lastline;) { uint linestart = data->line[line++]; uint linelen = data->line[line] - linestart; char *buffer = data->buf + linestart; if (!highlight) { phpdbg_write("line", "line=\"%u\" code=\"%.*s\"", " %05u: %.*s", line, linelen, buffer); } else { if (highlight != line) { phpdbg_write("line", "line=\"%u\" code=\"%.*s\"", " %05u: %.*s", line, linelen, buffer); } else { phpdbg_write("line", "line=\"%u\" code=\"%.*s\" current=\"current\"", ">%05u: %.*s", line, linelen, buffer); } } if (*(buffer + linelen - 1) != '\n' || !linelen) { phpdbg_out("\n"); } } phpdbg_xml(""); } /* }}} */ void phpdbg_list_function(const zend_function *fbc) /* {{{ */ { const zend_op_array *ops; if (fbc->type != ZEND_USER_FUNCTION) { phpdbg_error("list", "type=\"internalfunction\" function=\"%s\"", "The function requested (%s) is not user defined", fbc->common.function_name); return; } ops = (zend_op_array *) fbc; phpdbg_list_file(ops->filename, ops->line_end - ops->line_start + 1, ops->line_start, 0); } /* }}} */ void phpdbg_list_function_byname(const char *str, size_t len) /* {{{ */ { HashTable *func_table = EG(function_table); zend_function* fbc; char *func_name = (char*) str; size_t func_name_len = len; /* search active scope if begins with period */ if (func_name[0] == '.') { if (EG(scope)) { func_name++; func_name_len--; func_table = &EG(scope)->function_table; } else { phpdbg_error("inactive", "type=\"noclasses\"", "No active class"); return; } } else if (!EG(function_table)) { phpdbg_error("inactive", "type=\"function_table\"", "No function table loaded"); return; } else { func_table = EG(function_table); } /* use lowercase names, case insensitive */ func_name = zend_str_tolower_dup(func_name, func_name_len); phpdbg_try_access { if ((fbc = zend_hash_str_find_ptr(func_table, func_name, func_name_len))) { phpdbg_list_function(fbc); } else { phpdbg_error("list", "type=\"nofunction\" function=\"%s\"", "Function %s not found", func_name); } } phpdbg_catch_access { phpdbg_error("signalsegv", "function=\"%s\"", "Could not list function %s, invalid data source", func_name); } phpdbg_end_try_access(); efree(func_name); } /* }}} */ zend_op_array *phpdbg_compile_file(zend_file_handle *file, int type) { phpdbg_file_source data, *dataptr; zend_file_handle fake = {{0}}; zend_op_array *ret; char *filename = (char *)(file->opened_path ? file->opened_path->val : file->filename); uint line; char *bufptr, *endptr; char resolved_path_buf[MAXPATHLEN]; zend_stream_fixup(file, &data.buf, &data.len); data.filename = filename; data.line[0] = 0; if (file->handle.stream.mmap.old_closer) { /* do not unmap */ file->handle.stream.closer = file->handle.stream.mmap.old_closer; } #if HAVE_MMAP if (file->handle.stream.mmap.map) { data.map = file->handle.stream.mmap.map; } #endif fake.type = ZEND_HANDLE_MAPPED; fake.handle.stream.mmap.buf = data.buf; fake.handle.stream.mmap.len = data.len; fake.free_filename = 0; fake.opened_path = file->opened_path; fake.filename = filename; fake.opened_path = file->opened_path; *(dataptr = emalloc(sizeof(phpdbg_file_source) + sizeof(uint) * data.len)) = data; if (VCWD_REALPATH(filename, resolved_path_buf)) { filename = resolved_path_buf; } for (line = 0, bufptr = data.buf - 1, endptr = data.buf + data.len; ++bufptr < endptr;) { if (*bufptr == '\n') { dataptr->line[++line] = (uint)(bufptr - data.buf) + 1; } } dataptr->lines = ++line; dataptr->line[line] = endptr - data.buf; dataptr = erealloc(dataptr, sizeof(phpdbg_file_source) + sizeof(uint) * line); zend_hash_str_add_ptr(&PHPDBG_G(file_sources), filename, strlen(filename), dataptr); phpdbg_resolve_pending_file_break(filename); ret = PHPDBG_G(compile_file)(&fake, type); fake.opened_path = NULL; zend_file_handle_dtor(&fake); return ret; } void phpdbg_free_file_source(phpdbg_file_source *data) { #if HAVE_MMAP if (data->map) { munmap(data->map, data->len + ZEND_MMAP_AHEAD); } else #endif if (data->buf) { efree(data->buf); } efree(data); } void phpdbg_init_list(void) { PHPDBG_G(compile_file) = zend_compile_file; zend_hash_init(&PHPDBG_G(file_sources), 1, NULL, (dtor_func_t) phpdbg_free_file_source, 0); zend_compile_file = phpdbg_compile_file; }