Глава 17. Отладка сценариев Perl. Руководство по стилю программирования
Коротко
Вероятно, вы способны написать замечательный, прозрачный код, который сразу будет работать безотказно. Но для многих программистов, ошибки — неотъемлемая часть творчества. Вам, возможно, захочется взглянуть на эту главу, чтобы помочь им, поскольку речь пойдет об отладчике Perl.
Интерпретатор Perl, запущенный с ключом -d, переходит в режим отладчика. Отладчик — это интерактивная оболочка, позволяющая вводить команды отладки, исследовать код, устанавливать точки прерывания, изменять значения переменных, и т. д.. Если отладчик Perl на ваш вкус недостаточно мощен, существует много других отладчиков, как коммерческих, так и некоммерческих. Проверьте архив CPAN и выберите наиболее подходящий. Также учтите, что если вы работаете с редактором GNU Emacs или XEmacs, то можете использовать его для взаимодействия с отладчиком Perl, обеспечивая полностью интегрированную оболочку для разработки программного обеспечения на Perl.
Подсказка: Отладчик Perl ориентирован на построчный вывод. Это значит, что в определенных случаях он выводит больше строк текста, чем может поместиться на экране компьютера. В результате могут возникать проблемы. В качестве меры, исправляющей положение, можно предложить вводить команды отладчика с префиксом |, то есть через канал. В этом случае вывод проходит через промежуточный фильтр (pager), позволяющий просматривать страницу за страницей.
Пример сеанса отладки
Рассмотрим пример сеанса работы с отладчиком. Предположим, что в файле debug.pl хранится сценарий:
$variable1 = 5;
$variable2 = 10;
$variable1 += 5;
print "\$variable1 = $variable1\n";
print "\$variable2 = $variable2\n";
Чтобы загрузить сценарий в отладчик, введем команду:
%perl -d debug.pl
Отладчик загружается и выводит приглашение для ввода команды DB<1> — число в угловых скобках показывает номер отладочной команды:
Loading DB routines from perl5db.pl version 1.0401
Emacs support available.
Enter g or `h h’ for help.
Main::(debug.pl:1): $variable1 = 5;
DB<1>
После приглашения введите символ минуса (–). Эта команда выведет исходный текст программы:
DB<1> –
1==> $variable1 = 5;
2: $variable2 = 10;
3: $variable1 += 5;
4: print "\$variable1 = $variable1\n";
5: print "\$variable2 = $variable2\n";
6
Обратите внимание на символ ==> в строке кода с номером один. Он отмечает положение указателя отладчика — текущую выполняемую строчку. Чтобы выполнить несколько строк кода и остановиться, мы устанавливаем точку прерывания (breakpoint) на строке 4 (команда «b 4»). Таким образом, выполнение программы остановится, когда отладчик доходит до точки прерывания. Мы также используем команду «c» для выполнения кода программы от положения указателя до точки прерывания:
DB<1> b 4
DB<2> c
main::(debug.pl:4): print "\$variable1 = $variable1\n";
Теперь посмотрим на код. Указатель отладчика находится в строке 4, — обратите внимание на символ «b», который означает точку прерывания:
DB<2> –
1: $variable1 = 5;
2: $variable2 = 10;
3: $variable1 += 5;
4==>b print "\$variable1 = $variable1\n";
5: print "\$variable2 = $variable2\n";
6
Кроме выполнения вплоть до точки прерывания, можно выполнять код пошагово, используя команду «s». В нашем примере мы увидим, что указатель отладчика переместился на следующую строчку:
DB<2> s
main::(debug.pl:5): print "\$variable2 = $variable2\n";
DB<2>
Другая полезная техника — проверка значений переменных и выражений. В режиме наблюдения за переменной или выражением, отладчик сообщает, когда происходит что-то, изменяющее значение переменной или выражения. В следующем примере команда «W» устанавливает режим наблюдения для переменной $variable1 — заметьте, как отладчик останавливает работу программы и дает нам знать, что он встретил строчку кода, изменяющую $variable1:
DB<1> W $variable1
DB<2> c
Watchpoint 0: $variable1 changed:
old value: undef
new value: '5'
Работая с отладчиком, вы можете делать гораздо более интересные вещи, — например, изменять значения переменных, вычислять выражения и даже выполнять команды Perl перед очередным шагом в программе.
Кроме режима использования отладчика, Perl предлагает ряд конструктивных предложений по поводу стиля программирования. Мы познакомимся в этой главе с некоторыми из них, а пока перейдем к разделу «Непосредственные решения».
Непосредственные решения
Перехват ошибок времени выполнения
Перед тем, как приступить к отладке кода, обратите внимание, что до некоторой степени можно перехватывать ошибки во время выполнения программы. Для этого служат специальные переменные: $? (ошибка, возникшая в дочернем процессе), $! (ошибка последнего вызова системной функции), $^E (расширенное сообщение об ошибке), $@ (ошибка при последнем выполнении функции eval).
Что касается перехвата ошибок во время выполнения программы, то у Perl нет специальных команд типа try-catch, существующих, например, в языках С++, Delphi, Python. (Потенциально опасный код помещается внутрь блока try. При появлении ошибки управление передается блоку catch.) Однако подобие такого блока модно сконструировать с помощью команды eval.
Рассмотрим пример. Подпрограмма try будет выполнять код, переданный ей в качестве параметра. Чтобы передать ец блок кода, заключенный в фигурные скобки, мы задаем прототип подпрограммы так, чтобы она понимала в качестве параметра ссылку на анонимную подпрограмму. Чтобы перехватить прерывание по ошибке, мы выполняем вызов подпрограммы внутри команды eval (в этом случае ошибка не приводит к завершению программы). Если внутри eval происходит ошибка, сообщение о ней заносится в переменную $@. Это позволяет определить факт ошибки и вывести сообщение о ее причине (в нашем случае попытка деления на ноль):
sub try (&) {
my $code = shift;
eval {&$code};
if ($@) {print $@;}
};
try {
$operand1 = 1;
$operand2 = 0;
result = $operand1 / $operand2;
};
Illegal division by zero at try.pl line 9.
Модно также задать собственный обработчик ошибок. Для этого надо перехватить сигнал __WARN__ подпрограммой обработчиком. Как и все обработчики сигналов, она заносится в хеш-таблицу %SIG:
local $SIG{__WARN__} = sub {print "Error!\n"};
В случае возникновения ошибки вызывается эта подпрограмма.
На этом обсуждение обработки программных ошибок закончено. Логическая ошибка программы — это совсем другая тема: вам надо воспользоваться отладчиком. По этому поводу — см. следующий раздел.
Запуск отладчика
Как запустить сценарий Perl в отладчикеё? Для этого надо при старте интерпретатора использовать ключ -d:
%perl -d debug.pl
Когда задан этот ключ, Perl загружает ваш сценарий в режиме отладки.
Подсказка: Если вы используете режим отладки, но не указываете файл, в котором расположен отлаживаемый сценарий, придется ввести его вручную с клавиатуры. Лишь затем, после завершающего __END__, интерпретатор перейдет в режим отладки.
Вместе с ключом -d вы можете использовать ключ -D, устанавливающий параметры режима отладки (они перечислены в таблице 17.1). Параметры отладчика указываются в виде букв (например, -Df) или числа (например, -D256). Вы можете объединять несколько параметров отладки — для этого либо перечисляют сразу несколько букв (например, -Dts), либо задают число, являющееся суммой отдельных числовых величин (например, -D10 равносильно -Dts).
Чтобы интерпретатор смог использовать параметры отладки, он должен быть скомпилирован с ключом -DDEBUGGING (устанавливается автоматически, если при компилияции использован ключ -g).
Таблица 17.1. Параметры режима отладки
Число |
Буква |
Значение |
||
1 |
p |
Обрабатывать ошибки синтаксического разбора |
||
2 |
s |
Выводить состояние стека вызовов подпрограмм |
||
4 |
l |
Разрешить контекстно-зависимую обработку стека |
||
8 |
t |
Разрешить трассировку выполнения сценария |
||
16 |
o |
Разрешить проверку перегрузки методов |
||
32 |
c |
Поддерживать преобразование строк в числа и наоборот |
||
64 |
P |
Поддерживать препроцессор печати |
||
128 |
m |
Разрешить выделение блоков памяти |
||
256 |
f |
Разрешить обработку форматов |
||
512 |
r |
Разрешить синтаксический разбор и выполнение регулярных выражений |
||
1024 |
x |
Разрешить вывод дампов деревьев синтаксического разбора |
||
2048 |
u |
Поддерживать проверку меченых данных |
||
4096 |
L |
Проверка «утечек» памяти, то есть появление блоков памяти, которые не используются ни сценарием, ни отладчиком, и не входят в область свободных блоков |
||
8192 |
H |
Разрешить дампы хешей |
||
16384 |
X |
Разрешить оперативное реагирование отладчика |
||
32768 |
D |
Разрешить режим очистки |
Доступные команды отладчика
Чтобы узнать, какие есть команды у отладчика, используйте команду «h» (сокращение от help). В ответ на приглашение отладчика ввести команду, вы можете указать одну из команд вывода подсказки:
h
|h
h h
h команда
Первая форма команды «h» выводит на экран краткую справку по всем командам отладчика. Как правило, эта «краткая справка» слишком длинна, чтобы уместиться на экране. Если в начале команды будет стоять символ «|», выдача на экран будет проходить через внутренний фильтр отладчика, позволяющий просматривать справочный текст страница за страницей.
Подсказка: Символ вертикальной черты (|), как уже было сказано в начале этой главы, воздействует на любой текст, выводимый отладчиком на экран, а не только на результат работы команды «h». То есть, если при выполнении очередной команды отладчика вы видите, что информация скрылась за верхним краем экрана, и перед глазами расположен только конец нужного вам текста, просто повторите эту команду, поставив в ее начале символ «|».
Команда «h h» позволит увидеть выжимку из краткой справки по командам отладчика, выводимой по команде «h». Это еще более краткий текст, отформатированный в две колонки. Она слишком лаконична, чтобы быть информативной для человека, не имеющего никакой информации о командах отладчика. Зато она позволяет увидеть полный список имен команд, не прибегая к постраничному просмотру информации. Теперь, используя команду h в формате «h команда», вы получите развернутую справку по каждой команде в отдельности. Наиболее существенные и часто используемые команды отладчика разъясняются в последующих разделах этой главы.
Просмотр исходного кода
После того, как исходный текст вашего сценария будет загружен в память отладчика, вам может понадобиться увидеть этот код, чтобы освежить его в памяти. Для этого используется одна из команд, управляющих выводом листинга («l» является сокращением от list, «w» — от window):
В следующем примере мы выводим на экран первые три строки сценария:
DB<1> l 1-3
1==> $variable1 = 5;
2: $variable2 = 10;
3: $variable1 += 5;
Пошаговое выполнение
Чтобы продвигаться по коду с помощью отладчика, вы можете сделать один шаг вашей программы, выполнив одну строчку кода. Для этого используется команда «s» (сокращение от step):
Если строка сценария или указанное вами выражение включает вызовы функций, строки, составляющие тело функции, также будут выполняться по шагам. (Чтобы выполнить вызов функции как единое целое, используйте команду «n».) Обратите внимание, что пустая строка, введенная после команды «s» или «n», приводит к повтору команды, — то есть, если вы выполнили очередную строку сценария с помощью команды «s» без параметров, то нажатие клавиши Enter приведет к выполнению следующей строки сценария.
В этом примере мы выполняем по шагам три команды печати:
DB<1> –
1==> print "Hello\n";
2: print "from\n";
3: print "Perl!\n";
4
DB<1> s
Hello
main::(d.pl:2): print "from\n";
DB<1> s
From
main::(d.pl:3): print "Perl!\n";
DB<1> s
Perl!
Пошаговое выполнение без захода в подпрограммы
Команда «n» (сокращение от next) запрещает пошаговое выполнение подпрограмм, вызываемых в процессе выполнения очередной строки сценария или в процессе вычисления выражения. Формат команды «n» и ее действия аналогичны рассмотренной в предыдущем разделе команде «s» за тем исключением, что вызовы подпрограмм выполняются как единое целое, не приводя к пошаговой трассировке тела подпрограммы:
n
n выражение
Вы можете использовать нажатие на клавишу Enter, чтобы повторить последнюю команду «s» или «n».
Установка точек прерывания
Когда отладчик доходит до установленной точки прерывания, он приостанавливает работу сценария, и вы можете проверить, что происходит в программе. Отладчик Perl обеспечивает довольно большую гибкость по части установки точек прерывания. Команда «b» (сокращение от break), устанавливающая точки прерывания, выглядит следующим образом:
Команды «b», «b строка», «b подпрограмма» и «b postpone подпрограмма» позволяют указывать в качестве необязательного параметра условие (например, $variable == 0). Условие представляет собой выражение Perl. Если при достижении указанной строки, но до ее выполнения, вычисление этого выражения соответствует условию истина, то выполнение сценария прерывается, если же проверка возвращает значение ложь — то оно продолжается дальше. Поскольку в качестве условия бессмысленно задавать фиксированное число (оно либо всегда истинно, либо — в случае, если это ноль, — всегда ложно), то отладчик Perl гарантированно способен отличить команду «b строка» от команды «b условие».
В следующем примере мы устанавливаем точку прерывания на четвертой строке исходного кода и выполняем сценарий целиком до того момента, пока не достигнем этой строки (см. также описание команды «c» в разделе «Запуск программы до следующей точки остановки» далее в этой главе):
DB<1> –
1==> print "Hello\n";
2: print "from\n";
3: print "Perl!\n";
4: print "Hello again.\n";
5
DB<1> b 4
DB<2> c
Hello
from
Perl!
main::(d.pl:4): print "Hello again.\n";
DB<2>
Удаление точек прерывания
Чтобы удалить точки прерывания, вы можете использовать следующие команды отладчика (сокращение от delete):
Запуск программы до следующей точки остановки
Команда «c» (сокращение от continue) выполняет очередной фрагмент сценария:
Пример использования команды «c» приведен ранее в разделе «Установка точек прерывания».
Если в процессе выполнения команды «c», был достигнут конец сценария, вы увидите сообщение:
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info.
Если теперь ввести команду «R» (сокращение от Restart), отладчик вернется к началу сценария, очистив все переменные, стек вызовов подпрограмм, и т. д., но сохранив установленные точки прерывания. Тем самым получите еще один шанс разобраться, что же происходит в вашей программе. Если же ввести команду «q», отладчик закончит свою работу.
Печать выражения
Чтобы вывести на экран компьютера значение какого-либо выражения (например, значение переменной), используется команда «p» (сокращение от print):
p выражение
В следующем примере мы выводим с помощью команды «p» значение переменной $variable1:
DB> p $variable1
5
Вычисление выражения
Чтобы вычислить выражение Perl, достаточно ввести его в ответ на очередное приглашение отладчика. В качестве выражений выступают любые команды Perl. Если команда очевидным образом не вмещается в одну строчку, вы можете продолжить ее на следующей, введя последним символом обратную косую черту (\). Отладчик выведет в качестве приглашения текст «cont:» и будет ждать продолжения. Пример (печать текста три раза с помощью оператора цикла):
DB<1> for (1..3) { \
cont: print "Hello from Perl!\n"; \
cont: }
Hello from Perl!
Hello from Perl!
Hello from Perl!
Изменение значений переменных
Вы можете изменить значение переменной, присвоив ей новое значение (см. предыдущий раздел). Пример:
DB<1> p $variable1
5
DB<2> $variable1=10;
DB<3> p $variable1
10
Установка глобальных условий
С помощью команды «W» (сокращение от Watch) можно указать глобальные условия, проверяющие изменения значений переменных:
Самый простой случай — это когда в качестве выражения указана переменная. Тогда работа сценария прерывается, как только изменяется значение переменной. (Заметьте, что оператор присвоения, присваиваивающий переменной то же самое значение, которое в ней уже находится, не прерывает работы сценария.) Пример:
main::(debug.pl): $variable1 = 5;
DB<1> W $variable1
DB<2> c
Watchpoint 0: $variable1 changed:
old value: undef
new value: '5'
Однако команда «W» позволяет задавать в качестве аргумента не только переменные, но и целые выражения. В этом случае прерывание нормальной работы сценария возникает, если изменится соответствующее выражение как единое целое. Команда «W» достаточно сообразительна, чтобы вычислять значение выражения и выполнять проверку, только когда меняются входящие в выражение компоненты. Необходимо отметить, что если вы указываете команде «W» в качестве аргумента выражение, а не просто имя переменной, все переменные, входящие в выражение, уже должны иметь определенное значение. Если одна или несколько переменных имеют значение undef, вы получите довольно длинный список ошибок, возникающих внутри отладчика (он ведь тоже представляет собой сценарий Perl!) Причем такая диагностика будет выводиться при каждом изменении переменных, входящих в выражение, пока все они не получат определенное значение.
Установка отладочных действий
Действие — это команды Perl или отладчика, которые выполняются каждый раз перед выводом приглашения отладчика или сразу после вывода приглашения. Они оказываются полезными, если, например, вы хотите знать значение некоторого выражения после каждого шага при пошаговом выполнении сценария. Вы можете использовать следующие команды отладчика, чтобы управлять его действиями:
Пример — перед выводом очередного приглашения отладчик печатает текущее значение переменной $variable1:
DB<1> –
1==> $variable1 = 5;
2: $variable1 += 5;
3: $variable1 += 5;
DB<1> < print "\$variable1 = $variable1\n";
DB<2> s
main::(debug.pl:2) $variable1 += 5;
$variable1 = 5;
DB<2> s
main::(debug.pl:3) $variable1 += 5;
$variable1 = 10;
Выход из отладчика
Чтобы выйти из отладчика, используйте команду «q» (сокращение от quit).
Руководство по стилю программирования на Perl
У разработчиков Perl имеется много предложений по стилю программирования для этого языка. Многие — всего лишь вопрос вкуса. Учитывайте этот факт, соглашаясь или нет с приведенным ниже списком сразу же по мере его прочтения. Итак: