FreeBSD. XHProf for PHP Performance Analysis
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:

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):

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:
- 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).
- 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”.
Links
The posts where I learned about XHProf:
Recommended reading:
Comments are closed. Read the original discussion in Russian →
