Skip to content

Latest commit

 

History

99 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

php-perfidious

ci Codecov Coveralls License: AGPL v3+ Language Tag stability-experimental

This extension provides access to the performance monitoring counters exposed by the Linux perf_events kernel API.

Requirements

As we are calling Linux kernel APIs, this extension will only work on Linux.

  • PHP 8.1 - 8.5
  • libcap
  • libpfm4

Installation

PIE

Install the build toolchain and required system libraries first. On Ubuntu and Debian:

apt install build-essential git libcap-dev libpfm4-dev php-dev

After installing PIE, install the current development version from a source checkout:

git clone https://github.com/jbboehr/php-perfidious.git
cd php-perfidious
pie install

Source

You will need a few packages, including libcap and libpfm4. On Ubuntu and Debian, this should be:

apt install build-essential git libcap-dev libpfm4-dev php-dev

Now clone the repo and compile the extension:

git clone https://github.com/jbboehr/php-perfidious.git
cd php-perfidious
phpize
./configure
make
make test
sudo make install

Add the extension to your php.ini:

echo extension=perfidious.so | tee -a /path/to/your/php.ini

Finally, restart the web server.

Usage

See also the examples directory and the stub.

For example, you can programmatically open and access the counters.

$handle = Perfidious\open(["perf::PERF_COUNT_SW_CPU_CLOCK:u"]);
$handle->enable();

for ($i = 0; $i < 3; $i++) {
    var_dump($handle->readArray());
    sleep(1);
}
array(1) {
  ["perf::PERF_COUNT_SW_CPU_CLOCK:u"]=>
  int(3190)
}
array(1) {
  ["perf::PERF_COUNT_SW_CPU_CLOCK:u"]=>
  int(51270)
}
array(1) {
  ["perf::PERF_COUNT_SW_CPU_CLOCK:u"]=>
  int(86560)
}

Or you can configure a global or per-request handle:

// with the following INI settings:
// perfidious.request.enable=1
// perfidious.request.metrics=perf::PERF_COUNT_SW_CPU_CLOCK:u,perf::PERF_COUNT_SW_PAGE_FAULTS:u,perf::PERF_COUNT_SW_CONTEXT_SWITCHES:u
var_dump(Perfidious\request_handle()?->read());
object(Perfidious\ReadResult)#%d (%d) {
  ["timeEnabled"]=>
  int(260840)
  ["timeRunning"]=>
  int(260840)
  ["values"]=>
  array(3) {
    ["perf::PERF_COUNT_SW_CPU_CLOCK:u"]=>
    int(142740)
    ["perf::PERF_COUNT_SW_PAGE_FAULTS:u"]=>
    int(64)
    ["perf::PERF_COUNT_SW_CONTEXT_SWITCHES:u"]=>
    int(0)
  }
}

Events

We use the libpfm4 event name encoding to open events. To see a list of all events, execute examples/all-events.php with the extension loaded or see the libpfm4 documentation. Some notable generic perf events are:

  • perf::PERF_COUNT_HW_CPU_CYCLES:u
  • perf::PERF_COUNT_HW_INSTRUCTIONS:u
  • perf::PERF_COUNT_SW_PAGE_FAULTS:u
  • perf::PERF_COUNT_SW_CONTEXT_SWITCHES:u

Configuration

Name Default Changeable Description
perfidious.global.enable 0 PHP_INI_SYSTEM Set to 1 to enable the global handle. This handle is kept open between requests. You can read from this handle via e.g. var_dump(Perfidious\global_handle()?->read());.
perfidious.global.metrics perf::PERF_COUNT_HW_CPU_CYCLES:u, perf::PERF_COUNT_HW_INSTRUCTIONS:u PHP_INI_SYSTEM The metrics to monitor with the global handle.
perfidious.overflow_mode 0 PHP_INI_SYSTEM Sets the overflow behavior when casting counters from uint64_t to zend_long. See the constants Perfidious\OVERFLOW_* for other values. Note that when set to Perfidious\OVERFLOW_WARN, read and readArray may return NULL, despite their type signatures indicating otherwise.
perfidious.request.enable 0 PHP_INI_SYSTEM Set to 1 to enable the per-request handle. This handle is kept open between requests, but reset before and after. You can read from this handle via e.g. var_dump(Perfidious\request_handle()?->read());
perfidious.request.metrics perf::PERF_COUNT_HW_CPU_CYCLES:u, perf::PERF_COUNT_HW_INSTRUCTIONS:u PHP_INI_SYSTEM The metrics to monitor with the request handle.

