Удаленная отладка с использованием xdebug port forwarding
Каждый разработчик время от времени сталкивается с непонятными ошибками. Как правило, их причина неочевидна и чтобы их пофиксить, нужно детально понять, что происходит в коде. Модуль xdebug — инструмент для профессионального поиска ошибок. Когда выполнение кода доходит до точки останова, xdebug присоединяется к IDE, которая слушает порт (по умолчанию 9000). Эта схема хорошо работает на localhost. Если вы находитесь в прямой видимости удаленного сервера, то можно прописать в конфиге xdebug ваш ip, и производить отладку аналогично. Но если компьютер разработчика находится за NAT, то прямой доступ с сервера к нему невозможен.
Я давно использую xdebug на localhost, но, разобравшись с port forwarding, научился отлаживать на удаленном сервере и решил написать инструкцию, которая может быть полезной каждому разработчику, а главное — она работает.
В качестве IDE я использовал PhpStorm, браузер Chrome, ОС Ubuntu. Port forwarding без проблем работает и в windows.
Настройки сервера
sudo apt-get install php5-xdebug sudo nano /etc/php5/conf.d/xdebug.ini
Опция xdebug.idekey может быть PhpStorm1, netbeans-xdebug, XDEBUG_ECLIPSE, в зависимости от IDE, или пустым.
zend_extension=/usr/lib/php5/20100525/xdebug.so xdebug.profiler_enable = 0 xdebug.remote_enable = 1 xdebug.remote_host = 127.0.0.1 xdebug.remote_port=9000 xdebug.remote_handler=dbgp xdebug.idekey=PhpStorm1
В итоге вывод phpinfo() должен содержать информацию о xdebug:
Настройки клиента
Chrome Xdebug helper
Если планируется использовать idekey, то для Chrome нужно поставить Xdebug helper.
В настройках расширения параметр IDE key нужно указать PhpStorm1 и в Domain filter вписать host. При отладке в адресной строке нужно нажимать Debugging Enabled.
Настройка PhpStorm
File -> Settings -> xdebug. Port 9000. На панели Edit debug configuration -> Add New Configuration -> PHP Web Application
Нужно указать Name, Start URL, создать новый сервер. У сервера указать Name, Host, Port, path mapping. Сохранить все.
Теперь можно поставить точку останова и нажать на кнопку debug, чтобы IDE начала слушать порт. Запустится браузер со страницей Server + Start URL, а в параметре будет XDEBUG_SESSION_START, который определяет идентификатор сессии. Не теряйте его при отладке.
Port forwarding
Xdebug стучится в порт, который слушает IDE. Если мы хотим отлаживать удаленный сервер и сидим за NATом, то наши порты недоступны. Оптимальный вариант — перенаправление удаленного порта на локальный с помощью ssh. Для работы под windows нужно установить Cygwin с пакетом ssh.
$ ssh -R 9000:127.0.0.1:9000 user@server
Проверка port forwarding
Нужно попробовать установить связь на удаленной машине на ее 9000 порт. Если все сделано правильно, то соединение будет установлено, по факту, с нашим локальным портом.
Успешеный коннект:
> telnet 127.0.0.1 9000 Trying 127.0.0.1. Connected to 127.0.0.1. Escape character is '^]'.
telnet: Unable to connect to remote host: Connection refused - Перенаправление не работает. Возможно проблема в антивирусе или файрволе. Connection closed by foreign host - IDE не слушает порт
Отладка PHP приложений на удаленном хосте при помощи XDebug и vim в Linux
В PHP приложениях отладка при помощи var_dump, debug_backtrace и прочих полезных функций не всегда удобна, и возникает потребность в полноценном отладчике. Эта статья — для тех, кто по каким-либо причинам не хочет использовать IDE, поддерживающие отладку PHP приложений из коробки, вроде NetBeans или PhpStorm, а хочет использовать для этих целей vim, и при этом отладка происходит на удаленном хосте.
Для vim существует плагин «DBGp client», но он позволяет нормально отлаживать только в случае, когда пути до всех файлов на удаленной и на локальной машинах одинаковые. Например, если локальной машине у вас есть:
/home/user/application/
/home/user/framework/
а на удаленной машине они расположены в:
/var/www/html/application/
/var/www/framework/
то отладить приложение при помощи «DBGp client» не получится, так как он ничего не знает о другом расположении исходников.
- Кратко — настройку всего необходимого для удаленной отладки приложения.
- Модификацию плагина для поддержки различных путей.
- Кратко — использование отладчика.
Установка
Настраиваем vim на локальном хосте
Скачайте и установите плагин:
$ cd ~/.vim
$ wget www.vim.org/scripts/download_script.php?src_id=7285 -O debugger.zip
$ unzip debugger.zip
$ rm debugger.zip
Внимание: vim должен быть собран с поддержкой python, проверить это можно при помощи «:version«, в выводе должна быть строка «+python».
Устанавливаем и настраиваем Xdebug на удаленном хосте
Установите расширение Xdebug любым удобным для вас способом. Например, на Debian Squeeze это делается просто:
# apt-get install php5-xdebug
А в общем случае, лучше почитать официальную инструкцию по установке.
Настройте подключение к локальному debug-клиенту — для этого в php.ini пропишите следующие строчки, заменив 192.168.1.110 на IP локальной машины (при необходимости порт тоже можно перенастроить):
xdebug.remote_enable = 1
xdebug.remote_port = 9000
xdebug.remote_host = 192.168.1.110
Настраиваем соответствие файлов
Идея простая — при отправке запроса дебагеру (например, на установку breakpoint), мы должны преобразовывать путь на локальном хосте в путь на удаленном хосте, и наоборот, при получении информации от дебагера (например, о том что сейчас он находится на какой-то строке какого-то файла), мы должны сделать обратное преобразование.
Дописываем в конец файла debugger.py следующий код:
class FileMapping: def __init__(self, mapping_file): self.local_to_remote = <> self.remote_to_local = <> mapping = open(mapping_file, 'r') for line in mapping: local, remote = line.split(' ') local = local.strip() remote = remote.strip() if not (local in self.local_to_remote): self.local_to_remote[local] = [] self.local_to_remote[local].append(remote) if not (remote in self.remote_to_local): self.remote_to_local[remote] = [] self.remote_to_local[remote].append(local) def local_to_remote_file(self, local): for local_path in self.local_to_remote.keys(): if local.startswith(local_path): # use the first mapping as we don't know which one we should take remote_path = self.local_to_remote[local_path][0] return remote_path + local[len(local_path):] def remote_to_local_file(self, remote): for remote_path in self.remote_to_local.keys(): if remote.startswith(remote_path): for local_path in self.remote_to_local[remote_path]: local = local_path + remote[len(remote_path):] # use the first existing file if os.path.exists(local): return local return None file_mapping = FileMapping('/home/alexey/mapping')
А в файл mapping (/home/alexey/mapping — замените на свой путь) записываем соответствие локальный и удаленных директорий, например:
/home/alexey/framework /var/www/framework
/home/alexey/application /var/www/html
Просматриваем код плагина в поисках мест, где от Xdebug приходят имена файла. В итоге, все они сводятся к вызову одного метода — set_srcview, в начало которого мы и добавляем изменение имени файла:
def set_srcview(self, file, line): """ set srcview windows to file:line and replace current sign """ file = file_mapping.remote_to_local_file(file)
Теперь ищем места, где наоборот от debug-клиента к Xdebug передаются имена файлов. Таких мест два:
1. Класс Debugger, метод run, заменяем строку
'-t line -f ' + self.breakpt.getfile(bno) + ' -n ' + str(self.breakpt.getline(bno)) + ' -s enabled', \
'-t line -f ' + file_mapping.local_to_remote_file(self.breakpt.getfile(bno)) + ' -n ' + str(self.breakpt.getline(bno)) + ' -s enabled', \
'-t line -f ' + self.breakpt.getfile(bno) + ' -n ' + str(self.breakpt.getline(bno)), \
'-t line -f ' + file_mapping.local_to_remote_file(self.breakpt.getfile(bno)) + ' -n ' + str(self.breakpt.getline(bno)), \
Уже поправленный debugger.py можно взять тут.
Использование
Запускаем отладку
Чтобы начать отладку web-приложения, необходимо запустить vim и нажать .
Далее, в течение 5 секунд (ниже описано, как увеличить интервал), необходимо запустить какой-либо PHP скрипт, передав GET-ом переменную XDEBUG_SESSION_START со значением 1, например просто открыв соответствующую страницу в браузере, например:
webdev/debug.php?XDEBUG_SESSION_START=1
Альтернативно в php.ini можно задать переменную xdebug.remote_autostart. В таком случае, при запуске любого PHP скрипта, Xdebug будет пытаться подключиться к debug-клиенту.
Подробнее можно прочитать в официальной документации Xdebug.
В итоге должно получиться что-то вроде этого — должен открыться скрипт, который вы запустили:
- WATCH WINDOW — просмотр контекста
- HELP WINDOW — краткое описание возможностей
- STACK WINDOW — стек вызова функций
- TRACE WINDOW — лог общения debug-клиента c Xdebug, полезно посмотреть если отладка не заработала
Настройка под себя
и заменив 5 на нужное вам количество секунд.
Комбинации клавиш задаются в debugger.vim, например себе я переназначил на нажатие «,dr»:
map ,dr :python debugger_run()
Текст в HELP WINDOW можно поменять в классе HelpWindow, методе on_create.
Навигация по коду
- Step into () — шаг с заходом внутрь функций.
- Step over () — шаг без захода внутрь функций.
- Step out () — выход из функции по стеку вверх.
- Run () — продолжить выполнение до следующего breakpoint.
- Stack up (:Up) — переход по стеку вверх (смотрите STACK WINDOW).
- Stack down (:Dn) — переход по стеку вниз (смотрите STACK WINDOW).
Просмотр текущего состояния
- Property get () — получить значение переменной (надо поставить курсор на нужную переменную и нажать ).
- Context get () — получить весь текущий контекст (грубо говоря, все переменные, доступные в данном контексте).
- Eval (,e) — выполнить произвольное выражение в текущем контексте и получить его значение.
Установка breakpoints
Toggle breakpoint (:Bp) — установить breakpoint в текущей строке, или удалить, если он уже есть.
На скриншоте — зеленая строка — это строка с breakpoint, красная — это текущая строка:
Resize
Дополнительно по можно переключатся между двумя режимами — полный, когда показываются различные вспомогательные окна справа, и простым — когда остается только окно с кодом.