Mark Dominus on 11 Oct 2003 04:36:17 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: Perl source filter question


Mike Cramer <cramer@webkist.com>:
> I guess it just feels smaller to me -- I don't need eval, I don't need 
> to know which functions *are* called, just which functions *could be* 
> called. It just felt smalled to me.

But it's *easy* to know which functions *are* called.  You just run
the program.  Finding out which functions *could be* called, without
running the program, requires one to foresee the future.

You said you don't need 'eval', but you do, because 'eval' is how
modules are loaded.

> I figured that Perl knows how to parse itself, so shouldn't it be able 
> to give me a look at what it finds? 

You could write a B:: module that runs over the op tree after
compilation and looks for all the 'select' ops.  The B::Utils module
would probably be useful for this.

A source filter isn't the way to go, because the source filter gets
the code *before* it's parsed, not after. 

Here's an alternative approach.  It's a module caled Detect_Select.
If you run your program with

        perl -MDetect_Select myprogram.pl

then it catches all calls to the 'select' function.  It emulates them
normally, but provides hooks so that you can instrument your program
to log a message to a log file whenever someone does a one-argument
select.

        package Detect_Select;
        use subs 'select';

        sub select {
          require Carp;
          my @args = @_;
          if (@args == 4) {
        #    Carp::carp("Four-arg select");
            return CORE::select($_[0], $_[1], $_[2], $_[3]);
          } elsif (@args == 0) {
        #    Carp::carp("Zero-arg select");
            return CORE::select();
          } elsif (@args == 1) {

            unless (defined (fileno SELECT_LOG)) {
              open SELECT_LOG, ">>", "/tmp/select-$$" or die $!;
            }

            my $handle = $_[0];
            my ($package, $filename, $line, $subname) = caller(1);
            if (! ref $handle && $handle !~ /::/) {
              $handle = "$package\::$handle";
            }
            print SELECT_LOG "select($handle) invoked by sub $subname at $filename line $line\n";
            return CORE::select($handle);
          } else {
            Carp::croak("Not enough arguments for select system call");
          }
        }

        END { close SELECT_LOG }

        BEGIN {
          # Replace built-in select with our synthetic select function
          *CORE::GLOBAL::select = \&select;
        }

        1;


Of course, to do this, you have to run the program.  And it can't
detect whether the program might call select at some time in the
future.



> I think my hope of a compile-time function list generator was
> significantly more complicated than the problem itself.

One thing you can get at compile time is the list of functions that
exist at compile time.  You can even get control after the normal
compile phase is over, so that you list the functions that were
defined in modules.  Naturally, it can't pick up autloaded functions,
functions that will be defined by 'eval' during the run pahse,
function loaded by 'require', and so on.  It also may run some code,
because loading a module in Perl runs the code in that module, an also
calls the module's 'import' function, if it exists.



        package List_Functions;

        my %function;

        CHECK {
          my @todo = ('');
          while (@todo) {
            my $package = shift @todo;
            redo if $package eq 'main';
            my $stash = \%{"$package\::"};
            while (my ($name, $glob) = each %$stash) {
              my $fullname = "$package\::$name";
              $fullname =~ s/^:://;
              if ($fullname =~ s/::$//) {
                push @todo, $fullname;
              } elsif (defined &$fullname) {
                my $address = 0 + \&$fullname;
                push @{$function{$address}}, $fullname;
              }
            }
          }

          for my $address (sort keys %function) {
            my ($first, @rest) = @{$function{$address}};
            printf("%#10x %s\n", $address, $first);
            for my $f (@rest) {
              print "           $f\n";
            }
          }

        }

        INIT { exit 0 }

        1;

To use this, do

        perl -MList_Functions my_program.pl

I hope this is of some amusement even if it's not what you want.


-
**Majordomo list services provided by PANIX <URL:http://www.panix.com>**
**To Unsubscribe, send "unsubscribe phl" to majordomo@lists.pm.org**