php性能工具profile,php代码性能profile利器xhprof工作原理浅析

xhprof的

工作原理

xhprof是php的第三方扩展,工作在zend层,以动态加载的方式被加载。php在解释执行的过程中,首先被解释成对应的token码,而后,编译成字节码(这里的字节码不是机器码,在PHP里面称其为opcode码)。xhprof就在php被加载,loading,引起自身extension的时候一起被加载,解释,编译。

xhprof本身通过对zend层函数处理相关的结构体的定义,比如:

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

};

来完成xhprof自定义的函数的初始化,完成xhprof本身的callback机制的注册。

在安装xhprof的过程中,我们需要去定义xhprof报告的输出目录,并且我们设置的时候,变量的名称只能是:xhprof.output_dir原因就在于在xhprof.c的文件中,对于php_ini_entry的设定就是

PHP_INI_BEGIN()

/* output directory:

* Currently this is not used by the extension itself.

* But some implementations of iXHProfRuns interface might

* choose to save/restore XHProf profiler runs in the

* directory specified by this ini setting.

*/

PHP_INI_ENTRY("xhprof.output_dir", "", PHP_INI_ALL, NULL)

PHP_INI_END()这里完成的对php.ini里有关xhprof的配置节的定义方式和方法。相关解释如下:

如果想要为你的模块创建一个 .ini 文件的配置节,可以使用宏 PHP_INI_BEGIN() 来标识这个节的开始,并用 PHP_INI_END() 表示该配置节已经结束。然后在两者之间我们用 PHP_INI_ENTRY() 来创建具体的配置项。HP_INI_ENTRY() 总共接收 4 个参数:配置项名称、初始值、改变这些值所需的权限以及在值改变时用于接收通知的函数句柄。配置项名称和初始值必须是一个字符串,即使它们是一个整数。HP_INI_SYSTEM 只允许在 php.ini 中改变这些值;PHP_INI_USER 允许用户在运行时通过像 .htaccess 这样的附加文件来重写其值;而 PHP_INI_ALL 则允许随意更改。

前面提到xhprof是在config.m4里面,作为$ext_shared的方式被动态加载进入zend层工作的,所以,这里还需要用zend_get_module进行一些处理。zend_get_module的作用为:

ZEND_GET_MODULE (extension_name)

Description:

Provides additional C code used if you want to build a dynamic loaded extension.

到这里,如果xhprof安装正常,那么就已经完成了php在启动的过程中,xhprof作为extension,被加载的方式的定义,和具体的方法的定义了。

前面大体是xhprof在安装后,是工作在哪一层?以何种方式被引入php?怎么被引入的?相关理解和解释,下面再看看xhprof是怎么收集到函数的执行性能profile数据的,例如cpu、memory的使用等信息数据

PHP_FUNCTION(xhprof_enable) {

long  xhprof_flags = 0;                                    /* XHProf flags */

zval *optional_array = NULL;         /* optional array arg: for future use */

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,

"|lz", &xhprof_flags, &optional_array) == FAILURE) {

return;

}

hp_get_ignored_functions_from_arg(optional_array);

这个函数里面是对xhprof_enable函数体的实现,在我们需要对一个函数进行xhprof的时候,需要在最开始进行xhprof_enable()的调用,enable里面可以传参数也可以默认为空,这里zend_parse_parameters的参数中,"ZEND_NUM_ARGS() TSRMLS_CC"部分基本是固定打法,接着的部分 代表参数的相应类型。在这里"|lz"的意思是$xhprof_flags is long typeof,$optional_array is array typeof).

zend_parse_parameters 详解

自动生成的PHP函数周围包含了一些注释,这些注释用于自动生成代码文档和vi、Emacs等编辑器的代码折叠。函数自身的定义使用了宏PHP_FUNCTION(),该宏可以生成一个适合于Zend引擎的函数原型。逻辑本身分成语义各部分,取得调用函数的参数和逻辑本身。为了获得函数传递的参数,可以使用zend_parse_parameters()API函数

所以如果获取xhprof_enable传入的参数如果失败,则会直接return。到下面,则是对hp_get_ignored_functions_from_arg的调用,看名字应该是对要忽略的函数名的获取,从该函数的本身内容

static void hp_get_ignored_functions_from_arg(zval *args) {

if (args != NULL) {

zval  *zresult = NULL;

zresult = hp_zval_at_key("ignored_functions", args);

hp_globals.ignored_function_names = hp_strings_in_zval(zresult);

} else {

hp_globals.ignored_function_names = NULL;

}

}可以看出,这个函数的确是对需要忽略的函数名的获取。

这里插一脚,php之所以可以在使用变量前不需要对该变量进行提前定义,是因为在php的zend引擎中有这么一个东西zval,在php里面,zend用它来作为一个容器,来处理所有的外部变量,还是直接贴一段代码,大家一起来看

typedef struct _zval_struct zval;

typedef struct _zend_class_entry zend_class_entry;

