xhprof 是 Facebook 09 年出的一个很优秀的 PHP profiler 工具,但 Facebook 后来迁移到 hhvm,早已不再维护,它在 PHP7 下有诸多 bug。

使用兼容的 xhprof 安装

xhprof 已经很久没有更新了,使用他人的repo1:

cd ~
git clone https://github.com/longxinH/xhprof
cd xhprof/extension/

查找 php7 下的 phpize 的位置。

which php71 # 查看 php71 的位置
ll /usr/bin/php71 # 查看软链接的位置
ls /opt/remi/php71/root/usr/bin/ # 查看所有命令

安装插件。

/opt/remi/php71/root/usr/bin/phpize
./configure --with-php-config=/opt/remi/php71/root/usr/bin/php-config  --enable-xhprof
sudo make
sudo make install

重启 php-fpm。

sudo service php71-php-fpm restart
php71 -m | grep xhprof

此时,安装成功。我们配置一下 xhprof 插件:

[xhprof]
extension=xhprof.so;
xhprof.output_dir=/var/tmp/xhprof

其中 xhprof.output_dir 是 xhprof 的输出目录,每次执行 xhprof 的 save_run 方法时都会生成一个 run_id.project_name.xhprof 文件。这个目录在哪里并不重要。

nginx 配置访问

当生成 .xhprof 文件之后,我们就可以利用 xhprof_lib 来展示结果了。

创建文件夹 /var/www/html/xhprof,然后配置 nginx 如下2

server {
    listen 80;
    root /var/www/html/xhprof/;
    server_name your_host;
    location = / {
        index index.php;
    }
    location ~ \.php {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

在 /var/www/html/xhprof/xhprof_html 下创建 index.php

<?php
    echo phpinfo();
?>

访问 http://your_host/index.php 查看是否正确显示 phpinfo。

当配置成功之后,将 xhprof 中的 xhprof_lib 和 xhprof_html 两个文件夹复制到 /var/www/html/xhprof/ 下,然后访问 http://your_host/xhprof_html/index.php 即可。

PS:安装 yum install graphviz 查看图形界面。

修改 nginx 配置中的 root 直接指向 xhprof_html,配置如下:

server {
    listen 80;
    root /var/www/html/xhprof/xhprof_html;
    server_name your_host;
    location = / {
        index index.php;
    }
    location ~ \.php {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

因为 xhprof_html 下已经有 index.php 了,所以可以直接访问 http://your_host。

# nginx 指向的路径
$ ls /var/www/html/xhprof
footer.php  header.php  index.php  xhprof_html  xhprof_lib

# xhprof 保存的数据
$ ls /var/tmp/xhprof
5979c9dfe223a.your_project.xhprof  597aae1684192.your_project.xhprof

使用方法

和之前一样,将要检查性能的代码包裹起来就可以了。

<?php
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
 
// 要检查性能的代码
 
$xhprof_data = xhprof_disable();
include_once  '/var/www/html/xhprof/xhprof_lib/utils/xhprof_lib.php';
include_once  '/var/www/html/xhprof/xhprof_lib/utils/xhprof_runs.php';
$xhprof_runs = new \XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, 'your_project');

当然这里也可以将前后的代码单独开一个php文件:header.php 和 footer.php。

// header.php
<?php
if (extension_loaded('xhprof')) {
    include_once 'xhprof_lib/utils/xhprof_lib.php';
    include_once 'xhprof_lib/utils/xhprof_runs.php';
    xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
}
?>
// footer.php
<?php
if (extension_loaded('xhprof')) {
    $profiler_namespace = 'your_project';
    $xhprof_data = xhprof_disable();
    $xhprof_runs = new XHProfRuns_Default();
    $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);
}
?>

然后用这两个文件包裹代码。如果是 Apache,可以在项目的 .htaccess 文件中添加:

#php_value auto_prepend_file /var/www/html/xhprof/header.php
#php_value auto_append_file /var/www/html/xhprof/footer.php

如果是 nginx + php-fpm,可以在php-fpm.d/www.conf 添加:

php_value[auto_prepend_file] = /var/www/html/xhprof/header.php
php_value[auto_append_file] = /var/www/html/xhprof/footer.php

个人还是更喜欢在需要的地方手动 require:

<?php
include_once  '/var/www/html/xhprof/header.php';

// 要检查性能的代码

include_once  '/var/www/html/xhprof/footer.php';

这样更加灵活,而且不会每个请求都生成一份报告,只在需要的时候生成。

Notice

  1. 如果遇到错误 failed to execute cmd: " dot -Tpng". stderr: ` (process:24220): Pango-WARNING **: Invalid UTF-8 string passed to pango_layout_set_text() '。暂时不清楚怎么解决,可以选择避开它。将 xhprof_lib/utils/callgraph_utils.php 的 121,122 行的打印和 exit 注释掉。
  2. 如果遇到错误 Error: either we can not find profile data for run_id xxx or the threshold 0.01 is too small or you do not have 'dot' image generation utility installed,无法生成 png 图片,可能是因为生成的文件中有不能识别的字符,修复如下3
    $cmd = " dot -T".$type;
    // 在 cmd 之后添加一个转码工作就可以了
    $dot_script = iconv("UTF-8", "ASCII//IGNORE", $dot_script);
    
  3. 如果想要更好看的 UI,可以参考以下链接1链接2链接3 (手动搭免费)。
  4. 下面是一些参数说明
性能点 描述
Inclusive Time 包括子函数所有执行时间
Exclusive Time/Self Time 函数执行本身花费的时间,不包括子树执行时间
Wall Time 花去了的时间或挂钟时间
CPU Time 用户耗的时间 + 内核耗的时间
Inclusive CPU 包括子函数一起所占用的 CPU
Exclusive CPU 函数自身所占用的 CPU
  1. xhprof issue某博文提到了一些替代 repo,除此之外还有 tideways 等 

  2. nginx 的配置参考了 某 gitbooknginx wiki 

  3. 这个在网上很难找到解决方案,我在 PHP 的 bug 平台找到了它:链接