← All posts

FreeBSD. XHProf for PHP Performance Analysis

XHProf profiler logo

I decided to try Facebook’s XHProf utility for profiling web application performance. I liked it so much that I decided to write a short post about it.

The utility analyzes how a website runs and displays information about the use of each function along the application’s execution path (for example, when loading a particular page on the site), namely: the number of calls to a particular function, the time spent, and the amount of memory and CPU resources consumed.

Profiling is the collection of program execution characteristics, such as the execution time of individual sections (usually subroutines), the number of correctly predicted conditional branches, the number of cache misses, etc. The tool used to analyze execution is called a profiler. Profiling is usually performed in conjunction with program optimization. Profiling on Wikipedia

The data is displayed as a table:

PHP profiling results table in XHProf

It can also draw a graph using GraphViz, but for that to work, GraphViz must first be built on the system (more on that below):

PHP application call graph in XHProf

XHProf is a PECL extension for PHP. It is written in C, which makes it fast enough to use on a production server to analyze real-world data.

Installation

I built the extension from source. To do this, go to the extension’s page and download the version you need; in my case, it was xhprof-0.9.2:


cd /tmp
fetch http://pecl.php.net/get/xhprof-0.9.2.tgz
tar -xzf xhprof-0.9.2.tgz
cd xhprof-0.9.2/extension/
phpize
./configure --with-php-config=/usr/local/bin/php-config
make && make install

Add the extension to php.ini or php/extensions.ini:


extension=xhprof.so

You can install the XHProf extension from ports (/usr/ports/devel/pecl-xhprof/). In May, when I had almost finished writing this article, it wasn’t there yet.

In php.ini, specify the directory that will serve as storage for your data:


[xhprof]                                                   
xhprof.output_dir=/usr/local/xhprof/out

And, of course, you need to create it too:


mkdir -p /usr/local/xhprof/out

XHProf UI

Now let’s set up the XHProf web interface. To do that, copy the source files into our directory:


mkdir /usr/local/xhprof/
cp -R /tmp/xhprof-0.9.2/ /usr/local/xhprof/

And add a new host in nginx:


server {
    listen 80;
    server_name xhprof.test;
    charset utf8;
    root /usr/local/xhprof/xhprof_html;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass php-fpm;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Restart nginx:


nginx -s reload

And map the domain in the local hosts file:


123.45.67.89 xhprof.test

Here, “123.45.67.89” is the IP address of the machine running XHProf, that is, the machine where we just installed it.

Building Graphviz

To generate visualizations, let’s build Graphviz (I strongly recommend doing this; it makes things much clearer).


cd /usr/ports/graphics/graphviz
make install clean

Building Ctype

You may also need Ctype


cd /usr/ports/textproc/php5-ctype
make install clean

Configuration

Now let’s configure XHProf so that it only profiles when we have a special cookie set. To do this, create two files, “/usr/local/xhprof/header.php” and “/usr/local/xhprof/footer.php”, with the following contents:

  1. Edit “/usr/local/xhprof/header.php”:

<?php
if(isset($_COOKIE['xhprof'])){
    if (extension_loaded('xhprof')) {
        $utils_path = "/usr/local/xhprof/xhprof_lib/utils/";
        include_once $utils_path.'xhprof_lib.php';
        include_once $utils_path.'xhprof_runs.php';
        xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
    }
}

It only runs if the “xhprof” cookie is set, includes the necessary libraries, and sets the required flags (more on that a little later).

  1. Edit “/usr/local/xhprof/footer.php”:

<?php
if(isset($_COOKIE['xhprof'])){
    if (extension_loaded('xhprof')) {
        $profiler_namespace = 'myapp'; // namespace for your application
        $xhprof_data = xhprof_disable();
        $xhprof_runs = new XHProfRuns_Default();
        $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);

        // url to the XHProf UI libraries (change the host name and path)
        $profiler_url = sprintf('http://xhprof.test/index.php?run=%s&source=%s', $run_id, $profiler_namespace);
        echo '<a href="'.$profiler_url.'">Profiler output</a>';
    }
}

It only runs if the “xhprof” cookie is set, sets the namespace, and saves the collected metrics. It then displays a link to the XHProf web interface for further analysis.

Now add two lines to the end of php.ini so that these two scripts run at the beginning and end, respectively:


auto_prepend_file = /usr/local/xhprof/header.php
auto_append_file = /usr/local/xhprof/footer.php

More detailed documentation for XHProf: http://mirror.facebook.net/facebook/xhprof/doc.html

A Problem with dot

The dot utility is part of that same Graphviz, and sometimes you can run into a problem like this:

Error: either we can not find profile data for run_id 4ced888563969 or the threshold 0.01 is too small or you do not have ‘dot’ image generation utility installed.

First, open “/usr/local/xhprof/xhprof_lib/utils/callgraph_utils.php” and find the “xhprof_generate_image_by_dot” function (around line 96). Edit the $descriptorspec array, specifying the path to the log file:


  $descriptorspec = array(
       // stdin is a pipe that the child will read from
       0 => array("pipe", "r"),
       // stdout is a pipe that the child will write to
       1 => array("pipe", "w"),
       // stderr is a file to write to
       //2 => array("file", "/dev/null", "a")
       // Путь до файла лога
       2 => array("file", "/var/log/xhprof.log", "a+")
       );

Now run it and check the log. If you see:

dot: not found

Then simply specify the full path to dot:


$cmd = " /usr/local/bin/dot -T".$type;

This is around line 107 of the same file, “/usr/local/xhprof/xhprof_lib/utils/callgraph_utils.php”.

The posts where I learned about XHProf:

Recommended reading:

Comments are closed. Read the original discussion in Russian →