Skip to content

Commit f5149a7

Browse files
committed
JIT support #51
Use PHP 8.0 Observer API
1 parent 518a7d0 commit f5149a7

File tree

3 files changed

+304
-296
lines changed

3 files changed

+304
-296
lines changed

extension/php_xhprof.h

Lines changed: 18 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ extern zend_module_entry xhprof_module_entry;
3131
#include "TSRM.h"
3232
#endif
3333

34+
#if PHP_VERSION_ID >= 80000
35+
#include "zend_observer.h"
36+
#endif
37+
3438

3539
/**
3640
* **********************
@@ -39,7 +43,7 @@ extern zend_module_entry xhprof_module_entry;
3943
*/
4044

4145
/* XHProf version */
42-
#define XHPROF_VERSION "2.2.3"
46+
#define XHPROF_VERSION "2.3.0-dev"
4347

4448
#define XHPROF_FUNC_HASH_COUNTERS_SIZE 1024
4549

@@ -80,60 +84,6 @@ extern zend_module_entry xhprof_module_entry;
8084
typedef unsigned char uint8;
8185
#endif
8286

83-
/*
84-
* Start profiling - called just before calling the actual function
85-
* NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT
86-
* OF THE FUNCTION WHERE THIS MACRO IS CALLED.
87-
* TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE
88-
* CALLING FUNCTION OR BY CALLING TSRMLS_FETCH()
89-
* TSRMLS_FETCH() IS RELATIVELY EXPENSIVE.
90-
*/
91-
#define BEGIN_PROFILING(entries, symbol, profile_curr, execute_data) \
92-
do { \
93-
/* Use a hash code for zend_string. */ \
94-
zend_ulong hash_code = ZSTR_HASH(symbol); \
95-
profile_curr = !hp_ignore_entry_work(hash_code, symbol); \
96-
if (profile_curr) { \
97-
if (execute_data != NULL) { \
98-
symbol = hp_get_trace_callback(symbol, execute_data); \
99-
} \
100-
hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry(); \
101-
(cur_entry)->hash_code = hash_code % XHPROF_FUNC_HASH_COUNTERS_SIZE; \
102-
(cur_entry)->name_hprof = symbol; \
103-
(cur_entry)->prev_hprof = (*(entries)); \
104-
/* Call the universal callback */ \
105-
hp_mode_common_beginfn((entries), (cur_entry)); \
106-
/* Call the mode's beginfn callback */ \
107-
XHPROF_G(mode_cb).begin_fn_cb((entries), (cur_entry)); \
108-
/* Update entries linked list */ \
109-
(*(entries)) = (cur_entry); \
110-
} \
111-
} while (0)
112-
113-
/*
114-
* Stop profiling - called just after calling the actual function
115-
* NOTE: PLEASE MAKE SURE TSRMLS_CC IS AVAILABLE IN THE CONTEXT
116-
* OF THE FUNCTION WHERE THIS MACRO IS CALLED.
117-
* TSRMLS_CC CAN BE MADE AVAILABLE VIA TSRMLS_DC IN THE
118-
* CALLING FUNCTION OR BY CALLING TSRMLS_FETCH()
119-
* TSRMLS_FETCH() IS RELATIVELY EXPENSIVE.
120-
*/
121-
#define END_PROFILING(entries, profile_curr) \
122-
do { \
123-
if (profile_curr) { \
124-
hp_entry_t *cur_entry; \
125-
/* Call the mode's endfn callback. */ \
126-
/* NOTE(cjiang): we want to call this 'end_fn_cb' before */ \
127-
/* 'hp_mode_common_endfn' to avoid including the time in */ \
128-
/* 'hp_mode_common_endfn' in the profiling results. */ \
129-
XHPROF_G(mode_cb).end_fn_cb((entries)); \
130-
cur_entry = (*(entries)); \
131-
/* Free top entry and update entries linked list */ \
132-
(*(entries)) = (*(entries))->prev_hprof; \
133-
hp_fast_free_hprof_entry(cur_entry); \
134-
} \
135-
} while (0)
136-
13787
/* Bloom filter for function names to be ignored */
13888
#define INDEX_2_BYTE(index) (index >> 3)
13989
#define INDEX_2_BIT(index) (1 << (index & 0x7));
@@ -156,6 +106,9 @@ typedef struct hp_entry_t {
156106
zend_ulong tsc_start; /* start value for TSC counter */
157107
zend_ulong cpu_start;
158108
zend_ulong hash_code; /* hash_code for the function name */
109+
#if PHP_VERSION_ID >= 80000
110+
int is_trace;
111+
#endif
159112
} hp_entry_t;
160113

