Infinite loop in perl Carp module - perl

We have some code which catches an exception, logs the message and then calls Carp::longmess to get the stacktrace.
So a simplified view of what we are doing is:
eval { <some SOAP::Lite stuff> };
if( my $err = $# )
{
logwrite( "Caught Error: $err" );
}
The logwrite function is essentially:
sub logwrite($)
{
my $msg = $_[0];
my($pkg,$fil,$lin)=caller;
my $timestamp = POSIX::strftime(...);
print STDERR "$timestamp $fil/$lin $msg\n";
print STDERR "$timestamp $fil/$lin Stack trace:\n" . Carp::longmess . "\n";
}
But in the log I am seeing:
20111030 Module.pm/42 Caught Error: at line
Use of uninitialized value in caller at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 22.
Use of uninitialized value in string eq at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 91.
Use of uninitialized value in numeric lt (<) at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 200.
Use of uninitialized value in pattern match (m//) at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 55.
Use of uninitialized value in concatenation (.) or string at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 55.
Use of uninitialized value in concatenation (.) or string at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 142.
Use of uninitialized value in concatenation (.) or string at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 142.
Use of uninitialized value in concatenation (.) or string at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 142.
Use of uninitialized value in concatenation (.) or string at /usr/lib/perl5/5.8.8/Carp/Heavy.pm line 142.
...
And that sequence of warnings from the Carp/Heavy.pm module repeats over and over again indefiniately, blowing out the logifle. So we eventually kill it off. These warnings look like they're being triggered by the call to Carp::longmess. The other intersting thing here is the $# variable appears to just be at. It as the at added by die, but no actual error message or line number.
Has anyone seen this before or have any idea what's coing on with the Carp package? This is rare, but has happenned a handful of times over the past month or so, and we have hundreds of these jobs running every day.

Your code works for me on perl v5.10.1, with Carp.pm version 1.11.
However, note that what it does is perhaps not what you expect: the backtrace produced by longmess will show where the logwrite function was called from, not where the actual error occurred inside the eval.

I realize this doesn't answer your actual question, but . . . since apparently $msg eq 'at line ' in this case, maybe you should just bypass the issue by tacking unless $msg eq 'at line ' onto the end of the print ... Carp::longmess ... statement? (I mean, unless someone proposes a real solution.)

Related

Perl Use of uninitialized value in regexp compilation at warning

I have pasted small snippet of code below:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my $start_data;
my $name = "Data_abc";
while(<DATA>){
my $line = $_;
if ($line =~ /^Start:\s+/){
my ($st, $data) = split(/\s+/,$line);
$start_data = $data;
}
for( $name ){
/^$start_data/ and do { next; }
}
print "END of execution\n";
}
print $start_data;
__DATA__
===============================
2020-05-20 Name
===============================
Start: Data_abc
Load: Load_data
Script is working as expected but it throws up warning -
Use of uninitialized value $start_data in regexp compilation at storage_problem.pl line 18,
Since I have already declared $start_data at the beginning, why this warning it shows?
$start_data was declared, but you did not assign it a value before you tried to read it in the regex. Therefore, it is undefined.
When I run your code, I get 3 warning messages, corresponding to your 1st 3 lines of DATA. Those 3 lines do not match your regex (they don't start with Start:).
Since you did not initialize $start_data with a value, you get the uninitialized warning.
Once the 4th line is read, you stop getting the warnings because $start_data is assigned a value (Data_abc).
The code provided by OP declares $start_data but does not initialize it.
On read of first line this $start_data checked in /^$start_data/ regular expression which is equivalent to /undef/ what causes following message
Use of uninitialized value $start_data in regexp compilation at storage_problem.pl line 18,
Perhaps the code should be written us following
use strict;
use warnings;
use feature 'say';
use autodie;
my $start_data;
my $name = "Data_abc";
while(<DATA>){
next unless /$name/;
$start_data = $1 if /^Start:\s+(\w+)/;
}
say 'END of execution';
say "Start: $start_data" if defined $start_data;
__DATA__
===============================
2020-05-20 Name
===============================
Start: Data_abc
Load: Load_data
Because there is no guarantee that that if block is going to execute.
You can either ask if the variable is set before to read it, or just initialize to whatever value makes sense for your use case.

Use of uninitialized value in concatenation (.) or string at ./Merge_gcov_generalised.pl line 226, <FILE2> line 284046 (#1)

I have written a perl script to merge the gcov files from different machines.
Below is the subroutine i have written for merging.
sub merge_gcov()
{
open(FILE1, "<$_[0]") or die "can not open file";
open(FILE2, "<$_[1]") or die "can not open file";
open(FILE3, ">$_[2]") or die "can not open file";
my ($line1, $line2 , $flag );
while ( 1 )
{
$line1 = <FILE1>; # read them each
$line2 = <FILE2>;
last unless ( $line1 || $line2 ); # if both empty exit loop
#
# otherwise test for which one just finished
#
unless( $line1 )
{
$flag = 1;
last;
}
unless( $line2 )
{
$flag = 2;
last
}
#
# now do the voodo on the two lines
#
chomp($line2);
chomp($line1);
if($line1=~/^\s*-/ and $line2 =~/^\s*-/)
{
print FILE3 "$line1\n";
}
elsif($line1=~/^\s*#####/ and $line2 =~/^\s*#####/)
{
print FILE3 "$line1\n";
}
elsif($line1=~/^\s*#####:\s{0,}(\d{1,})/ and $line2 =~/^\s{0,}(\d{1,})/)
{
print FILE3 "$line2\n"
}
elsif($line1=~/^\s{0,}(\d{1,})/ and $line2 =~/^\s*#####:\s{0,}(\d{1,})/)
{
print FILE3 "$line1\n"
}
elsif($line1=~/^\s{0,}(\d{1,})/ and $line2 =~/\s{0,}(\d{1,})/)
{
my #values1 = split(/:/, "$line1");
my #values2 = split(/:/, "$line2");
print FILE3 (" ",$values1[0]+$values2[0]),":","$values1[1]:","$values1[2]\n";
}
else
{
print FILE3 "$line1\n";
print FILE3 "$line2\n";
}
}
close(FILE3);
}
Merging is done properly but after merging i am getting below error....
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 284046 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 284414 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 302995 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 311633 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 311962 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 321536 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 323445 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 329553 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 336009 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 336330 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 338188 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 343170 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 349037 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 349610 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 633937 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 634509 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 634877 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 653458 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 662096 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 662425 (#1)
Use of uninitialized value in concatenation (.) or string at
./Merge_gcov_generalised.pl line 226, <FILE2> line 671999 (#1)
Use of uninitialized value in concatenation (.) or string at
I know i don't get these warnings when i used no warnings,But is there any other way to remove these warnings.
Line 226 is :print FILE3 (" ",$values1[0]+$values2[0]),":","$values1[1]:","$values1[2]\n";
If all you want to do is get rid of that particular warning that you already expect and have accounted for, then all you have to do is this:
{ no warnings 'uninitialized';
# **Small** bit of code causing the problem.
say "A string and $probably_undefined";
}
The reason I stress, small code is that Perl does not have a thousand warning categories to turn on and off by pragma. It has several rather broad categories that if you leave disarmed for too large a space will bite you. The idea is to look at each warned condition and decide if an uninitialized value has no effect--or one that you can at least account for.
You test for one or the other of the lines being uninitialized but you never use that information, so you're printing an undef concatenated with a newline in your final else; this is the source of your warnings.
Your gcov files actually have a bunch of excess newlines in them, caused by one file running out first, one per each unmatched line. They're only valid because gcov is either very forgiving in what it accepts, or because gcov stops reading the input silently when it sees the first bare newline. The first is great; the second is very bad.
To fix this, we need to figure out where we expect to have an undef and what to do when we find one. This code can generate undef in $line1 and $line2 when we hit the end of each file. Let's look at how you use these to see where undef is OK and where it's not.
Testing for both lines being false (e.g., undef): this is a good use of undef.
Setting $flag: good usage, but bad logic, since you never use $flag again.
Pattern matches: good usage, because pattern matches against nothing should (and do) fail, and this is correct for your code.
Printing a line that might be undef: bad usage, as this will generate your warning and output an extra empty newline.
Because the pattern matches will all fail against an undef, you can just skip the code that sets $flag altogether and make the last else look like this:
...
else {
# One or the other file has run out already. Note that
# the $line variables being undef here is OK because that's
# an expected possible value that we're actually testing for.
# We could used "defined $lineX" but since undef itself is
# false, this is okay.
print FILE3 "$line1" if $line1;
print FILE3 "$line2" if $line2;
}
Now for some undef philosophizing.
An uninitialized variable warning is almost always a logic error somewhere, despite Perl being nice about fixing it up. If you are getting these, you should look for the following:
Am I calling something that is returning an undef? If so, am I calling it wrong, or with bad parameters? Can I substitute a default value without it being erroneous, like 0 or '' (using the defined-or // operator), if my call is valid? Do I need a table or sub to give me default values instead? If I can't use a default value, what's wrong with my logic?
If that's not the case, am I referencing an uninitialized array or hash entry? Perl will auto-vivify those entries and set them to undef. Is my index or key bad? If not, should I be setting those entries to a default value myself, bounds-checking my index for an array, or checking if the hash element exists for a hash? Otherwise the criteria in 1. apply.
If neither of those is the case, I have an uninitialized scalar variable. Why isn't it initialized? What should I have initialized it to? What assumption did I forget to make (by not initializing the scalar to a known value)?
Generally speaking, unless your code is supposed to issue a warning (e.g., you added a warn to it), a warning means something isn't as you expected it to be. You should look at that and see why, not just hide the diagnostic.

Prevent Perl from printing identical warning messages

Consider the following nonsense script as an example:
use strict;
use warnings;
my $uninitialisedValue;
while(<>){
print ${$uninitialisedValue}{$_},"\n";
}
Which is run from the command line:
$ perl warningPrinter.pl < longfile.txt
Regardless of what standard input contains, standard output will be full of:
Use of uninitialized value in print at warningPrinter.pl line 16, <> line 1.
Use of uninitialized value in print at warningPrinter.pl line 16, <> line 2.
Use of uninitialized value in print at warningPrinter.pl line 16, <> line 3.
Use of uninitialized value in print at warningPrinter.pl line 16, <> line 4.
...
I work with very long files, so receiving this as output when testing my script is at the very least mildly irritating. It can take a while for the process to respond to a Ctrl + C termination signal and my terminal is suddenly filled with the same error message.
Is there a way of either getting Perl to print just the first instance of an identical and reoccurring warning message, or to just make warning messages fatal to the execution of the script? Seeing as I have never produced a script that works despite having warnings in them, I would accept either. But it's probably more convenient if I can get Perl to print identical warnings just once.
I thought I would show you how unique warning logic might be created. I don't recommend it though:
my %printed;
local $SIG{__WARN__} = sub {
my $message = shift;
my ( $msg, $loc ) = $message =~ m/(.*?) at (.*?line \d+)/;
print $message unless $printed{$loc}{$msg}++;
};
I should say that I do not recommend this as a general practice. Because it's better to have a warning policy. It's either an operation that can take an undefined value, or you don't want to handle an undef value. I try to remove all warnings from my completed code.
In the first case, putting no warnings 'uninitialized'; in the for loop is a much easier--and regular thing to do. In the second case, you'd probably want to fail.
However, if it is something you would actually like to handle but warn once about, say that you wanted robust handling of the data, but wanted to warn upstream processes that you got some bad data, you could go about creating a sub warn_once:
{ use Carp ();
my %warned;
sub warn_once {
my $message = shift;
my ( $msg, $loc ) = $message =~ m/(.*?) at (.*?line \d+)/;
Carp::carp( $message ) unless $warned{$loc}{$msg}++;
};
}
And call it like this:
while ( <> ) {
warn_once( '$uninitialisedValue is uninitialized' )
unless defined( $uninitialisedValue)
;
no warnings 'uninitialized';
print ${$uninitialisedValue}{$_},"\n";
}
Then you have decided something.

Why doesn't die $template->error() show a line number?

In the following short program:
use Template;
my $template = Template->new (INCLUDE_PATH => ".");
$template->process ("non-existent-file")
or die $template->error ();
why doesn't die produce a line number and newline? My output looks like this:
~ 502 $ perl template.pl
file error - non-existent-file: not found ~ 503 $
Template is returning an error object of type Template::Exception. The object has overloaded stringification which applies when the value is printed, but when die looks at the value, it sees a reference and doesn't append the line number and newline. Force the value into a string earlier to fix the problem:
use Template;
my $template = Template->new (INCLUDE_PATH => ".");
$template->process ("non-existent-file")
or die '' . $template->error ();
prints
file error - non-existent-file: not found at scratchpad.pl line 25.
While #Eric's answer does solve the OPs issue, I suggest appending a space instead of pre-pending an empty string.
The reason is that if there are problems in the template, the error will be reported as coming from the template text instead of the line number in the perl file (which is what I want). See this short example:
use Template;
my $template = Template->new();
# Clearly a division by zero bug
$template->process(\"[% 1 / 0 %]")
or die $template->error();
This results in:
undef error - Illegal division by zero at input text line 1.
Which isn't very helpful. I want the perl file location. Instead I suggest:
my $template = Template->new();
$template->process(\"[% 1 / 0 %]")
or die $template->error() . ' ';
which produces:
undef error - Illegal division by zero at input text line 1.
at test.pl line 11.
This way I get the line number in the perl file too. It does look a little ugly, though. (You can stop reading now, if you like...)
An even more correct way would be:
use Template;
my $template = Template->new();
$template->process(\"[% 1 / 0 %]")
or do {
my $error = $template->error . '';
chomp $error;
die $error;
};
which produces this output:
undef error - Illegal division by zero at input text line 1. at t2.pl line 15.
But it is just so verbose and has a weird . in there. I've actually ended up creating:
sub templateError {
my ($template) = #_;
my $string = $template->error->as_string;
chomp $string;
$string =~ s/(line \d+)\.$/$1/;
return $string;
}
...
use Template;
my $template = Template->new ();
$template->process (\"[% 1 / 0 %]")
or die templateError($template);
So that I get this:
undef error - Illegal division by zero at input text line 1 at test.pl line 30.
as well as this for the OPs example:
file error - non-existent-file: not found at test.pl line 31.

Why do I get "uninitialized value" warnings when I use Date::Manip's sortByLength?

How might this block of code in Date/Manip.pm from the Date::Manip module:
#*Get rid of a problem with old versions of perl
no strict "vars";
# This sorts from longest to shortest element
sub sortByLength {
return (length $b <=> length $a);
}
use strict "vars";
I get this warning:
Use of uninitialized value in length at /perl/lib/perl5.8/Date/Manip.pm line 244.
The problem is not actually located there; the function is just being called with invalid (undef) parameters. To get a better trace of where it came from, try this:
$SIG{__WARN__} = sub {
require Carp;
Carp::confess("Warning: $_[0]");
};
This will print a stacktrace for all warnings.
Either $a or $b are undef. Check the list you are feeding to the sort that uses this subroutine to see if you have an undefined value.
How are you using this code?
If warnings for uninitialized diagnostics were enabled (perhaps via blanket -w or use warnings;) and if sortByLength were somehow called as a normal subroutine, rather than as a sort {} function, you would likely see this error:
$ perl -Mwarnings=uninitialized -e 'sub sbl { (length $b <=> length $a) } sbl'
Use of uninitialized value in length at -e line 1.
Use of uninitialized value in length at -e line 1.
Here I get two warnings, because both $a and $b are uninitialized. Hard to say without more context.