The other way to do this (without using the debugger hooks, which I think make sense) might be to use method wrappers and direct manipulation of the symbol table to replace all the subroutines of interest with wrapped versions of themselves that call your statistics functions. Since this is such a handy tool in general for code instrumentation, it would surprise me if someone hadn't written it already. But the basis of it is:
sub A { print "in A\n" }
my $oldA = *{main::A}{CODE};
sub A { print "entering A\n"; &$oldA(@_); print "leaving A\n"; }
You'd use that along with the facts that<bl>
the symbol table for package X is a global hash named %X:: or %main::X::you can scan %main:: (recursively) for other symbol tables (their keys end in ::).</bl>