161114
typedef struct hp_ignored_functions {
@@ -176,9 +129,6 @@ typedef void (*hp_end_function_cb) (hp_entry_t **entries);
176129
* GLOBAL STATIC VARIABLES
177130
* ***********************
178131
*/
179-
/* Pointer to the original execute function */
180-
static void (*_zend_execute_ex) (zend_execute_data *execute_data);
181-
ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data);
182132

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

191-
/* Pointer to the original compile string function (used by eval) */
192141
#if PHP_VERSION_ID < 80000
142+
/* Pointer to the original compile string function (used by eval) */
193143
static zend_op_array * (*_zend_compile_string) (zval *source_string, char *filename);
194144
ZEND_DLEXPORT zend_op_array* hp_compile_string(zval *source_string, char *filename);
145+
146+
/* Pointer to the original execute function */
147+
static void (*_zend_execute_ex) (zend_execute_data *execute_data);
148+
ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data);
195149
#else
150+
/* Pointer to the original compile string function (used by eval) */
196151
static zend_op_array * (*_zend_compile_string) (zend_string *source_string, const char *filename);
197152
ZEND_DLEXPORT zend_op_array* hp_compile_string(zend_string *source_string, const char *filename);
153+
154+
static zend_observer_fcall_handlers tracer_observer(zend_execute_data *execute_data);
155+
static void tracer_observer_begin(zend_execute_data *ex);
156+
static void tracer_observer_end(zend_execute_data *ex, zval *return_value);
198157
#endif
199158

200159
/**
@@ -211,7 +170,6 @@ static void hp_end();
211170
static inline zend_ulong cycle_timer();
212171

213172
static void hp_free_the_free_list();
214-
static hp_entry_t *hp_fast_alloc_hprof_entry();
215173
static void hp_fast_free_hprof_entry(hp_entry_t *p);
216174

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

221179
static inline void hp_array_del(zend_string **names);
222180

223-
zend_string *hp_get_trace_callback(zend_string *symbol, zend_execute_data *data);
224181
void hp_init_trace_callbacks();
225182

226183
double get_timebase_conversion();
@@ -311,5 +268,4 @@ PHP_FUNCTION(xhprof_sample_disable);
311268
#endif
312269

313270
extern ZEND_DECLARE_MODULE_GLOBALS(xhprof);
314-
315271
#endif /* PHP_XHPROF_H */