typedef union _zvalue_value {

long lval; /* long value */

double dval; /* double value */

struct {

char *val;

int len;

} str;

HashTable *ht; /* hash table value */

struct {

zend_class_entry *ce;

HashTable *properties;

} obj;

} zvalue_value;

struct _zval_struct {

/* Variable information */

zvalue_value value; /* value */

zend_uchar type; /* active type 见3*/

zend_uchar is_ref; /*是否为引用*/

zend_ushort refcount;

};

(zend.h 208行)

上面的union _zvalue_value就是定义的联合体。它里面包含了常见的long、double,字符串型(以结构体的方式,分别有字符串首地址,字符串长度)、哈希表、类结构体(object)根据用户不同的赋值,该容器对处理的外部变量呈现不同的类型。zend将外部变量的值,type,是否被引用,引用计数等信息就存储在封装的_zval_struct结构体中。这就是为什么php可以不用在需要使用变量的时候,不用先定义可以直接用的原因。

好,现在回来继续看xhprof.c文件。

PHP_FUNCTION(xhprof_enable) {

long  xhprof_flags = 0;                                    /* XHProf flags */

zval *optional_array = NULL;         /* optional array arg: for future use */

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,

"|lz", &xhprof_flags, &optional_array) == FAILURE) {

return;

}

hp_get_ignored_functions_from_arg(optional_array);

hp_begin(XHPROF_MODE_HIERARCHICAL, xhprof_flags TSRMLS_CC);

}在这个函数定义中,执行完需要ignore的函数的逻辑之后,进入hp_begin的执行,这个函数的原型为:

static void hp_begin(long level, long xhprof_flags TSRMLS_DC) {

if (!hp_globals.enabled) {

int hp_profile_flag = 1;

hp_globals.enabled      = 1;

hp_globals.xhprof_flags = (uint32)xhprof_flags;

/* Replace zend_compile with our proxy */

_zend_compile_file = zend_compile_file;

zend_compile_file  = hp_compile_file;

/* Replace zend_execute with our proxy */

_zend_execute = zend_execute;

zend_execute  = hp_execute;

/* Replace zend_execute_internal with our proxy */

_zend_execute_internal = zend_execute_internal;

if (!(hp_globals.xhprof_flags & XHPROF_FLAGS_NO_BUILTINS)) {

/* if NO_BUILTINS is not set (i.e. user wants to profile builtins),

* then we intercept internal (builtin) function calls.

*/

zend_execute_internal = hp_execute_internal;

}

/* Initialize with the dummy mode first Having these dummy callbacks saves

* us from checking if any of the callbacks are NULL everywhere. */

hp_globals.mode_cb.init_cb     = hp_mode_dummy_init_cb;

hp_globals.mode_cb.exit_cb     = hp_mode_dummy_exit_cb;

hp_globals.mode_cb.begin_fn_cb = hp_mode_dummy_beginfn_cb;

hp_globals.mode_cb.end_fn_cb   = hp_mode_dummy_endfn_cb;

/* Register the appropriate callback functions Override just a subset of

* all the callbacks is OK. */

switch(level) {

case XHPROF_MODE_HIERARCHICAL:

hp_globals.mode_cb.begin_fn_cb = hp_mode_hier_beginfn_cb;

hp_globals.mode_cb.end_fn_cb   = hp_mode_hier_endfn_cb;

break;

case XHPROF_MODE_SAMPLED:

...(后面的函数内容都大同小异,就不再全部贴上来了,有兴趣的可以到xhprof/extension里面去查看xhprof.c)

这里可以看到

if (!hp_globals.enabled) {...如果xhprof的enable为打开的状态,则进入该分支流程,下面做了对应的对zend的处理方法的replace,比如,zend_compile_file,zend_execute,zend_execute_internal etc。而这些zend的方法作用分别是完成文件的compile和execute、后面带有internal的是对内部函数的执行。xhprof就是通过对zend的内部函数的replace,然后在对应的zend处理函数中进行所需要的数据的抓取和存储,这个可以在被replace之后的hp_execute (zend_op_array *ops TSRMLS_DC) 里面看到

BEGIN_PROFILING(&hp_globals.entries, func, hp_profile_flag);之后就是进入了xhprof本身的自定义的一些任务的开始了,比如初始化一些内存空间来做好后面需要抓取的数据的存储,对于抓取到的profiling data的数据的处理等

在最后结束的时候(也就是disable的时候)

xhprof通过

/* Remove proxies, restore the originals */

zend_execute          = _zend_execute;

zend_execute_internal = _zend_execute_internal;

zend_compile_file     = _zend_compile_file;

对清理掉xhprof对zend的相关三个函数的proxies,从而关闭了xhprof对php代码执行的数据的收集工作。大体上就是这么多

由于本身能力有限,所以当中不免会引入一些错误和不足,诚邀您对当中的不足、错误之处进行补充和指出,谢谢

QQ:75587880

备注: xhprof,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值