Skip to content

New instrumentation API #5582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
}
internal_function->type = ZEND_INTERNAL_FUNCTION;
internal_function->module = EG(current_module);
ZEND_MAP_PTR_NEW(internal_function->instrument_cache);
memset(internal_function->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*));

if (scope) {
Expand Down
8 changes: 8 additions & 0 deletions Zend/zend_closures.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ static int zend_create_closure_from_callable(zval *return_value, zval *callable,
call.function_name = mptr->common.function_name;
call.scope = mptr->common.scope;

// TODO: Is this correct???
static const zend_instrument_fcall_cache *dummy_handlers = ZEND_INSTRUMENT_FCALL_NOT_INSTRUMENTED;
ZEND_MAP_PTR_INIT(call.instrument_cache, (zend_instrument_fcall_cache **) &dummy_handlers);

zend_free_trampoline(mptr);
mptr = (zend_function *) &call;
}
Expand Down Expand Up @@ -395,6 +399,10 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
invoke->internal_function.module = 0;
invoke->internal_function.scope = zend_ce_closure;
invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);

// TODO: Is this correct???
static const zend_instrument_fcall_cache *dummy_handler = ZEND_INSTRUMENT_FCALL_NOT_INSTRUMENTED;
ZEND_MAP_PTR_INIT(invoke->internal_function.instrument_cache, (zend_instrument_fcall_cache **) &dummy_handler);
return invoke;
}
/* }}} */
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6258,9 +6258,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /*
op_array->fn_flags |= ZEND_ACC_PRELOADED;
ZEND_MAP_PTR_NEW(op_array->run_time_cache);
ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
ZEND_MAP_PTR_NEW(op_array->instrument_cache);
} else {
ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL);

ZEND_MAP_PTR_INIT(op_array->instrument_cache,
zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->instrument_cache, NULL);
}

op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES);
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ struct _zend_op_array {
uint32_t num_args;
uint32_t required_num_args;
zend_arg_info *arg_info;
ZEND_MAP_PTR_DEF(struct zend_instrument_fcall_cache *, instrument_cache);
/* END of common elements */

