Vit Spinka
Vit Spinka

Reputation: 21

How to determine location of a perl warning - Use of each() on hash after insertion

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

Answers (3)

zdim
zdim

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

ikegami
ikegami

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

Mohit
Mohit

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

Related Questions