当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP使用Reli分析性能,生成PHP性能火焰图

发布:smiling 来源: PHP粉丝网  添加日期:2024-03-16 13:07:09 浏览: 评论:0 

Reli 是一个用PHP编写的采样分析器(或VM状态检查器),它可以从进程外部读取有关运行PHP脚本的信息,它是一个独立的CLI工具,因此目标程序不需要任何修改,检测和可视化PHP脚本中的瓶颈,它不仅提供函数级的分析,而且还提供行级或操作码级的解析。

Reli 是一个用PHP编写的采样分析器(或VM状态检查器)。它可以从进程外部读取有关运行PHP脚本的信息。它是一个独立的CLI工具,因此目标程序不需要任何修改。

能做什么?

检测和可视化PHP脚本中的瓶颈。它不仅提供函数级的分析,而且还提供行级或操作码级的解析。

即使调用了很多快速函数,也不会产生累积开销的分析,因为这是一个采样分析器。

调查bug或性能故障的原因。即使PHP脚本处于无法解释的无响应状态,您也可以使用它来找出它在内部做什么。

查找内存瓶颈或内存泄漏

如何工作?

解析解释器的ELF二进制文件

从 /proc/<pid>/maps读取内存映射

通过FFI使用ptrace(2)和process_vm_readv(2)来读取外部进程的内存

分析PHP虚拟机(又名Zend Engine)中的内部数据结构

需求

PHP 8.1+(NTS/ZTS)

64bit Linux x86_64

必须启用FFI扩展(编译增加配置--with-ffi)

必须启用PCNTL扩展

安装

  1. git clone git@github.com:reliforp/reli-prof.git 
  2. cd reli-prof 
  3. /usr/local/php-8.2.14/bin/php /home/www/build/composer.phar install 
  4. /usr/local/php-8.2.14/bin/php ./reli 

使用

1、获取跟踪配置

  1. www@ShaoBoWan:~/build/reli-prof$ /usr/local/php-8.2.14/bin/php ./reli inspector:trace --help 
  2. Description: 
  3.   periodically get call trace from an outer process or thread 
  4.  
  5. Usage: 
  6.   inspector:trace [options] [--] [<cmd> [<args>...]] 
  7.  
  8. Arguments: 
  9.   cmd                                        command to execute as a target: either pid (via -p/--pid) or cmd must be specified 
  10.   args                                       command line arguments for cmd 
  11.  
  12. Options: 
  13.   -p, --pid=PID                              process id 
  14.   -d, --depth[=DEPTH]                        max depth 
  15.   -s, --sleep-ns[=SLEEP-NS]                  nanoseconds between traces (default: 1000 * 1000 * 10) 
  16.   -r, --max-retries[=MAX-RETRIES]            max retries on contiguous errors of read (default: 10) 
  17.   -S, --stop-process[=STOP-PROCESS]          stop the target process while reading its trace (default: off) 
  18.       --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process 
  19.       --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process 
  20.       --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto) 
  21.       --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target) 
  22.       --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target) 
  23.   -t, --template[=TEMPLATE]                  template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy) 
  24.   -o, --output=OUTPUT                        path to write output from this tool (default: stdout) 
  25.   -h, --help                                 Display help for the given command. When no command is given display help for the list command 
  26.   -q, --quiet                                Do not output any message 
  27.   -V, --version                              Display this application version 
  28.       --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output 
  29.   -n, --no-interaction                       Do not ask any interactive question 
  30.   -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 

2、守护模式启动

  1. www@ShaoBoWan:~/build/reli-prof$ /usr/local/php-8.2.14/bin/php ./reli inspector:daemon --help 
  2. Description: 
  3.   concurrently get call traces from processes whose command-lines match a given regex 
  4.  
  5. Usage: 
  6.   inspector:daemon [options] 
  7.  
  8. Options: 
  9.   -P, --target-regex=TARGET-REGEX            regex to find target processes which have matching command-line (required) 
  10.   -T, --threads[=THREADS]                    number of workers (default: 8) 
  11.   -d, --depth[=DEPTH]                        max depth 
  12.   -s, --sleep-ns[=SLEEP-NS]                  nanoseconds between traces (default: 1000 * 1000 * 10) 
  13.   -r, --max-retries[=MAX-RETRIES]            max retries on contiguous errors of read (default: 10) 
  14.   -S, --stop-process[=STOP-PROCESS]          stop the target process while reading its trace (default: off) 
  15.       --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process 
  16.       --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process 
  17.       --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto) 
  18.       --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target) 
  19.       --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target) 
  20.   -t, --template[=TEMPLATE]                  template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy) 
  21.   -o, --output=OUTPUT                        path to write output from this tool (default: stdout) 
  22.   -h, --help                                 Display help for the given command. When no command is given display help for the list command 
  23.   -q, --quiet                                Do not output any message 
  24.   -V, --version                              Display this application version 
  25.       --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output 
  26.   -n, --no-interaction                       Do not ask any interactive question 
  27.   -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug 

