File Coverage

blib/lib/Devel/StackTrace.pm
Criterion Covered Total %
statement 136 137 99.2
branch 50 58 86.2
condition 15 20 75.0
subroutine 22 22 100.0
pod 9 9 100.0
total 232 246 94.3


line stmt bran cond sub pod time code
1             package Devel::StackTrace;
2              
3 10     10   551849 use 5.006;
  10         136  
4              
5 10     10   60 use strict;
  10         23  
  10         238  
6 10     10   50 use warnings;
  10         18  
  10         451  
7              
8             our $VERSION = '2.03';
9              
10 10     10   2945 use Devel::StackTrace::Frame;
  10         29  
  10         319  
11 10     10   66 use File::Spec;
  10         21  
  10         231  
12 10     10   52 use Scalar::Util qw( blessed );
  10         21  
  10         592  
13              
14             use overload
15 10         77 '""' => \&as_string,
16 10     10   8850 fallback => 1;
  10         8629  
17              
18             sub new {
19 36     36 1 16225 my $class = shift;
20 36         148 my %p = @_;
21              
22             $p{unsafe_ref_capture} = !delete $p{no_refs}
23 36 100       123 if exists $p{no_refs};
24              
25 36         184 my $self = bless {
26             index => undef,
27             frames => [],
28             raw => [],
29             %p,
30             }, $class;
31              
32 36         115 $self->_record_caller_data;
33              
34 36         140 return $self;
35             }
36              
37             sub _record_caller_data {
38 36     36   61 my $self = shift;
39              
40 36   66     285 my $filter = $self->{filter_frames_early} && $self->_make_frame_filter;
41              
42             # We exclude this method by starting at least one frame back.
43 36   100     147 my $x = 1 + ( $self->{skip_frames} || 0 );
44              
45 36 100       103 while (
46             my @c
47             = $self->{no_args}
48             ? caller( $x++ )
49             : do {
50             ## no critic (Modules::ProhibitMultiplePackages, Variables::ProhibitPackageVars)
51             package # the newline keeps dzil from adding a version here
52             DB;
53 117         170 @DB::args = ();
54 117         701 caller( $x++ );
55             }
56             ) {
57              
58 85         126 my @args;
59              
60             ## no critic (Variables::ProhibitPackageVars)
61 85 100       192 @args = $self->{no_args} ? () : @DB::args;
62             ## use critic
63              
64 85         190 my $raw = {
65             caller => \@c,
66             args => \@args,
67             };
68              
69 85 50 66     186 next if $filter && !$filter->($raw);
70              
71 85 100       604 unless ( $self->{unsafe_ref_capture} ) {
72 107 100       288 $raw->{args} = [ map { ref $_ ? $self->_ref_to_string($_) : $_ }
73 79         98 @{ $raw->{args} } ];
  79         147  
74             }
75              
76 85         142 push @{ $self->{raw} }, $raw;
  85         234  
77             }
78             }
79              
80             sub _ref_to_string {
81 12     12   16 my $self = shift;
82 12         19 my $ref = shift;
83              
84 12 50 66     104 return overload::AddrRef($ref)
85             if blessed $ref && $ref->isa('Exception::Class::Base');
86              
87 12 100       53 return overload::AddrRef($ref) unless $self->{respect_overload};
88              
89             ## no critic (Variables::RequireInitializationForLocalVars)
90 2         3 local $@;
91 2         6 local $SIG{__DIE__};
92             ## use critic
93              
94 2         4 my $str = eval { $ref . q{} };
  2         30  
95              
96 2 100       13 return $@ ? overload::AddrRef($ref) : $str;
97             }
98              
99             sub _make_frames {
100 34     34   53 my $self = shift;
101              
102 34   66     119 my $filter = !$self->{filter_frames_early} && $self->_make_frame_filter;
103              
104 34         79 my $raw = delete $self->{raw};
105 34         54 for my $r ( @{$raw} ) {
  34         92  
106 80 100 100     218 next if $filter && !$filter->($r);
107              
108 73         181 $self->_add_frame( $r->{caller}, $r->{args} );
109             }
110             }
111              
112             my $default_filter = sub {1};
113              
114             sub _make_frame_filter {
115 34     34   51 my $self = shift;
116              
117 34         57 my ( @i_pack_re, %i_class );
118 34 100       81 if ( $self->{ignore_package} ) {
119             ## no critic (Variables::RequireInitializationForLocalVars)
120 2         5 local $@;
121 2         9 local $SIG{__DIE__};
122             ## use critic
123              
124             $self->{ignore_package} = [ $self->{ignore_package} ]
125 2 50       6 unless eval { @{ $self->{ignore_package} } };
  2         4  
  2         26  
126              
127             @i_pack_re
128 2 100       7 = map { ref $_ ? $_ : qr/^\Q$_\E$/ } @{ $self->{ignore_package} };
  2         30  
  2         7  
129             }
130              
131 34         60 my $p = __PACKAGE__;
132 34         284 push @i_pack_re, qr/^\Q$p\E$/;
133              
134 34 100       99 if ( $self->{ignore_class} ) {
135             $self->{ignore_class} = [ $self->{ignore_class} ]
136 2 50       7 unless ref $self->{ignore_class};
137 2         3 %i_class = map { $_ => 1 } @{ $self->{ignore_class} };
  2         6  
  2         4  
138             }
139              
140 34         59 my $user_filter = $self->{frame_filter};
141              
142             return sub {
143 80 100   80   124 return 0 if grep { $_[0]{caller}[0] =~ /$_/ } @i_pack_re;
  84         374  
144 78 100       191 return 0 if grep { $_[0]{caller}[0]->isa($_) } keys %i_class;
  6         75  
145              
146 74 100       135 if ($user_filter) {
147 5         16 return $user_filter->( $_[0] );
148             }
149              
150 69         169 return 1;
151 34         240 };
152             }
153              
154             sub _add_frame {
155 73     73   106 my $self = shift;
156 73         88 my $c = shift;
157 73         130 my $p = shift;
158              
159             # eval and is_require are only returned when applicable under 5.00503.
160 73 50       146 push @$c, ( undef, undef ) if scalar @$c == 6;
161              
162 73         480 push @{ $self->{frames} },
163             Devel::StackTrace::Frame->new(
164             $c,
165             $p,
166             $self->{respect_overload},
167             $self->{max_arg_length},
168             $self->{message},
169             $self->{indent}
170 73         98 );
171             }
172              
173             sub next_frame {
174 13     13 1 683 my $self = shift;
175              
176             # reset to top if necessary.
177 13 100       34 $self->{index} = -1 unless defined $self->{index};
178              
179 13         25 my @f = $self->frames;
180 13 100       36 if ( defined $f[ $self->{index} + 1 ] ) {
181 10         25 return $f[ ++$self->{index} ];
182             }
183             else {
184 3         7 $self->{index} = undef;
185             ## no critic (Subroutines::ProhibitExplicitReturnUndef)
186 3         10 return undef;
187             }
188             }
189              
190             sub prev_frame {
191 9     9 1 41 my $self = shift;
192              
193 9         15 my @f = $self->frames;
194              
195             # reset to top if necessary.
196 9 100       18 $self->{index} = scalar @f unless defined $self->{index};
197              
198 9 100 66     28 if ( defined $f[ $self->{index} - 1 ] && $self->{index} >= 1 ) {
199 6         16 return $f[ --$self->{index} ];
200             }
201             else {
202             ## no critic (Subroutines::ProhibitExplicitReturnUndef)
203 3         4 $self->{index} = undef;
204 3         6 return undef;
205             }
206             }
207              
208             sub reset_pointer {
209 1     1 1 2 my $self = shift;
210              
211 1         2 $self->{index} = undef;
212              
213 1         2 return;
214             }
215              
216             sub frames {
217 59     59 1 129 my $self = shift;
218              
219 59 100       118 if (@_) {
220             die
221             "Devel::StackTrace->frames can only take Devel::StackTrace::Frame args\n"
222 2 50       7 if grep { !$_->isa('Devel::StackTrace::Frame') } @_;
  3         28  
223              
224 2         7 $self->{frames} = \@_;
225 2         8 delete $self->{raw};
226             }
227             else {
228 57 100       151 $self->_make_frames if $self->{raw};
229             }
230              
231 59         195 return @{ $self->{frames} };
  59         170  
232             }
233              
234             sub frame {
235 2     2 1 6 my $self = shift;
236 2         5 my $i = shift;
237              
238 2 50       8 return unless defined $i;
239              
240 2         9 return ( $self->frames )[$i];
241             }
242              
243             sub frame_count {
244 1     1 1 5 my $self = shift;
245              
246 1         3 return scalar( $self->frames );
247             }
248              
249 3     3 1 9 sub message { $_[0]->{message} }
250              
251             sub as_string {
252 18     18 1 81 my $self = shift;
253 18         26 my $p = shift;
254              
255 18         41 my @frames = $self->frames;
256 18 100       47 if (@frames) {
257 15         24 my $st = q{};
258 15         18 my $first = 1;
259 15         30 for my $f (@frames) {
260 36         86 $st .= $f->as_string( $first, $p ) . "\n";
261 36         63 $first = 0;
262             }
263              
264 15         86 return $st;
265             }
266              
267 3         9 my $msg = $self->message;
268 3 50       22 return $msg if defined $msg;
269              
270 0           return 'Trace begun';
271             }
272              
273             {
274             ## no critic (Modules::ProhibitMultiplePackages, ClassHierarchies::ProhibitExplicitISA)
275             package # hide from PAUSE
276             Devel::StackTraceFrame;
277              
278             our @ISA = 'Devel::StackTrace::Frame';
279             }
280              
281             1;
282              
283             # ABSTRACT: An object representing a stack trace
284              
285             __END__