Skip to content

Commit

Permalink
JIT support #51
Browse files Browse the repository at this point in the history
Use PHP 8.0 Observer API
  • Loading branch information
longxinH committed Mar 31, 2021
1 parent 518a7d0 commit f5149a7
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 296 deletions.
80 changes: 18 additions & 62 deletions extension/php_xhprof.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ extern zend_module_entry xhprof_module_entry;
#include "TSRM.h"
#endif

#if PHP_VERSION_ID >= 80000
#include "zend_observer.h"
#endif


/**
* **********************
Expand All @@ -39,7 +43,7 @@ extern zend_module_entry xhprof_module_entry;
*/

/* XHProf version */
#define XHPROF_VERSION "2.2.3"
#define XHPROF_VERSION "2.3.0-dev"

#define XHPROF_FUNC_HASH_COUNTERS_SIZE 1024

Expand Down Expand Up @@ -80,60 +84,6 @@ extern zend_module_entry xhprof_module_entry;
typedef unsigned char uint8;
#endif

/*
* Start profiling - called just before calling the actual function
* NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT
* OF THE FUNCTION WHERE THIS MACRO IS CALLED.
* TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE
* CALLING FUNCTION OR BY CALLING TSRMLS_FETCH()
* TSRMLS_FETCH() IS RELATIVELY EXPENSIVE.
*/
#define BEGIN_PROFILING(entries, symbol, profile_curr, execute_data) \
do { \
/* Use a hash code for zend_string. */ \
zend_ulong hash_code = ZSTR_HASH(symbol); \
profile_curr = !hp_ignore_entry_work(hash_code, symbol); \
if (profile_curr) { \
if (execute_data != NULL) { \
symbol = hp_get_trace_callback(symbol, execute_data); \
} \
hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry(); \
(cur_entry)->hash_code = hash_code % XHPROF_FUNC_HASH_COUNTERS_SIZE; \
(cur_entry)->name_hprof = symbol; \
(cur_entry)->prev_hprof = (*(entries)); \
/* Call the universal callback */ \
hp_mode_common_beginfn((entries), (cur_entry)); \
/* Call the mode's beginfn callback */ \
XHPROF_G(mode_cb).begin_fn_cb((entries), (cur_entry)); \
/* Update entries linked list */ \
(*(entries)) = (cur_entry); \
} \
} while (0)

/*
* Stop profiling - called just after calling the actual function
* NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT
* OF THE FUNCTION WHERE THIS MACRO IS CALLED.
* TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE
* CALLING FUNCTION OR BY CALLING TSRMLS_FETCH()
* TSRMLS_FETCH() IS RELATIVELY EXPENSIVE.
*/
#define END_PROFILING(entries, profile_curr) \
do { \
if (profile_curr) { \
hp_entry_t *cur_entry; \
/* Call the mode's endfn callback. */ \
/* NOTE(cjiang): we want to call this 'end_fn_cb' before */ \
/* 'hp_mode_common_endfn' to avoid including the time in */ \
/* 'hp_mode_common_endfn' in the profiling results. */ \
XHPROF_G(mode_cb).end_fn_cb((entries)); \
cur_entry = (*(entries)); \
/* Free top entry and update entries linked list */ \
(*(entries)) = (*(entries))->prev_hprof; \
hp_fast_free_hprof_entry(cur_entry); \
} \
} while (0)

/* Bloom filter for function names to be ignored */
#define INDEX_2_BYTE(index) (index >> 3)
#define INDEX_2_BIT(index) (1 << (index & 0x7));
Expand All @@ -156,6 +106,9 @@ typedef struct hp_entry_t {
zend_ulong tsc_start; /* start value for TSC counter */
zend_ulong cpu_start;
zend_ulong hash_code; /* hash_code for the function name */
#if PHP_VERSION_ID >= 80000
int is_trace;
#endif
} hp_entry_t;

typedef struct hp_ignored_functions {
Expand All @@ -176,9 +129,6 @@ typedef void (*hp_end_function_cb) (hp_entry_t **entries);
* GLOBAL STATIC VARIABLES
* ***********************
*/
/* Pointer to the original execute function */
static void (*_zend_execute_ex) (zend_execute_data *execute_data);
ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data);