案例

这里跟踪一个webman框架进程

  1. www@ShaoBoWan:~/build/webman01$ php start.php status 
  2. Workerman[start.php] status  
  3. ----------------------------------------------GLOBAL STATUS---------------------------------------------------- 
  4. Workerman version:4.0.37          PHP version:8.0.7 
  5. start time:2024-01-01 00:19:33   run 0 days 0 hours    
  6. load average: 0.07, 0.12, 0.98   event-loop:\Workerman\Events\Event 
  7. 2 workers       3 processes 
  8. worker_name  exit_status      exit_count 
  9. webman       0                0 
  10. monitor      0                0 
  11. ----------------------------------------------PROCESS STATUS--------------------------------------------------- 
  12. pid memory  listening           worker_name  connections send_fail timers  total_request qps    status 
  13. 2184553 0.65M   http://0.0.0.0:8787 webman       0           0         0       0             0      [idle] 
  14. 2184554 0.65M   http://0.0.0.0:8787 webman       0           0         0       0             0      [idle] 
  15. 2184555 0.57M   none                monitor      0           0         1       0             0      [idle] 
  16. ----------------------------------------------PROCESS STATUS--------------------------------------------------- 
  17. Summary 0M      -                   -            0           0         1       0             0      [Summary] 

1、跟踪脚本

  1. /usr/local/php-8.2.14/bin/php ./reli i:trace -- php -r "fgets(STDIN);" 
  2.  
  3. fgets <internal>:-1 
  4. 1 <main> Command line code:1 
  5.  
  6. fgets <internal>:-1 
  7. 1 <main> Command line code:1 
  8.  
  9. ... 

2、附加到正在运行的进程

  1. sudo /usr/local/php-8.2.14/bin/php ./reli i:trace -p 2184553 
  2.  
  3. [sudo] password for www:  
  4. 0 EventBase::loop <internal>:-1 
  5. 1 Workerman\Events\Event::loop /home/www/build/webman01/vendor/workerman/workerman/Events/Event.php:193 
  6. 2 Workerman\Worker::run /home/www/build/webman01/vendor/workerman/workerman/Worker.php:2435 
  7. 3 Workerman\Worker::forkOneWorkerForLinux /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1555 
  8. 4 Workerman\Worker::forkWorkersForLinux /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1397 
  9. 5 Workerman\Worker::forkWorkers /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1371 
  10. 6 Workerman\Worker::runAll /home/www/build/webman01/vendor/workerman/workerman/Worker.php:549 
  11. 7 <main> /home/www/build/webman01/start.php:112 
  12.  
  13. ... 

3、获取进程内存地址

sudo /usr/local/php-8.2.14/bin/php ./reli i:eg -p 2184553

0x55e17fe1b1a0

4、跟踪当前进程正在执行的操作码

如果用户想分析一个真正的CPU受限的应用程序,那么他或她不仅想知道哪一行是慢的,还想知道操作码是什么。在这种情况下,使用 --template=phpspy_with_opcode 与 inspector:trace 或 inspector:daemon 。

sudo /usr/local/php-8.2.14/bin/php ./reli i:trace --template=phpspy_with_opcode -p 2184553

输出如下所示

  1. 0 EventBase::loop <internal>:-1 
  2. 1 Workerman\Events\Event::loop /home/www/build/webman01/vendor/workerman/workerman/Events/Event.php:193:ZEND_DO_FCALL 
  3. 2 Workerman\Worker::run /home/www/build/webman01/vendor/workerman/workerman/Worker.php:2435:ZEND_DO_FCALL 
  4. 3 Workerman\Worker::forkOneWorkerForLinux /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1555:ZEND_DO_FCALL 
  5. 4 Workerman\Worker::forkWorkersForLinux /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1397:ZEND_DO_FCALL 
  6. 5 Workerman\Worker::forkWorkers /home/www/build/webman01/vendor/workerman/workerman/Worker.php:1371:ZEND_DO_FCALL 
  7. 6 Workerman\Worker::runAll /home/www/build/webman01/vendor/workerman/workerman/Worker.php:549:ZEND_DO_FCALL 
  8. 7 <main> /home/www/build/webman01/start.php:112:ZEND_DO_FCALL 
  9.  
  10. ... 

