Reputation: 21
I have a pretty big perl program, packed with PAR::Packer to an executable. It uses quite a lot of modules.
Usually, when perl gives me a warning, like use of undefined value, it prints the location of the error, and then it's easy to go deeper and debug it.
However, I now face an error (introduced in 5.22, I think), and when that happens, I get no location:
Use of each() on hash after insertion without resetting hash iterator results in undefined behaviour, Perl interpreter: 0xa94010
I tried setting use warnings FATAL=>'all';
, but nothing changes, the program does not die.
Maybe it comes from a module? How else can I determine location of the warning?
Upvotes: 2
Views: 347
Reputation: 66881
The warnings pragma emits warnings for legal code, from a set of given categories. While the FATAL
changes them into fatal errors, it does not change how warn
works. Since it is not known how this is emitted there's a good chance that overriding the __WARN__
hook may help
local $SIG{__WARN__} = \&Carp::confess; # or just die
or you may as well drop local
just this one time.
Another thing to try is to override CORE::GLOBAL::warn
BEGIN { *CORE::GLOBAL::warn = sub { die } } # before use Module;
This affects modules as well, as will the __WARN__
signal if set in BEGIN
before modules.
Note that Carp::Always accomplishes this, and more. Also, it is normally activiated simply when running the program, with -MCarp::Always
. Thanks to ikegami for a clarification.
See warn and %SIG hash in perlvar, and this Effective Perler article.
Finally, just how many calls to each
do you have? Check them all.
It is explained in comments that prints come from XS, found via debugger, but it is still unknown what code triggers this. Then try tie
-ing the stream to a class, where a trace is triggered on relevant text. A minimal example
TraceError.pm
package TraceError;
use warnings;
use strict;
use Carp qw(longmess confess);
sub TIEHANDLE { bless {} }
sub PRINT {
my $self = shift;
my $prn = join '', @_;
# print "STDERR: $prn"; # TEST
print @_; # or print STDERR @_;
# if ($prn =~ /\QUse of each() on hash after insertion/) # in your code
if ($prn =~ /TRACE/) { # test
print longmess(@_);
}
}
1;
Change to the commented out if
line so to scan prints for the text of your error message. The rest below is just a test of this. In your code you need the first two lines from main.pl
, to use
this class and tie
the stream (filehandle) to it and then all prints (to STDERR
) go by way of PRINT
above.
main.pl
use TraceError;
tie *STDERR,'TraceError';
use warnings;
use strict;
use Pack qw(issue_warn);
call_for_err(Pack->new);
sub call_for_err {
my ($pck) = @_;
$pck->issue_warn("TRACE call_for_err()"); # should catch this print
$pck->issue_warn("from call_for_err()"); # but not this
};
Pack.pm
package Pack;
use warnings;
use strict;
use Exporter qw(import);
our @EXPORT_OK = qw(issue_warn);
sub new { bless {}, $_[0] }
sub issue_warn {
my $self = shift;
warn "In ", __PACKAGE__, ", issue_warn(@_).";
}
1;
Output
In Pack, issue_warn(TRACE, call_for_err()). at Pack.pm line 12. at Pack.pm line 12. Pack::issue_warn('Pack=HASH(0x7016e8)', 'TRACE call_for_err()') called at main.pl line 12 main::call_for_err('Pack=HASH(0x7016e8)') called at main.pl line 8
The tie
-ing class should be written far more nicely, firstly to take arguments (text to search for, stream or handle to print to). See perltie and Tie::Handle, discussions on perlmonks, posts on SO such as this one, and above all the chapter "Tying Filehandles" in Camel (3rd Ed).
Upvotes: 2
Reputation: 385897
use strict;
use warnings;
sub g {
my %h = ( a => 4, b => 5 );
my $done = 0;
while (my ($k, $v) = each(%h)) {
print("$k\n");
$h{c} = 6 if !$done++;
}
}
sub f {
g();
}
f();
You should have gotten a line number!
>perl a.pl
a
Use of each() on hash after insertion without resetting hash iterator results in undefined behavior, Perl interpreter: 0xe688d8 at a.pl line 8.
b
c
Carp::Always will extend that into a stack trace.
>perl -MCarp::Always a.pl
a
Use of each() on hash after insertion without resetting hash iterator results in undefined behavior, Perl interpreter: 0x1e88d8 at a.pl line 8.
main::g() called at a.pl line 15
main::f() called at a.pl line 18
c
b
Upvotes: 1
Reputation: 608
I think you can use diagnostics or splain. I have used diagnostics and it helps in case we are not able to track from where a warning is being generated.
You only need to put the line use diagnostics;
in your code that's it. You can find detailed explanation for this from the Perl Maven Site - Link.
Thanks.
Upvotes: -1