line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Enbugger; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# COPYRIGHT AND LICENCE |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright (C) 2007,2008,2009 WhitePages.com, Inc. with primary |
6
|
|
|
|
|
|
|
# development by Joshua ben Jore. |
7
|
|
|
|
|
|
|
# |
8
|
|
|
|
|
|
|
# This program is distributed WITHOUT ANY WARRANTY, including but not |
9
|
|
|
|
|
|
|
# limited to the implied warranties of merchantability or fitness for |
10
|
|
|
|
|
|
|
# a particular purpose. |
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
# The program is free software. You may distribute it and/or modify |
13
|
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by |
14
|
|
|
|
|
|
|
# the Free Software Foundation (either version 2 or any later version) |
15
|
|
|
|
|
|
|
# and the Perl Artistic License as published by O’Reilly Media, Inc. |
16
|
|
|
|
|
|
|
# Please open the files named gpl-2.0.txt and Artistic for a copy of |
17
|
|
|
|
|
|
|
# these licenses. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
BEGIN { |
20
|
3
|
|
|
3
|
|
3913
|
$VERSION = '2.016'; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
3
|
|
|
3
|
|
22
|
use XSLoader (); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
535
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
BEGIN { |
26
|
3
|
|
|
3
|
|
1957
|
XSLoader::load( 'Enbugger', $VERSION ); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Provide minimal debugger hooks. |
30
|
|
|
|
|
|
|
# |
31
|
|
|
|
|
|
|
# When perl has debugging enabled, it always calls these functions |
32
|
|
|
|
|
|
|
# at hook points. It dies if they're missing. These stub functions |
33
|
|
|
|
|
|
|
# don't do anything except provide something that will keep perl |
34
|
|
|
|
|
|
|
# from dying from lack of hooks. |
35
|
|
|
|
|
|
|
{ |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Generate needed code for stubs. |
38
|
3
|
|
|
|
|
17
|
my $src = "package DB;\n"; |
|
3
|
|
|
|
|
6
|
|
39
|
3
|
|
|
|
|
18
|
my $need_stubs; |
40
|
3
|
|
|
|
|
8
|
for my $sub (qw( DB sub )) { |
41
|
6
|
|
|
|
|
22
|
my $globref = $DB::{$sub}; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Don't try replacing an existing function. |
44
|
6
|
50
|
33
|
|
|
38
|
if ( $globref and defined &$globref ) { |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
else { |
47
|
|
|
|
|
|
|
# Generate a stub method. |
48
|
6
|
|
|
|
|
14
|
$src .= "sub $sub {};\n"; |
49
|
6
|
|
|
|
|
15
|
$need_stubs = 1; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Create stubs. |
54
|
3
|
50
|
|
|
|
15
|
if ( $need_stubs ) { |
55
|
3
|
|
|
|
|
8
|
$src .= "return 1;\n"; |
56
|
3
|
|
|
0
|
0
|
244
|
my $ok = eval $src; |
|
0
|
|
|
0
|
1
|
|
|
|
0
|
|
|
|
|
|
|
57
|
3
|
50
|
|
|
|
14
|
die $@ unless $ok; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Compile and load everything following w/ debugger hooks. |
63
|
|
|
|
|
|
|
# |
64
|
|
|
|
|
|
|
# That is, everything I'm asking to compile now could possibly be |
65
|
|
|
|
|
|
|
# debugged if we do the loading. Most of everything else in the |
66
|
|
|
|
|
|
|
# Enbugger namespace is explicitly removed from the debugger by |
67
|
|
|
|
|
|
|
# making sure it's COP nodes are compiled with "nextstate" instead |
68
|
|
|
|
|
|
|
# of "dbstate" hooks. |
69
|
3
|
|
|
|
|
6963
|
Enbugger->_compile_with_dbstate(); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# I don't know the real minimum version. I've gotten failure |
74
|
|
|
|
|
|
|
# reports from 5.5 that show it's missing the COP opcodes I'm |
75
|
|
|
|
|
|
|
# altering. |
76
|
0
|
|
|
0
|
|
|
use 5.006_000; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
0
|
|
|
use strict; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
0
|
|
|
use B::Utils (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
81
|
0
|
|
|
0
|
|
|
use Carp (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
82
|
0
|
|
|
0
|
|
|
use Scalar::Util (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Public class settings. |
85
|
0
|
|
|
0
|
|
|
use vars qw( $DefaultDebugger %DBsub ); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
0
|
|
|
0
|
|
|
use constant (); # just to load it. |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
BEGIN { |
90
|
|
|
|
|
|
|
# Compile all of Enbugger:: w/o debugger hooks. |
91
|
0
|
|
|
0
|
|
|
Enbugger->_compile_with_nextstate(); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
our( $DEBUGGER, $DEBUGGER_CLASS, %REGISTERED_DEBUGGERS ); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
###################################################################### |
100
|
|
|
|
|
|
|
# Public API |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
BEGIN { |
103
|
0
|
|
|
0
|
|
|
my $src = "no warnings 'redefine';\n"; |
104
|
0
|
|
|
|
|
|
for my $sub (qw( stop write )) { |
105
|
0
|
|
|
|
|
|
$src .= <<"SRC"; |
106
|
0
|
|
|
|
|
|
#line @{[__LINE__+1]} "@{[__FILE__]}" |
|
0
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub $sub { |
108
|
0
|
|
|
0
|
1
|
|
my ( \$class ) = \@_; |
|
0
|
|
|
0
|
1
|
|
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Fetch and install the real implementation. |
111
|
0
|
|
|
|
|
|
my \$debuggerSubClass = \$class->DEBUGGER_CLASS; |
|
0
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
*Enbugger::$sub = \$debuggerSubClass->can('_${sub}'); |
|
0
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# Redispatch to the implementation. |
116
|
0
|
|
|
|
|
|
goto &Enbugger::$sub; |
|
0
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
}; |
118
|
|
|
|
|
|
|
SRC |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
$src .= "return 1;\n"; |
122
|
0
|
|
|
0
|
|
|
my $ok = eval $src; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
123
|
0
|
50
|
|
|
|
|
die $@ unless $ok; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
0
|
|
|
BEGIN { $DefaultDebugger = 'perl5db' } |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub DEBUGGER_CLASS () { |
133
|
0
|
0
|
|
0
|
1
|
|
unless ( defined $DEBUGGER_CLASS ) { |
134
|
0
|
|
|
|
|
|
Enbugger->load_debugger; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# Install a replacement method that doesn't know how to load |
138
|
|
|
|
|
|
|
# debuggers. |
139
|
|
|
|
|
|
|
# |
140
|
|
|
|
|
|
|
# There's no need to always have a 100% capable function around |
141
|
|
|
|
|
|
|
# once there's no possibility for change. |
142
|
0
|
|
|
|
|
|
my $ok = eval <<"DEBUGGER_CLASS"; |
143
|
0
|
|
|
0
|
|
|
#line @{[__LINE__]} "@{[__FILE__]}" |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
no warnings 'redefine'; |
145
|
|
|
|
|
|
|
sub DEBUGGER_CLASS () { |
146
|
|
|
|
|
|
|
"\Q$DEBUGGER_CLASS\E" |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
return 1; |
149
|
|
|
|
|
|
|
DEBUGGER_CLASS |
150
|
|
|
|
|
|
|
|
151
|
0
|
0
|
|
|
|
|
die $@ unless $ok; |
152
|
|
|
|
|
|
|
|
153
|
0
|
|
|
|
|
|
goto &Enbugger::DEBUGGER_CLASS; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub _stop; |
165
|
|
|
|
|
|
|
sub _write; |
166
|
|
|
|
|
|
|
sub _load_debugger; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
BEGIN { |
174
|
|
|
|
|
|
|
# There is an automatically registered "null" debugger which is |
175
|
|
|
|
|
|
|
# really just a known empty thing that exists only so I can match |
176
|
|
|
|
|
|
|
# against it and thereby know it can be replaced. |
177
|
0
|
|
|
0
|
|
|
$REGISTERED_DEBUGGERS{''} = { |
178
|
|
|
|
|
|
|
null => 1, |
179
|
|
|
|
|
|
|
symbols => [qw[ sub DB ]], |
180
|
|
|
|
|
|
|
}; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub load_debugger { |
184
|
0
|
|
|
0
|
1
|
|
my ( $class, $requested_debugger ) = @_; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Choose a debugger to load if none was specified. |
187
|
0
|
50
|
|
|
|
|
if ( not defined $requested_debugger ) { |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Don't bother if we've already loaded a debugger. |
190
|
0
|
50
|
|
|
|
|
return if $DEBUGGER; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# Choose the default. |
193
|
0
|
|
|
|
|
|
$requested_debugger = $DefaultDebugger; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Don't load a debugger if there is one loaded already. |
197
|
|
|
|
|
|
|
# |
198
|
|
|
|
|
|
|
# Enbugger already populates %DB:: with &DB and &sub so I'll check |
199
|
|
|
|
|
|
|
# for something that I didn't create. |
200
|
|
|
|
|
|
|
my %debugger_symbols = |
201
|
0
|
|
|
|
|
|
map {; $_ => 0b01 } |
|
0
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
keys %DB::; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Compare all registered debuggers to our process. |
206
|
0
|
|
|
|
|
|
my %debugger_matches; |
207
|
0
|
|
|
|
|
|
for my $debugger ( keys %REGISTERED_DEBUGGERS ) { |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Find the intersection vs the difference. |
210
|
0
|
|
|
|
|
|
my $intersection = 0; |
211
|
0
|
|
|
|
|
|
my %match = %debugger_symbols; |
212
|
0
|
|
|
|
|
|
for my $symbol ( @{$REGISTERED_DEBUGGERS{$debugger}{symbols}} ) { |
|
0
|
|
|
|
|
|
|
213
|
0
|
50
|
|
|
|
|
if ( ( $match{$symbol} |= 0b10 ) == 0b11 ) { |
214
|
0
|
|
|
|
|
|
++ $intersection; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Score. |
219
|
0
|
|
|
|
|
|
my $difference = |
220
|
|
|
|
|
|
|
keys(%match) - $intersection; |
221
|
0
|
|
|
|
|
|
my $score = $difference / $intersection; |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
|
$debugger_matches{$debugger} = $score; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# Select the best matching debugger. |
227
|
0
|
|
|
|
|
|
my ( $best_debugger ) = |
228
|
0
|
|
|
|
|
|
sort { $debugger_matches{$a} <=> $debugger_matches{$b} } |
229
|
|
|
|
|
|
|
keys %debugger_matches; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# It is ok to replace the null debugger but an error to replace |
233
|
|
|
|
|
|
|
# anything else. Also, there's nothing to do if we've already |
234
|
|
|
|
|
|
|
# loaded the requested debugger. |
235
|
0
|
50
|
|
|
|
|
if ( $REGISTERED_DEBUGGERS{$best_debugger}{null} ) { |
|
|
0
|
|
|
|
|
|
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
elsif ( $best_debugger eq $requested_debugger ) { |
238
|
0
|
|
|
|
|
|
return; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
else { |
241
|
0
|
|
|
|
|
|
Carp::confess("Can't replace the existing $best_debugger debugger with $requested_debugger"); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Debugger's name -> Debugger's class. |
246
|
0
|
|
|
|
|
|
$DEBUGGER = $requested_debugger; |
247
|
0
|
|
|
|
|
|
$DEBUGGER_CLASS = "${class}::$DEBUGGER"; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Debugger's class -> Debugger's .pm file. |
250
|
0
|
|
|
|
|
|
my $debugger_class_file = $DEBUGGER_CLASS; |
251
|
0
|
|
|
|
|
|
$debugger_class_file =~ s#::#/#g; |
252
|
0
|
|
|
|
|
|
$debugger_class_file .= '.pm'; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# Load the file. |
255
|
|
|
|
|
|
|
# |
256
|
|
|
|
|
|
|
# Be darn sure we're compiling COP nodes with pp_nextstate |
257
|
|
|
|
|
|
|
# instead of pp_dbstate. It sucks to start debugging your |
258
|
|
|
|
|
|
|
# debugger by accident. Incidentally... this is a great place |
259
|
|
|
|
|
|
|
# to hack if you /do/ want to make debugging a debugger a |
260
|
|
|
|
|
|
|
# possibility. |
261
|
|
|
|
|
|
|
# |
262
|
|
|
|
|
|
|
# Further, note that some debugger supports have already been loaded |
263
|
|
|
|
|
|
|
# by __PACKAGE__->register_debugger(...) below. In general, this |
264
|
|
|
|
|
|
|
# is for things I've needed to use myself. |
265
|
0
|
|
|
|
|
|
Enbugger->_compile_with_nextstate(); |
266
|
0
|
|
|
|
|
|
require $debugger_class_file; |
267
|
0
|
|
|
|
|
|
$DEBUGGER_CLASS->_load_debugger; |
268
|
0
|
|
|
|
|
|
$DEBUGGER_CLASS->instrument_runtime; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Subsequent compilation will use pp_dbstate like expected. |
272
|
0
|
|
|
|
|
|
$DEBUGGER_CLASS->_instrumented_ppaddr(); |
273
|
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
|
return; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
|
279
|
0
|
|
|
0
|
|
|
sub _uninstrumented_ppaddr { $_[0]->_compile_with_nextstate() } |
280
|
0
|
|
|
0
|
|
|
sub _instrumented_ppaddr { $_[0]->_compile_with_dbstate() } |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
sub _load_debugger; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
sub register_debugger { |
294
|
0
|
|
|
0
|
1
|
|
my ( $class, $debugger ) = @_; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# name -> class |
297
|
0
|
|
|
|
|
|
my $enbugger_subclass = "Enbugger::$debugger"; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# class -> module file |
300
|
0
|
|
|
|
|
|
my $enbugger_subclass_file = $enbugger_subclass; |
301
|
0
|
|
|
|
|
|
$enbugger_subclass_file =~ s<::>>g; |
302
|
0
|
|
|
|
|
|
$enbugger_subclass_file .= '.pm'; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Load it. *Assume* PL_ppaddr[OP_NEXTSTATE] is something |
305
|
|
|
|
|
|
|
# useful like Perl_pp_nextstate still. |
306
|
|
|
|
|
|
|
# |
307
|
|
|
|
|
|
|
# TODO: localize PL_ppaddr[OP_NEXTSTATE] during this compilation to |
308
|
|
|
|
|
|
|
# be Perl_pp_nextstate. |
309
|
0
|
|
|
|
|
|
require $enbugger_subclass_file; |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
|
312
|
0
|
|
|
|
|
|
my $src = <<"REGISTER_DEBUGGER"; |
313
|
0
|
|
|
|
|
|
#line @{[__LINE__]} "@{[__FILE__]}" |
|
0
|
|
|
|
|
|
|
314
|
0
|
|
|
0
|
0
|
|
sub load_$debugger { |
|
0
|
|
|
0
|
0
|
|
|
|
0
|
|
|
0
|
0
|
|
|
315
|
0
|
|
|
|
|
|
my ( \$class ) = \@_; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
316
|
0
|
|
|
|
|
|
\$class->load_debugger( '$debugger' ); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
return; |
318
|
|
|
|
|
|
|
}; |
319
|
|
|
|
|
|
|
REGISTER_DEBUGGER |
320
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
$src .= "return 1;\n"; |
322
|
0
|
|
|
|
|
|
my $ok = eval $src; |
323
|
0
|
50
|
|
|
|
|
die $@ unless $ok; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub load_source { |
331
|
0
|
|
|
0
|
1
|
|
my ( $class ) = @_; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# Load the original program. |
334
|
0
|
|
|
|
|
|
$class->load_file($0); |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# Load all modules. |
337
|
0
|
50
|
|
|
|
|
for ( grep { defined and -e } values %INC ) { |
|
0
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
$class->load_file($_); |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
$class->initialize_dbline; |
342
|
|
|
|
|
|
|
|
343
|
0
|
|
|
|
|
|
return; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub initialize_dbline { |
348
|
0
|
|
|
0
|
0
|
|
my $file; |
349
|
0
|
|
|
|
|
|
for ( my $cx = 1; my ( $package, $c_file ) = caller $cx; ++ $cx ) { |
350
|
0
|
100
|
|
|
|
|
if ( $package !~ /^Enbugger/ ) { |
351
|
0
|
|
|
|
|
|
$file = $c_file; |
352
|
0
|
|
|
|
|
|
last; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
0
|
50
|
|
|
|
|
if ( not defined $file ) { |
357
|
0
|
|
|
|
|
|
*DB::dbline = []; |
358
|
0
|
|
|
|
|
|
*DB::dbline = {}; |
359
|
0
|
|
|
|
|
|
Enbugger::set_magic_dbfile( \%DB::dbline ); |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
else { |
362
|
0
|
|
|
0
|
|
|
no strict 'refs'; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
*DB::dbline = \*{"main::_<$file"}; |
|
0
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub load_file { |
371
|
0
|
|
|
0
|
1
|
|
my ($class, $file) = @_; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# The symbols by which we'll know ye. |
374
|
0
|
|
|
|
|
|
my $base_symname = "_<$file"; |
375
|
0
|
|
|
|
|
|
my $symname = "main::$base_symname"; |
376
|
|
|
|
|
|
|
|
377
|
0
|
|
|
0
|
|
|
no strict 'refs'; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
my $glob = \*$symname; |
380
|
|
|
|
|
|
|
|
381
|
0
|
50
|
33
|
|
|
|
if ( ! *$symname{ARRAY} && -f $file ) { |
382
|
|
|
|
|
|
|
# Read the source. |
383
|
|
|
|
|
|
|
# Open the file. |
384
|
0
|
|
|
|
|
|
my $fh; |
385
|
0
|
50
|
|
|
|
|
if ( not open $fh, '<', $file ) { |
386
|
0
|
|
|
|
|
|
Carp::croak( "Can't open $file for reading: $!" ); |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# Load our source code. All source must be installed as at least PVIV or |
390
|
|
|
|
|
|
|
# some asserts in op.c may fail. Later, I'll assign better pointers to each |
391
|
|
|
|
|
|
|
# line in instrument_op. |
392
|
0
|
|
|
|
|
|
local $/ = "\n"; |
393
|
0
|
|
|
|
|
|
*$glob = [ |
394
|
0
|
|
|
|
|
|
map { Scalar::Util::dualvar( 0, $_ ) } |
395
|
|
|
|
|
|
|
( "BEGIN { require 'perl5db.pl' } # Generated by " . __FILE__, |
396
|
|
|
|
|
|
|
readline $fh ) |
397
|
|
|
|
|
|
|
]; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
0
|
50
|
|
|
|
|
if ( ! *$glob{HASH} ) { |
401
|
0
|
|
|
|
|
|
my %breakpoints; |
402
|
0
|
|
|
|
|
|
Enbugger::set_magic_dbfile(\%breakpoints); |
403
|
0
|
|
|
|
|
|
*$glob = \%breakpoints; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
0
|
|
33
|
|
|
|
$$symname ||= $file; |
407
|
|
|
|
|
|
|
|
408
|
0
|
|
|
|
|
|
return; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub instrument_runtime { |
418
|
|
|
|
|
|
|
# Now do the *real* work. |
419
|
0
|
|
|
0
|
1
|
|
my ( $class ) = @_; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# PL_DBgv = gv_fetchpvs("DB::DB", GV_ADDMULTI, SVt_PVGV); |
422
|
0
|
50
|
|
|
|
|
eval 'sub DB::DB {}' if ! defined &DB::DB; |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# Load the source code for all loaded files. Too bad about (eval 1) |
425
|
|
|
|
|
|
|
# though. This doesn't work. Why not!?! |
426
|
0
|
|
|
|
|
|
$class->load_source; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# PL_DBsingle = GvSV((gv_fetchpvs("DB::single", GV_ADDMULTI, SVt_PV))); |
429
|
|
|
|
|
|
|
# if (!SvIOK(PL_DBsingle)) |
430
|
|
|
|
|
|
|
# sv_setiv(PL_DBsingle, 0); |
431
|
0
|
50
|
|
|
|
|
$DB::single = 0 if ! defined $DB::single; |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# PL_DBtrace = GvSV((gv_fetchpvs("DB::trace", GV_ADDMULTI, SVt_PV))); |
434
|
|
|
|
|
|
|
# if (!SvIOK(PL_DBtrace)) |
435
|
|
|
|
|
|
|
# sv_setiv(PL_DBtrace, 0); |
436
|
0
|
50
|
|
|
|
|
$DB::trace = 0 if ! defined $DB::trace; |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# PL_DBsignal = GvSV((gv_fetchpvs("DB::signal", GV_ADDMULTI, SVt_PV))); |
439
|
|
|
|
|
|
|
# if (!SvIOK(PL_DBsignal)) |
440
|
|
|
|
|
|
|
# sv_setiv(PL_DBsignal, 0); |
441
|
0
|
50
|
|
|
|
|
$DB::signal = 0 if ! defined $DB::signal; |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# Walk over all optrees. |
444
|
|
|
|
|
|
|
# * Transform nextstate COP* nodes to dbstate COP* nodes as appropriate |
445
|
|
|
|
|
|
|
# * Set ${"main::_<$file"}[X] array elements with COP* pointers |
446
|
|
|
|
|
|
|
# * Capture function name start/end line numbers |
447
|
0
|
|
|
|
|
|
B::Utils::walkallops_simple( \ &Enbugger::instrument_op ); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# Provide %DB::sub. |
450
|
0
|
|
|
|
|
|
%DB::sub = |
451
|
0
|
|
|
|
|
|
map { $_ => sprintf '%s:%d-%d', @{$DBsub{$_}} } |
|
0
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
keys %DBsub; |
453
|
0
|
|
|
|
|
|
undef %DBsub; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub instrument_op { |
461
|
0
|
|
|
0
|
1
|
|
my ( $op ) = @_; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# Must be a B::COP node. |
464
|
0
|
100
|
100
|
|
|
|
if ( $$op and B::class( $op ) eq 'COP' ) { |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# @{"_<$file"} entries where there are COP entries are |
467
|
|
|
|
|
|
|
# dualvars of pointers to the COP nodes that will get |
468
|
|
|
|
|
|
|
# OPf_SPECIAL toggled to indicate breakpoints. |
469
|
0
|
|
|
|
|
|
my $ptr = $$op; |
470
|
0
|
|
|
|
|
|
my $source = do { |
471
|
0
|
|
|
0
|
|
|
no strict 'refs'; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
472
|
0
|
|
|
|
|
|
\ @{"main::_<$B::Utils::file"}; |
|
0
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
}; |
474
|
0
|
50
|
|
|
|
|
if ( $ptr ) { |
475
|
0
|
|
|
|
|
|
$source->[$B::Utils::line] = Scalar::Util::dualvar( $ptr, $source->[$B::Utils::line] ); |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
0
|
100
|
|
|
|
|
if ($DBsub{$B::Utils::sub}) { |
479
|
0
|
100
|
|
|
|
|
$DBsub{$B::Utils::sub}[1] = $B::Utils::line if $B::Utils::line < $DBsub{$B::Utils::sub}[1]; |
480
|
0
|
100
|
|
|
|
|
$DBsub{$B::Utils::sub}[2] = $B::Utils::line if $B::Utils::line > $DBsub{$B::Utils::sub}[2]; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
else { |
483
|
0
|
|
|
|
|
|
$DBsub{$B::Utils::sub} = [ $B::Utils::file, ($B::Utils::line) x 2 ]; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
#print $op->file ."\t".$op->line."\t".$o->stash->NAME."\t"; |
487
|
|
|
|
|
|
|
# Disable or enable debugging for this opcode. |
488
|
0
|
100
|
|
|
|
|
if ( $op->stash->NAME =~ /^(?=[DE])(?:DB|Enbugger)(?:::|\z)/ ) { |
489
|
|
|
|
|
|
|
#print 'next'; |
490
|
0
|
|
|
|
|
|
Enbugger::_nextstate_cop( $op ); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
else { |
493
|
0
|
|
|
|
|
|
Enbugger::_dbstate_cop( $op ); |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
sub import { |
503
|
0
|
|
|
0
|
|
|
my $class = shift @_; |
504
|
|
|
|
|
|
|
|
505
|
0
|
50
|
|
|
|
|
if ( @_ ) { |
506
|
0
|
|
|
|
|
|
my $selected_debugger = shift @_; |
507
|
0
|
|
|
|
|
|
$DefaultDebugger = $selected_debugger; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
BEGIN { |
513
|
0
|
|
|
0
|
|
|
__PACKAGE__->register_debugger( 'perl5db' ); |
514
|
0
|
|
|
|
|
|
__PACKAGE__->register_debugger( 'trepan' ); |
515
|
0
|
|
|
|
|
|
__PACKAGE__->register_debugger( 'NYTProf' ); |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
# TODO: __PACKAGE__->register_debugger( 'ebug' ); |
518
|
|
|
|
|
|
|
# TODO: __PACKAGE__->register_debugger( 'sdb' ); |
519
|
|
|
|
|
|
|
# TODO: __PACKAGE__->register_debugger( 'ptkdb' ); |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# Anything compiled after this statement runs will be debuggable. |
523
|
|
|
|
|
|
|
Enbugger->_compile_with_dbstate(); |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
## Local Variables: |
526
|
|
|
|
|
|
|
## mode: cperl |
527
|
|
|
|
|
|
|
## mode: auto-fill |
528
|
|
|
|
|
|
|
## cperl-indent-level: 4 |
529
|
|
|
|
|
|
|
## tab-width: 8 |
530
|
|
|
|
|
|
|
## End: |
531
|
|
|
|
|
|
|
|
532
|
0
|
|
|
0
|
|
|
no warnings 'void'; ## no critic |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
'But this is the internet, dear, stupid is one of our prime exports.'; |