So glad you asked. I was just thinking about this the other day, and wrote this script called... wait for it...

"subvirgin"

subvirgin finds virgin subroutines. Well, more accurately it lets you find virgin subroutines by a process of elimination, because it logs each subroutine as its used. You still have to do the work of figuring out which subroutines were not used. OK, so there's some room for improvement there, I admit, but still, it's pretty nifty if I do say so myself. One more caveat: it won't do a great job unless you can, through your own ingenuity and luck, get the code you are examining to run through most of its possible execution paths.

All it does is insert some code at the top of each subroutine, using an appropriately-named subroutine, "insert_instrument()" ... cough. This code calls a small SVGN.pm module that logs the name of each subroutine as it runs.

In case it's not obvious, the main value of this tool would be in situations where you are faced with a large, unfamiliar Perl code base and have been told that some of the code is old, orphaned code that is no longer used, and you want to determine which code is still used and which is not (though for the negatives, it won't be with 100% reliability, unfortunately).

You can add your own code to the SVGN module shown at the bottom, so it could do fancier stuff, like, for example, printing the subroutine's caller and arguments. But for now I'll leave that as an exercise for the reader.

Here's the code. Oh, and it doesn't do anonymous or nested subroutines at the moment... sorry about that. Should be easy to fix.

**** WARNING: This has not been extensively tested, and more to the point, IT WILL DO A DESTRUCTIVE UPDATE OF ALL PERL FILES IT FINDS IN THE CURRENT DIRECTORY so use it carefully and work on a copy of your code. ***

#!/usr/bin/perl # # subvirgin v0.42 # use strict; use warnings; # TODO: matches, but does not capture, prototypes and attributes # determine whether this matters, and, if so, add it die "\n\nWARNING: this program has not been extensively tested, and it + will do a destructive update of any .pl, .pm, and .cgi files it find +s in the current directory. If you are sure you want to run this prog +ram, work in a special directory containing an extra copy of the perl + files you want to instrument, and remove this line before running th +e program.\n\n"; opendir(DIR, ".") or die "error: $!"; my @files = grep(!/^(subvirgin\.pl|SVGN\.pm)$/, grep(/\.(?:pl|cgi|pm)$/, readdir(DIR))); closedir(DIR); foreach my $file (@files) { insert_instrument($file); } sub insert_instrument { my $file = shift; my ($mode,$atime,$mtime) = (stat($file))[2,8,9]; open(IN, $file) or die "error: $!"; open(OUT, ">$file.tmp") or die "error: $!"; my $sub_name = ''; my $at_start = 1; my $package = ''; while (my $line=<IN>) { chomp($line); next if ($line =~ /^use SVGN;$/); if ($line =~ m{^package\s+(.*);\s*$}) { $package = $1 . '::'; print OUT "$line\n"; } elsif ($line =~ m{^\s*1;\s*$}) { $package = ''; print OUT "$line\n"; } elsif ($line =~ /^\s*(#.*)?$/) { print OUT "$line\n"; } elsif ($at_start) { print OUT "use SVGN;\n"; print OUT "$line\n"; $at_start = 0; } elsif ($sub_name ne '') { next if ($line =~ /^\s*SVGN::doit/); my $indent = ""; if ($line =~ /^(\s*)/) { $indent = $1; } print OUT "${indent}SVGN::doit(\"$package$sub_name\");\n"; print OUT "$line\n"; $sub_name = ''; } elsif ($line =~ m{^ ( # everything up to open curly (\s*) # optional leading space sub\s+ # sub declaration (\S+)\s* # name of subroutine (?:\([^)]*?\)\s*)? # optional prototype (?:\s*:\s*\S+\s*)? # optional attributes \{ # open subroutine block ) \s* # nuke any space before capture (\S.*)? # catch one-line subroutines $}x ) { $sub_name = $3; if (defined($4) && $4 ne '') { # for one-line subroutines my $body = $4; $line = "$1 SVGN::doit(\"$package$sub_name\"); $4"; print OUT "$line\n"; $sub_name = ''; } else { print OUT "$line\n"; } } elsif ($line !~ m{^(\s*)SVGN::doit\(\S+\)$}) { print OUT "$line\n"; } } close(OUT); unlink($file); rename($file, "$file.old"); rename("$file.tmp", $file); utime $atime, $mtime, $file; chmod $mode & 07777, $file; }
And here's the skeleton for SVGN.pm
package SVGN; use strict; sub doit { my $name = shift; open(LOG, ">>log.txt") or die "error: $!"; print LOG "$name\n"; close(LOG); } 1;

In reply to Re: Cleaning up unused subroutines by dmorgo
in thread Cleaning up unused subroutines by koknat

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.