Рубрики
Разработка

Профилирование и отладка php-приложений с помощью xhprof & FirePHP.

Всем веб-разработчикам, особенно в высоконагруженных проектах, рано или поздно приходится сталкиваться с профилированием своих приложений. Конечно, все мы знаем xdebug, с помощью которого можно проводить отладку серверной части. Однако, в тяжелых RIA-приложениях значительно чаще приходится отлаживаться в связке фронтенда+бэкэнд, всякие ajax-запросы, скорость отработки конкретных скриптов и все такое прочее. И для этих задач есть довольно-таки не плохой набор инструментов. Это xhprof и firephp.

Краткая справка

xhprof — php-расширение от facebook. Это иерархический профайлер, который позволяет собирать такую статистику как время выполнения каждой функции, использование памяти, время ожидания, количество вызовов и многое другое. Это расширение доступно из репозитория PECL: http://pecl.php.net/package/xhprof. Почитать документацию можно тут: http://php.net/xhprof.

FirePHP — это расширение для firebug, которое в связке со своим маленьким php-классом, позволяет транслировать в консоль firebug’а данные от php, например всякие var_dump и прочую отладочную информацию. Главный плюс этого расширения в том, что вся трансляция отладочной информации происходит через заголовки и не замусоривает страницы и вообще никак не ломает логику работы приложения. Официальный сайт: http://firephp.org/.

Основная идея.

Общий алгоритм профилирования заключается в следующем:

  1. В начале страницы включаем профайлинг с помощью xhprof_enable()
  2. В конце страницы выключаем профайлинг с помощью xhprof_disable() и сохраняем собранные данные с помощью save_run()
  3. Далее с помощью php-класса firephp передаем ссылку на данные профайлинга на клиентскую часть
  4. В консоли firebug’а открываем нужную нам информацию
  5. Радуемся 🙂

Еще хочется сказать, что, конечно, ручное добавление этих функций в свои php-скрипты — это здорово. Но хочется, чтобы эта информация всегда была под рукой во время разработки, и при этом не попадала на боевые сервера. Мы решаем эту задачу следующим образом:

В наших проектах почти во всех скриптах в начале подключается рабочий файлик с загрузчиком классов, подключением функций и прочими нужными штуками. Поэтому включение профайлинга мы вынесли в этот файл. А для того, чтобы иметь возможность включать/выключать отладочный режим по своему желанию добавили проверку на конфигурационную константу, плюс обернули эти проверки в некие мета-тэги, которые автоматически удаляются при сборке проекта. Тоже самое относится и к выключению профайлинга и записывании информации в заголовки с помощью firephp — эти задачи решает одна функция, которая вызывается в конце каждого php-скрипта и так же обернута в мета-тэги. Выглядит это примерно так:

	// В конфиг-файле приложения прописаны вот такие константы

	/** Режим работы среды окружения * */
	define("APPLICATION_ENV", "dev"); // dev - отладка | pro - продакшин
	/** Путь до профайлера */
	define("XHPROF_ROOT", __DIR__ . '/ExtProcs/debug/xhprof-0.9.2');


	/***************************************************************************************
	 *	Далее в файлике, который подгружается в начале каждого скрипта запускаем профайлинг
	 *	DEV_START и DEV_END - это наши мета-тэги, все что между ними вырезается при сборке
	 ***************************************************************************************/

	//-- DEV_START
	//-- в режиме отладки подключаем debug библиотеки
	if (APPLICATION_ENV === 'dev') {
		// Подгружаем firephp
		require_once(__DIR__ . '/includes/ExtProcs/debug/firephp/FirePHP.class.php');
		//-- подгружаем профайлер
		require_once (XHPROF_ROOT . '/xhprof_lib/utils/xhprof_lib.php');
		require_once (XHPROF_ROOT . '/xhprof_lib/utils/xhprof_runs.php');
		// Инициализируем профайлинг с нужными флагами. Подробное описание флагов 
		// можно найти на http://php.net/manual/ru/xhprof.constants.php
		xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
	}
	//-- DEV_END


	// Ну и вот такая функция вызывается в конце каждого скрипта
	// Ее вызов так же обернут в DEV_START и DEV_END

	/**
	 *  Создаем ссылку на результат профайлинга и выводим это в консоль
	 */
	function dev_boot_Down() {
		if (APPLICATION_ENV === 'dev') {
			// Инициализируем экземпляр firephp
			$firephp = FirePHP::getInstance(true);
			// Выключаем профайлинг и сохраняем данные
			$xhprof_data = xhprof_disable();
			$xhprof_runs = new XHProfRuns_Default();
			$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_testing");
			// Формируем ссылку на данные профайлинга и записываем ее в консоль
			$link = "http://" . $_SERVER['HTTP_HOST'] . "/includes/ExtProcs/debug/xhprof-0.9.2/xhprof_html/index.php?run={$run_id}&source=xhprof_testing\n";
			$firephp->info($link, 'profiling data');
		}
	}

Не буду вдаваться в подробности установки данных расширений, ибо тут все просто. Скажу только про некоторые моменты настройки. В xhproof предусмотрена всего одна конфигурационная переменная — xhprof.output_dir, которая указывает на папку, куда будут сохраняться данные профайлинга. Поэтому убедитесь, что в указанную директорию у пользователя, из-под которого выполняются php-скрипты есть права на запись. Так что пропишите в свой php.ini что-то вроде этого:

[xhprof]
extension=xhprof.so
xhprof.output_dir="/var/tmp/xhprof"

Так же не плохо поставить что-то типа dot или Graphviz для рисования графов вызовов. У меня под MacOS X стоит Graphviz.

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

Скопировав и открыв ссылку на данные профайлинга можно увидеть нечто похожее на это. Количество столбцов с значениями зависит от флагов, указанных в xhprof_enable().

А если перейти по ссылке [View Full Callgraph], то увидим граф вызовов функций нашего скрипта.

Ну, на этом, пожалуй, и все. Единственное, что добавлю, что для питонистов есть такой проектик как FirePy, который как ни сложно догадаться, позволяет транслировать в firebug данные из питона. Если у кого-то есть свои подходы к отладке и профилированию веб-приложений — с радостью послушаю.