StackTrace.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:12k
源码类别:

外挂编程

开发平台:

Windows_Unix

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