Профилирование и отладка 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');
         // Инициализируем профайлинг с нужными флагами. Подробное описание флагов
         // можно найти на 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');
         }
       }

    * This source code was highlighted with Source Code Highlighter.

    Не буду вдаваться в подробности установки данных расширений, ибо тут все просто. Скажу только про некоторые моменты настройки. В 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], то увидим граф вызовов функций нашего скрипта.


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

    P.S. Добавлю только, что во всем этом безобразии принимал активное участие товарищ kryapolov

    UPD 1: Для Питонистов есть такой проектик как FirePy, который как ни сложно догадаться, позволяет транслировать в firebug данные из питона.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 22
    • 0
      Очень давно вот здесь писал о профилировнии кода с XHProf на боевых серверах. В функции dev_boot_Down(если по вашему) я выводил на страницу абсолютно позиционированный див со ссылкой на профайлинг — было очень удобно.
      • 0
        упс :) да. Ну тут получилось более наглядно. А по поводу дива с ссылкой — оно конечно удобнее, но, например, у нас в приложении большинство запросов — это ajax-запросы. А переписывать еще и js-логику не хотелось. Но там где можно — да, такой див будет удобен.
      • +1
        Почему в блог «Веб-разработка», это же касается только PHP?
        • 0
          Нууу не совсем согласен с вами. Да, профилирование php, но вывод всего этого дела в браузере, а точнее в консоль firefox/firebug. Так что все-таки тут не только php.
          • 0
            Это понятно, но при отсутствии PHP это всё теряет смысл. В веб-разработке слишком много всего, где PHP вообще не участвует. Может перенесете? Есть хабы PHP, Клиентская оптимизация.
            • 0
              Клиентская тут точно не при чем. Но да, php походит лучше всего;)
      • +2
        Сделайте пожалуйста граф кликабельным, в большем разрешении.
        • 0
          не знаю конечно зачем, ну ладно, сделал.
        • 0
          + имеется скомпилированная версия под Win, а вот насколько рабочая, не могу сказать, познается методом проб и ошибок ;)
          • 0
            Спасибо за ссылку.

            Поставил себе (Win7x64, apache + php5.3 VC9 TS) — работает без проблем, графы создает.
          • 0
            Для Chrome что-то подобное есть?
            • 0
              есть расширение Webug в котором реализован соответствующий функционал, на домашней страничке все подробно описано.
              • +1
              • +1
                Меня заинтриговало слово «отладка», думал, что есть что-то интереснее xdebug в этом направлении.
                Может я чего-то не понимаю, но где тут отладка?)
              • 0
                //-- 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');
                // Инициализируем профайлинг с нужными флагами. Подробное описание флагов
                // можно найти на php.net/manual/ru/xhprof.constants.php
                xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
                }
                //-- DEV_END


                я делал так:
                на продакшене xhprof не устанавливается по определению.

                if (extension_loaded('xhprof')) {
                echo "profiler enablen";
                include_once '/home/a_kalendarev/Загрузки/xhprof-0.9.2/xhprof_lib/utils/xhprof_lib.php';
                include_once '/home/a_kalendarev/Загрузки/xhprof-0.9.2/xhprof_lib/utils/xhprof_runs.php';
                xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
                }
                  $link->someMethod($Data);
                 
                if (extension_loaded('xhprof')) {
                $xhprof_data = xhprof_disable();
                $xhprof_runs = new XHProfRuns_Default();
                $run_id = $xhprof_runs->save_run($xhprof_data, 'app');
                //echo "profile disable id=$run_id n";
                echo "Report: http://prof/index.php?run=$run_id&source=xhprof_test";
                }
                 
                • 0
                  Это конечно хорошо. НО лишняя проверка, даже две в каждом скрипте. А если у вас высоконагруженный проект? Не айс.
                  • 0
                    можно так:
                    if (extension_loaded('xhprof')) {
                    define('APPDEBUG', true)
                    } else {
                    define('APPDEBUG', false)
                    }
                     
                    //далее по тексту...
                    • 0
                      Если более подробно, то я перед деплоем делаю билд: app.phar
                      билд компилится скриптом create_phar.php, который выпиливает всю отладку и прочую дрянь напрочь + учитывает специфику площадки:
                      так как я деплоюсь на несколько площадок, каждая из которых имеет свои фишки: vk.com, facebook.com, odnoklassniki.ru и кол-во площадок будет увеличиваться :)
                      по этому эти фишечки необходимо учитываются при деплое.

                      Да, проект высоконагруженный, ты угадал :)
                  • +2
                    Я это A GUI for XHProf здесь оставлю, в своё время сберегло мне много нервов, может и ещё кому поможет.
                    • По ссылке на втором скрине LFI кто-то раскручивал :))

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.