int cache_size; /* number of run_time_cache_slots * sizeof(void*) */
Expand Down Expand Up @@ -452,6 +453,7 @@ typedef struct _zend_internal_function {
uint32_t num_args;
uint32_t required_num_args;
zend_internal_arg_info *arg_info;
ZEND_MAP_PTR_DEF(struct zend_instrument_fcall_cache *, instrument_cache);
/* END of common elements */

zif_handler handler;
Expand All @@ -475,6 +477,7 @@ union _zend_function {
uint32_t num_args;
uint32_t required_num_args;
zend_arg_info *arg_info; /* index -1 represents the return value info, if any */
ZEND_MAP_PTR_DEF(struct zend_instrument_fcall_cache *, instrument_cache);
} common;

zend_op_array op_array;
Expand Down
34 changes: 30 additions & 4 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

/* Virtual current working directory support */
#include "zend_virtual_cwd.h"
#include "zend_instrument.h"

#ifdef HAVE_GCC_GLOBAL_REGS
# if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386)
Expand Down Expand Up @@ -137,6 +138,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
0, /* num_args */
0, /* required_num_args */
NULL, /* arg_info */
NULL,
ZEND_FN(pass), /* handler */
NULL, /* module */
{NULL,NULL,NULL,NULL} /* reserved */
Expand Down Expand Up @@ -3307,10 +3309,7 @@ static int zend_check_symbol(zval *pz)
#define CHECK_SYMBOL_TABLES()
#endif

ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value)
{
execute_data->func->internal_function.handler(execute_data, return_value);
}
ZEND_API extern inline void execute_internal(zend_execute_data *execute_data, zval *return_value);

ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table) /* {{{ */
{
Expand Down Expand Up @@ -3508,6 +3507,9 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name) /*
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache_i(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}
return fbc;
}
return NULL;
Expand All @@ -3523,6 +3525,9 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name,
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache_i(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}
return fbc;
}
return NULL;
Expand Down Expand Up @@ -3555,6 +3560,12 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu
ZEND_MAP_PTR_SET(op_array->run_time_cache, ptr);
memset(ptr, 0, op_array->cache_size);
}
if (!ZEND_MAP_PTR(op_array->instrument_cache)) {
ZEND_MAP_PTR_INIT(op_array->instrument_cache,
zend_arena_alloc(&CG(arena), sizeof(void*)));
ZEND_MAP_PTR_SET(op_array->instrument_cache, NULL);
zend_instrument_fcall_install((zend_function *)op_array);
}
EX(run_time_cache) = RUN_TIME_CACHE(op_array);

EG(current_execute_data) = execute_data;
Expand All @@ -3579,6 +3590,9 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array *
if (!RUN_TIME_CACHE(op_array)) {
init_func_run_time_cache(op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(op_array->instrument_cache))) {
zend_instrument_fcall_install((zend_function *)op_array);
}
i_init_func_execute_data(op_array, return_value, 1 EXECUTE_DATA_CC);

#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
Expand Down Expand Up @@ -3930,6 +3944,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}
} else {
if (ZSTR_VAL(function)[0] == '\\') {
lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0);
Expand All @@ -3948,6 +3965,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}
called_scope = NULL;
}

Expand Down Expand Up @@ -3992,6 +4012,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zend_o
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}

return zend_vm_stack_push_call_frame(call_info,
fbc, num_args, object_or_called_scope);
Expand Down Expand Up @@ -4077,6 +4100,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
init_func_run_time_cache(&fbc->op_array);
}
if (UNEXPECTED(!ZEND_MAP_PTR_GET(fbc->common.instrument_cache))) {
zend_instrument_fcall_install(fbc);
}

return zend_vm_stack_push_call_frame(call_info,
fbc, num_args, object_or_called_scope);
Expand Down
15 changes: 14 additions & 1 deletion Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "zend_compile.h"
#include "zend_hash.h"
#include "zend_instrument.h"
#include "zend_operators.h"
#include "zend_variables.h"

Expand All @@ -39,7 +40,19 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *execute_data, zend_
ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value);
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value);
ZEND_API void execute_ex(zend_execute_data *execute_data);
ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value);

ZEND_API inline void execute_internal(zend_execute_data *execute_data, zval *return_value) {
zend_instrument_fcall_cache *cache = (zend_instrument_fcall_cache *)
ZEND_MAP_PTR_GET(execute_data->func->common.instrument_cache);
if (UNEXPECTED(cache != ZEND_INSTRUMENT_FCALL_NOT_INSTRUMENTED)) {
zend_instrument_fcall_call_begin(cache, execute_data);
execute_data->func->internal_function.handler(execute_data, return_value);
zend_instrument_fcall_call_end(cache, execute_data, return_value);
} else {
execute_data->func->internal_function.handler(execute_data, return_value);
}
}

ZEND_API zend_class_entry *zend_lookup_class(zend_string *name);
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags);
ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex);
Expand Down
12 changes: 10 additions & 2 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,10 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
const zend_op *current_opline_before_exception = EG(opline_before_exception);

zend_init_func_execute_data(call, &func->op_array, fci->retval);
zend_instrument_fcall_cache *cache = ZEND_MAP_PTR_GET(func->common.instrument_cache);
if (cache != ZEND_INSTRUMENT_FCALL_NOT_INSTRUMENTED) {
zend_instrument_fcall_call_begin(cache, call);
}
zend_execute_ex(call);
EG(opline_before_exception) = current_opline_before_exception;
if (call_via_handler) {
Expand All @@ -807,12 +811,16 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;

ZEND_ASSERT(func->type == ZEND_INTERNAL_FUNCTION);

if (UNEXPECTED(!ZEND_MAP_PTR_GET(func->common.instrument_cache))) {
zend_instrument_fcall_install(func);
}

ZVAL_NULL(fci->retval);
call->prev_execute_data = EG(current_execute_data);
EG(current_execute_data) = call;
if (EXPECTED(zend_execute_internal == NULL)) {
/* saves one function call if zend_execute_internal is not used */
func->internal_function.handler(call, fci->retval);
execute_internal(call, fci->retval);
} else {
zend_execute_internal(call, fci->retval);
}
Expand Down
133 changes: 133 additions & 0 deletions Zend/zend_instrument.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Levi Morrison <levim@php.net> |
| Sammy Kaye Powers <sammyk@php.net> |
+----------------------------------------------------------------------+
*/

