http://qs1969.pair.com?node_id=379080


in reply to Package level scope callbacks.

If I understand you correctly, you want to trap each entry and exit to the subroutines of a certain package.

As theorbtwo has noted, you can trap each and every subroutine call that happens in perl with &DB::sub, and then you check for the called package. A small example is (for perl 5.8.2)

BEGIN { sub DB::sub { my ($r, @r, $s); $s = $DB::sub; $s=~/^T::/ or goto &$s; warn "entering $s\n"; if (wantarray) { @r = &$s; } elsif (defined wantarray) { $r = &$s; } else { &$s; } warn "exitting $s\n"; wantarray ? @r : $r; } $^P |= 1; } { package T; $r = "r"; sub f { $_[0].$r.n(); } sub n { @l = l(); 2, chr(@l); } sub l { -5..4; } } sub g { T::f($_[0]."a"); }; print g("b");
(Thanks to wog for bringing $^P in my attention in Re^2: How's your Perl? (II).)

The above aspect, however, has some problems. It might be slow as it has to catch all subroutine calls, not only those in a certain package, it does not work for anonymous subs, and it might be difficult to make it work if you use the perl debugger or use DB::sub for any other purpose.

As a different approach, you could replace each subroutine in the package with a different subroutine. This, however, has the backdraw that the subroutines you want to trap have to be defined by the time you install the trapping code, as a special case you can not trap AUTOLOADED subs this way. An example is:

{ for $n (keys %T::) { if (defined &{$T::{$n}}) { my $s = $n; my $f = \&{$T::{$n}}; $T::{$n} = sub { my ($r, @r); warn "entering $s\n"; if (wantarray) { @r = &$f; } elsif (defined wantarray) { $r = &$f; } else { &$f; } warn "exitting $s\n"; wantarray ? @r : $r; } } } } { package T; $r = "r"; sub f { print $_[0].$r.n(); } sub n { @l = l(); 2, chr(@l); } sub l { -5..4; } } sub g { T::f($_[0]."a"); }; g("b");

(Pity overloading "&{}" can not catch named subroutine calls, as that would have been a very elegant solution.)

Update: Note that these codes get much shorter if you only want to trace subroutine entry, not exit, as you don't have to mess with the contexts. You might only want to trace calls that come from a different package. That should be possible with caller.