global.enable and request.enable only really do something useful under a persistent-worker SAPI like php-fpm: the global handle is opened once and never reset, so it accumulates across every request the worker ever handles; the request handle is reset at the start and end of each request, so it reflects just that one request. Under the CLI SAPI, every invocation is its own process with exactly one "request", so the two behave identically there.

Troubleshooting

Q: I get an error pid greater than zero and CAP_PERFMON not set

A: You need to grant CAP_PERFMON when monitoring a process other than the current process, for example:

sudo capsh --caps="cap_perfmon,cap_setgid,cap_setuid,cap_setpcap+eip" \
  --user=`whoami` \
  --addamb='cap_perfmon' \
  -- -c 'php -d extension=modules/perfidious.so examples/watch.php --interval 2 --pid 1'

Q: I get an error like perf_event_open() failed for perf::PERF_COUNT_HW_INSTRUCTIONS: Permission denied

A: You may need to adjust kernel.perf_event_paranoid, for example:

sudo sysctl -w kernel.perf_event_paranoid=1

Q: I get an error like perf_event_open() failed for perf::PERF_COUNT_SW_DUMMY: Operation not permitted when running inside of docker.

A: You may need to run your docker container with CAP_PERFMON:

docker run --rm -ti --cap-add CAP_PERFMON

If it still doesn't work, and you're running an older release of docker, see this issue.

Q: I get an error like perf_event_open() failed for perf::PERF_COUNT_HW_INSTRUCTIONS: No such file or directory

A: If you are using GitHub Actions, or on some other kind of virtualization, perf events may not be supported. For GitHub Actions, see this issue

Q: I'm able to read data, but the counters are all zero.

A: This may happen for a few reasons:

  1. If you are monitoring several hardware events (e.g. perf::PERF_COUNT_HW_INSTRUCTIONS), the PMU may not have enough capacity to handle all of them. The limit appears to be per physical CPU core. In testing on my Zen4 CPU, it appeared that the maximum hardware counters was around 4-6. If you have any more information on how to tell how many "slots" are available, please let me know.

  2. If, for some reason, the kernel is unable to schedule all events in the group, it will not schedule any of them. Try removing events until you get some non-zero data, or opening separate handles. Note also that some events may be low-frequency.

Q: Building from a git checkout fails with a compiler warning treated as an error (-Werror).

A: Building inside the project's own nix develop shell always treats warnings as errors by design, so we catch them during development. A plain git clone + phpize && ./configure build, and PECL/release-tarball installs, default to non-fatal warnings instead - if you hit this outside the nix devShell, please file an issue, since it likely means a warning that's fine on our compilers isn't on yours. You can also pass --enable-compile-warnings=yes explicitly to ./configure to disable it yourself.

References

License

php-perfidious is licensed under the GNU Affero General Public License version 3 with the Romic Exception:

AGPL-3.0-only WITH romic-exception

The Romic Exception permits php-perfidious to be linked or combined with other code without subjecting that other code to the AGPL merely because of the linking or combination. Modifications to the covered project remain subject to the Project License, including its source-availability requirements for modified versions made available over a computer network.

See LICENSE.md and docs/LICENSE_EXCEPTION.md for the complete terms.

Contributions are accepted under the terms in CONTRIBUTING.md. Unless a contributor elects the CLA route, each contribution is offered under AGPL-3.0-only WITH romic-exception OR Apache-2.0, at each recipient's option, while the public project incorporates it under the Project License. The Apache-2.0 alternative applies only to the contributor-authored portions and does not make the project as a whole available under Apache-2.0.

A contributor may instead elect the CLA, keeping the contribution publicly under the Project License while granting the Project Steward the additional rights specified there.

Alternative commercial licenses may be available from the Project Steward. Contact John Boehr at jbboehr@gmail.com.

About

Provides access to the performance monitoring counters exposed by the Linux perf_events kernel API.

Topics

Resources

Code of conduct

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages