Likeless has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

The essence of this question is: what could cause two virtually identical pieces of code to be behaving differently?

The first piece, a complete script, is this:

#!/usr/bin/perl -- use File::Spec; use Image::Magick; my $imbase = Image::Magick->new(magick=>'png') or die("Image creation +failed."); # Read the predefined letter PNG for each letter my $x = $imbase->Read(map { File::Spec->catfile('/my/web/httpd/htdocs/ +mt/images/captcha-source/',$_.'.png') } (a..z)); if ($x) { die($x); }
The second piece, (an excerpt from a module), is this:
require Image::Magick; my $imbase = Image::Magick->new(magick=>'png') or return $app->error($app->translate("Image creation failed.")); # Read the predefined letter PNG for each letter in $code my $x = $imbase->Read(map { File::Spec->catfile($base, $_ . '.png') } +split(//, $code)); if ($x) { return $app->error($app->translate("Image error: [_1]", $x)); }
The first piece runs from the command line without error.

The second piece, running in a web browser, returns this error:

close Image error: Exception 425: Corrupt image `/my/web/httpd/htdocs/mt/images/captcha-source/k.png'

I've done some debugging, and have confirmed that:
- The image file in question is valid
- There is only one copy of Image::Magick on the system
- There are 2 copies of File::Spec, but I tested both in the first script, and neither caused an error

So I'm wondering: what else could cause the error? I'm not aware of any possible cause to make these two code snippets respond differently.

The module in question is from a third party piece of open source software (Movable Type Open Source). I have pasted the complete subroutine
sub _generate_captcha { my $self = shift; my ($app, $code, $format) = @_; $format ||= 'png'; my $len = LENGTH(); my $cfg = $app->config; my $base = $cfg->CaptchaSourceImageBase; unless ($base) { require File::Spec; $base = File::Spec->catfile(MT->instance->config_dir, 'mt-stat +ic', 'images', 'captcha-source'); $base = undef unless (-d $base); } return $app->error($app->translate('You need to configure CaptchaS +ourceImageBase.')) unless $base; require Image::Magick; my $imbase = Image::Magick->new(magick=>'png') or return $app->error($app->translate("Image creation failed." +)); # Read the predefined letter PNG for each letter in $code my $x = $imbase->Read(map { File::Spec->catfile($base, $_ . '.png' +) } split(//, $code)); if ($x) { return $app->error($app->translate("Image error: [_1]", $x)); } # Futz with the size and blurriness of each letter foreach my $i (0..($len - 1)) { my $a = int rand int(WIDTH() / 14); my $b = int rand int(HEIGHT() / 12); $imbase->[$i]->Resize(width => $a, height => $b, blur => rand( +3)); } # Combine all the individual tiles into one block my $tile_geom = join('x', $len, 1); my $geometry_str = join('x', WIDTH(), HEIGHT()); my $im = $imbase->Montage(geometry => $geometry_str, tile => $tile_geom); $im->Blur(); # Add some lines and dots to the image for my $i (0..($len * WIDTH() * HEIGHT() / 14+200-1)) { my $a = int rand($len * WIDTH()); my $b = int rand HEIGHT(); my $c = int rand($len * WIDTH()); my $d = int rand HEIGHT(); my $index = $im->Get("pixel[$a, $b]"); if ($i < ($len * WIDTH() * HEIGHT() / 14+200) / 100) { $im->Draw(primitive => 'line', stroke => $index, points => "$a, $b, $c, $d"); } elsif ($i < ($len * WIDTH() * HEIGHT() / 14+200) / 2) { $im->Set("pixel[$c, $d]" => $index); } else { $im->Set("pixel[$c, $d]" => "black"); } } # Read in the background file my $a = int rand(5) + 1; my $background = Image::Magick->new(); $background->Read(File::Spec->catfile($base, 'background' . $a . ' +.png')); $background->Resize(width => ($len * WIDTH()), height => HEIGHT()) +; $im->Composite(compose => "Bumpmap", tile => 'False', image => $background); $im->Modulate(brightness => 105); $im->Border(fill => 'black', width => 1, height => 1, geometry => join('x', WIDTH() * $len, HEIGHT())); my @blobs = $im->ImageToBlob(magick=>$format); return $blobs[0]; }
I'm not asking for full debugging (though if you happen to know the cause that's great). It's more of a perl theory question for me: what is capable of causing those 2 calls to $imbase->Read to respond differently when it seems to me they should be running identical procedures on identical data?

Thank you!

Replies are listed 'Best First'.
Re: Strange behaviour from two pieces of similar code in different running environments
by starbolin (Hermit) on May 15, 2008 at 00:26 UTC

    The two snippets are not the same. The first imports Image::Magic with the use operator and the second uses the require operator which does not import names from the Image::Magic:: name-space.


    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
Re: Strange behaviour from two pieces of similar code in different running environments
by psini (Deacon) on May 14, 2008 at 22:06 UTC

    A question: do you get an error only reading k.png or it fails on any letter?

    Rule One: Do not act incautiously when confronting a little bald wrinkly smiling man.

Re: Strange behaviour from two pieces of similar code in different running environments
by moritz (Cardinal) on May 14, 2008 at 22:09 UTC
    As you said the environment could be slightly different, so for example missing permissions or a full RAM could cause a load failure with a not-so-accurate error message.
      It has happenned with all the letters I've tested, of which there have been 7 so far.

      The two scripts are running on the same machine simultaneously. The RAM is plentiful there.
        Do they run as the same user? (And if you have selinux enabled: in the same security context?)

        Does any of the scripts try to lock any of the files? (You can find out with strace I think). What about space on /tmp/?

Re: Strange behaviour from two pieces of similar code in different running environments
by ysth (Canon) on May 15, 2008 at 07:13 UTC
    Some things to check: different permissions due to running as a different user, different environment variables, taint mode.
Re: Strange behaviour from two pieces of similar code in different running environments
by starbolin (Hermit) on May 16, 2008 at 05:56 UTC

    Ok. I'll put in a quarter and play again. The Image::Magick module is just a thin wrapper on the libMagick libraries. Unlike a pure perl module, which is interpreted, the libMagick libraries have to be dynamically linked during a load phase. Many common problems encountered when linking is when calling and called routines are compiled with different compilers or when executables were linked with different header files. To cover the first how was the Image::Magick module installed? Has it been tested? Was it compiled against the same perl distribution as is currently being used? To cover the second, is the CGI server using the same perl executable as the command line example? Are there different perl executables living on the system?

    The DynaLoader process relies on the @ISA array to lookup symbol names. For your test script to accurately model the behavior of the CGI script @ISA would have to be correctly initialized to be the same as the CGI version


    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}