Re: Troubleshooting perl runtime errors
by BrowserUk (Patriarch) on Nov 16, 2016 at 14:52 UTC
|
$e = sprintf( '%s', undef );;
Use of uninitialized value in sprintf at (eval 28) line 1, <STDIN> lin
+e 20.
And this shows how to trap that error, get access to its text (as the first argument to the $SIG{__WARN__} pseudo-signal handler routine):
eval{
local $SIG{__WARN__} = sub{
printf STDERR "Warning detected: %s", $_[0];
};
$e = sprintf( '%s', undef );
};;
Warning detected: Use of uninitialized value in sprintf at (eval 29) l
+ine 1, <STDIN> line 21.
Substitute your log file handle for STDERR; and decorate or modify the output as you see fit.
See perlvar (search for "__WARN__") for more information.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
Re: Troubleshooting perl runtime errors
by pryrt (Abbot) on Nov 16, 2016 at 15:13 UTC
|
For logging, use something like Log::Any. In the line just before where the error is occuring, I would $log->debugf("DIcas_text='%s', HR=%s", \@HR); -- but that will add to your log whether or not there was an error.
As for debugging the problem: see perldiag#Use of uninitialized value: something in your @HR is coming up as an undef.
Armed with that knowledge, I might change the logging to something like what's used in the following SSCCE:
use warnings;
use strict;
use Log::Any qw($log);
use Log::Any::Adapter ('File', './debug.log');
sub errcheck_array {
my $logger = shift;
my $prefix = shift;
my $count = grep {!defined} @_; # count the number of undefined el
+ements in input array
return unless $count; # this line will prevent a log mes
+sage unless there's an undef; comment it out to log _all_ calls, whet
+her or not there's an undefined value
$logger->debugf("%s: %d undefined values found in %s", $prefix, $c
+ount, \@_);
1;
}
foreach my $hr ( [qw(a b c)], [a => undef, b => undef, 'c'], [qw(x y z
+)] ) {
my @HR = @$hr;
my $DIcas_text = '%s' . ',%s'x$#HR;
errcheck_array($log, "sprintf('$DIcas_text', \@HR)", @HR);
my $EVmsg = sprintf($DIcas_text, @HR); print "STD OUTPUT> $EVmsg\
+n";
}
| [reply] [d/l] [select] |
|
c:\@Work\Perl>perl -wMstrict -le
"print 'perl version ', $];
;;
my @HR;
;;
printf qq{A: %s \n}, @HR;
;;
++$#HR;
printf qq{B: %s \n}, @HR;
"
perl version 5.008009
Use of uninitialized value in printf at -e line 1.
A:
Use of uninitialized value in printf at -e line 1.
B:
perl version 5.010001
Use of uninitialized value in printf at -e line 1.
A:
Use of uninitialized value in printf at -e line 1.
B:
perl version 5.012003
Missing argument in printf at -e line 1.
A:
Use of uninitialized value in printf at -e line 1.
B:
perl version 5.014004
Missing argument in printf at -e line 1.
A:
Use of uninitialized value in printf at -e line 1.
B:
(See 'Missing argument in %s' in perldiag in version 5.12 and after.)
Update: Posted this reply before seeing other, closely related replies: this from Monk::Thomas and this from hippo.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
Yeah, with 5.8.x, the warnings are indistinguishable. And thanks for pointing out that the empty array will also create that error: my errcheck_array() didn't handle the empty array condition.
Further, while both my method and the BrowserUk/stevieb method will help you find what's wrong, neither will help sanitize the input to prevent the warning in a live-system (for example, if you need your debug logging live until the unlikely data triggers it, but you still need the production code to set a reasonable value for $EVmsg): it's safer in production code to try to sanitize the input, to prevent such a condition from affecting your users. For that, you could wrap an error-checker-and-sanitizer around the sprintf command, or otherwise sanitize before running the sprintf. This example has a wrapper sprintf_prevent_error() and a sanitize() function which cleans and logs, but keeps the sprintf separate:
use warnings;
use strict;
use Log::Any qw($log);
use Log::Any::Adapter ('File', './debug.log', log_level => 'debug');
sub sprintf_prevent_error {
my $format = shift;
my @a = @_;
# if there isn't a format, log an error message and return an erro
+r string
# might want to return undef instead, and check $EVmsg for undef
+ before blindly doing anything else with it...
unless(defined $format) {
$log->debugf("sprintf('%s',%s): format is not defined", $forma
+t, \@_);
return "<WARNING: sprintf format not defined>";
}
# if there aren't any arguments, the array is undefined; log an er
+ror message and return an error string
# might want to return undef instead, and check $EVmsg for undef
+ before blindly doing anything else with it...
unless(@a) {
$log->debugf("sprintf('%s',%s): array is empty", $format, \@_)
+;
return "<WARNING: array is empty>";
}
# count the number of undefined elements in input array, and log t
+he error message
# but _don't_ return; we will sanitize the inputs and continue t
+o create the sprintf
my $count = grep {!defined} @a;
$log->debugf("sprintf('%s',%s): sanitizing %d undefined values", $
+format, \@_, $count) if $count;
# sanitizes the input: replace any undefined elements with the emp
+ty string
$_//='' for @a;
# return the final sprintf of the sanitized input
sprintf $format, @a;
}
sub sanitize {
$log->debugf("sanitize(%s): calling", \@_);
unless(@_) {
# log and return if there's nothing to sanitize
$log->debugf("sanitize(%s): nothing to sanitize", \@_);
return;
};
my $count = grep {!defined} @_; # count the undefined argument
+s
$log->debugf("sprintf(%s): sanitizing %d undefined values", \@_, $
+count) if $count;
$_//='' for @_;
}
foreach my $hr ( [qw(a b c)], [d => undef, e => undef, 'f'], [qw(x y z
+)], [] ) {
my @HR = @$hr;
my $DIcas_text = @HR ? '%s' . ',%s'x$#HR : 'empty array format';
my $EVmsg = sprintf_prevent_error($DIcas_text, @HR);
print "STD OUTPUT> $EVmsg\n";
$EVmsg = sprintf_prevent_error(undef, @HR);
print "STD OUTPUT> $EVmsg\n";
sanitize($DIcas_text, @HR);
$EVmsg = sprintf($DIcas_text, @HR);
}
| [reply] [d/l] [select] |
Re: Troubleshooting perl runtime errors
by stevieb (Canon) on Nov 16, 2016 at 15:31 UTC
|
I'm going to bite on BrowserUK's post, and add a bit to it. Here, still using his technique, we create a hash reference with the variables we want to log if the warning contains uninitialized, and dump them all with their names and values to the log file.
Since this appears to be happening only in one spot in your script, doing it this way is trivial. It's quite a bit more complex if you are seeing these things in numerous spots in a 1194+ line script.
use warnings;
use strict;
use Data::Dumper;
open my $log_fh, '>', 'log.txt' or die $!;
my @HR = (1, 2, undef, 3);
my $thing = undef;
my $blah = 99;
{
local $SIG{__WARN__} = sub {
if ($_[0] =~ /uninitialized/){
my $warn = {
'@HR' => \@HR,
'$thing' => $thing,
'$blah' => $blah,
};
print $log_fh Dumper $warn;
print $log_fh "\n";
}
};
my $x = sprintf $thing, @HR;
}
...and this is what is logged:
$VAR1 = {
'$blah' => 99,
'$thing' => undef,
'@HR' => [
1,
2,
undef,
3
]
};
update: typo fixes, removed eval. /update | [reply] [d/l] [select] |
Re: Troubleshooting perl runtime errors
by Monk::Thomas (Friar) on Nov 16, 2016 at 16:00 UTC
|
Using perl 5.10 I get the 'Use of uninitialized value in sprintf at <script> line <line>.' only if $DIcas_text string specifies more values then @HR actually contains. If a value in @HR is undefined or the format string is undefined I get a different error message.
#!/usr/bin/perl
use strict;
use warnings;
my ($DIcas_text, @HR, $s);
$DIcas_text = "%s %s %s %s";
@HR = qw(a b c);
$s = sprintf($DIcas_text, @HR);
$DIcas_text = "%s %s %s";
@HR = ('a', undef, 'c');
$s = sprintf($DIcas_text, @HR);
$DIcas_text = undef;
@HR = qw(a b c);
$s = sprintf($DIcas_text, @HR);
output
Use of uninitialized value in sprintf at /tmp/u.pl line 10.
Use of uninitialized value $HR[1] in sprintf at /tmp/u.pl line 14.
Use of uninitialized value $DIcas_text in sprintf at /tmp/u.pl line 18
+.
If you get something similar with Perl 5.8 then this might help to figure out what exactly is wrong. | [reply] [d/l] [select] |
|
$ perl -v
This is perl, v5.8.8 built for x86_64-linux-thread-multi
Copyright 1987-2006, Larry Wall
Perl may be copied only under the terms of either the Artistic License
+ or the
GNU General Public License, which may be found in the Perl 5 source ki
+t.
Complete documentation for Perl, including FAQ lists, should be found
+on
this system using "man perl" or "perldoc perl". If you have access to
+ the
Internet, point your browser at http://www.perl.org/, the Perl Home Pa
+ge.
$ cat uninit.pl
#!/usr/bin/perl
use strict;
use warnings;
my ($DIcas_text, @HR, $s);
$DIcas_text = "%s %s %s %s";
@HR = qw(a b c);
$s = sprintf($DIcas_text, @HR);
$DIcas_text = "%s %s %s";
@HR = ('a', undef, 'c');
$s = sprintf($DIcas_text, @HR);
$DIcas_text = undef;
@HR = qw(a b c);
$s = sprintf($DIcas_text, @HR);
$ ./uninit.pl
Use of uninitialized value in sprintf at ./uninit.pl line 10.
Use of uninitialized value in sprintf at ./uninit.pl line 14.
Use of uninitialized value in sprintf at ./uninit.pl line 18.
| [reply] [d/l] |
|
That's a pity. Thanks for verifying!
| [reply] |
Re: Troubleshooting perl runtime errors
by lvirden (Novice) on Nov 16, 2016 at 19:20 UTC
|
All of these suggestions are great. I will have to study them to determine the best general answer.
As I mentioned, the script is dealing with all sorts of events streaming from our thousands of devices. New devices in particular stream in SNMP that may not be patterned like the rest of the inputs.
I have to study the input more to determine how best to sanitize/normalize/ensure that all the required fields are present with expected values, etc.
I really appreciate all your help!
Thank you and have a wonderful day. | [reply] |