home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / StackTrace.pm < prev    next >
Encoding:
Perl POD Document  |  2003-09-25  |  10.4 KB  |  491 lines

  1. package Devel::StackTrace;
  2.  
  3. use 5.005;
  4.  
  5. use strict;
  6. use vars qw($VERSION);
  7.  
  8. use fields qw( index frames );
  9.  
  10. use overload
  11.     '""' => \&as_string,
  12.     fallback => 1;
  13.  
  14. $VERSION = '1.04';
  15.  
  16. 1;
  17.  
  18. sub new
  19. {
  20.     my $proto = shift;
  21.     my $class = ref $proto || $proto;
  22.  
  23.     my $self = bless { index => undef,
  24.                frames => [],
  25.              }, $class;
  26.  
  27.     $self->_add_frames(@_);
  28.  
  29.     return $self;
  30. }
  31.  
  32. sub _add_frames
  33. {
  34.     my $self = shift;
  35.     my %p = @_;
  36.  
  37.     $p{no_refs} = delete $p{no_object_refs} if exists $p{no_object_refs};
  38.  
  39.     my (%i_pack, %i_class);
  40.     if ($p{ignore_package})
  41.     {
  42.     $p{ignore_package} = [$p{ignore_package}] unless ref $p{ignore_package};
  43.     %i_pack = map {$_ => 1} @{ $p{ignore_package} };
  44.     }
  45.  
  46.     if ($p{ignore_class})
  47.     {
  48.     $p{ignore_class} = [$p{ignore_class}] unless ref $p{ignore_class};
  49.     %i_class = map {$_ => 1} @{ $p{ignore_class} };
  50.     }
  51.  
  52.     my $p = __PACKAGE__;
  53.     $i_pack{$p} = 1;
  54.  
  55.     my $x = 0;
  56.     my @c;
  57.     while ( do { package DB; @c = caller($x++) } )
  58.     {
  59.     # Do the quickest ones first.
  60.     next if $i_pack{ $c[0] };
  61.     next if grep { $c[0]->isa($_) } keys %i_class;
  62.  
  63.         $self->_add_frame( $p{no_refs}, \@c )
  64.             if @c;
  65.     }
  66. }
  67.  
  68. sub _add_frame
  69. {
  70.     my $self = shift;
  71.     my $no_refs = shift;
  72.     my $c = shift;
  73.  
  74.     # eval and is_require are only returned when applicable under 5.00503.
  75.     push @$c, (undef, undef) if scalar @$c == 6;
  76.  
  77.     my @a = @DB::args;
  78.  
  79.     if ( $no_refs )
  80.     {
  81.         @a = map { ( ref $_
  82.                      ? ( UNIVERSAL::isa( $_, 'Exception::Class::Base' ) ?
  83.                          do { if ( $_->can('show_trace') )
  84.                               {
  85.                                   my $t = $_->show_trace;
  86.                                   $_->show_trace(0);
  87.                                   my $s = "$_";
  88.                                   $_->show_trace($t);
  89.                                   $s;
  90.                               }
  91.                               else
  92.                               {
  93.                                   # hack but should work with older
  94.                                   # versions of E::C::B
  95.                                   $_->{message};
  96.                               } }
  97.                          : "$_"
  98.                        )
  99.                      : $_
  100.                    ) } @a;
  101.     }
  102.  
  103.     push @{ $self->{frames} }, Devel::StackTraceFrame->new( $c, \@a );
  104. }
  105.  
  106. sub next_frame
  107. {
  108.     my $self = shift;
  109.  
  110.     # reset to top if necessary.
  111.     $self->{index} = -1 unless defined $self->{index};
  112.  
  113.     if (defined $self->{frames}[ $self->{index} + 1 ])
  114.     {
  115.     return $self->{frames}[ ++$self->{index} ];
  116.     }
  117.     else
  118.     {
  119.     $self->{index} = undef;
  120.     return undef;
  121.     }
  122. }
  123.  
  124. sub prev_frame
  125. {
  126.     my $self = shift;
  127.  
  128.     # reset to top if necessary.
  129.     $self->{index} = scalar @{ $self->{frames} } unless defined $self->{index};
  130.  
  131.     if (defined $self->{frames}[ $self->{index} - 1 ] && $self->{index} >= 1)
  132.     {
  133.     return $self->{frames}[ --$self->{index} ];
  134.     }
  135.     else
  136.     {
  137.     $self->{index} = undef;
  138.     return undef;
  139.     }
  140. }
  141.  
  142. sub reset_pointer
  143. {
  144.     my $self = shift;
  145.  
  146.     $self->{index} = undef;
  147. }
  148.  
  149. sub frames
  150. {
  151.     my $self = shift;
  152.  
  153.     return @{ $self->{frames} };
  154. }
  155.  
  156. sub frame
  157. {
  158.     my $self = shift;
  159.     my $i = shift;
  160.  
  161.     return unless defined $i;
  162.  
  163.     return $self->{frames}[$i];
  164. }
  165.  
  166. sub frame_count
  167. {
  168.     my $self = shift;
  169.  
  170.     return scalar @{ $self->{frames} };
  171. }
  172.  
  173. sub as_string
  174. {
  175.     my $self = shift;
  176.  
  177.     my $st = '';
  178.     my $first = 1;
  179.     foreach my $f (@{ $self->{frames} })
  180.     {
  181.     $st .= $f->as_string($first) . "\n";
  182.     $first = 0;
  183.     }
  184.  
  185.     return $st;
  186. }
  187.  
  188. package Devel::StackTraceFrame;
  189.  
  190. use strict;
  191. use vars qw($VERSION);
  192.  
  193. use fields qw( package filename line subroutine hasargs wantarray evaltext is_require hints bitmask args );
  194.  
  195. $VERSION = '0.6';
  196.  
  197. # Create accessor routines
  198. BEGIN
  199. {
  200.     no strict 'refs';
  201.     foreach my $f ( qw( package filename line subroutine hasargs
  202.                         wantarray evaltext is_require hints bitmask args ) )
  203.     {
  204.     next if $f eq 'args';
  205.     *{$f} = sub { my $s = shift; return $s->{$f} };
  206.     }
  207. }
  208.  
  209. sub new
  210. {
  211.     my $proto = shift;
  212.     my $class = ref $proto || $proto;
  213.  
  214.     my $self = bless {}, $class;
  215.  
  216.     my @fields =
  217.         ( qw( package filename line subroutine hasargs wantarray evaltext is_require ) );
  218.     push @fields, ( qw( hints bitmask ) ) if $] >= 5.006;
  219.  
  220.     @{ $self }{ @fields } = @{$_[0]};
  221.  
  222.     $self->{args} = $_[1] ? $_[1] : [];
  223.  
  224.     return $self;
  225. }
  226.  
  227. sub args
  228. {
  229.     my $self = shift;
  230.  
  231.     return @{ $self->{args} };
  232. }
  233.  
  234. sub as_string
  235. {
  236.     my $self = shift;
  237.     my $first = shift;
  238.  
  239.     my $sub = $self->subroutine;
  240.     # This code stolen straight from Carp.pm and then tweaked.  All
  241.     # errors are probably my fault  -dave
  242.     if ($first)
  243.     {
  244.     $sub = 'Trace begun';
  245.     }
  246.     else
  247.     {
  248.     # Build a string, $sub, which names the sub-routine called.
  249.     # This may also be "require ...", "eval '...' or "eval {...}"
  250.     if (my $eval = $self->evaltext)
  251.     {
  252.         if ($self->is_require)
  253.         {
  254.         $sub = "require $eval";
  255.         }
  256.         else
  257.         {
  258.         $eval =~ s/([\\\'])/\\$1/g;
  259.         $sub = "eval '$eval'";
  260.         }
  261.     }
  262.     elsif ($sub eq '(eval)')
  263.     {
  264.         $sub = 'eval {...}';
  265.     }
  266.  
  267.     # if there are any arguments in the sub-routine call, format
  268.     # them according to the format variables defined earlier in
  269.     # this file and join them onto the $sub sub-routine string
  270.     #
  271.     # We copy them because they're going to be modified.
  272.     #
  273.     if ( my @a = $self->args )
  274.     {
  275.         for (@a)
  276.         {
  277.         # set args to the string "undef" if undefined
  278.         $_ = "undef", next unless defined $_;
  279.  
  280.         # force stringification
  281.         $_ .= '' if ref $_;
  282.  
  283.         s/'/\\'/g;
  284.  
  285.         # 'quote' arg unless it looks like a number
  286.         $_ = "'$_'" unless /^-?[\d.]+$/;
  287.  
  288.         # print control/high ASCII chars as 'M-<char>' or '^<char>'
  289.         s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
  290.         s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
  291.         }
  292.  
  293.         # append ('all', 'the', 'arguments') to the $sub string
  294.         $sub .= '(' . join(', ', @a) . ')';
  295.         $sub .= ' called';
  296.     }
  297.     }
  298.  
  299.     return "$sub at " . $self->filename . ' line ' . $self->line;
  300. }
  301.  
  302. 1;
  303.  
  304.  
  305. __END__
  306.  
  307. =head1 NAME
  308.  
  309. Devel::StackTrace - Stack trace and stack trace frame objects
  310.  
  311. =head1 SYNOPSIS
  312.  
  313.   use Devel::StackTrace;
  314.  
  315.   my $trace = Devel::StackTrace->new;
  316.  
  317.   print $trace->as_string; # like carp
  318.  
  319.   # from top (most recent) of stack to bottom.
  320.   while (my $frame = $trace->next_frame)
  321.   {
  322.       print "Has args\n" if $f->hasargs;
  323.   }
  324.  
  325.   # from bottom (least recent) of stack to top.
  326.   while (my $frame = $trace->prev_frame)
  327.   {
  328.       print "Sub: ", $f->subroutine, "\n";
  329.   }
  330.  
  331. =head1 DESCRIPTION
  332.  
  333. The Devel::StackTrace module contains two classes, Devel::StackTrace
  334. and Devel::StackTraceFrame.  The goal of this object is to encapsulate
  335. the information that can found through using the caller() function, as
  336. well as providing a simple interface to this data.
  337.  
  338. The Devel::StackTrace object contains a set of Devel::StackTraceFrame
  339. objects, one for each level of the stack.  The frames contain all the
  340. data available from caller() as of Perl 5.6.0 though this module still
  341. works with 5.00503.
  342.  
  343. This code was created to support my L<Exception::Class::Base> class
  344. (part of Exception::Class) but may be useful in other contexts.
  345.  
  346. =head1 'TOP' AND 'BOTTOM' OF THE STACK
  347.  
  348. When describing the methods of the trace object, I use the words 'top'
  349. and 'bottom'.  In this context, the 'top' frame on the stack is the
  350. most recent frame and the 'bottom' is the least recent.
  351.  
  352. Here's an example:
  353.  
  354.   foo();  # bottom frame is here
  355.  
  356.   sub foo
  357.   {
  358.      bar();
  359.   }
  360.  
  361.   sub bar
  362.   {
  363.      Devel::StackTrace->new;  # top frame is here.
  364.   }
  365.  
  366. =head1 Devel::StackTrace METHODS
  367.  
  368. =over 4
  369.  
  370. =item * new(%named_params)
  371.  
  372. Returns a new Devel::StackTrace object.
  373.  
  374. Takes the following parameters:
  375.  
  376. =item -- ignore_package => $package_name OR \@package_names
  377.  
  378. Any frames where the package is one of these packages will not be on
  379. the stack.
  380.  
  381. =item -- ignore_class => $package_name OR \@package_names
  382.  
  383. Any frames where the package is a subclass of one of these packages
  384. (or is the same package) will not be on the stack.
  385.  
  386. Devel::StackTrace internally adds itself to the 'ignore_package'
  387. parameter, meaning that the Devel::StackTrace package is B<ALWAYS>
  388. ignored.  However, if you create a subclass of Devel::StackTrace it
  389. will not be ignored.
  390.  
  391. =item -- no_refs => $boolean
  392.  
  393. If this parameter is true, then Devel::StackTrace will not store
  394. references internally when generating stacktrace frames.  This lets
  395. your objects go out of scope.
  396.  
  397. Devel::StackTrace replaces any references with their stringified
  398. representation.
  399.  
  400. =item * next_frame
  401.  
  402. Returns the next Devel::StackTraceFrame object down on the stack.  If
  403. it hasn't been called before it returns the first frame.  It returns
  404. undef when it reaches the bottom of the stack and then resets its
  405. pointer so the next call to C<next_frame> or C<prev_frame> will work
  406. properly.
  407.  
  408. =item * prev_frame
  409.  
  410. Returns the next Devel::StackTraceFrame object up on the stack.  If it
  411. hasn't been called before it returns the last frame.  It returns undef
  412. when it reaches the top of the stack and then resets its pointer so
  413. pointer so the next call to C<next_frame> or C<prev_frame> will work
  414. properly.
  415.  
  416. =item * reset_pointer
  417.  
  418. Resets the pointer so that the next call C<next_frame> or
  419. C<prev_frame> will start at the top or bottom of the stack, as
  420. appropriate.
  421.  
  422. =item * frames
  423.  
  424. Returns a list of Devel::StackTraceFrame objects.  The order they are
  425. returned is from top (most recent) to bottom.
  426.  
  427. =item * frame ($index)
  428.  
  429. Given an index, returns the relevant frame or undef if there is not
  430. frame at that index.  The index is exactly like a Perl array.  The
  431. first frame is 0 and negative indexes are allowed.
  432.  
  433. =item * frame_count
  434.  
  435. Returns the number of frames in the trace object.
  436.  
  437. =item * as_string
  438.  
  439. Calls as_string on each frame from top to bottom, producing output
  440. quite similar to the Carp module's cluck/confess methods.
  441.  
  442. =back
  443.  
  444. =head1 Devel::StackTraceFrame METHODS
  445.  
  446. See the L<caller> documentation for more information on what these
  447. methods return.
  448.  
  449. =over 4
  450.  
  451. =item * package
  452.  
  453. =item * filename
  454.  
  455. =item * line
  456.  
  457. =item * subroutine
  458.  
  459. =item * hasargs
  460.  
  461. =item * wantarray
  462.  
  463. =item * evaltext
  464.  
  465. Returns undef if the frame was not part of an eval.
  466.  
  467. =item * is_require
  468.  
  469. Returns undef if the frame was not part of a require.
  470.  
  471. =item * args
  472.  
  473. Returns the arguments passed to the frame.  Note that any arguments
  474. that are references are returned as references, not copies.
  475.  
  476. =head2 These only contain data as of Perl 5.6.0 or later
  477.  
  478. =item * hints
  479.  
  480. =item * bitmask
  481.  
  482. =head1 AUTHOR
  483.  
  484. Dave Rolsky, <autarch@urth.org>
  485.  
  486. =head1 SEE ALSO
  487.  
  488. Exception::Class
  489.  
  490. =cut
  491.