{"id":13562456,"url":"https://github.com/reliforp/reli-prof","last_synced_at":"2026-01-28T01:36:35.993Z","repository":{"id":37005880,"uuid":"244758877","full_name":"reliforp/reli-prof","owner":"reliforp","description":"A sampling profiler or a memory profiler for PHP written in PHP, which reads information about running PHP VM from outside the process. You can find performance bottlenecks or memory leaks of your scripts without changing the target script or loading extensions.","archived":false,"fork":false,"pushed_at":"2026-01-27T06:11:53.000Z","size":1996,"stargazers_count":504,"open_issues_count":52,"forks_count":16,"subscribers_count":8,"default_branch":"0.12.x","last_synced_at":"2026-01-27T19:26:53.020Z","etag":null,"topics":["elf","ffi","performance","php","php-ffi","php-profiler","profiler","profiling","sampling-profiler","zend-engine"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reliforp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-03-03T22:56:10.000Z","updated_at":"2026-01-21T12:13:52.000Z","dependencies_parsed_at":"2023-07-15T23:13:48.785Z","dependency_job_id":"07a30893-8c91-4e26-9a63-e78e77ff183f","html_url":"https://github.com/reliforp/reli-prof","commit_stats":{"total_commits":583,"total_committers":8,"mean_commits":72.875,"dds":"0.41509433962264153","last_synced_commit":"ae904334c36f77af617f532eab4c4f9066fefeb8"},"previous_names":["sj-i/reli-prof"],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/reliforp/reli-prof","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reliforp%2Freli-prof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reliforp%2Freli-prof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reliforp%2Freli-prof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reliforp%2Freli-prof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reliforp","download_url":"https://codeload.github.com/reliforp/reli-prof/tar.gz/refs/heads/0.12.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reliforp%2Freli-prof/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28831848,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T23:29:49.665Z","status":"ssl_error","status_checked_at":"2026-01-27T23:25:58.379Z","response_time":168,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elf","ffi","performance","php","php-ffi","php-profiler","profiler","profiling","sampling-profiler","zend-engine"],"created_at":"2024-08-01T13:01:08.835Z","updated_at":"2026-01-28T01:36:35.959Z","avatar_url":"https://github.com/reliforp.png","language":"PHP","readme":"# Reli\n![Minimum PHP version: 8.1.0](https://img.shields.io/badge/php-8.1.0%2B-blue.svg)\n[![Packagist](https://img.shields.io/packagist/v/reliforp/reli-prof.svg)](https://packagist.org/packages/reliforp/reli-prof)\n[![Github Actions](https://github.com/reliforp/reli-prof/workflows/build/badge.svg)](https://github.com/reliforp/reli-prof/actions)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/reliforp/reli-prof/badges/quality-score.png?b=0.11.x)](https://scrutinizer-ci.com/g/reliforp/reli-prof/?branch=0.11.x)\n[![Coverage Status](https://coveralls.io/repos/github/reliforp/reli-prof/badge.svg?branch=0.11.x)](https://coveralls.io/github/reliforp/reli-prof?branch=0.11.x)\n![Psalm coverage](https://shepherd.dev/github/reliforp/reli-prof/coverage.svg?)\n\nReli is a sampling profiler (or a VM state inspector) written in PHP. It can read information about running PHP script from outside of the process. It's a stand alone CLI tool, so target programs don't need any modifications. The former name of this tool was sj-i/php-profiler. \n\n## What can I use this for?\n- Detecting and visualizing bottlenecks in PHP scripts\n  - It provides not only at the function level of profiling but also at line level or opcode level resolution\n- Profiling without accumulated overhead even when a lot of fast functions called as this is a sampling profiler (see the links below, tideways, xhprof, and the profiler of xdebug, many profilers have this overhead)\n  - [Profiling Overhead and PHP 7](https://tideways.com/profiler/blog/profiling-overhead-and-php-7)\n  - [nikic/sample_prof](https://github.com/nikic/sample_prof)\n- Investigating the cause of a bug or performance failure\n  - Even if a PHP script is in an unexplained unresponsive state, you can use this to find out what it is doing internally.\n- [Finding memory bottlenecks or memory leaks](https://github.com/reliforp/reli-prof/blob/0.11.x/docs/memory-profiler.md)\n\n## How it works\nIt's implemented by using following techniques:\n\n- Parsing ELF binary of the interpreter\n- Reading memory map from /proc/\\\u003cpid\\\u003e/maps\n- Reading memory of outer process by using ptrace(2) and process_vm_readv(2) via FFI\n- Analyzing internal data structure in the PHP VM (aka Zend Engine)\n\nIf you have a bit of extra CPU resource, the overhead of this software would be negligible.\n\n## Differences to phpspy, when to use reli\nReli is heavily inspired by [adsr/phpspy](https://github.com/adsr/phpspy).\n\nThe main difference between the two is that reli is written in almost pure PHP while phpspy is written in C.\nIn profiling, there are cases you want to customize how and what information to get.\nIf customizability for PHP developers matters, you can use this software at the cost of performance. (Although, we hope the cost is not too big.)\n\nAdditionally, reli can find VM state from ZTS interpreters. For example, in the daemon mode, traces of threads started via [ext-parallel](https://github.com/krakjoe/parallel) are automatically retrieved. Currently this cannot be done with phpspy only.\nReli also provides functionality to only get the address of EG from targets, so you can use actual profiling with phpspy if you want, even when the target is ZTS.\n\nOther features of reli that phpspy does not currently have include:\n\n- Output more accurate line numbers\n- Customize output format with PHP templates\n- Get running opcodes of the PHP-VM\n- Automatic retrieval of the target PHP version from stripped PHP binaries\n- Output traces in speedscope format\n- Deeply analyzing memory usage of the target process\n\nThere is no particular reason why these features cannot be implemented on the phpspy side, so it may be possible to do them on phpspy in the future.\n\nOn the other hand, there are a few things that phpspy can do but reli cannot yet.\n\n- Redirecting output of child processes\n- Forcing the address of EG\n- Run more faster with lower overhead.\n- etc.\n\nMuch of what can be done with phpspy will be done with reli in the future.\n\n## Requirements\n### Supported PHP versions\n#### Execution\n- PHP 8.1+ (NTS / ZTS)\n- 64bit Linux x86_64\n- FFI extension must be enabled.\n- PCNTL extension must be enabled.\n\n#### Target\n- PHP 7.0+ (NTS / ZTS)\n- 64bit Linux x86_64\n\nOn targeting ZTS, the target process must load libpthread.so, and also you must have unstripped binary of the interpreter and the libpthread.so, to find EG from the TLS.\n\n## Installation\n### From Composer\n```bash\ncomposer create-project reliforp/reli-prof\ncd reli-prof\n./reli\n```\n\n### From Git\n```bash\ngit clone git@github.com:reliforp/reli-prof.git\ncd reli-prof\ncomposer install\n./reli\n```\n\n### From Docker\n```bash\ndocker pull reliforp/reli-prof\ndocker run -it --security-opt=\"apparmor=unconfined\" --cap-add=SYS_PTRACE --pid=host reliforp/reli-prof\n```\n\n## Usage\n### Get call traces\n```bash\n./reli inspector:trace --help\nDescription:\n  periodically get call trace from an outer process or thread\n\nUsage:\n  inspector:trace [options] [--] [\u003ccmd\u003e [\u003cargs\u003e...]]\n\nArguments:\n  cmd                                        command to execute as a target: either pid (via -p/--pid) or cmd must be specified\n  args                                       command line arguments for cmd\n\nOptions:\n  -p, --pid=PID                              process id\n  -d, --depth[=DEPTH]                        max depth\n  -s, --sleep-ns[=SLEEP-NS]                  nanoseconds between traces (default: 1000 * 1000 * 10)\n  -r, --max-retries[=MAX-RETRIES]            max retries on contiguous errors of read (default: 10)\n  -S, --stop-process[=STOP-PROCESS]          stop the target process while reading its trace (default: off)\n      --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process\n      --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process\n      --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto)\n      --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target)\n      --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target)\n  -t, --template[=TEMPLATE]                  template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy)\n  -o, --output=OUTPUT                        path to write output from this tool (default: stdout)\n  -h, --help                                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                                Do not output any message\n  -V, --version                              Display this application version\n      --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction                       Do not ask any interactive question\n  -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n```\n\n### Daemon mode\n```bash\n./reli inspector:daemon --help\nDescription:\n  concurrently get call traces from processes whose command-lines match a given regex\n\nUsage:\n  inspector:daemon [options]\n\nOptions:\n  -P, --target-regex=TARGET-REGEX            regex to find target processes which have matching command-line (required)\n  -T, --threads[=THREADS]                    number of workers (default: 8)\n  -d, --depth[=DEPTH]                        max depth\n  -s, --sleep-ns[=SLEEP-NS]                  nanoseconds between traces (default: 1000 * 1000 * 10)\n  -r, --max-retries[=MAX-RETRIES]            max retries on contiguous errors of read (default: 10)\n  -S, --stop-process[=STOP-PROCESS]          stop the target process while reading its trace (default: off)\n      --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process\n      --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process\n      --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto)\n      --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target)\n      --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target)\n  -t, --template[=TEMPLATE]                  template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy)\n  -o, --output=OUTPUT                        path to write output from this tool (default: stdout)\n  -h, --help                                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                                Do not output any message\n  -V, --version                              Display this application version\n      --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction                       Do not ask any interactive question\n  -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n```\n\n### top-like mode\n```bash\n./reli inspector:top --help\nDescription:\n  show an aggregated view of traces in real time in a form similar to the UNIX top command.\n\nUsage:\n  inspector:top [options]\n\nOptions:\n  -P, --target-regex=TARGET-REGEX            regex to find target processes which have matching command-line (required)\n  -T, --threads[=THREADS]                    number of workers (default: 8)\n  -d, --depth[=DEPTH]                        max depth\n  -s, --sleep-ns[=SLEEP-NS]                  nanoseconds between traces (default: 1000 * 1000 * 10)\n  -r, --max-retries[=MAX-RETRIES]            max retries on contiguous errors of read (default: 10)\n  -S, --stop-process[=STOP-PROCESS]          stop the target process while reading its trace (default: off)\n      --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process\n      --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process\n      --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto)\n      --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target)\n      --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target)\n  -h, --help                                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                                Do not output any message\n  -V, --version                              Display this application version\n      --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction                       Do not ask any interactive question\n  -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n```\n\n### Get the address of EG\n```bash\n./reli inspector:eg --help\nDescription:\n  get EG address from an outer process or thread\n\nUsage:\n  inspector:eg_address [options] [--] [\u003ccmd\u003e [\u003cargs\u003e...]]\n\nArguments:\n  cmd                                        command to execute as a target: either pid (via -p/--pid) or cmd must be specified\n  args                                       command line arguments for cmd\n\nOptions:\n  -p, --pid=PID                              process id\n      --php-regex[=PHP-REGEX]                regex to find the php binary loaded in the target process\n      --libpthread-regex[=LIBPTHREAD-REGEX]  regex to find the libpthread.so loaded in the target process\n      --php-version[=PHP-VERSION]            php version (auto|v7[0-4]|v8[0123]) of the target (default: auto)\n      --php-path[=PHP-PATH]                  path to the php binary (only needed in tracing chrooted ZTS target)\n      --libpthread-path[=LIBPTHREAD-PATH]    path to the libpthread.so (only needed in tracing chrooted ZTS target)\n  -h, --help                                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                                Do not output any message\n  -V, --version                              Display this application version\n      --ansi|--no-ansi                       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction                       Do not ask any interactive question\n  -v|vv|vvv, --verbose                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n```\n\n## [Experimental] Dump the memory usage of the target process\n```bash\n./reli inspector:memory --help\nDescription:\n  [experimental] get memory usage from an outer process\n\nUsage:\n  inspector:memory [options] [--] [\u003ccmd\u003e [\u003cargs\u003e...]]\n\nArguments:\n  cmd                                                                command to execute as a target: either pid (via -p/--pid) or cmd must be specified\n  args                                                               command line arguments for cmd\n\nOptions:\n      --stop-process|--no-stop-process                               stop the process while inspecting (default: on)\n      --pretty-print|--no-pretty-print                               pretty print the result (default: off)\n      --memory-limit-error-file=MEMORY-LIMIT-ERROR-FILE              file path where memory_limit is exceeded\n      --memory-limit-error-line=MEMORY-LIMIT-ERROR-LINE              line number where memory_limit is exceeded\n      --memory-limit-error-max-depth[=MEMORY-LIMIT-ERROR-MAX-DEPTH]  max attempts to trace back the VM stack on memory_limit error [default: 512]\n  -p, --pid=PID                                                      process id\n      --php-regex[=PHP-REGEX]                                        regex to find the php binary loaded in the target process\n      --libpthread-regex[=LIBPTHREAD-REGEX]                          regex to find the libpthread.so loaded in the target process\n      --php-version[=PHP-VERSION]                                    php version (auto|v7[0-4]|v8[0123]) of the target (default: auto)\n      --php-path[=PHP-PATH]                                          path to the php binary (only needed in tracing chrooted ZTS target)\n      --libpthread-path[=LIBPTHREAD-PATH]                            path to the libpthread.so (only needed in tracing chrooted ZTS target)\n  -h, --help                                                         Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                                                        Do not output any message\n  -V, --version                                                      Display this application version\n      --ansi|--no-ansi                                               Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction                                               Do not ask any interactive question\n  -v|vv|vvv, --verbose                                               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\n```\n\n## Examples\n### Trace a script\n```bash\n$ ./reli i:trace -- php -r \"fgets(STDIN);\"\n0 fgets \u003cinternal\u003e:-1\n1 \u003cmain\u003e \u003cinternal\u003e:-1\n\n0 fgets \u003cinternal\u003e:-1\n1 \u003cmain\u003e \u003cinternal\u003e:-1\n\n0 fgets \u003cinternal\u003e:-1\n1 \u003cmain\u003e \u003cinternal\u003e:-1\n\n\u003cpress q to exit\u003e\n...\n```\n\n### Attach to a running process\n```bash\n$ sudo php ./reli i:trace -p 2182685\n0 time_nanosleep \u003cinternal\u003e:-1\n1 Reli\\Lib\\Loop\\LoopMiddleware\\NanoSleepMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/NanoSleepMiddleware.php:33\n2 Reli\\Lib\\Loop\\LoopMiddleware\\KeyboardCancelMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/KeyboardCancelMiddleware.php:39\n3 Reli\\Lib\\Loop\\LoopMiddleware\\RetryOnExceptionMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/RetryOnExceptionMiddleware.php:37\n4 Reli\\Lib\\Loop\\Loop::invoke /home/sji/work/reli/src/Lib/Loop/Loop.php:26\n5 Reli\\Command\\Inspector\\GetTraceCommand::execute /home/sji/work/reli/src/Command/Inspector/GetTraceCommand.php:133\n6 Symfony\\Component\\Console\\Command\\Command::run /home/sji/work/reli/vendor/symfony/console/Command/Command.php:291\n7 Symfony\\Component\\Console\\Application::doRunCommand /home/sji/work/reli/vendor/symfony/console/Application.php:979\n8 Symfony\\Component\\Console\\Application::doRun /home/sji/work/reli/vendor/symfony/console/Application.php:299\n9 Symfony\\Component\\Console\\Application::run /home/sji/work/reli/vendor/symfony/console/Application.php:171\n10 \u003cmain\u003e /home/sji/work/reli/reli:45\n\n0 time_nanosleep \u003cinternal\u003e:-1\n1 Reli\\Lib\\Loop\\LoopMiddleware\\NanoSleepMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/NanoSleepMiddleware.php:33\n2 Reli\\Lib\\Loop\\LoopMiddleware\\KeyboardCancelMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/KeyboardCancelMiddleware.php:39\n3 Reli\\Lib\\Loop\\LoopMiddleware\\RetryOnExceptionMiddleware::invoke /home/sji/work/reli/src/Lib/Loop/LoopMiddleware/RetryOnExceptionMiddleware.php:37\n4 Reli\\Lib\\Loop\\Loop::invoke /home/sji/work/reli/src/Lib/Loop/Loop.php:26\n5 Reli\\Command\\Inspector\\GetTraceCommand::execute /home/sji/work/reli/src/Command/Inspector/GetTraceCommand.php:133\n6 Symfony\\Component\\Console\\Command\\Command::run /home/sji/work/reli/vendor/symfony/console/Command/Command.php:291\n7 Symfony\\Component\\Console\\Application::doRunCommand /home/sji/work/reli/vendor/symfony/console/Application.php:979\n8 Symfony\\Component\\Console\\Application::doRun /home/sji/work/reli/vendor/symfony/console/Application.php:299\n9 Symfony\\Component\\Console\\Application::run /home/sji/work/reli/vendor/symfony/console/Application.php:171\n10 \u003cmain\u003e /home/sji/work/reli/reli:45\n\n\u003cpress q to exit\u003e\n...\n```\nThe executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)\n\n### Daemon mode\n```bash\n$ sudo php ./reli i:daemon -P \"^/usr/sbin/httpd\"\n``` \nThe executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)\n\n### Get the address of EG\n```bash\n$ sudo php ./reli i:eg -p 2183131\n0x555ae7825d80\n``` \nThe executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)\n\n### Show currently executing opcodes at traces\nIf a user wants to profile a really CPU-bound application, then he or she wouldn't only want to know what line is slow, but what opcode is. In such cases, use `--template=phpspy_with_opcode` with `inspector:trace` or `inspector:daemon`.\n\n```bash\n$ sudo php ./reli i:trace --template=phpspy_with_opcode -p \u003cpid of the target process or thread\u003e\n```\n\nThe output would be like the following.\n\n```\n0 \u003cVM\u003e::ZEND_ASSIGN \u003cVM\u003e:-1\n1 Mandelbrot::iterate /home/sji/work/test/mandelbrot.php:33:ZEND_ASSIGN\n2 Mandelbrot::__construct /home/sji/work/test/mandelbrot.php:12:ZEND_DO_FCALL\n3 \u003cmain\u003e /home/sji/work/test/mandelbrot.php:45:ZEND_DO_FCALL\n\n0 \u003cVM\u003e::ZEND_ASSIGN \u003cVM\u003e:-1\n1 Mandelbrot::iterate /home/sji/work/test/mandelbrot.php:30:ZEND_ASSIGN\n2 Mandelbrot::__construct /home/sji/work/test/mandelbrot.php:12:ZEND_DO_FCALL\n3 \u003cmain\u003e /home/sji/work/test/mandelbrot.php:45:ZEND_DO_FCALL\n```\n\nThe currently executing opcode becomes the first frame of the callstack.\nSo visualizations of the trace like flamegraph can show the usage of opcodes.\n\nFor informational purposes, executing opcodes are also added to each end of the call frames. Except for the first frame, opcodes for function calls such as ZEND_DO_FCALL should appear there.\n\nIf JIT is enabled at the target process, this information may be slightly inaccurate.\n\n### Use in a docker container and target a process on host\n```bash\n$ docker pull reliforp/reli-prof\n$ docker run -it --security-opt=\"apparmor=unconfined\" --cap-add=SYS_PTRACE --pid=host reliforp/reli-prof i:trace -p \u003cpid of the target process or thread\u003e\n```\n\n### Generate flamegraphs from traces\n```bash\n$ ./reli i:trace -o traces -- php ./vendor/bin/psalm.phar --no-cache\n$ ./reli c:flamegraph \u003ctraces \u003eflame.svg\n$ google-chrome flame.svg\n```\n\nThe generated flamegraph below visualizes traces from the execution of the psalm command.\n\n![flame](https://user-images.githubusercontent.com/6488121/153741551-3f0fc730-c748-4908-b8ac-7c3f46a5bdbc.svg)\n\n### Generate the [speedscope](https://github.com/jlfwong/speedscope) format from phpspy compatible traces\n```bash\n$ sudo php ./reli i:trace -p \u003cpid of the target process or thread\u003e \u003etraces\n$ ./reli c:speedscope \u003ctraces \u003eprofile.speedscope.json\n$ speedscope profile.speedscope.json\n```\n\nSee [#101](https://github.com/reliforp/reli-prof/pull/101).\n\n### Generate the callgrind format output from phpspy compatible traces and visualize it with kcachegrind\n```bash\n$ ./reli c:callgrind \u003ctraces \u003ecallgrind.out\n$ kcachegrind callgrind.out\n  ```\n\n### Dump the memory usage of the target process\n\n\u003e [!CAUTION]\n\u003e **Don't upload the output of this command to the internet, because it can contain sensitive information of the target script!!!**\n\n\u003e [!WARNING]  \n\u003e This feature is in an experimental stage and may be less stable than others. The contents of the output may change in the near future.\n\n```bash\n$ sudo php ./reli i:memory -p 2183131 \u003e2183131.memory_dump.json\n$ cat 2183131.memory_dump.json | jq .summary\n```\n\nOnly NTS targets are supported for now.\n\nThe output would be like the following.\n\n```bash\n[\n  {\n    \"zend_mm_heap_total\": 10485760,\n    \"zend_mm_heap_usage\": 7642504,\n    \"zend_mm_chunk_total\": 10485760,\n    \"zend_mm_chunk_usage\": 7642504,\n    \"zend_mm_huge_total\": 0,\n    \"zend_mm_huge_usage\": 0,\n    \"vm_stack_total\": 262144,\n    \"vm_stack_usage\": 8224,\n    \"compiler_arena_total\": 917504,\n    \"compiler_arena_usage\": 815480,\n    \"possible_allocation_overhead_total\": 549645,\n    \"possible_array_overhead_total\": 378768,\n    \"memory_get_usage\": 8263440,\n    \"memory_get_real_usage\": 12582912,\n    \"cached_chunks_size\": 2097152,\n    \"heap_memory_analyzed_percentage\": 92.48574443573136,\n    \"php_version\": \"v82\"\n  }\n]\n\n```\n\nAnd you can get the call trace from the dump.\n\n```bash\n$ cat 2183131.memory_dump.json | jq '.context.call_frames[]|objects|.function_name'\n\"time_nanosleep\"\n\"Reli\\\\Lib\\\\Loop\\\\LoopMiddleware\\\\NanoSleepMiddleware::invoke\"\n\"Reli\\\\Lib\\\\Loop\\\\LoopMiddleware\\\\KeyboardCancelMiddleware::invoke\"\n\"Reli\\\\Lib\\\\Loop\\\\LoopMiddleware\\\\RetryOnExceptionMiddleware::invoke\"\n\"Reli\\\\Lib\\\\Loop\\\\Loop::invoke\"\n\"Reli\\\\Command\\\\Inspector\\\\GetTraceCommand::execute\"\n\"Symfony\\\\Component\\\\Console\\\\Command\\\\Command::run\"\n\"Symfony\\\\Component\\\\Console\\\\Application::doRunCommand\"\n\"Symfony\\\\Component\\\\Console\\\\Application::doRun\"\n\"Symfony\\\\Component\\\\Console\\\\Application::run\"\n\"\"\n```\n\nYou can also see the contents of the local variables of a specific call frame.\n\n```bash\n$ cat 2183131.memory_dump.json | jq '.context.call_frames[]|objects|select(.function_name==\"time_nanosleep\")'\n{\n  \"#node_id\": 1,\n  \"#type\": \"CallFrameContext\",\n  \"function_name\": \"time_nanosleep\",\n  \"local_variables\": {\n    \"#node_id\": 2,\n    \"#type\": \"CallFrameVariableTableContext\",\n    \"$args_to_internal_function[0]\": {\n      \"#node_id\": 3,\n      \"#type\": \"ScalarValueContext\",\n      \"value\": 0\n    },\n    \"$args_to_internal_function[1]\": {\n      \"#node_id\": 4,\n      \"#type\": \"ScalarValueContext\",\n      \"value\": 9743095\n    }\n  }\n}\n```\n\nIf a context is referencing another location in the dump file, it can also be extracted with `jq`.\n\n```bash\n$ cat 2183131.memory_dump.json | jq '.context.call_frames[\"7\"].local_variables'\n{\n  \"#node_id\": 1433,\n  \"#type\": \"CallFrameVariableTableContext\",\n  \"command\": {\n    \"#reference_node_id\": 368\n  },\n  \"input\": {\n    \"#reference_node_id\": 1395\n  },\n  \"output\": {\n    \"#reference_node_id\": 54\n  },\n  \"helper\": {\n    \"#reference_node_id\": 591\n  },\n  \"commandSignals\": {\n    \"#reference_node_id\": 69\n  }\n}\n\n$ cat 2183131.memory_dump.json | jq '..|objects|select(.\"#node_id\"==368)|.' | head -n 20\n{\n  \"#node_id\": 368,\n  \"#type\": \"ObjectContext\",\n  \"#locations\": [\n    {\n      \"address\": 139988652434432,\n      \"size\": 472,\n      \"refcount\": 6,\n      \"type_info\": 3221409800,\n      \"class_name\": \"Reli\\\\Command\\\\Inspector\\\\GetTraceCommand\"\n    }\n  ],\n  \"object_handlers\": {\n    \"#reference_node_id\": 7\n  },\n  \"object_properties\": {\n    \"#node_id\": 369,\n    \"#type\": \"ObjectPropertiesContext\",\n    \"php_globals_finder\": {\n      \"#node_id\": 370,\n      \"#type\": \"ObjectContext\",\n      \"#locations\": [\n        {\n```\n\nYou can also extract all references to a specific object.\n\n```bash\n$ cat 2183131.memory_dump.json | jq 'path(..|objects|select(.\"#reference_node_id\"==368 or .\"#node_id\"==368))|join(\".\")'\n\"context.call_frames.1.this.chain.callable.closure.this_ptr\"\n\"context.call_frames.1.this.chain.callable.closure.this_ptr.application.commands.array_elements.inspector:trace.value\"\n\"context.call_frames.1.this.chain.callable.closure.this_ptr.application.runningCommand\"\n\"context.call_frames.5.this\"\n\"context.call_frames.6.this\"\n\"context.call_frames.7.local_variables.command\"\n\"context.call_frames.8.local_variables.command\"\n\"context.objects_store.285\"\n\n```\n\nThe refcount of the object recorded in the memory location is 6 in this example. Calling methods via `$obj-\u003ecall()` adds refcount by 1, but `$this-\u003ecall()` doesn't add refcount. References from objects_store don't add refcount too. So all 6 references are analyzed here.\n\nSee [./docs/memory-profiler.md](https://github.com/reliforp/reli-prof/blob/0.11.x/docs/memory-profiler.md) for more info.\n\n## Troubleshooting\n### I get an error message \"php module not found\" and can't get a trace!\nIf your PHP binary uses a non-standard binary name that does not end with `/php`, use the `--php-regex` option to specify the name of the executable (or shared object) that contains the PHP interpreter.\n\n### I don't think the trace is accurate.\nThe `-S` option will give you better results. Using this option stops the execution of the target process for a moment at every sampling, but the trace obtained will be more accurate. If you don't stop the VMs from running when profiling CPU-heavy programs such as benchmarking programs, you may misjudge the bottleneck, because you will miss more VM states that transition very quickly and are not detected well.\n\n### Trace retrieval from ZTS target does not work on Ubuntu 21.10 or later.\nTry to specify `--libpthread-regex=\"libc.so\"` as an option.\n\n### I can't get traces on Amazon Linux 2.\nFirst, try `cat /proc/\u003cpid\u003e/maps` to check the memory map of the target PHP process. If the first module does not indicate the location of the PHP binary and looks like an anonymous region, try to specify `--php-regex=\"^$\"` as an option.\n\n## Goals\nWe would like to achieve the following 5 goals through this project.\n\n- To be able to closely observe what is happening inside a running PHP script.\n- To be a framework for PHP programmers to create a freely customizable PHP profiler.\n- To be experimentation for the use of PHP outside of the web, where recent improvements of PHP like JIT and FFI have opened the door.\n- Another entry point for PHP programmers to learn about PHP's internal implementation.\n- To create a program that is fun to write for me.\n\n## LICENSE\n- MIT (mostly)\n- tools/flamegraph/flamegraph.pl is copied from https://github.com/brendangregg/FlameGraph and licenced under the CDDL 1.0. See tools/flamegraph/docs/cddl1.txt and the header of the script.\n- Some C headers defining internal structures are extracted from php-src. They are licensed under the Zend Engine License or the PHP License. See src/Lib/PhpInternals/Headers . So here are the words required by the Zend Engine License and the PHP License.\n```\nThis product includes the Zend Engine, freely available at\n     http://www.zend.com\n```\n\n```\nThis product includes PHP software, freely available from\n     \u003chttp://www.php.net/software/\u003e\n```\n\n## What does the name \"Reli\" mean?\nGiven its functionality, you might naturally think that the name stands for \"Reverse Elephpantineer's Lovable Infrastructure\". But unfortunately, it's not true.\n\n\"Reli\" means nothing, though you are free to think of this tool as something reliable, religious, relishable, or whatever other reli-s you like.\n\nInitially, the name of this tool was just \"php-profiler\".\nDue to a licensing problem ([#175](https://github.com/reliforp/reli-prof/issues/175)), this simple good name had to be changed.\n\nSo we applied a randomly chosen string manipulation function to the original name. `strrev('php-profiler')` results to `'reliforp-php'`, and it can be read as \"reli for p(php)\".\n\nThus, the name of this tool is \"Reli for PH*\" now. And you can also call it just \"Reli\".\n\n## See also\n- [adsr/phpspy](https://github.com/adsr/phpspy)\n    - Reli is heavily inspired by phpspy.\n","funding_links":[],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freliforp%2Freli-prof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freliforp%2Freli-prof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freliforp%2Freli-prof/lists"}