版本:xhprof v2.3.2
extension/php_xhprof.h
register_trace_callback
#define register_trace_callback(function_name, cb) zend_hash_str_update_mem(XHPROF_G(trace_callbacks), function_name, sizeof(function_name) - 1, &cb, sizeof(hp_trace_callback));
会在使用hp_init_trace_callbacks处使用
hp_entry_t
记录程序的性能分析信息
typedef struct hp_entry_t {
struct hp_entry_t *prev_hprof; /* ptr to prev entry being profiled */
zend_string *name_hprof; /* function name */
int rlvl_hprof; /* recursion level for function */
long int mu_start_hprof; /* memory usage */
long int pmu_start_hprof; /* peak memory usage */
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 的使用可以参考:https://blog.csdn.net/wangqiulin123456/article/details/8284939
hp_ignored_functions
存放忽略统计的函数
typedef struct hp_ignored_functions {
zend_string **names;
zend_ulong filter[XHPROF_MAX_IGNORED_FUNCTIONS];
} hp_ignored_functions;
定义回调函数类型
typedef zend_string* (*hp_trace_callback) (zend_string *symbol, zend_execute_data *data);
/* Various types for XHPROF callbacks */
typedef void (*hp_init_cb) ();
typedef void (*hp_exit_cb) ();
typedef void (*hp_begin_function_cb) (hp_entry_t **entries, hp_entry_t *current);
typedef void (*hp_end_function_cb) (hp_entry_t **entries);
声明函数
/* Pointer to the origianl execute_internal function */
static void (*_zend_execute_internal) (zend_execute_data *data, zval *return_value);
ZEND_DLEXPORT void hp_execute_internal(zend_execute_data *execute_data, zval *return_value);
/* Pointer to the original compile function */
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);
#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
static void hp_register_constants(INIT_FUNC_ARGS);
static void hp_begin(zend_long level, zend_long xhprof_flags);
static void hp_stop();
static void hp_end();
static inline zend_ulong cycle_timer();
static void hp_free_the_free_list();
static void hp_fast_free_hprof_entry(hp_entry_t *p);
static void incr_us_interval(struct timeval *start, zend_ulong incr);
static void hp_get_ignored_functions_from_arg(zval *args);
static inline void hp_array_del(zend_string **names);
void hp_init_trace_callbacks();
double get_timebase_conversion();
hp_ignored_functions *hp_ignored_functions_init(zval *values);
hp_mode_cb
存放回调函数
typedef struct hp_mode_cb {
hp_init_cb init_cb;
hp_exit_cb exit_cb;
hp_begin_function_cb begin_fn_cb;
hp_end_function_cb end_fn_cb;
} hp_mode_cb;
定义 xhprof 变量结构
ZEND_BEGIN_MODULE_GLOBALS(xhprof)
/* Indicates if xhprof is currently enabled */
int enabled;
/* Indicates if xhprof was ever enabled during this request */
int ever_enabled;
/* 保存所有统计信息,是个数组 */
zval stats_count;
/* Indicates the current xhprof mode or level */
int profiler_level;
/* Top of the profile stack */
hp_entry_t *entries;
/* freelist of hp_entry_t chunks for reuse... */
hp_entry_t *entry_free_list;
/* Callbacks for various xhprof modes */
hp_mode_cb mode_cb;
/* ---------- Mode specific attributes: ----------- */
/* Global to track the time of the last sample in time and ticks */
struct timeval last_sample_time;
zend_ulong last_sample_tsc;
/* XHPROF_SAMPLING_INTERVAL in ticks */
zend_long sampling_interval;
zend_ulong sampling_interval_tsc;
zend_long sampling_depth;
/* XHProf flags */
uint32 xhprof_flags;
zend_string *root;
// 函数的调用次数
zend_ulong func_hash_counters[XHPROF_FUNC_HASH_COUNTERS_SIZE];
// 回调函数
HashTable *trace_callbacks;
/* Table of ignored function names and their filter */
hp_ignored_functions *ignored_functions;
double timebase_conversion;
zend_bool collect_additional_info;
ZEND_END_MODULE_GLOBALS(xhprof)
其中 ZEND_BEGIN_MODULE_GLOBALS
和 ZEND_END_MODULE_GLOBALS
是定义在 zend/zend_Api.h
中的两个宏
#define ZEND_BEGIN_MODULE_GLOBALS(module_name) \
typedef struct _zend_##module_name##_globals {
#define ZEND_END_MODULE_GLOBALS(module_name) \
} zend_##module_name##_globals;
经过预编译后,就会变成
typedef struct _zend_xhprof_globals {
int enabled;
......
} zend_xhprof_globals;
声明模块函数
PHP_MINIT_FUNCTION(xhprof);
PHP_MSHUTDOWN_FUNCTION(xhprof);
PHP_RINIT_FUNCTION(xhprof);
PHP_RSHUTDOWN_FUNCTION(xhprof);
PHP_MINFO_FUNCTION(xhprof);gg
PHP_FUNCTION(xhprof_enable);
PHP_FUNCTION(xhprof_disable);
PHP_FUNCTION(xhprof_sample_enable);
PHP_FUNCTION(xhprof_sample_disable);
定义 XHPROF_G 宏
#define XHPROF_G(v) (xhprof_globals.v)
这个宏的作用就是方便获取全局变量 xhprof_globals
的属性值
声明 xhprof_globals 全局变量
extern ZEND_DECLARE_MODULE_GLOBALS(xhprof);
相关宏定义:
zend/zend_API.h
#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \
zend_##module_name##_globals module_name##_globals;
预处理后的结果:
extern zend_xhprof_globals xhprof_globals;
表示声明了一个 xhprof_globals
的全局变量
关于 extern 的使用可以参考这篇文章: http://blog.csdn.net/xingjiarong/article/details/47656339
extension/xhprof.c
1. 绑定 xhprof 扩展的四个静态函数
即:
xhprof_enable
xhprof_disable
xhprof_sample_enable
xhprof_sample_disable
ZEND_BEGIN_ARG_INFO_EX(arginfo_xhprof_enable, 0, 0, 0)
ZEND_ARG_INFO(0, flags)
ZEND_ARG_INFO(0, options)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_xhprof_disable, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_xhprof_sample_enable, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_xhprof_sample_disable, 0)
ZEND_END_ARG_INFO()
// 加载到php模块中的固定格式
zend_function_entry xhprof_functions[] = {
PHP_FE(xhprof_enable, arginfo_xhprof_enable)
PHP_FE(xhprof_disable, arginfo_xhprof_disable)
PHP_FE(xhprof_sample_enable, arginfo_xhprof_sample_enable)
PHP_FE(xhprof_sample_disable, arginfo_xhprof_sample_disable)
{NULL, NULL, NULL}
};
相关宏定义:
main/php.c
#define PHP_FE ZEND_FE
zend/zend_API.h
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, NULL, 0, pass_by_ref, 0, 0 },
#define ZEND_FN(name) zif_##name
#define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags },
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
static const zend_internal_arg_info name[] = { \
{ (const char*)(zend_uintptr_t)(required_num_args), NULL, 0, return_reference, 0, 0 },
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
#define ZEND_END_ARG_INFO() };
所以,上面这段代码经过预编译后,就变成:
static const zend_internal_arg_info arginfo_xhprof_enable[] = { { (const char*)(zend_uintptr_t)(0), NULL, 0, 0, 0, 0 },
{ "flags", NULL, 0, 0, 0, 0 },
{ "options", NULL, 0, 0, 0, 0 },
};
static const zend_internal_arg_info arginfo_xhprof_disable[] = { { (const char*)(zend_uintptr_t)(-1), NULL, 0, ZEND_RETURN_VALUE, 0, 0 },
};
static const zend_internal_arg_info arginfo_xhprof_sample_enable[] = { { (const char*)(zend_uintptr_t)(-1), NULL, 0, ZEND_RETURN_VALUE, 0, 0 },
};
static const zend_internal_arg_info arginfo_xhprof_sample_disable[] = { { (const char*)(zend_uintptr_t)(-1), NULL, 0, ZEND_RETURN_VALUE, 0, 0 },
};
zend_function_entry xhprof_functions[] = {
{ "xhprof_enable", zif_xhprof_enable, arginfo_xhprof_enable, (uint32_t) (sizeof(arginfo_xhprof_enable)/sizeof(struct _zend_internal_arg_info)-1), 0 },
{ "xhprof_disable", zif_xhprof_disable, arginfo_xhprof_disable, (uint32_t) (sizeof(arginfo_xhprof_disable)/sizeof(struct _zend_internal_arg_info)-1), 0 },
{ "xhprof_sample_enable", zif_xhprof_sample_enable, arginfo_xhprof_sample_enable, (uint32_t) (sizeof(arginfo_xhprof_sample_enable)/sizeof(struct _zend_internal_arg_info)-1), 0 },
{ "xhprof_sample_disable", zif_xhprof_sample_disable, arginfo_xhprof_sample_disable, (uint32_t) (sizeof(arginfo_xhprof_sample_disable)/sizeof(struct _zend_internal_arg_info)-1), 0 },
{NULL, NULL, NULL}
};
其中, zif_xhprof_enable
,zif_xhprof_disable
,zif_xhprof_sample_enable
,zif_xhprof_sample_disable
为具体实现的函数地址,其实现位置在此
2. 定义 xhprof 扩展
php 扩展要求的结构:
/* Callback functions for the xhprof extension */
zend_module_entry xhprof_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"xhprof", /* Name of the extension */
xhprof_functions, /* List of functions exposed */
PHP_MINIT(xhprof), /* Module init callback */
PHP_MSHUTDOWN(xhprof), /* Module shutdown callback */
PHP_RINIT(xhprof), /* Request init callback */
PHP_RSHUTDOWN(xhprof), /* Request shutdown callback */
PHP_MINFO(xhprof), /* Module info callback */
#if ZEND_MODULE_API_NO >= 20010901
XHPROF_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
定义模块名字及绑定四个生命周期的回调函数
PHP_MINIT(xhprof)
绑定PHP_MINIT_FUNCTION(xhprof)
PHP_MSHUTDOWN(xhprof)
绑定PHP_MSHUTDOWN_FUNCTION(xhprof)
PHP_RINIT(xhprof)
绑定PHP_RINIT_FUNCTION(xhprof)
PHP_RSHUTDOWN(xhprof)
绑定PHP_RSHUTDOWN_FUNCTION(xhprof)
绑定原理:
main/php.c
#define PHP_MINIT ZEND_MODULE_STARTUP_N
#define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N
#define PHP_RINIT ZEND_MODULE_ACTIVATE_N
#define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N
#define PHP_MINIT_FUNCTION ZEND_MODULE_STARTUP_D
#define PHP_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D
#define PHP_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D
#define PHP_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D
#define ZEND_MODULE_STARTUP_N(module) zm_startup_##module
#define ZEND_MODULE_SHUTDOWN_N(module) zm_shutdown_##module
#define ZEND_MODULE_ACTIVATE_N(module) zm_activate_##module
#define ZEND_MODULE_DEACTIVATE_N(module) zm_deactivate_##module
#define ZEND_MODULE_STARTUP_D(module) int ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS)
#define ZEND_MODULE_SHUTDOWN_D(module) int ZEND_MODULE_SHUTDOWN_N(module)(SHUTDOWN_FUNC_ARGS)
#define ZEND_MODULE_ACTIVATE_D(module) int ZEND_MODULE_ACTIVATE_N(module)(INIT_FUNC_ARGS)
#define ZEND_MODULE_DEACTIVATE_D(module) int ZEND_MODULE_DEACTIVATE_N(module)(SHUTDOWN_FUNC_ARGS)
3. 定义模块配置文件
PHP_INI_BEGIN()
PHP_INI_ENTRY("xhprof.output_dir", "", PHP_INI_ALL, NULL)
STD_PHP_INI_ENTRY("xhprof.collect_additional_info", "0", PHP_INI_ALL, OnUpdateBool, collect_additional_info, zend_xhprof_globals, xhprof_globals)
#define STRINGIFY_(X) #X
#define STRINGIFY(X) STRINGIFY_(X)
STD_PHP_INI_ENTRY("xhprof.sampling_interval", STRINGIFY(XHPROF_DEFAULT_SAMPLING_INTERVAL), PHP_INI_ALL, OnUpdateLong, sampling_interval, zend_xhprof_globals, xhprof_globals)
STD_PHP_INI_ENTRY("xhprof.sampling_depth", STRINGIFY(INT_MAX), PHP_INI_ALL, OnUpdateLong, sampling_depth, zend_xhprof_globals, xhprof_globals)
PHP_INI_END()
静态函数的实现
PHP_FUNCTION(xhprof_enable)
{
....
}
PHP_FUNCTION(xhprof_sample_enable)
{
....
}
....
相关宏定义:
main/php.c
#define PHP_FUNCTION ZEND_FUNCTION
zend/zend.h
#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value
zend/zend_API.h
#define ZEND_FN(name) zif_##name
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
经过预编译后:
void zif_xhprof_enable(zend_execute_data *execute_data, zval *return_value)
{
....
}
....
4. MINIT
PHP_MINIT_FUNCTION(xhprof)
{
ZEND_INIT_MODULE_GLOBALS(xhprof, php_xhprof_init_globals, NULL);
REGISTER_INI_ENTRIES();
// 注册一些常量
hp_register_constants(INIT_FUNC_ARGS_PASSTHRU);
// 设置代理
_zend_compile_file = zend_compile_file;
zend_compile_file = hp_compile_file;
_zend_compile_string = zend_compile_string;
zend_compile_string = hp_compile_string;
_zend_execute_ex = zend_execute_ex;
zend_execute_ex = hp_execute_ex;
_zend_execute_internal = zend_execute_internal;
zend_execute_internal = hp_execute_internal;
....
}
设置了四个代理函数:
- hp_compile_file
- hp_compile_string
- hp_execute_ex
- hp_execute_internal
相关宏定义:
Zend/zend_API.h
#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor) \
globals_ctor(&module_name##_globals);
Zend/zend_ini.h
#define REGISTER_INI_ENTRIES() zend_register_ini_entries(ini_entries, module_number)
经过预处理后的代码:
int zm_startup_xhprof(int type, int module_number)
{
// 初始化全局 xhprof 参数
// xhprof_globals 在 php_xhprof.h 中声明
php_xhprof_init_globals(&xhprof_globals);;
// 注册模块
// ini_entries 是个全局变量,在 zend_ini.h 中声明
zend_register_ini_entries(ini_entries, module_number);
......
_zend_compile_file = zend_compile_file;
zend_compile_file = hp_compile_file;
_zend_compile_string = zend_compile_string;
zend_compile_string = hp_compile_string;
_zend_execute_ex = zend_execute_ex;
zend_execute_ex = hp_execute_ex;
_zend_execute_internal = zend_execute_internal;
zend_execute_internal = hp_execute_internal;
.....
}
初始化全局 zhprof_globals
变量
static void php_xhprof_init_globals(zend_xhprof_globals *xhprof_globals)
{
....
}
hp_execute_ex
ZEND_DLEXPORT void hp_execute_ex (zend_execute_data *execute_data)
{
int is_profiling = 1;
if (!XHPROF_G(enabled)) {
_zend_execute_ex(execute_data);
return;
}
//zend_execute_data *real_execute_data = execute_data->prev_execute_data;
is_profiling = begin_profiling(NULL, execute_data);
_zend_execute_ex(execute_data);
if (is_profiling == 1 && XHPROF_G(entries)) {
end_profiling();
}
}
hp_mode_hier_endfn_cb
void hp_mode_hier_endfn_cb(hp_entry_t **entries)
{
hp_entry_t *top = (*entries);
zval *counts;
char symbol[SCRATCH_BUF_LEN];
long int mu_end;
long int pmu_end;
double wt, cpu;
/* Get end tsc counter */
wt = cycle_timer() - top->tsc_start;
/* Get the stat array */
hp_get_function_stack(top, 2, symbol, sizeof(symbol));
counts = zend_hash_str_find(Z_ARRVAL(XHPROF_G(stats_count)), symbol, strlen(symbol));
if (counts == NULL) {
zval count_val;
array_init(&count_val);
counts = zend_hash_str_update(Z_ARRVAL(XHPROF_G(stats_count)), symbol, strlen(symbol), &count_val);
}
/* Bump stats in the counts hashtable */
hp_inc_count(counts, "ct", 1);
hp_inc_count(counts, "wt", wt);
if (XHPROF_G(xhprof_flags) & XHPROF_FLAGS_CPU) {
cpu = cpu_timer() - top->cpu_start;
/* Bump CPU stats in the counts hashtable */
hp_inc_count(counts, "cpu", cpu);
}
if (XHPROF_G(xhprof_flags) & XHPROF_FLAGS_MEMORY) {
/* Get Memory usage */
mu_end = zend_memory_usage(0);
pmu_end = zend_memory_peak_usage(0);
/* Bump Memory stats in the counts hashtable */
hp_inc_count(counts, "mu", mu_end - top->mu_start_hprof);
hp_inc_count(counts, "pmu", pmu_end - top->pmu_start_hprof);
}
XHPROF_G(func_hash_counters[top->hash_code])--;
}
xhprof_enable
PHP_FUNCTION(xhprof_enable)
{
zend_long xhprof_flags = 0; /* XHProf flags */
zval *optional_array = NULL; /* optional array arg: for future use */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lz", &xhprof_flags, &optional_array) == FAILURE) {
return;
}
hp_get_ignored_functions_from_arg(optional_array);
hp_begin(XHPROF_MODE_HIERARCHICAL, xhprof_flags);
}
设置全局变量的回调函数
static void hp_begin(zend_long level, zend_long xhprof_flags)
{
if (!XHPROF_G(enabled)) {
XHPROF_G(enabled) = 1;
XHPROF_G(xhprof_flags) = (uint32)xhprof_flags;
// 设置全局变量里的回调函数
XHPROF_G(mode_cb).init_cb = hp_mode_dummy_init_cb;
XHPROF_G(mode_cb).exit_cb = hp_mode_dummy_exit_cb;
XHPROF_G(mode_cb).begin_fn_cb = hp_mode_dummy_beginfn_cb;
XHPROF_G(mode_cb).end_fn_cb = hp_mode_dummy_endfn_cb;
switch (level) {
case XHPROF_MODE_HIERARCHICAL:
XHPROF_G(mode_cb).begin_fn_cb = hp_mode_hier_beginfn_cb;
XHPROF_G(mode_cb).end_fn_cb = hp_mode_hier_endfn_cb;
break;
case XHPROF_MODE_SAMPLED:
XHPROF_G(mode_cb).init_cb = hp_mode_sampled_init_cb;
XHPROF_G(mode_cb).begin_fn_cb = hp_mode_sampled_beginfn_cb;
XHPROF_G(mode_cb).end_fn_cb = hp_mode_sampled_endfn_cb;
break;
}
hp_init_profiler_state(level);
XHPROF_G(root) = zend_string_init(ROOT_SYMBOL, sizeof(ROOT_SYMBOL) - 1, 0);
begin_profiling(XHPROF_G(root), NULL);
}
}
void hp_init_profiler_state(int level)
{
/* Setup globals */
if (!XHPROF_G(ever_enabled)) {
XHPROF_G(ever_enabled) = 1;
XHPROF_G(entries) = NULL;
}
XHPROF_G(profiler_level) = (int)level;
/* Init stats_count */
if (Z_TYPE(XHPROF_G(stats_count)) != IS_UNDEF) {
zval_ptr_dtor(&XHPROF_G(stats_count));
}
if (XHPROF_G(root)) {
zend_string_release(XHPROF_G(root));
}
array_init(&XHPROF_G(stats_count));
hp_init_trace_callbacks();
/* Call current mode's init cb */
XHPROF_G(mode_cb).init_cb();
}
hp_init_trace_callbacks
void hp_init_trace_callbacks()
{
hp_trace_callback callback;
if (!XHPROF_G(collect_additional_info)) {
return;
}
if (XHPROF_G(trace_callbacks)) {
return;
}
XHPROF_G(trace_callbacks) = NULL;
ALLOC_HASHTABLE(XHPROF_G(trace_callbacks));
if (!XHPROF_G(trace_callbacks)) {
return;
}
zend_hash_init(XHPROF_G(trace_callbacks), 8, NULL, hp_free_trace_callbacks, 0);
callback = hp_trace_callback_sql_query;
register_trace_callback("PDO::exec", callback);
register_trace_callback("PDO::query", callback);
register_trace_callback("mysql_query", callback);
register_trace_callback("mysqli_query", callback);
register_trace_callback("mysqli::query", callback);
callback = hp_trace_callback_pdo_statement_execute;
register_trace_callback("PDOStatement::execute", callback);
callback = hp_trace_callback_curl_exec;
register_trace_callback("curl_exec", callback);
}
register_trace_callback("PDOStatement::execute", callback);
经过预编译后,变成:
zend_hash_str_update_mem((xhprof_globals.trace_callbacks), "PDOStatement::execute", sizeof("PDOStatement::execute") - 1, &callback, sizeof(hp_trace_callback));;
7. xhprof_disable
PHP_FUNCTION(xhprof_disable)
{
if (XHPROF_G(enabled)) {
hp_stop();
RETURN_ZVAL(&XHPROF_G(stats_count), 1, 0);
}
/* else null is returned */
}
static void hp_stop()
{
while (XHPROF_G(entries)) {
end_profiling();
}
XHPROF_G(enabled) = 0;
}
extension/trace.h
begin_profiling
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;
}
end_profiling
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);
}
hp_mode_common_beginfn
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;
}
}
}
// 函数调用次数加1
XHPROF_G(func_hash_counters[current->hash_code])++;
/* Init current function's recurse level */
current->rlvl_hprof = recurse_level;
}
PHP执行流程
- 在
cli 模式
下,每次执行脚本都会走一遍这个流程 - 在
php-fpm
模式下,php_module_startup
和php_module_shutdown
中会执行一次,php_request_startup
,php_execute_script
,php_request_shutdown
每次请求都会执行一次
以 cli 模式梳理执行流程
main() (sapi/cli/php_cli.c)
php_cli_startup()(sapi/cli/php_cli.c)
php_module_startup(main/main.c)
zend_startup()(Zend/zend.c)
do_cli()(sapi/cli/php_cli.c)
php_request_startup()(main/main.c)
php_execute_script()(main/main.c)
php_request_shutdown()(main/main.c)
php_module_shutdown()(main/main.c)
zend_startup
设置编译和执行opcode的函数
Zend/zend.c
int zend_startup(zend_utility_functions *utility_functions, char **extensions) /* {{{ */
{
....
// 设置编译文件和执行opcode的函数
zend_compile_file = compile_file;
zend_execute_ex = execute_ex;
....
}
compile_file(zend/zend_language_scanner.l)
execute_ex(zend/zend_vm_execute.h)
zend/zend_language_scanner.l
ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
{
zend_lex_state original_lex_state;
zend_op_array *op_array = NULL;
zend_save_lexical_state(&original_lex_state);
if (open_file_for_scanning(file_handle)==FAILURE) {
if (type==ZEND_REQUIRE) {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
zend_bailout();
} else {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
}
} else {
op_array = zend_compile(ZEND_USER_FUNCTION);
}
zend_restore_lexical_state(&original_lex_state);
return op_array;
}
zend/zend_vm_execute.h
ZEND_API void execute_ex(zend_execute_data *ex)
{
....
while (1) {
#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)
((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
if (UNEXPECTED(!OPLINE)) {
#else
if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0)) {
#endif
....
}
}
zend_error_noreturn(E_CORE_ERROR, "Arrived at end of main loop which shouldn't happen");
}
php_execute_script
main/main.c
PHPAPI int php_execute_script(zend_file_handle *primary_file)
{
....
zend_execute_scripts()
....
}
Zend/zend.c
ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...) /* {{{ */
{
....
op_array = zend_compile_file(file_handle, type);
if (op_array) {
zend_execute(op_array, retval);
}
....
}