From: Bob Weinand Date: Mon, 22 Jun 2015 10:56:29 +0000 (+0200) Subject: Add phpdbg userland API to fetch oplog X-Git-Tag: php-7.0.0alpha2~2^2~19 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=29aad0ef45618a25d5f87031a3e4e74ba1f6ac58;p=php Add phpdbg userland API to fetch oplog We may want to add some opcode info later, so that "opcodes" mode is more helpful --- diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index 7b7ed177df..38bd8f897a 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -245,8 +245,7 @@ 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; } @@ -256,6 +255,18 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ 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; } /* }}} */ @@ -403,6 +414,132 @@ static PHP_FUNCTION(phpdbg_prompt) 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); +} + +/* {{{ proto void phpdbg_end_oplog() */ +static PHP_FUNCTION(phpdbg_end_oplog) +{ + phpdbg_oplog_entry *cur = PHPDBG_G(oplog_list)->start; + phpdbg_oplog_list *prev = PHPDBG_G(oplog_list)->prev; + + HashTable *options; + 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; + } + + 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; + zval *file_buf; + zend_string *last_function = (void *)~(uintptr_t)0; + zend_class_entry *last_scope = NULL; + zval *fn_buf; + + HashTable *insert_ht; + zend_long insert_idx; + + do { + zend_op_array *op_array = cur->op_array; + if (op_array->filename != last_file) { + last_file = op_array->filename; + file_buf = zend_hash_find(Z_ARR_P(return_value), last_file); + if (!file_buf) { + zval ht; + array_init(&ht); + file_buf = zend_hash_add_new(Z_ARR_P(return_value), last_file, &ht); + } + } + insert_ht = Z_ARR_P(file_buf); + + if (by_function) { + 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(last_function->len + last_scope->name->len + 2, "%.*s::%.*s", last_function->len, last_function->val, last_scope->name->len, last_scope->name->val); + } + fn_buf = zend_hash_find(Z_ARR_P(return_value), fn_name); + if (!fn_buf) { + zval ht; + array_init(&ht); + fn_buf = zend_hash_add_new(Z_ARR_P(return_value), fn_name, &ht); + } + } + insert_ht = Z_ARR_P(fn_buf); + } + + 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) { + zval zv; + ZVAL_LONG(&zv, 0); + num = zend_hash_index_add_new(insert_ht, insert_idx, &zv); + } + 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() @@ -436,6 +573,12 @@ 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_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) @@ -445,6 +588,8 @@ 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) #ifdef PHP_FE_END PHP_FE_END #else diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h index 2cb588d086..7fb8083793 100644 --- a/sapi/phpdbg/phpdbg.h +++ b/sapi/phpdbg/phpdbg.h @@ -130,6 +130,7 @@ #include "phpdbg_btree.h" #include "phpdbg_watch.h" #include "phpdbg_bp.h" +#include "phpdbg_opcode.h" #ifdef PHP_WIN32 # include "phpdbg_sigio_win32.h" #endif @@ -264,6 +265,10 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable file_sources; FILE *oplog; /* opline log */ + zend_arena *oplog_arena; /* arena for storing oplog */ + phpdbg_oplog_list *oplog_list; /* list of oplog starts */ + phpdbg_oplog_entry *oplog_cur; /* current oplog entry */ + struct { FILE *ptr; int fd; diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index cff756a5ed..ffc5db04d4 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -226,6 +226,15 @@ void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, ze free(decode); } } + + if (PHPDBG_G(oplog_list)) { + phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry)); + cur->op = (zend_op *) execute_data->opline; + cur->op_array = &execute_data->func->op_array; + cur->next = NULL; + PHPDBG_G(oplog_cur)->next = cur; + PHPDBG_G(oplog_cur) = cur; + } } /* }}} */ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags) /* {{{ */ diff --git a/sapi/phpdbg/phpdbg_opcode.h b/sapi/phpdbg/phpdbg_opcode.h index 6848f4014d..dc9d2d9dd0 100644 --- a/sapi/phpdbg/phpdbg_opcode.h +++ b/sapi/phpdbg/phpdbg_opcode.h @@ -28,4 +28,17 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars); void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags); void phpdbg_print_opline_ex(zend_execute_data *execute_data, HashTable *vars, zend_bool ignore_flags); +typedef struct _phpdbg_oplog_entry phpdbg_oplog_entry; +struct _phpdbg_oplog_entry { + phpdbg_oplog_entry *next; + zend_op_array *op_array; + zend_op *op; +}; + +typedef struct _phpdbg_oplog_list phpdbg_oplog_list; +struct _phpdbg_oplog_list { + phpdbg_oplog_list *prev; + phpdbg_oplog_entry *start; +}; + #endif /* PHPDBG_OPCODE_H */