[boulder.pm] Catching incorrect subroutine calls

Jay Kominek jay.kominek at colorado.edu
Fri Aug 4 00:34:00 CDT 2000


I was playing with the AUTOLOAD subroutine earlier tonight, and then I
decided that I should send something useful to the mailing list. So I
decided to cross both things.

When programming, one occasionally makes typos. As unpleasent as it is
to admit our own imperfection, it can and does happen. :) When it happens
with subroutines in Perl, you get the exceedingly useful message:

[jkominek at nehwon /home/jkominek]$ perl -e '&foo;'
Undefined subroutine &main::foo called at -e line 1.

Spectacular. It tells us what subroutine doesn't exist, and what line it
is on. What more could you possibly want?

Not much for static subroutines. However, what if you're actually writing
_interesting_ Perl and you're dynamically defining subroutines, or eval'ing
code that you've constructed with strings that were originally Unicode
cyrillic that you've turned into Perl with a regular expression?

You're in trouble.

AUTOLOAD to the rescue.

AUTOLOAD automatically catches all invalid subroutine references and executes
itself. All of the subroutine arguments are placed into @_, the standard
place, while the name of the subroutine you tried to call is in $AUTOLOAD.

AUTOLOAD {
  die "You tried to call $AUTOLOAD with arguments: @_\n";
}

My. Thats pretty useful. Now you can see what you tried to call, what package
it was in, and the arguments you passed to it. That'll do a pretty good job
of narrowing it down.

But wait, we can do better yet!

With a little bit of magic, we can find out what called us!

AUTOLOAD {
  my $i=0;
  my(@call, at stack);
  print "*** Wrong subroutine call auto-detector\n";
  for(;;) {
    @call = caller($i++); # Goes back $i calls in the stack
    last unless @call;    # once you go too far back it returns empty lists
    unshift @stack, [@call]; # stick them in backwards
  }
  print "* Stack trace\n";
  foreach my $call (@stack) {
    my @call = @{$call};
    print "File $call[1], package $call[0], line $call[2] called $call[3], $call[4] argument(s)\n";
  }
  # die, because whatever subroutine you wanted to call didn't get called
  # so things are probably fairly hosed at this point.
  die "* You tried to call $AUTOLOAD with arguments: q\{@_\}\n";
  # If you need more help, use Data::Dumper and then:
  # foreach my $structureref (@listofbigimportantdatastructureshere) {
  #  print Dumper($structureref);
  # }
}

^ My masterpiece for the evening.

While debugging, you simply place a copy of that into every package you
have. If you look up the caller function, you can also get it to print out
the code which was eval'd for one of the call steps, assuming it was a
string eval, not a block eval.

An example program, and its output, assuming that the above subroutine
is defined in package Magic:

#!/usr/bin/perl
sub foo { &bar; }
sub bar { Magic::FlyingGerbil("frobinification",
                              {'10am' => "softwareengineering",
                               '2pm' => "astrophysics1",
                               '3pm' => "algorithms"}); }
&bar;

This will produce output of:

*** Wrong subroutine call auto-detector
* Stack trace
File foo.pl, package main, line 17 called main::bar, 0 argument(s)
File foo.pl, package main, line 13 called Magic::AUTOLOAD, 1 argument(s)
* You tried to call Magic::FlyingGerbil with arguments: q{frobinification HASH(0x80cb8fc)}

You might consider replacing the die line with something that can also
Data::Dumper references so you can see what was in them. Or just
Data::Dumper \@_

I just realized that there is a way to get Perl to automatically do something
like this with the debugging stuff.

I'm going to pretend it isn't there, though, because if I admit to it,
I'll have wasted all this time.

But even if I do admit to it, this is more modifyable than the hardcoded
dump thing, and it shows people how to use AUTOLOAD.

All code was roughly tested, but I've never actually used it for the purpose
described here in development code, just my test program. If it blows up
and takes out part of Boulder.. well. I hope I don't live in that part,
then. Code is public domain. If you make money off of it, feel free to buy
me dr pepper. If you don't make money off of it, feel free to buy me dr
pepper.

There hasn't been a meeting in awhile, right? Lets see. I'd be up for
sitting around in some public place in two or three weeks. I'm not keen
on the entire meeting-in-a-restraunt since I'm too cheap to eat out.
Maybe wait for school to start? We could stick up signs in the engineering
center on campus then.

- Jay Kominek <jay.kominek at colorado.edu>
  If at first you don't succeed,
  Increase the amperage.



More information about the Boulder-pm mailing list