当前执行的操作码成为调用堆栈的第一帧。因此,像火焰图这样的可视化跟踪可以显示操作码的使用情况。

出于信息目的,执行操作码也被添加到调用帧的每一端。除了第一个帧,函数调用的操作码(如ZEND_DO_FCALL)应该出现在那里。如果在目标流程中启用了JIT,则此信息可能会稍微不准确。

火焰图

火焰图是由 Brendan Gregg 发明的一种可视化方法,用于展示某一种系统资源或性能指标,是如何定量分布在目标软件里所有的代码路径上的。

系统资源指标可以是 CPU 时间、off-CPU 时间、内存使用、硬盘使用、延时等任何其他你能想到的资源。

代码路径 可以定义为目标软件代码中的调用栈轨迹。调用栈轨迹通常是由一组函数调用帧组成的,通常出现在 GDB 命令 bt 的输出中,以及 Python 或 Java 程序的异常错误信息当中。比如下面是一个 PHP 调用栈轨迹的样例:

  1. C:ngx_http_lua_ngx_timer_at 
  2. at 
  3. cache.lua:43 
  4. cache.lua:record_timing 
  5. router.lua:338 
  6. router.lua:route 
  7. v2_routing.lua:1214 
  8. v2_routing.lua:route 
  9. access_by_lua.lua:130 

1、Psalm 静态检查

Psalm 是一个用于PHP的开源静态分析工具,可以帮助识别代码中明显的和难以识别的bug。Psalm既适用于大型遗留代码库,也适用于小型现代代码库,可以帮助防止绝大多数类型相关的runtime 错误,并且能够利用其他语言中流行的安全编码模式。Psalm还可以自动修复发现的许多错误以改进代码。

  1. cd /home/www/build/reli-prof 
  2.  
  3. sudo /usr/local/php-8.2.14/bin/php ./reli i:trace -o traces -- /usr/local/php-8.2.14/bin/php ./vendor/bin/psalm.phar --no-cache 
  4.  
  5. Install the opcache extension to make use of JIT on PHP 8.0+ for a 20%+ performance boost! 
  6.  
  7. Target PHP version: 8.1 (inferred from composer.json) Enabled extensions: ffi. 
  8. Scanning files... 
  9. Analyzing files... 
  10.  
  11. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  60 / 421 (14%) 
  12. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 120 / 421 (28%) 
  13. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 180 / 421 (42%) 
  14. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 240 / 421 (57%) 
  15. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 300 / 421 (71%) 
  16. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 360 / 421 (85%) 
  17. ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 420 / 421 (99%) 
  18. ░ 
  19. ------------------------------ 
  20.                                 
  21.        No errors found!        
  22.                                 
  23. ------------------------------ 
  24.  
  25. Checks took 7.42 seconds and used 134.539MB of memory 
  26. Psalm was able to infer types for 99.9825% of the codebase 

