xhprof 源码分析

版本: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_GLOBALSZEND_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);

php 执行的生命周期

定义 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执行流程

在这里插入图片描述

  1. cli 模式下,每次执行脚本都会走一遍这个流程
  2. php-fpm 模式下,php_module_startupphp_module_shutdown 中会执行一次,php_request_startupphp_execute_scriptphp_request_shutdown 每次请求都会执行一次

以 cli 模式梳理执行流程

  1. main() (sapi/cli/php_cli.c)
  2. php_cli_startup()(sapi/cli/php_cli.c)
    • php_module_startup(main/main.c)
      • zend_startup()(Zend/zend.c)
  3. 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)
  4. 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;
	....
}
  1. compile_file(zend/zend_language_scanner.l)
  2. 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);
		}
	....
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值