Skip to content

Commit

Permalink
Merge pull request #154 from sj-i/fix-readme
Browse files Browse the repository at this point in the history
Update readme
  • Loading branch information
sj-i authored Feb 6, 2022
2 parents a248bb7 + 2abed4b commit 1b82a3e
Showing 1 changed file with 86 additions and 19 deletions.
105 changes: 86 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
[![Packagist](https://img.shields.io/packagist/v/sj-i/php-profiler.svg)](https://packagist.org/packages/sj-i/php-profiler)
[![Packagist](https://img.shields.io/packagist/dt/sj-i/php-profiler.svg)](https://packagist.org/packages/sj-i/php-profiler)
[![Github Actions](https://github.com/sj-i/php-profiler/workflows/build/badge.svg)](https://github.com/sj-i/php-profiler/actions)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sj-i/php-profiler/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sj-i/php-profiler/?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/sj-i/php-profiler/badge.svg?branch=master)](https://coveralls.io/github/sj-i/php-profiler?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sj-i/php-profiler/badges/quality-score.png?b=0.4.x)](https://scrutinizer-ci.com/g/sj-i/php-profiler/?branch=0.4.x)
[![Coverage Status](https://coveralls.io/repos/github/sj-i/php-profiler/badge.svg?branch=0.4.x)](https://coveralls.io/github/sj-i/php-profiler?branch=0.4.x)
![Psalm coverage](https://shepherd.dev/github/sj-i/php-profiler/coverage.svg?)

php-profiler 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.
Expand All @@ -28,7 +28,27 @@ If customizability for PHP developers matters, you can use this software at the
Additionally, php-profiler 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.
php-profiler 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.

The php-profiler outputs more accurate line numbers, and additionally can show executing opcodes of the PHP-VM.
Other features of php-profiler that phpspy does not currently have include:

- Output more accurate line numbers
- Customize output format with PHP templates
- Get running opcodes of the PHP-VM
- Automatic retrieval of the version of stripped PHP binaries
- Output traces in speedscope format

There 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.

On the other hand, there are a few things that phpspy can do but php-profiler cannot yet.

- Redirecting output of child processes
- Forcing the address of EG
- Data retrieval from sapi_globals
- callgrind support
- Reading variables
- Run more faster with lower overhead.
- etc.

Much of what can be done with phpspy will be done with php-profiler in the future.

## Requirements
### Supported PHP versions
Expand Down Expand Up @@ -82,7 +102,7 @@ Options:
-S, --stop-process[=STOP-PROCESS] stop the target process while reading its trace (default: off)
--php-regex[=PHP-REGEX] regex to find the php binary loaded in the target process
--libpthread-regex[=LIBPTHREAD-REGEX] regex to find the libpthread.so loaded in the target process
--php-version[=PHP-VERSION] php version of the target (default: v80)
--php-version[=PHP-VERSION] php version of the target (default: auto)
--php-path[=PHP-PATH] path to the php binary (only needed in tracing chrooted ZTS target)
--libpthread-path[=LIBPTHREAD-PATH] path to the libpthread.so (only needed in tracing chrooted ZTS target)
-t, --template[=TEMPLATE] template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy)
Expand Down Expand Up @@ -112,7 +132,7 @@ Options:
-S, --stop-process[=STOP-PROCESS] stop the target process while reading its trace (default: off)
--php-regex[=PHP-REGEX] regex to find the php binary loaded in the target process
--libpthread-regex[=LIBPTHREAD-REGEX] regex to find the libpthread.so loaded in the target process
--php-version[=PHP-VERSION] php version of the target (default: v80)
--php-version[=PHP-VERSION] php version of the target (default: auto)
--php-path[=PHP-PATH] path to the php binary (only needed in tracing chrooted ZTS target)
--libpthread-path[=LIBPTHREAD-PATH] path to the libpthread.so (only needed in tracing chrooted ZTS target)
-t, --template[=TEMPLATE] template name (phpspy|phpspy_with_opcode|json_lines) (default: phpspy)
Expand Down Expand Up @@ -142,7 +162,7 @@ Options:
-S, --stop-process[=STOP-PROCESS] stop the target process while reading its trace (default: off)
--php-regex[=PHP-REGEX] regex to find the php binary loaded in the target process
--libpthread-regex[=LIBPTHREAD-REGEX] regex to find the libpthread.so loaded in the target process
--php-version[=PHP-VERSION] php version of the target (default: v80)
--php-version[=PHP-VERSION] php version of the target (default: auto)
--php-path[=PHP-PATH] path to the php binary (only needed in tracing chrooted ZTS target)
--libpthread-path[=LIBPTHREAD-PATH] path to the libpthread.so (only needed in tracing chrooted ZTS target)
-h, --help Display help for the given command. When no command is given display help for the list command
Expand Down Expand Up @@ -170,7 +190,7 @@ Options:
-p, --pid=PID process id
--php-regex[=PHP-REGEX] regex to find the php binary loaded in the target process
--libpthread-regex[=LIBPTHREAD-REGEX] regex to find the libpthread.so loaded in the target process
--php-version[=PHP-VERSION] php version of the target (default: v80)
--php-version[=PHP-VERSION] php version of the target (default: auto)
--php-path[=PHP-PATH] path to the php binary (only needed in tracing chrooted ZTS target)
--libpthread-path[=LIBPTHREAD-PATH] path to the libpthread.so (only needed in tracing chrooted ZTS target)
-h, --help Display help for the given command. When no command is given display help for the list command
Expand All @@ -184,32 +204,70 @@ Options:
## Examples
### Trace a script
```bash
php ./php-profiler inspector:trace -- php <script file>
$ ./php-profiler i:trace -- php -r "fgets(STDIN);"
0 fgets <internal>:-1
1 <main> <internal>:-1

0 fgets <internal>:-1
1 <main> <internal>:-1

0 fgets <internal>:-1
1 <main> <internal>:-1

<press q to exit>
...
```
### Attach to a running process
```bash
sudo php ./php-profiler inspector:trace -p <pid of the target process or thread>
$ sudo ~/.phpenv/versions/8.1snapshot/bin/php ./php-profiler i:tr -p 2182685
0 time_nanosleep <internal>:-1
1 PhpProfiler\Lib\Loop\LoopMiddleware\NanoSleepMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/NanoSleepMiddleware.php:33
2 PhpProfiler\Lib\Loop\LoopMiddleware\KeyboardCancelMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/KeyboardCancelMiddleware.php:39
3 PhpProfiler\Lib\Loop\LoopMiddleware\RetryOnExceptionMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/RetryOnExceptionMiddleware.php:37
4 PhpProfiler\Lib\Loop\Loop::invoke /home/sji/work/php-profiler/src/Lib/Loop/Loop.php:26
5 PhpProfiler\Command\Inspector\GetTraceCommand::execute /home/sji/work/php-profiler/src/Command/Inspector/GetTraceCommand.php:133
6 Symfony\Component\Console\Command\Command::run /home/sji/work/php-profiler/vendor/symfony/console/Command/Command.php:291
7 Symfony\Component\Console\Application::doRunCommand /home/sji/work/php-profiler/vendor/symfony/console/Application.php:979
8 Symfony\Component\Console\Application::doRun /home/sji/work/php-profiler/vendor/symfony/console/Application.php:299
9 Symfony\Component\Console\Application::run /home/sji/work/php-profiler/vendor/symfony/console/Application.php:171
10 <main> /home/sji/work/php-profiler/php-profiler:45

0 time_nanosleep <internal>:-1
1 PhpProfiler\Lib\Loop\LoopMiddleware\NanoSleepMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/NanoSleepMiddleware.php:33
2 PhpProfiler\Lib\Loop\LoopMiddleware\KeyboardCancelMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/KeyboardCancelMiddleware.php:39
3 PhpProfiler\Lib\Loop\LoopMiddleware\RetryOnExceptionMiddleware::invoke /home/sji/work/php-profiler/src/Lib/Loop/LoopMiddleware/RetryOnExceptionMiddleware.php:37
4 PhpProfiler\Lib\Loop\Loop::invoke /home/sji/work/php-profiler/src/Lib/Loop/Loop.php:26
5 PhpProfiler\Command\Inspector\GetTraceCommand::execute /home/sji/work/php-profiler/src/Command/Inspector/GetTraceCommand.php:133
6 Symfony\Component\Console\Command\Command::run /home/sji/work/php-profiler/vendor/symfony/console/Command/Command.php:291
7 Symfony\Component\Console\Application::doRunCommand /home/sji/work/php-profiler/vendor/symfony/console/Application.php:979
8 Symfony\Component\Console\Application::doRun /home/sji/work/php-profiler/vendor/symfony/console/Application.php:299
9 Symfony\Component\Console\Application::run /home/sji/work/php-profiler/vendor/symfony/console/Application.php:171
10 <main> /home/sji/work/php-profiler/php-profiler:45

<press q to exit>
...
```
The executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)
### Daemon mode
```bash
sudo php ./php-profiler inspector:daemon -P <regex to find target processes>
$ sudo php ./php-profiler i:daemon -P "^/usr/sbin/httpd"
```
The executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)
### Get the address of EG
```bash
sudo php ./php-profiler inspector:eg -p <pid of the target process or thread>
$ sudo php ./php-profiler i:eg -p 2183131
0x555ae7825d80
```
The executing process must have the CAP_SYS_PTRACE capability. (Usually run as root is enough.)
### Show currently executing opcodes at traces
If 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`.
```bash
sudo php ./php-profiler inspector:trace --template=phpspy_with_opcode -p <pid of the target process or thread>
$ sudo php ./php-profiler i:trace --template=phpspy_with_opcode -p <pid of the target process or thread>
```
The output would be like the following.
Expand All @@ -235,28 +293,37 @@ If JIT is enabled at the target process, this information may be slightly inaccu
### Use in a docker container and target a process on host
```bash
docker build -t php-profiler .
docker run -it --security-opt="apparmor=unconfined" --cap-add=SYS_PTRACE --pid=host php-profiler:latest vendor/bin/php-profiler inspector:trace -p <pid of the target process or thread>
$ docker build -t php-profiler .
$ docker run -it --security-opt="apparmor=unconfined" --cap-add=SYS_PTRACE --pid=host php-profiler:latest vendor/bin/php-profiler i:trace -p <pid of the target process or thread>
```
### Generate flamegraphs from traces
```bash
sudo php ./php-profiler inspector:trace -p <pid of the target process or thread> >traces
./php-profiler converter:flamegraph <traces >flame.svg
$ sudo php ./php-profiler i:trace -p <pid of the target process or thread> >traces
./php-profiler c:flamegraph <traces >flame.svg
```
### Generate the [speedscope](https://github.com/jlfwong/speedscope) format from phpspy compatible traces
```bash
sudo php ./php-profiler inspector:trace -p <pid of the target process or thread> >traces
./php-profiler converter:speedscope <traces >profile.speedscope.json
speedscope profile.speedscope.json
$ sudo php ./php-profiler i:trace -p <pid of the target process or thread> >traces
$ ./php-profiler c:speedscope <traces >profile.speedscope.json
$ speedscope profile.speedscope.json
```
See [#101](https://github.com/sj-i/php-profiler/pull/101).
# Goals
I would like to achieve the following 5 goals through this project.
- To be able to closely observe what is happening inside a running PHP script.
- To be a framework for PHP programmers to create a freely customizable PHP profiler.
- 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.
- Another entry point for PHP programmers to learn about PHP's internal implementation.
- To create programs that are fun to write for me.
# LICENSE
- MIT (mostly)
- 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.
- Some C headers defining internal structures are extracted from php-src. They are licensed under the zend engine license. See src/Lib/PhpInternals/Headers . So here are the words required by the zend engine license.
```
This product includes the Zend Engine, freely available at
Expand Down

0 comments on commit 1b82a3e

Please sign in to comment.