XHProf: профилирование PHP

Date April 27th, 2012 Author Vitaly Agapov

Черную кошку в темной комнате искать бесполезно, особенно если ее там нет, а черного бультерьера – небезопасно, особенно если он там есть.

Народная мудрость

Если приходится иметь дело с web-приложениями, написанными на php, то надо быть готовым разбираться с тем, почему приложение работает медленно, откуда растут ноги у тормозов, почему растёт использование системных ресурсов при увеличении нагрузки и так далее. Тут очень пригодятся инструменты, позволяющие собрать кое-какую статистику о различных аспектах работы приложения.

Особую прелесть приобретает этот процесс, если ты не являешься автором приложения, и оно представляет для тебя просто большой закрытый чёрный ящик. Тогда профилирование приложения без тщательного исследования кода позволит проделать в чёрном ящике дырочку для нашего любопытного взгляда.

Сегодня мы посмотрим на opensource-решение от компании Facebook – XHProf. Исходники были открыты в 2009-м году, правда с тех пор проект практически не развивается.

Профайлер позволяет произвести декомпозицию работы приложения до вызовов отдельных методов со сбором статистики по времени их отработки и использованию системных ресурсов. То есть если проблема в каком-то участке кода присутствует, то мы её найдем. Или хотя бы локализуем с определённой точностью.

То, что я напишу в этой статейке, будет иметь и общую информацию, но в большой степени всё-таки будет касаться частного случая – профилирования приложения, работающего в php-fpm, стоящего за nginx'ом. И всё это под Ubuntu 10.04.

Установка XHProf

Если речь идёт об Ubuntu (а моя речь именно о ней), то самым простым способом поставить XHProf будет PPA от Brian Mercer. Он же не так давно помог нам поставить и сам php-fpm на Ubuntu 10.04.

	sudo add-apt-repository ppa:brianmercer/php5-xhprof
	sudo apt-get update
	sudo apt-get install php5-xhprof
	sudo /etc/init.d/php5-fpm restart
	php -m | grep xhprof

Последняя команда покажет нам наличие модуля xhprof в списке модулей PHP. Если он есть, то всё ок.

Если речь идёт не про Ubuntu (впрочем, для неё это тоже справедливо), то можно поставить XHProf из PECL или собрать вручную.

Установка из PECL:

pecl install xhprof-0.9.2

Установка вручную:

	wget http://pecl.php.net/get/xhprof-0.9.2.tgz
	tar -xzvf xhprof-0.9.2.tgz
	cd xhprof-0.9.2
	phpize
	./configure --with-php-config=/usr/bin/php-config
	make && make test && make install

И при ручной уствноке надо в php.ini добавить расширение:

	extension=xhprof.so
	xhprof.output_dir=/tmp

Установка graphviz

Для создания наглядных симпатичных диаграмм, визуализирующих цепочки вызовов функций в процессе работы приложения, на понадобится пакет graphviz. В Ubuntu он есть в репозиториях:

sudo apt-get install graphviz

Настройка XHProf

Для профилирования надо включить в приложение библиотеки из поставки xhprof и вызвать функцию xhprof_enable. Так как профилировать обычно нужно не все запросы, а выборочные, то можно сделать условие по наличию в заголовках запроса cookie определённого вида. В разных примерах в других публикациях обычно используется cookie xhprof. Самый простой способ добавить нужный код – это воспользоваться директивами auto_prepend_file и auto_append_file в php.ini.

Создадим файл /usr/share/php5-xhprof/header.php:

<?php
if(isset($_COOKIE['xhprof'])){
    if (extension_loaded('xhprof')) {
        $utils_path = "/usr/share/php5-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);
    }
}
?>

Создадим файл footer.php:

<?php
if(isset($_COOKIE['xhprof'])){
    if (extension_loaded('xhprof')) {
        $profiler_namespace = 'someapp';
        $xhprof_data = xhprof_disable();
        $xhprof_runs = new XHProfRuns_Default();
        $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);
        $profiler_url = sprintf('http://mysite:81/index.php?run=%s&source=%s', $run_id, $profiler_namespace);
        echo '<a href="'.$profiler_url.'">XHProf</a>';
    }
}
?>

Этот кусок кода, который будет отрабатывать в конце обработки запроса, сохранит собранные данные и добавит в конце ссылку на страницу профайлера. URL, само собой, надо поправить на тот, который приведёт нас на нужный виртуал хост. Об этом чуть позже.

