php代码性能调优profile利器xhprof工作原理浅析(2)

发表于:2013-01-30来源:Csdn作者:yzongyu点击数: 标签:xhprof
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 l

  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代码执行的数据的收集工作。大体上就是这么多

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

原文转自:http://blog.csdn.net/yzongyu/article/details/8457209