| 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; |