Чтобы эти сниппеты срабатывали в нужные нам моменты, надо добавить в php.ini (в моём случае это /etc/php5/fpm/php.ini) строки:

auto_prepend_file = /usr/share/php5-xhprof/header.php
auto_append_file = /usr/share/php5-xhprof/footer.php

Само собой, можно вставлять соответствующие куски прямо в код страницы, чтобы профайлить не всё приложение целиком, а только какую-то его часть.

Настройка nginx

Для просмотра результатов профилирования в читабельном (и смотрибельном) виде, нужно настроить nginx. Я для этих целей сделал отдельный virtual-host, и это, само собой, не единственное возможное решение.

server {
        listen   81;
        server_name  mysite;
        root   /usr/share/php5-xhprof/xhprof_html;
        access_log  /var/log/nginx/xhprof.access.log;
        location ~ \..*/.*\.php$ {
                return 403;
        }
        location / {
                index index.php;
        }
        location ~ \.php$ {
                fastcgi_pass   unix:/tmp/phpfpm.sock;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include        /etc/nginx/fastcgi_params;
        }
}

Проверяем

Для установки cookie xhprof можно воспользовать плагином для Firefox – Cookies  Manager +. С помощью этого инструмента создаём новую куку, у которой в имени указываем xhprof, в содержимом – что угодно, а в узле – наш FQDN, с которым мы ходим на сайт.

После этого заходим на сайт и видим внизу страницы ссылку на профайл.

Если последнее утверждение истинно, то:

Вуаля!

Автоматизация

Бывают ситуации, когда приложение работает нормально, а проблемы начинаются в определеное время или вообще бессистемно. И хочется нам иметь результаты профилирования в исторической, так сказать, перспективе. Чтобы можно было посмотреть, как работало приложение, скажем, вчера в десять вечера.

Для этого можно пошаманить с header.php и footer.php, придумав хитрые условия. Например, рандомный запуск профайлера для каждого сотого запроса.

header.php:

 if (rand(1, 100) == 1) {
   $xhprof_on = true;
   ...
 }

footer.php:

 if ($xhprof_on) {
   $xhprof_data = xhprof_disable();
   ...
 }

Но мы придумали более изящное (а может быть – более костыльное и кривое, кому как покажется) решение.

Для этого на любом сервере (хоть бы и на том же самом) с помощью cron с любой периодичностью запускаем команду:

/usr/bin/wget –no-cookies –header "Cookie: xhprof=1" 'http://mysite/' -o /dev/null -O /mnt/tmpfs/$(/bin/date \+\%Y\%m\%d_\%H\%M\%S)

Тогда на каждый из этих запросов можно будет посмотреть профайл. Ссылку можно либо взять из самой скаченной и сложенной в tmpfs страницы, либо посмотрев файлы с профилями, складывающиеся в xhprof.output_dir. В нашем случае это /tmp. Там при профилировании создаются текстовые файлы вида:

4f9a53e1b44f9.someapp

Хэш из названия файла можно подставить в url вида:

http://mysite:81/callgraph.php?run=4f9a53e1b44f9&source=someapp

XHProf и Drupal

Drupal предоставляет несколько инструментов для взаимодействия с XHProf. Первый – это плагин XHProf (http://drupal.org/project/XHProf), который позволяет отказаться от нативного веб-интерфейса XHProf, от дополнительных виртуальных хостов. И ещё он реализует разные приятные плюшки.

Но мне больше нравится более стандартный плагин Devel, который в своих настройках (admin/config/development/devel) позволяет включить XHProf, если оный модуль для PHP установлен в системе. В настройках также надо указать путь к библиотекам XHProf (/usr/share/php5-xhprof/) и относительный URL, по которому будут доступны результаты профилирования.

Но надо быть готовым к тому, что результат будет слегка зубодробительным.

Ссылки:

PECL:
http://pecl.php.net/package/xhprof

Хорошая статья про XHProf на FreeBSD:
http://adw0rd.ru/2010/freebsd-xhprof-php/

Статья на Хабре:
http://habrahabr.ru/post/78210/

Tags: , ,
Category: Drupal, Nginx, PHP, Web-dev | No comments »

Comments

Leave a comment

 Comment Form