xhprof原理分析
今日一早看到有人star tidyway的 xhprof扩展,好奇性能分析在PHP中是怎么做的,就简单分析了下
发现实现原理很简单直接,简单记录下
项目地址: https://github.com/tideways/php-xhprof-extension
从源码上看 xhprof 主要点有几点
入口
初始化时在PHP_MINIT_FUNCTION 做了一个比较有意思的操作、替换掉了PHP关键入口函数指针 地址: https://github.com/tideways/php-xhprof-extension/blob/f6aed09e98caf1eab58d2fd12dd42fe23e33c7dc/tideways_xhprof.c#L67
PHP_MINIT_FUNCTION(tideways_xhprof)
{
REGISTER_INI_ENTRIES();
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY", TIDEWAYS_XHPROF_FLAGS_MEMORY, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_MU", TIDEWAYS_XHPROF_FLAGS_MEMORY_MU, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU", TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_CPU", TIDEWAYS_XHPROF_FLAGS_CPU, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS", TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC", TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU", TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU, CONST_CS | CONST_PERSISTENT);
_zend_execute_internal = zend_execute_internal;
zend_execute_internal = tideways_xhprof_execute_internal; //here 这里替换
_zend_execute_ex = zend_execute_ex;
zend_execute_ex = tideways_xhprof_execute_ex; //here 这里替换
return SUCCESS;
}
原理
替换的两个指针用途
- zend_execute_ex 每一个php函数执行都会调用关键入口
- zend_execute_internal 每一个c函数执行的调用关键入口
可以理解成我们每一个函数和c函数调用之前都会经过这个proxy(类似AOP方式) 然后在每个入口和结束时记录一下frame 即可。。。
替换后的 zend_execute_internal
地址:https://github.com/tideways/php-xhprof-extension/blob/f6aed09e98caf1eab58d2fd12dd42fe23e33c7dc/tideways_xhprof.c#L166
ZEND_DLEXPORT void tideways_xhprof_execute_internal(zend_execute_data *execute_data, zval *return_value) {
int is_profiling = 1;
if (!TXRG(enabled) || (TXRG(flags) & TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS) > 0) {
execute_internal(execute_data, return_value TSRMLS_CC);
return;
}
is_profiling = tracing_enter_frame_callgraph(NULL, execute_data TSRMLS_CC);
if (!_zend_execute_internal) {
execute_internal(execute_data, return_value TSRMLS_CC);
} else {
_zend_execute_internal(execute_data, return_value TSRMLS_CC);
}
if (is_profiling == 1 && TXRG(callgraph_frames)) {
tracing_exit_frame_callgraph(TSRMLS_C);
}
}
替换后的 zend_execute_ex
地址: https://github.com/tideways/php-xhprof-extension/blob/f6aed09e98caf1eab58d2fd12dd42fe23e33c7dc/tideways_xhprof.c#L187
ZEND_DLEXPORT void tideways_xhprof_execute_ex (zend_execute_data *execute_data) {
zend_execute_data *real_execute_data = execute_data;
int is_profiling = 0;
if (!TXRG(enabled)) {
_zend_execute_ex(execute_data TSRMLS_CC);
return;
}
is_profiling = tracing_enter_frame_callgraph(NULL, real_execute_data TSRMLS_CC);
_zend_execute_ex(execute_data TSRMLS_CC);
if (is_profiling == 1 && TXRG(callgraph_frames)) {
tracing_exit_frame_callgraph(TSRMLS_C);
}
}