为什么这个Perl BEGIN块在调试器中的行为有所不同?

时间:2008-11-26 21:08:43

标签: perl debugging compilation

我有一些在调试器外运行良好的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($@)
  {
    die "Really die"  unless($@->isa('MyEx'));
  }
}

print "OK\n";

如果你把它放在somefile.pl并运行它,它按预期打印“OK”。如果您在调试器中使用perl -d somefile.pl运行它,它会因此错误而死:

Can't call method "isa" without a package or object reference ...

结果是代码在调试器下运行时$@不是对象。相反,它是一个包含该字符串的未标记的标量:

" 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调试器模块中的错误吗?解决问题的最佳方法是什么? (我知道这有几个问题,但它们都是相关的。)

我在Mac OS X 10.5.5上使用perl 5.10.0。


Adam Bellaire建议的dieLevel看起来很有希望,而且确实有些事情(无法找出答案)对我来说是1。但我使用~/.perldb文件将其设置为0,问题仍然存在。实际上,我将所有三个相关设置都设置为0.我的~/.perldb文件:

parse_options('dieLevel=0 warnLevel=0 signalLevel=0');

我通过在调试器中运行o命令确认设置有效。当我运行perl -de 0以及运行实际的somefile.pl文件时,我看到它们都设置为0。


谢谢,布莱恩。我使用perlbug提交了一个错误(RT 60890),我开始在我的代码中的所有适当位置撒上local $SIG{'__DIE__'}。 (我还注意到perldoc perldebug似乎仍暗示默认dieLevel为0的错误。)

3 个答案:

答案 0 :(得分:14)

这是perl5db.pl创建__DIE__处理程序的问题。如果我在$SIG{__DIE__}中对eval进行本地化,则可以按预期进行操作。

 eval { 
    local $SIG{__DIE__};
    die MyEx->new 
    };

如果你不这样做,你将从DB :: dbdie获得处理程序,它使用Carp :: longmess。如果dieLevel为0,则不会发生这种情况,但默认情况下为1,如果未定义,则设置为1。这是2001年perl5db.pl的补丁,之前的默认值是0。

你应该用以下方式关闭它:

PERLDB_OPT="dieLevel=0" perl5.10.0 -d program

但之后在$SIG{__DIE__}中仍然存在代码引用,它是对dbdie的引用。我认为这是处理perl5db.pl的$prevdie中的全局变量dieLevel时的错误。在该子例程结束时,有:

# perl5db.pl dieLevel, around line 7777 
       elsif ($prevdie) {
            $SIG{__DIE__} = $prevdie;
            print $OUT "Default die handler restored.\n";
        }

但请注意,在恢复$SIG{__DIE__}之后,它会将之前的值保留在$prevdie中,这意味着其中的任何内容都会泄漏给另一个调用。当我运行该命令行时,在处理PERLDB_OPT之前有两次对dieLevel的调用,因此$prevdie可能很脏。

所以,就我所知,我不想再考虑perl5db.pl了。

答案 1 :(得分:5)

我认为任何时候代码在调试器中的行为方式都是错误的。

您的问题可能与此相关:Debugger corrupts symbol table munging。从本质上讲,调试器似乎与local一起玩一些技巧 - 可能是作为沙盒事物的一部分来提供交互性。显然,弄乱符号表会产生意想不到的副作用。我猜测调试器正在本地化$@,从而模糊了你的对象。我无法想到一个解决方法。

答案 2 :(得分:3)

是否可能有RC文件或环境变量(PERLDB_OPTS)正在修改调试器的dieLevel选项?我个人没有使用dieLevel,但显然当它被设置为大于零的值时,它可以强制堆栈展开,并且“倾向于无可救药地破坏任何严重处理其异常处理的程序。” (Quote from here)。