#include "zend.h"
#include "zend_API.h"
#include "zend_instrument.h"

struct zend_instrument_fcall_init_list {
struct zend_instrument_fcall_init_list *prev;
zend_instrument_fcall_init instrument;
};
typedef struct zend_instrument_fcall_init_list zend_instrument_fcall_init_list;

static zend_instrument_fcall_init_list *zend_instrument_fcalls;

ZEND_API void zend_instrument_init(void) {
zend_instrument_fcalls = NULL;
}

ZEND_API void zend_instrument_shutdown(void) {
zend_instrument_fcall_init_list *curr, *prev;
for (curr = zend_instrument_fcalls; curr; curr = prev) {
prev = curr->prev;
free(curr);
}
}

ZEND_API void zend_instrument_fcall_register(zend_instrument_fcall_init cb) {
zend_instrument_fcall_init_list *node =
malloc(sizeof(zend_instrument_fcall_init_list));
node->instrument = cb;
node->prev = zend_instrument_fcalls;
zend_instrument_fcalls = node;
}

struct zend_instrument_fcall_list {
struct zend_instrument_fcall_list *prev;
zend_instrument_fcall handlers;
size_t count;
};
typedef struct zend_instrument_fcall_list zend_instrument_fcall_list;


extern inline void zend_instrument_fcall_call_begin(
zend_instrument_fcall_cache *cache,
zend_execute_data *execute_data);

extern inline void zend_instrument_fcall_call_end(
zend_instrument_fcall_cache *cache,
zend_execute_data *execute_data,
zval *return_value);

static zend_instrument_fcall_list *zend_instrument_fcall_add(
zend_instrument_fcall_list *instruments,
zend_instrument_fcall handlers)
{
if (!handlers.begin && !handlers.end) {
return instruments;
}

zend_instrument_fcall_list *n =
emalloc(sizeof(zend_instrument_fcall_list));
if (instruments) {
n->prev = instruments;
n->count = instruments->count + 1;
} else {
n->prev = NULL;
n->count = 1;
}
n->handlers = handlers;
return n;
}

static zend_instrument_fcall_cache *zend_instrument_fcall_list_attach(
zend_function *function,
zend_instrument_fcall_list *handlers_list)
{
zend_instrument_fcall_cache *cache =
malloc(sizeof(zend_instrument_fcall_cache));
cache->instruments_len = handlers_list->count;
cache->handlers =
calloc(handlers_list->count, sizeof(zend_instrument_fcall));

zend_instrument_fcall *handlers = cache->handlers;
zend_instrument_fcall_list *curr;
for (curr = handlers_list; curr; curr = curr->prev) {
*handlers++ = curr->handlers;
}

ZEND_MAP_PTR_SET(function->common.instrument_cache, cache);

return cache;
}

ZEND_API void zend_instrument_fcall_install(zend_function *function) {
zend_instrument_fcall_list *fcall_list = NULL;
zend_instrument_fcall_init_list *elem;

for (elem = zend_instrument_fcalls; elem; elem = elem->prev) {
zend_instrument_fcall handlers = elem->instrument(function);
fcall_list = zend_instrument_fcall_add(fcall_list, handlers);
}

if (fcall_list) {
zend_instrument_fcall_list_attach(function, fcall_list);

// cleanup fcall_list
zend_instrument_fcall_list *curr, *prev;
for (curr = fcall_list; curr; curr = prev) {
prev = curr->prev;
efree(curr);
}
} else {
ZEND_MAP_PTR_SET(function->common.instrument_cache,
ZEND_INSTRUMENT_FCALL_NOT_INSTRUMENTED);
}
}
Loading