stmullins has asked for the wisdom of the Perl Monks concerning the following question:
How can I examine a Perl file (utility or module), and get list of all of the subroutines used in it, and where they come
from?
Our utilities use a lot of in-house modules, and it's routine for developers to simply paste in a long list of
'use modX;' statements without checking whether or not the module is actually needed. This is the case not only for
utilities, but for the modules themselves. Or, if a subroutine is no longer used in file, the use statement that
pulled it in isn't updated or removed. The result is that we've accumulated a lot of unused functionality, and can't
identify what we don't need. I'd like to figure out which subroutines in an imported module are actually used, so we
can clean up or consolidate our libraries. I could also build a map of our entire repository of utilities and modules
and give awards to those subroutines/modules that are frequently referenced ... :)
A couple of considerations:
- I can't execute the utility/module I'm analyzing. (perl -c is ok, tho' -- see approach 2 below)
This constraint eliminated the use of some promising CPAN modules.
- I need to recognize object methods as subroutine calls. (Happily, we don't use class inheritance.)
There are two approaches I've considered:
- Write a parser to make a list of the subroutines defined or called within a file (utility or module).
I started with this, but it quickly got messy, since subroutine calls may or may not use parentheses, might be
nested in other calls, etc.
- Let Perl do the parsing, and examine the symbol table for CODE symbols. This is where perl -c is handy for
compiling everything, then executing BEGIN blocks, but not executing anything else:
cat <utility|module> begin.pl | perl -c
where begin.pl is something like:
BEGIN {
print "$_=$main::{$_}\n" foreach grep(*{$main::{$_}}{CODE}, sort keys %main::);
}
This does list all of the code symbols defined in the utility ... and, unfortunately, those defined in any imported
modules, all listed as if belonging to main::. I'm not sure how to distinguish the imported symbols from those
defined in the target file.
Note that begin.pl is actually more complicated than the above because I want to harvest module locations from %INC,
and potentially look at the module symbol tables as well.
Any ideas?
Re: Identify subs and their source
by Khen1950fx (Canon) on May 25, 2011 at 07:30 UTC
|
Always search PerlMonks. I did a brief search and found:
Tabulate sub defs, sub calls in Perl code by
graff.
It'll work on a script, a module, modules, a directory, or directories. It uses
B::Deparse, so I think that it meets your constraints. Here's
the link for scan-source.perl.
A cool little tool that I use to analyse subs is
Sub::Mutate.
It can tell you if a sub is a method, function, XSUB, STANDALONE, etc. For example, I took the Dumper sub from Data::Dumper:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper::Simple;
use Sub::Mutate qw(
sub_body_type
sub_closure_role
sub_is_lvalue
sub_is_constant
sub_is_method
mutate_sub_is_method
sub_is_debuggable
mutate_sub_is_debuggable
sub_prototype
mutate_sub_prototype
);
my $sub = \&Dumper;
print my $type = sub_body_type($sub), "\n";
print $type = sub_closure_role($sub), "\n";
if ( sub_is_lvalue($sub) ) {
print "sub is an lvalue\n";
}
else {
print "sub is not an lvalue\n";
}
if ( sub_is_constant($sub)) {
print "sub is a constant\n";
}
else {
print "sub is not a constant\n";
}
if ( sub_is_method($sub)) {
print "sub is a method\n";
}
else {
print "sub is not a method\n";
}
if ( sub_is_debuggable($sub)) {
print "sub is debuggable\n";
}
else {
print "sub is not debuggable\n";
}
my $proto = sub_prototype($sub);
print Dumper($proto);
sub Dumper {
return Data::Dumper->Dump([@_]);
}
| [reply] [d/l] |
Re: Identify subs and their source
by davido (Cardinal) on May 24, 2011 at 23:58 UTC
|
There is B::Xref, but if you want to test whether every module that is used is actually used, as well as whether every module that is used is actually used, there is Test::Module::Used. I'm not sure if that's useful, but it or its source might point you in the right direction.
| [reply] [d/l] [select] |
Re: Identify subs and their source
by TomDLux (Vicar) on May 24, 2011 at 23:25 UTC
|
Sounds like you want something like Devel::dprof ( or NYTProf ), but not to track how long is spent in each subroutine, but rather to generate a list of all subroutines.
You could write something resembling the profiling module ... You might start with an actual profiling module, and strip away most of the functionality, merely storing the fully-qualified name of each subroutine. Or you could write something to parse tmon.out, the data file generated by the existing modules.
This is easier for small programs than large ones, especially considering the Devel:X module will slow things down drastically.
As Occam said: Entia non sunt multiplicanda praeter necessitatem.
| [reply] |
Re: Identify subs and their source
by John M. Dlugosz (Monsignor) on May 24, 2011 at 22:14 UTC
|
Regarding “I'm not sure how to distinguish the imported symbols from those defined in the target file.”, I've heard of something for that.
This is similar to what autoclean and Moose does.
I think if you use the Sub::Name module, you can find out the full name that the sub was declared in. That will tell you if it was imported.
| [reply] [d/l] |
|
|