/* Pointer to the origianl execute_internal function */
static void (*_zend_execute_internal) (zend_execute_data *data, zval *return_value);
Expand All @@ -188,13 +138,22 @@ ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, zval *re
static zend_op_array * (*_zend_compile_file) (zend_file_handle *file_handle, int type);
ZEND_DLEXPORT zend_op_array* hp_compile_file(zend_file_handle *file_handle, int type);

/* Pointer to the original compile string function (used by eval) */
#if PHP_VERSION_ID < 80000
/* Pointer to the original compile string function (used by eval) */
static zend_op_array * (*_zend_compile_string) (zval *source_string, char *filename);
ZEND_DLEXPORT zend_op_array* hp_compile_string(zval *source_string, char *filename);

/* Pointer to the original execute function */
static void (*_zend_execute_ex) (zend_execute_data *execute_data);
ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data);
#else
/* Pointer to the original compile string function (used by eval) */
static zend_op_array * (*_zend_compile_string) (zend_string *source_string, const char *filename);
ZEND_DLEXPORT zend_op_array* hp_compile_string(zend_string *source_string, const char *filename);

static zend_observer_fcall_handlers tracer_observer(zend_execute_data *execute_data);
static void tracer_observer_begin(zend_execute_data *ex);
static void tracer_observer_end(zend_execute_data *ex, zval *return_value);
#endif

/**
Expand All @@ -211,7 +170,6 @@ static void hp_end();
static inline zend_ulong cycle_timer();

static void hp_free_the_free_list();
static hp_entry_t *hp_fast_alloc_hprof_entry();
static void hp_fast_free_hprof_entry(hp_entry_t *p);

static void incr_us_interval(struct timeval *start, zend_ulong incr);
Expand All @@ -220,7 +178,6 @@ static void hp_get_ignored_functions_from_arg(zval *args);

static inline void hp_array_del(zend_string **names);

zend_string *hp_get_trace_callback(zend_string *symbol, zend_execute_data *data);
void hp_init_trace_callbacks();

double get_timebase_conversion();
Expand Down Expand Up @@ -311,5 +268,4 @@ PHP_FUNCTION(xhprof_sample_disable);
#endif

extern ZEND_DECLARE_MODULE_GLOBALS(xhprof);

#endif /* PHP_XHPROF_H */
190 changes: 190 additions & 0 deletions extension/trace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#ifndef XHPROF_TRACE_H
#define XHPROF_TRACE_H

static zend_always_inline void hp_mode_common_beginfn(hp_entry_t **entries, hp_entry_t *current)
{
hp_entry_t *p;

/* This symbol's recursive level */
int recurse_level = 0;

if (XHPROF_G(func_hash_counters[current->hash_code]) > 0) {
/* Find this symbols recurse level */
for (p = (*entries); p; p = p->prev_hprof) {
if (zend_string_equals(current->name_hprof, p->name_hprof)) {
recurse_level = (p->rlvl_hprof) + 1;
break;
}
}
}

XHPROF_G(func_hash_counters[current->hash_code])++;

/* Init current function's recurse level */
current->rlvl_hprof = recurse_level;
}

static zend_always_inline int hp_ignored_functions_filter_collision(hp_ignored_functions *functions, zend_ulong hash)
{
zend_ulong idx = hash % XHPROF_MAX_IGNORED_FUNCTIONS;
return functions->filter[idx];
}

static zend_always_inline int hp_ignore_entry_work(zend_ulong hash_code, zend_string *curr_func)
{
if (XHPROF_G(ignored_functions) == NULL) {
return 0;
}

hp_ignored_functions *functions = XHPROF_G(ignored_functions);

if (hp_ignored_functions_filter_collision(functions, hash_code)) {
int i = 0;
for (; functions->names[i] != NULL; i++) {
zend_string *name = functions->names[i];
if (zend_string_equals(curr_func, name)) {
return 1;
}
}
}

return 0;
}

