Skip to content
William Desportes edited this page Apr 13, 2022 · 9 revisions

How to use xhprof to send samples to the phpMyAdmin team

Install the xhprof extension using pecl

pecl update-channels
pecl install xhprof

Install the xhprof extension using apt

(You may need to add the DEB sury repo: https://gist.github.com/janus57/37b079a383dd0507149bd94fd35053d1)

apt install php-xhprof
# More specific
apt install php8.0-xhprof

Detect where ini files are loaded for the CLI

Run php --ini and it will say Scan for additional .ini files in

Example:

php --ini
Configuration File (php.ini) Path: /etc/php/7.4/cli
Loaded Configuration File:         /etc/php/7.4/cli/php.ini
Scan for additional .ini files in: /etc/php/7.4/cli/conf.d

Activate the extension (for the CLI)

Run echo 'extension=xhprof.so' > /etc/php/7.4/cli/conf.d/docker-php-ext-xhprof.ini to activate the extension. Note: /etc/php/7.4/cli/conf.d is a path given by php --ini

Activate the extension (for the php-pfm)

Run echo 'extension=xhprof.so' > /etc/php/7.4/fpm/conf.d/docker-php-ext-xhprof.ini to activate the extension. Note: /etc/php/7.4/fpm/conf.d is a path given by phpinfo(); on a web page in the "Scan this dir for additional .ini files" line

Restart your web-server

No phpMyAdmin is not an app that "runs". That said you need to restart your php-fpm worker pool or your apache2 webserver. It will read the new extension file config and load it.

Examples:

  • service apache2 restart
  • docker restart phpfpm74 (where phpfpm74 is the container name)
  • service php7.2-fpm restart
  • /etc/init.d/php7.2-fpm restart

Patch your index.php file

Add this to the start of the file after declare(strict_types=1);

class XhProfProfiling
{
    /** @var string */
    private $requestUuid;

    public function __construct()
    {
        $this->requestUuid = uniqid() . '.' . str_replace('+', '-', date('c'));
        if (! extension_loaded('xhprof')) {
            $this->log('The xhprof extension is not enabled');

            return;
        }

        xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
        $this->log('Loaded xhprof');
    }

    public function log(string $msg): void
    {
        // Enable this to print logs to error logs
        // error_log('[' . $this->requestUuid . '] ' . $msg);
    }

    public function __destruct()
    {
        global $route;

        if (! extension_loaded('xhprof')) {
            return;
        }

        $buildXhprofs = __DIR__ . '/build/profiles/';
        if (! is_dir($buildXhprofs)) {
            mkdir($buildXhprofs, 0777, true);
        }

        $xhprofData = xhprof_disable();
        $xhprofData = serialize($xhprofData);

        $this->requestUuid .= str_replace('/', '--', $route);

        $profilePath = $buildXhprofs . $this->requestUuid . '_capture.xhprof';
        $wrote = file_put_contents($profilePath, $xhprofData);
        $this->log('Wrote xhprof profile to "' . $profilePath . '": ' . ($wrote === false ? 'no' : 'yes'));

        $metaPath = $buildXhprofs . $this->requestUuid . '_metadata.json';
        $metadata = [
            'route' => $route,
            'method' => $_SERVER['REQUEST_METHOD'] ?? 'php ini bug',
            'params' => [
                'GET' => $_GET,
                'POST' => $_POST,
                'COOKIES' => $_COOKIE,
            ],
        ];

        // Replace tokens with "*"
        $ssoValue = $GLOBALS['cfg']['Server']['SignonSession'] ?? 'n/a';
        foreach (['phpMyAdmin', 'phpMyAdmin_https', $ssoValue] as $key) {
            if (! isset($metadata['params']['COOKIES'][$key])) {
                continue;
            }

            $metadata['params']['COOKIES'][$key] = str_repeat('*', strlen($metadata['params']['COOKIES'][$key]));
        }
        foreach (array_keys($metadata['params']) as $key) {
            if (! isset($metadata['params'][$key]['token'])) {
                continue;
            }

            $metadata['params'][$key]['token'] = str_repeat('*', strlen($metadata['params'][$key]['token']));
        }

        $metadata = (string) json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

        $wrote = file_put_contents($metaPath, $metadata);
        $this->log('Wrote metadata to "' . $metaPath . '": ' . ($wrote === false ? 'no' : 'yes'));
    }
}

$keepMeInMemory = new XhProfProfiling();

Send us the profiles and metadata files

Please check that the metadata files have nothing private

They normally contain no private data (only the queries could be in the files)

They are in {phpMyAdmin}/build/profiles/

Clone this wiki locally