以上会输出一个traces文件,大概内容如下

  1. array_merge <internal>:-1 
  2. 1 Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer::handleArrayItem phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php:398 
  3. 2 Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer::inferArrayType phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php:292 
  4. 3 Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer::infer phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php:210 
  5. 4 Psalm\Internal\PhpVisitor\Reflector\ClassLikeNodeScanner::visitClassConstDeclaration phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php:760 
  6. 5 Psalm\Internal\PhpVisitor\Reflector\ClassLikeNodeScanner::start phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php:451 
  7. 6 Psalm\Internal\PhpVisitor\ReflectorVisitor::enterNode phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php:116 
  8. 7 _HumbugBox427cd2d3980b\PhpParser\NodeTraverser::traverseArray phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:176 
  9. 8 _HumbugBox427cd2d3980b\PhpParser\NodeTraverser::traverseNode phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:105 
  10. 9 _HumbugBox427cd2d3980b\PhpParser\NodeTraverser::traverseArray phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:196 
  11. 10 _HumbugBox427cd2d3980b\PhpParser\NodeTraverser::traverse phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:85 
  12. 11 Psalm\Internal\Scanner\FileScanner::scan phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Scanner/FileScanner.php:51 
  13. 12 Psalm\Internal\Codebase\Scanner::scanFile phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Codebase/Scanner.php:398 
  14. 13 Psalm\Internal\Codebase\Scanner::scanAPath phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Codebase/Scanner.php:547 
  15. 14 Psalm\Internal\Codebase\Scanner::scanFilePaths phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Codebase/Scanner.php:310 
  16. 15 Psalm\Internal\Codebase\Scanner::scanFiles phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Codebase/Scanner.php:220 
  17. 16 Psalm\Config::visitComposerAutoloadFiles phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Config.php:1840 
  18. 17 Psalm\Internal\Analyzer\ProjectAnalyzer::visitAutoloadFiles phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php:249 
  19. 18 Psalm\Internal\Analyzer\ProjectAnalyzer::check phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php:335 
  20. 19 Psalm\Internal\Cli\Psalm::run phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/src/Psalm/Internal/Cli/Psalm.php:272 
  21. 20 <main> phar:///home/www/build/reli-prof/vendor/psalm/phar/psalm.phar/psalm:7 
  22. 21 <main> /home/www/build/reli-prof/vendor/psalm/phar/psalm.phar:14 
  23. 22 <main> /home/www/build/reli-prof/vendor/bin/psalm.phar:119 

3、生成火焰图

sudo /usr/local/php-8.2.14/bin/php ./reli c:flamegraph &lt;traces  &gt;flame.svg

1.使用浏览器打开预览flame.svg 图片

下来生成一个webman的

  1. Workerman[start.php] status  
  2. ----------------------------------------------GLOBAL STATUS---------------------------------------------------- 
  3. Workerman version:4.1.9          PHP version:7.4.16 
  4. start time:2023-12-22 22:56:56   run 9 days 3 hours    
  5. load average: 0.04, 0.03, 0.09   event-loop:\Workerman\Events\Select 
  6. ----------------------------------------------PROCESS STATUS--------------------------------------------------- 
  7. pid memory  listening           worker_name  connections send_fail timers  total_request qps    status 
  8. 15456 16.42M  http://0.0.0.0:7788 webman       0           0         3       939           0      [idle] 
  9. ----------------------------------------------PROCESS STATUS--------------------------------------------------- 
  10. Summary 131M    -                   -            0           0         28      9191          0      [Summary] 
  11. pid = 15456 
  12.  
  13. sudo /usr/local/php-8.2.14/bin/php ./reli i:trace -p 15456 -o traces-15456 -- /usr/local/php-8.2.14/bin/php ./vendor/bin/psalm.phar --no-cache 
  14.  
  15. sudo /usr/local/php-8.2.14/bin/php ./reli c:flamegraph <traces-15456  >flame-15456.svg 

一个简单Workerman火焰图到手啦!

其他

google-chrome安装(非必要)

下载

  1. $ wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 
  2. --2023-12-31 10:43:53--  https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 
  3. Resolving dl.google.com (dl.google.com)... 120.253.253.225 
  4. Connecting to dl.google.com (dl.google.com)|120.253.253.225|:443... connected. 
  5. HTTP request sent, awaiting response... 200 OK 
  6. Length: 104953176 (100M) [application/x-debian-package] 
  7. Saving to: ‘google-chrome-stable_current_amd64.deb' 
  8.  
  9. google-chrome-stable_current_amd64.deb                  100%[==============================================================================================================================>] 100.09M  11.9MB/s    in 7.4s     
  10.  
  11. 2023-12-31 10:44:00 (13.5 MB/s) - ‘google-chrome-stable_current_amd64.deb' saved [104953176/104953176] 

安装

sudo apt install ./google-chrome-stable_current_amd64.deb

FFI

PHP编译安装启用FFI扩展(编译增加配置--with-ffi)时出现错误No package 'libffi' found,检查后发现是因为缺少libffi库文件,利用以下命令安装,解决问题:

sudo apt install libffi-dev

proc_open 无权限问题

PHP Warning:  proc_open(): Exec failed: Permission denied in /home/www/build/reli-prof/src/Command/Converter/FlameGraphCommand.php on line 44

PHP Warning:  proc_open(): Exec failed: Permission denied in /home/www/build/reli-prof/src/Command/Converter/FlameGraphCommand.php on line 55

赋予reli-prof目录执行权限

sudo chmod 0775 -R /home/www/build/reli-prof

Tags: Reli分析性能 PHP性能火焰图

分享到: