Category: Misc
Author/Contact Info /msg particle
Description: Update -- 9 jun 2002: now posted on the CPAN as Devel::TraceSubs version 0.01. check it out there for more info, and all future updates and patches.

Devel::TrackSub allows you to track the entry and exit of subroutines in a list of namespaces you specify. It takes advantage of Hook::LexWrap's do the dirty work of wrapping the subs and return the proper caller context

i think it's ready for CPAN, but i'd like whatever feedback you have. i'm still working on the pod while i wait for my CPAN id.

Note: you'll have to install it in a Devel subdirectory to get it working properly in your scripts until i have the full distro on CPAN.

Update -- 21 may 2002: version 0.02 -- patch provided by Jenda to allow the return of subroutine params

package Devel::TrackSub;
use 5.006;
use strict;
use warnings;
use Hook::LexWrap;
use Carp;
our $VERSION = 0.02;


sub new {
  my( $class, %arg ) = @_;
  bless { 
    pre => defined $arg{pre} ? $arg{pre} : 'Entering',
    post => defined $arg{post} ? $arg{post} : 'Leaving',
    level => defined $arg{level} ? $arg{level} : '~~',
    prefix => defined $arg{prefix} ? $arg{prefix} : '@ ',
    verbose => $arg{verbose} ? '' : "\n",
    params => $arg{params} ? 1 : 0,
    traced => {},
  }, $class;
}

sub _stack_depth { # compute stack depth
  my @stack;
  while( my $sym = caller(1 + scalar @stack) )
  { push @stack, $sym }
  return wantarray ? @stack : scalar @stack;
}

sub _gen_wrapper { # return a wrapper subroutine
  my( $self ) = ( shift );
  my( $direction, $pkg, $sym, $start ) = @_;
  return sub{
    carp( 
      $self->{level} x $self->_stack_depth(),
      $self->{prefix},
      $direction, ' ',
      $pkg, $sym, $self->{verbose},
      $pkg, $sym, 
      ( $start && $self->{params} 
          ? "( '" . join( "', '", @_[0..$#_-1] ) . "' )" 
          : () 
      ),
      $self->{verbose},
    )
  }
}

sub _warning { # return a warning message
  my( $self ) = ( shift);
  return 'Warning: ', __PACKAGE__, ': ', @_, $self->{verbose}
}

sub trace($;*) { # trace all named subs in given symbols
  no strict 'refs'; # professional driver on a closed course
  my( $self ) = ( shift );

  PACKAGE: for my $pkg ( @_ ) {

    ref $pkg
      and carp $self->_warning( "references not allowed ($pkg)" )
      and next PACKAGE;

    $pkg =~ /^\*/
      and carp $self->_warning( "globs not allowed ($pkg)" )
      and next PACKAGE;

    !defined %{ $pkg }
      and carp $self->_warning( "non-existant package ($pkg)" )
      and next PACKAGE;

    $pkg eq __PACKAGE__ . '::'
      and carp $self->_warning( "Can't ", __PACKAGE__, " myself.",
        " This way lies madness." )
      and next PACKAGE;

    my( $sym, $glob );

    SYMBOL: while ( ($sym, $glob) = each %{ $pkg } ) {

      $pkg eq $sym and next SYMBOL;
      $self->{traced}{ $pkg . $sym } and next SYMBOL;

      if( defined *{ $glob }{CODE} ) {
        my $desc = $pkg . $sym . $self->{verbose};

        $self->{traced}{$pkg . $sym}++;

        Hook::LexWrap::wrap $pkg . $sym,
          pre => $self->_gen_wrapper( $self->{pre}, $pkg, $sym, 1 ),
          post => $self->_gen_wrapper( $self->{post}, $pkg, $sym, 0 );
      }
    }
  }
}

Devel::TrackSub => 'particle';

__END__


=head1 NAME

Devel::TrackSub - Subroutine wrappers for debugging

=head1 VERSION

This document describes version 0.02 of Devel::TrackSub,
released 21 May 2002.

=head1 SYNOPSIS

  package foo;
  sub bar { print "foobar\n" }

  package main;
  use Devel::TrackSub;

  sub foo { print "foo\n"; foo::bar() }

  my $pkg = 'main::';

  my $dbg = Devel::TrackSub->new( pre => '>', post => '<', params => 1
+ );
  $dbg->trace(
    'foo::',            # valid
    $pkg,               # valid
    'main',             # invalid -- no trailing colons
    'joe::',            # invalid -- non-existant
    $dbg,               # invalid -- references not allowed
    'Debug::SubWrap::', # invalid -- self-reference not allowed
    *main::,            # invalid -- globs not allowed
  );

=head1 DESCRIPTION

Devel::TrackSub allows you to track the entry and exit of subroutines 
+in a list of namespaces you specify. It returns the proper stack dept
+h, and can be configured to return the parameters passed. Error check
+ing prevents silent failures, and unexpected errors. It takes advanta
+ge of Hook::LexWrap's do the dirty work of wrapping the subs and retu
+rn the proper caller context.

=head2 EXPORT

None. Give a hoot, don't pollute!


=head1 AUTHOR

particle, E<lt>particle@artfromthemachine.comE<gt>

=head1 SPECIAL THANKS

Jenda, http://perlmonks.org/index.pl?node_id=105128 for providing a pa
+tch to allow the return of subroutine params

=head1 SEE ALSO

L<Hook::LexWrap>.

=cut
Replies are listed 'Best First'.
Re: Devel::TrackSub - Subroutine wrappers for debugging
by Jenda (Abbot) on May 20, 2002 at 23:04 UTC

    I like this one thanks :-)

    I'd suggest one little addition:

    Line 16: verbose => $arg{verbose} ? '' : "\n", ++ params => $arg{params} ? 1 : 0, Line 31: -- my( $direction, $pkg, $sym ) = @_; ++ my( $direction, $pkg, $sym , $start) = @_; Line 37: -- $pkg, $sym, $self->{verbose}, ++ $pkg, $sym, ++ ( $start && $self->{params} ? "( '".join( "', '", @_[0..$#_-1] +)."')" : ()), ++ $self->{verbose}, Line 85: -- pre => $self->_gen_wrapper( $self->{pre}, $pkg, $sym, ), -- post => $self->_gen_wrapper( $self->{post}, $pkg, $sym, ); ++ pre => $self->_gen_wrapper( $self->{pre}, $pkg, $sym, 1 ), ++ post => $self->_gen_wrapper( $self->{post}, $pkg, $sym, 0 +);
    to allow printing the parameters to the procedure.

    The code doesn't print data structures, if some of the parameters is a reference you'll see just SCALAR(xxxx), but I do not think that much info is needed. If you think otherwise you could use Data::Dumper to print them out:

    Line 5: use Hook::LexWrap; ++ use Data::Dumper; Line 31: return sub{ ++ local $Data::Dumper::Indent = 0; ++ local $Data::Dumper::Terse = 1; Line 37: -- $pkg, $sym, $self->{verbose}, ++ $pkg, $sym, ++ ( $start && $self->{params} ? '( '.join( ", ", Dumper(@_[0..$# +_-1])).")" : ()), ++ $self->{verbose},

      Jenda

    == Jenda@Krynicky.cz == http://Jenda.Krynicky.cz ==
    Always code as if the guy who ends up maintaining your code
    will be a violent psychopath who knows where you live.
          -- Rick Osborne, osborne@gateway.grumman.com
      ooh, i like that very much. thank you. i'll be sure to add your suggestions tomorrow.

      ~Particle *accelerates*