static zend_always_inline zend_string *hp_get_function_name(zend_execute_data *execute_data)
{
zend_function *curr_func;
zend_string *real_function_name;

if (!execute_data) {
return NULL;
}

curr_func = execute_data->func;

if (!curr_func->common.function_name) {
return NULL;
}

if (curr_func->common.scope != NULL) {
real_function_name = strpprintf(0, "%s::%s", curr_func->common.scope->name->val, ZSTR_VAL(curr_func->common.function_name));
} else {
real_function_name = zend_string_copy(curr_func->common.function_name);
}

return real_function_name;
}

static zend_always_inline zend_string *hp_get_trace_callback(zend_string *function_name, zend_execute_data *data)
{
zend_string *trace_name;
hp_trace_callback *callback;

if (XHPROF_G(trace_callbacks)) {
callback = (hp_trace_callback*)zend_hash_find_ptr(XHPROF_G(trace_callbacks), function_name);
if (callback) {
trace_name = (*callback)(function_name, data);
} else {
return function_name;
}
} else {
return function_name;
}

zend_string_release(function_name);

return trace_name;
}

static zend_always_inline hp_entry_t *hp_fast_alloc_hprof_entry()
{
hp_entry_t *p;

p = XHPROF_G(entry_free_list);

if (p) {
XHPROF_G(entry_free_list) = p->prev_hprof;
return p;
} else {
return (hp_entry_t *)malloc(sizeof(hp_entry_t));
}
}

static zend_always_inline void hp_fast_free_hprof_entry(hp_entry_t *p)
{
if (p->name_hprof != NULL) {
zend_string_release(p->name_hprof);
}

/* we use/overload the prev_hprof field in the structure to link entries in
* the free list.
* */
p->prev_hprof = XHPROF_G(entry_free_list);
XHPROF_G(entry_free_list) = p;
}

static zend_always_inline int begin_profiling(zend_string *root_symbol, zend_execute_data *execute_data)
{
zend_string *function_name;
hp_entry_t **entries = &XHPROF_G(entries);

if (root_symbol == NULL) {
function_name = hp_get_function_name(execute_data);
} else {
function_name = zend_string_copy(root_symbol);
}

if (function_name == NULL) {
return 0;
}

zend_ulong hash_code = ZSTR_HASH(function_name);
int profile_curr = !hp_ignore_entry_work(hash_code, function_name);
if (profile_curr) {
if (execute_data != NULL) {
function_name = hp_get_trace_callback(function_name, execute_data);
}

hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry();
(cur_entry)->hash_code = hash_code % XHPROF_FUNC_HASH_COUNTERS_SIZE;
(cur_entry)->name_hprof = function_name;
(cur_entry)->prev_hprof = (*(entries));
#if PHP_VERSION_ID >= 80000
(cur_entry)->is_trace = 1;
#endif
/* Call the universal callback */
hp_mode_common_beginfn((entries), (cur_entry));
/* Call the mode's beginfn callback */
XHPROF_G(mode_cb).begin_fn_cb((entries), (cur_entry));
/* Update entries linked list */
(*(entries)) = (cur_entry);
} else {
#if PHP_VERSION_ID >= 80000
hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry();
(cur_entry)->name_hprof = (*(entries))->name_hprof;
(cur_entry)->prev_hprof = (*(entries));
(cur_entry)->is_trace = 0;
(*(entries)) = (cur_entry);
#endif
zend_string_release(function_name);
}

return profile_curr;
}

static zend_always_inline void end_profiling()
{
hp_entry_t *cur_entry;
hp_entry_t **entries = &XHPROF_G(entries);

/* Call the mode's endfn callback. */
/* NOTE(cjiang): we want to call this 'end_fn_cb' before */
/* 'hp_mode_common_endfn' to avoid including the time in */
/* 'hp_mode_common_endfn' in the profiling results. */
XHPROF_G(mode_cb).end_fn_cb(entries);
cur_entry = (*(entries));
/* Free top entry and update entries linked list */
(*(entries)) = (*(entries))->prev_hprof;
hp_fast_free_hprof_entry(cur_entry);
}
#endif
Loading

0 comments on commit f5149a7

Please sign in to comment.