extension/trace.h

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
#ifndef XHPROF_TRACE_H
2+
#define XHPROF_TRACE_H
3+
4+
static zend_always_inline void hp_mode_common_beginfn(hp_entry_t **entries, hp_entry_t *current)
5+
{
6+
hp_entry_t *p;
7+
8+
/* This symbol's recursive level */
9+
int recurse_level = 0;
10+
11+
if (XHPROF_G(func_hash_counters[current->hash_code]) > 0) {
12+
/* Find this symbols recurse level */
13+
for (p = (*entries); p; p = p->prev_hprof) {
14+
if (zend_string_equals(current->name_hprof, p->name_hprof)) {
15+
recurse_level = (p->rlvl_hprof) + 1;
16+
break;
17+
}
18+
}
19+
}
20+
21+
XHPROF_G(func_hash_counters[current->hash_code])++;
22+
23+
/* Init current function's recurse level */
24+
current->rlvl_hprof = recurse_level;
25+
}
26+
27+
static zend_always_inline int hp_ignored_functions_filter_collision(hp_ignored_functions *functions, zend_ulong hash)
28+
{
29+
zend_ulong idx = hash % XHPROF_MAX_IGNORED_FUNCTIONS;
30+
return functions->filter[idx];
31+
}
32+
33+
static zend_always_inline int hp_ignore_entry_work(zend_ulong hash_code, zend_string *curr_func)
34+
{
35+
if (XHPROF_G(ignored_functions) == NULL) {
36+
return 0;
37+
}
38+
39+
hp_ignored_functions *functions = XHPROF_G(ignored_functions);
40+
41+
if (hp_ignored_functions_filter_collision(functions, hash_code)) {
42+
int i = 0;
43+
for (; functions->names[i] != NULL; i++) {
44+
zend_string *name = functions->names[i];
45+
if (zend_string_equals(curr_func, name)) {
46+
return 1;
47+
}
48+
}
49+
}
50+
51+
return 0;
52+
}
53+
54+
static zend_always_inline zend_string *hp_get_function_name(zend_execute_data *execute_data)
55+
{
56+
zend_function *curr_func;
57+
zend_string *real_function_name;
58+
59+
if (!execute_data) {
60+
return NULL;
61+
}
62+
63+
curr_func = execute_data->func;
64+
65+
if (!curr_func->common.function_name) {
66+
return NULL;
67+
}
68+
69+
if (curr_func->common.scope != NULL) {
70+
real_function_name = strpprintf(0, "%s::%s", curr_func->common.scope->name->val, ZSTR_VAL(curr_func->common.function_name));
71+
} else {
72+
real_function_name = zend_string_copy(curr_func->common.function_name);
73+
}
74+
75+
return real_function_name;
76+
}
77+
78+
static zend_always_inline zend_string *hp_get_trace_callback(zend_string *function_name, zend_execute_data *data)
79+
{
80+
zend_string *trace_name;
81+
hp_trace_callback *callback;
82+
83+
if (XHPROF_G(trace_callbacks)) {
84+
callback = (hp_trace_callback*)zend_hash_find_ptr(XHPROF_G(trace_callbacks), function_name);
85+
if (callback) {
86+
trace_name = (*callback)(function_name, data);
87+
} else {
88+
return function_name;
89+
}
90+
} else {
91+
return function_name;
92+
}
93+
94+
zend_string_release(function_name);
95+
96+
return trace_name;
97+
}
98+
99+
static zend_always_inline hp_entry_t *hp_fast_alloc_hprof_entry()
100+
{
101+
hp_entry_t *p;
102+
103+
p = XHPROF_G(entry_free_list);
104+
105+
if (p) {
106+
XHPROF_G(entry_free_list) = p->prev_hprof;
107+
return p;
108+
} else {
109+
return (hp_entry_t *)malloc(sizeof(hp_entry_t));
110+
}
111+
}
112+
113+
static zend_always_inline void hp_fast_free_hprof_entry(hp_entry_t *p)
114+
{
115+
if (p->name_hprof != NULL) {
116+
zend_string_release(p->name_hprof);
117+
}
118+
119+
/* we use/overload the prev_hprof field in the structure to link entries in
120+
* the free list.
121+
* */
122+
p->prev_hprof = XHPROF_G(entry_free_list);
123+
XHPROF_G(entry_free_list) = p;
124+
}
125+
126+
static zend_always_inline int begin_profiling(zend_string *root_symbol, zend_execute_data *execute_data)
127+
{
128+
zend_string *function_name;
129+
hp_entry_t **entries = &XHPROF_G(entries);
130+
131+
if (root_symbol == NULL) {
132+
function_name = hp_get_function_name(execute_data);
133+
} else {
134+
function_name = zend_string_copy(root_symbol);
135+
}
136+
137+
if (function_name == NULL) {
138+
return 0;
139+
}
140+
141+
zend_ulong hash_code = ZSTR_HASH(function_name);
142+
int profile_curr = !hp_ignore_entry_work(hash_code, function_name);
143+
if (profile_curr) {
144+
if (execute_data != NULL) {
145+
function_name = hp_get_trace_callback(function_name, execute_data);
146+
}
147+
148+
hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry();
149+
(cur_entry)->hash_code = hash_code % XHPROF_FUNC_HASH_COUNTERS_SIZE;
150+
(cur_entry)->name_hprof = function_name;
151+
(cur_entry)->prev_hprof = (*(entries));
152+
#if PHP_VERSION_ID >= 80000
153+
(cur_entry)->is_trace = 1;
154+
#endif
155+
/* Call the universal callback */
156+
hp_mode_common_beginfn((entries), (cur_entry));
157+
/* Call the mode's beginfn callback */
158+
XHPROF_G(mode_cb).begin_fn_cb((entries), (cur_entry));
159+
/* Update entries linked list */
160+
(*(entries)) = (cur_entry);
161+
} else {
162+
#if PHP_VERSION_ID >= 80000
163+
hp_entry_t *cur_entry = hp_fast_alloc_hprof_entry();
164+
(cur_entry)->name_hprof = (*(entries))->name_hprof;
165+
(cur_entry)->prev_hprof = (*(entries));
166+
(cur_entry)->is_trace = 0;
167+
(*(entries)) = (cur_entry);
168+
#endif
169+
zend_string_release(function_name);
170+
}
171+
172+
return profile_curr;
173+
}
174+
175+
static zend_always_inline void end_profiling()
176+
{
177+
hp_entry_t *cur_entry;
178+
hp_entry_t **entries = &XHPROF_G(entries);
179+
180+
/* Call the mode's endfn callback. */
181+
/* NOTE(cjiang): we want to call this 'end_fn_cb' before */
182+
/* 'hp_mode_common_endfn' to avoid including the time in */
183+
/* 'hp_mode_common_endfn' in the profiling results. */
184+
XHPROF_G(mode_cb).end_fn_cb(entries);
185+
cur_entry = (*(entries));
186+
/* Free top entry and update entries linked list */
187+
(*(entries)) = (*(entries))->prev_hprof;
188+
hp_fast_free_hprof_entry(cur_entry);
189+
}
190+
#endif

0 commit comments

Comments
 (0)