File Coverage

lib/Devel/Trepan/DB/Backtrace.pm
Criterion Covered Total %
statement 15 82 18.2
branch 0 46 0.0
condition 0 23 0.0
subroutine 5 7 71.4
pod 0 2 0.0
total 20 160 12.5


line stmt bran cond sub pod time code
1 12     12   80 use warnings; no warnings 'redefine';
  12     12   33  
  12         383  
  12         66  
  12         32  
  12         374  
2 12     12   69 use English qw( -no_match_vars );
  12         30  
  12         76  
3 12     12   2716 use B;
  12         30  
  12         1305  
4              
5             =pod
6              
7             =head2 tbacktrace(skip[,count,scan_for_DB])
8              
9             Collect the traceback information available via C<caller()>. Some
10             filtering and cleanup of the data is done.
11              
12             C<skip> defines the number of stack frames to be skipped, working
13             backwards from the most current frame before the call the debugger
14             DB::DB call if scan_for_DB is set, or the most-current frame.
15              
16             C<count> determines the total number of call frames to be returned; all of
17             them (well, the first 10^9) are returned if C<count> is omitted.
18              
19             This routine returns a list of hashes, from most-recent to least-recent
20             stack frame. Each has the following keys and values:
21              
22             =over 4
23              
24             =item *
25              
26             C<wantarray> - C<.> (null), C<$> (scalar), or C<@> (array)
27              
28             =item *
29              
30             C<fn> - subroutine name, or C<eval> information
31              
32             =item *
33              
34             C<args> - undef, or a reference to an array of arguments
35              
36             =item *
37              
38             C<file> - the file in which this item was defined (if any)
39              
40             =item *
41              
42             C<line> - the line on which it was defined
43              
44             =item *
45              
46             C<evaltext> - eval text if we are in an eval.
47              
48             =back
49              
50             =cut
51              
52             # NOTE: this routine needs to be in package DB for us to be able to pick up the
53             # subroutine args.
54             sub tbacktrace($;$$$) {
55 0     0 0   my ($self, $skip, $count, $scan_for_DB_sub) = @_;
56 0 0         $skip = 0 unless defined($skip);
57 0 0         $count = 1e9 unless defined($count);
58              
59 0   0       $scan_for_DB_sub ||= 1;
60             # print "scan: $scan_for_DB_sub\n";
61              
62             # These variables are used to capture output from caller();
63 0           my ( $pkg, $file, $line, $fn, $hasargs, $wantarray, $evaltext, $is_require );
64              
65 0           my $i=0;
66 0 0         if ($scan_for_DB_sub) {
67 12     12   71 no warnings qw(once uninitialized); # For $DB::event
  12         34  
  12         10563  
68 0 0         my $db_fn = ($DB::event eq 'post-mortem') ? 'catch' : 'DB';
69             # Warning: There is a bug caller that lists DB:DB as the function
70             # name rather than the name the debugged program may have been in
71 0           while (my ($pkg, $file, $line, $fn) = caller($i++)) {
72 0 0 0       if ("DB::$db_fn" eq $fn or ('DB' eq $pkg && $db_fn eq $fn)) {
      0        
73 0           $i--;
74 0           last ;
75             }
76             }
77             }
78              
79 0           $scan_for_DB_sub = $i;
80 0           $count += $i;
81             # print "++count: $count, i $i $DB::event\n"; # XX debug
82 0 0         $i -= 2 if $DB::event eq 'call';
83              
84 0           my ( @a, $args_ary );
85 0           my @callstack = ();
86              
87             # # XXX Okay... why'd we do that?
88 0           my $nothard = not $DB::frame & 8;
89 0           local $DB::frame = 0;
90              
91             # Start out at the skip count, $i.
92             # If we haven't reached the number of frames requested, and caller() is
93             # still returning something, stay in the loop. (If we pass the requested
94             # number of stack frames, or we run out - caller() returns nothing - we
95             # quit.
96             # Up the stack frame index to go back one more level each time.
97 0   0       while ($i <= $count and
98             ($pkg, $file, $line, $fn, $hasargs, $wantarray, $evaltext,
99             $is_require) = caller($i))
100             {
101 0           my $addr = Devel::Callsite::callsite($i);
102              
103             ## print "++file: $file, line $line $fn\n"; # XX if $DB::DEBUGME;
104 0           $i++;
105 0 0 0       next if $pkg eq 'DB' && ($fn eq 'sub' || $fn eq 'lsub' ||
      0        
106             $file =~ m{Devel/Trepan/DB/Sub\.pm$});
107             # Go through the arguments and save them for later.
108 0           @a = ();
109 0           for my $arg (@DB::args) {
110 0           my $type;
111 0 0 0       if ( not defined $arg ) { # undefined parameter
    0 0        
    0          
112 0           push @a, "undef";
113             }
114              
115             elsif ( $nothard and tied $arg ) { # tied parameter
116 0           push @a, "tied";
117             }
118             elsif ( $nothard and $type = ref $arg ) { # reference
119 0           push @a, "ref($type)";
120             }
121             else { # can be stringified
122 0           local $_ =
123             "$arg"; # Safe to stringify now - should not call f().
124              
125             # Backslash any single-quotes or backslashes.
126 0           s/([\'\\])/\\$1/g;
127              
128             # Single-quote it unless it's a number or a colon-separated
129             # name.
130 0 0         s/(.*)/'$1'/s
131             unless /^(?: -?[\d.]+ | \*[\w:]* )$/x;
132              
133             # Turn high-bit characters into meta-whatever.
134 0           s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
  0            
135              
136             # Turn control characters into ^-whatever.
137 0           s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
  0            
138              
139 0           push( @a, $_ );
140             } ## end else [ if (not defined $arg)
141             } ## end for $arg (@args)
142              
143             # If $wantarray is true, this is array (@)context.
144             # If $wantarray is false, this is scalar ($) context.
145             # If neither, $wantarray isn't defined. (This is apparently a 'can't
146             # happen' trap.)
147 0 0         $wantarray = $wantarray ? '@' : ( defined $wantarray ? '$' : '.' );
    0          
148              
149             # if the sub has args ($hasargs true), make an anonymous array of the
150             # dumped args.
151 0 0         $args_ary = $hasargs ? [@a] : undef;
152              
153             # remove trailing newline-whitespace-semicolon-end of line sequence
154             # from the eval text, if any.
155 0 0         $evaltext =~ s/\n\s*\;\s*\Z// if $evaltext;
156              
157             # Escape backslashed single-quotes again if necessary.
158 0 0         $evaltext =~ s/([\\\'])/\\$1/g if $evaltext;
159              
160             # if the require flag is true, the eval text is from a require.
161 0 0         if ($is_require) {
    0          
    0          
162 0           $fn = "require '$evaltext'";
163             }
164              
165             # if it's false, the eval text is really from an eval.
166             elsif ( defined $is_require ) {
167 0           $fn = "eval '$evaltext'";
168             }
169              
170             # If the sub is '(eval)', this is a block eval, meaning we don't
171             # know what the eval'ed text actually was.
172             elsif ( $fn eq '(eval)' ) {
173 0           $fn = "eval {...}";
174             }
175              
176             # Stick the collected information into @callstack a hash reference.
177 0           push(@callstack,
178             {
179             addr => $addr,
180             args => $args_ary,
181             evaltext => $evaltext,
182             file => $file,
183             fn => $fn,
184             line => $line,
185             pkg => $pkg,
186             wantarray => $wantarray,
187             }
188             );
189              
190             # Stop processing frames if the user hit control-C.
191             # last if $signal;
192             } ## end for ($i = $skip ; $i < ...
193              
194             ## use Data::Printer; Data::Printer::p @callstack; # XXX
195              
196             # The function and args for the stopped line is DB::DB,
197             # but we want it to be the function and args of the last call.
198             # Se we need to adjust those in @callstack.
199             # And the function and args for the file and line that called us
200             # should also be the prior function and args.
201 0 0         if ($scan_for_DB_sub) {
202 0           my $len = @callstack;
203 0 0         if ($len) {
204 0           for (my $i=1; $i < $len; $i++) {
205 0           $callstack[$i-1]->{args} = $callstack[$i]->{args};
206 0           $callstack[$i-1]->{fn} = $callstack[$i]->{fn};
207 0           $callstack[$i-1]->{wantarray} = $callstack[$i]->{wantarray};
208             }
209             # $callstack[$len]->{args} = undef;
210             # $callstack[$len]->{fn} = undef;
211             }
212             }
213              
214 0 0         if ($DB::event eq 'call') {
215 0 0         unshift @callstack, {
216             addr => $DB::addr,
217             file => $DB::filename,
218             fn => $DB::subroutine,
219             line => $DB::lineno,
220             pkg => $DB::package,
221             args => $DB::hasargs,
222             wantarray => $DB::wantarray ? $DB::wantarray : '',
223             };
224             }
225             # use Data::Printer; Data::Printer::p @callstack;
226              
227 0           @callstack;
228             }
229              
230             unless (caller) {
231             require Data::Dumper;
232             import Data::Dumper;
233             $DB::frame = 0;
234             our @callstack = tbacktrace(undef,undef,undef,0);
235             our $sep = '-' x 20 . "\n";
236             # print Dumper(@callstack), "\n";
237             # print $sep;
238             sub five {
239 0     0 0   @callstack = tbacktrace(undef,undef,undef,0);
240 0           print Dumper(@callstack), "\n";
241 0           print $sep;
242 0           @callstack = tbacktrace(undef,1,undef,0);
243 0           print Dumper(@callstack), "\n";
244 0           print $sep;
245 0           @callstack = tbacktrace(1,0,undef,0);
246 0           print Dumper(@callstack), "\n";
247 0           print $sep;
248 0           5;
249             }
250             my $five = five();
251             # $five = eval "@callstack = tbacktrace(undef, undef, undef, 0)";
252             # print Dumper(@callstack), "\n";
253             print $sep;
254             $five = eval "five";
255             print Dumper(@callstack), "\n";
256             }
257              
258             1;