Почему этот блок Perl BEGIN действует по-разному в отладчике?
У меня есть код Perl, который отлично работает вне отладчика:
% perl somefile.pl
но когда я запускаю его внутри отладчика:
% perl -d somefile.pl
он ведет себя по-другому.
Указанные файлы (их несколько) являются частью набора тестов для большого модуля Perl (~ 20K строк кода). Тесты выполняют большую работу по настройке во время компиляции и используют блоки BEGIN. Здесь приведен минимальный код воспроизведения:
BEGIN
{
package MyEx;
sub new { bless {}, shift }
package main;
eval { die MyEx->new };
if([email protected])
{
die "Really die" unless([email protected]>isa('MyEx'));
}
}
print "OK\n";
Если вы поместите это в somefile.pl
и запустите его, он напечатает "ОК", как ожидалось. Если вы запустите его в отладчике с perl -d somefile.pl
, он умрет с этой ошибкой:
Can't call method "isa" without a package or object reference ...
Результатом является то, что [email protected]
не является объектом, когда код работает под отладчиком. Вместо этого он представляет собой непревзойденный скаляр, содержащий эту строку:
" at somefile.pl line 9
eval {...} called at somefile.pl line 9
main::BEGIN() called at somefile.pl line 16
eval {...} called at somefile.pl line 16
"
(Внутренние символы новой строки и интервал сохранены. Это буквальный текст, даже "...".)
Мне нужен такой код для запуска в отладчике. Использование отладчика в наборе тестов является важной частью моего рабочего процесса. Модуль использует объекты исключений и делает много вещей во время компиляции и ожидает, что объект был брошен как объект при захвате.
Мой вопрос (наконец) заключается в следующем: как я могу заставить это работать? Есть ли обходной путь? Является ли это ошибкой в модуле отладчика perl? Какой лучший способ решить эту проблему? (Я знаю, что несколько вопросов, но все они связаны друг с другом.)
Я использую perl 5.10.0 для Mac OS X 10.5.5.
Идея dieLevel, предложенная Адамом Беллаиром, выглядела многообещающей, и действительно, что-то (не может узнать, что) устанавливает для меня 1. Но я устанавливаю его в 0 с помощью файла ~/.perldb
, и проблема сохраняется. Фактически, я установил все три связанных параметра в 0. Мой ~/.perldb
файл:
parse_options('dieLevel=0 warnLevel=0 signalLevel=0');
Я подтвердил, что настройки действуют, запустив команду o
в отладчике. Я вижу, что все они равны 0, когда я запускаю perl -de 0
, а также при запуске фактического файла somefile.pl
.
Спасибо, Брайан. Я использовал perlbug
для записи ошибки (RT 60890), и я начал посыпать local $SIG{'__DIE__'}
во всех соответствующих местах в моем код. (Я также заметил в ошибке, что perldoc perldebug
по-прежнему подразумевает, что значение по умолчанию dieLevel
равно 0.)
Ответы
Ответ 1
Это проблема с perl5db.pl созданием обработчиков __DIE__
. Если я локализую $SIG{__DIE__}
в вашем eval
, все будет работать так, как вы ожидаете.
eval {
local $SIG{__DIE__};
die MyEx->new
};
Если вы этого не сделаете, вы получаете обработчик из DB:: dbdie, который использует Carp:: longmess. Это не должно происходить, если dieLevel равно 0, но по умолчанию оно равно 1, и оно устанавливается равным 1, если оно не определено. Это был патч для perl5db.pl еще в 2001 году, и ранее значением по умолчанию было 0.
Вы должны отключить это:
PERLDB_OPT="dieLevel=0" perl5.10.0 -d program
Но после этого есть еще ссылка на код в $SIG{__DIE__}
, и это ссылка на dbdie. Я думаю, что это ошибка в обработке глобальной переменной $prevdie
в perl5db.pl dieLevel
. В конце этой подпрограммы есть:
# perl5db.pl dieLevel, around line 7777
elsif ($prevdie) {
$SIG{__DIE__} = $prevdie;
print $OUT "Default die handler restored.\n";
}
Но обратите внимание, что после восстановления $SIG{__DIE__}
он сохраняет предыдущее значение в $prevdie
, что означает, что там есть утечки на другой вызов. Когда я запускаю эту командную строку, есть два вызова dieLevel, прежде чем обрабатывать PERLDB_OPT
, поэтому $prevdie
, вероятно, загрязнен.
Итак, что я до сих пор не хотел больше думать о perl5db.pl.
Ответ 2
Я считаю, что ошибка в любом коде времени ведет себя по-разному в отладчике.
Ваша проблема может быть связана с этим: Отладчик искажает отображение таблицы символов. По сути, отладчик, похоже, играет некоторые трюки с local
- предположительно, как часть песочницы для обеспечения интерактивности. Очевидно, что беспорядок с таблицей символов может иметь неожиданные побочные эффекты. Я предполагаю, что отладчик локализует [email protected]
и тем самым скрывает ваш объект. Я не могу придумать обход.
Ответ 3
Возможно ли, что у вас есть RC файл или переменная среды (PERLDB_OPTS
), которая изменяет параметр dieLevel
отладчика? Я лично не использовал dieLevel
, но, видимо, когда он установил значение больше нуля, он может заставить отключить сканирование и "имеет тенденцию безнадежно уничтожать любую программу, которая серьезно относится к обработке исключений". (Цитата отсюда).