line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
1
|
|
|
1
|
|
755
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
41
|
|
3
|
1
|
|
|
1
|
|
6
|
use File::Path 'mkpath'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
67
|
|
4
|
1
|
|
|
1
|
|
960
|
use File::Copy 'copy'; |
|
1
|
|
|
|
|
2975
|
|
|
1
|
|
|
|
|
1552
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
my $VERSION = '0.02'; # Changelog at end |
7
|
|
|
|
|
|
|
die "Debugging cycle detected" # set to -1 to allow extra iteration |
8
|
|
|
|
|
|
|
if ++$ENV{PERL_DEBUG_MCODE_CYCLE} > 1; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
my %opt; |
11
|
|
|
|
|
|
|
$opt{$1} = shift while ($ARGV[0] || 0) =~ /^-([dq1])$/; |
12
|
|
|
|
|
|
|
if ($opt{1}) { |
13
|
|
|
|
|
|
|
open STDERR, '>&STDOUT' or warn "can't redirect STDERR to STDOUT"; |
14
|
|
|
|
|
|
|
} else { |
15
|
|
|
|
|
|
|
open STDOUT, '>&STDERR' or warn "can't redirect STDOUT to STDERR"; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my $bd = 'dbg-bld'; |
19
|
|
|
|
|
|
|
@ARGV >= 1 or die <
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Usage: $0 [-d] [-q] [-1] check-module [failing-script1 failing-script2 ...] |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
A tool to simplify remote debugging of build problems for XSUB modules. |
24
|
|
|
|
|
|
|
By default, output goes to STDERR (to pass through the test suite wrappers). |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
If CHECK-MODULE is non-empty (and not 0) checks whether it may be |
27
|
|
|
|
|
|
|
loaded (with -Mblib). If FAILING-SCRIPTS are present, rebuilds the |
28
|
|
|
|
|
|
|
current distribution with debugging (in subdirectory $bd), and |
29
|
|
|
|
|
|
|
machine-code-debugs Perl crash when running each FAILING-SCRIPT. |
30
|
|
|
|
|
|
|
Outputs as much info about the crash as it can massage from gdb or |
31
|
|
|
|
|
|
|
dbx. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
If any problem is detected, outputs the MakeMaker arguments (extracted |
34
|
|
|
|
|
|
|
from the generated Makefiles). Some minimal intelligence to avoid a |
35
|
|
|
|
|
|
|
flood of useless information is applied: if CHECK-MODULE cannot be |
36
|
|
|
|
|
|
|
loaded (but there is no crash during loading), no debugging for |
37
|
|
|
|
|
|
|
FAILING-SCRIPTs is done. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Options: With -d, prefers dbx to gdb (DEFAULT: prefer gdb). |
40
|
|
|
|
|
|
|
With -q and no FAILING-SCRIPTs, won't print anything unless a |
41
|
|
|
|
|
|
|
failure of loading is detected. |
42
|
|
|
|
|
|
|
With -1, all output goes to STDOUT. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Assumptions: |
45
|
|
|
|
|
|
|
Should be run in the root of a distribution, or its immediate subdir. |
46
|
|
|
|
|
|
|
Running Makefile.PL with OPTIMIZE=-g builds debugging version. |
47
|
|
|
|
|
|
|
If FAILING-SCRIPTs are relative paths, they should be local w.r.t. the |
48
|
|
|
|
|
|
|
root of the distribution. |
49
|
|
|
|
|
|
|
gdb (or dbx) is fresh enough to understand the options we throw in. |
50
|
|
|
|
|
|
|
Building in a subdirectory does not break a module (e.g., there is |
51
|
|
|
|
|
|
|
no dependence on its position in its parent distribution, if any). |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Creates a subdirectory ./$bd. Add it to `clean' in Makefile.PL. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Version: $VERSION |
56
|
|
|
|
|
|
|
EOP |
57
|
|
|
|
|
|
|
my ($chk_module) = (shift); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub report_Makefile ($) { |
60
|
0
|
|
|
0
|
|
|
my $f = shift; |
61
|
0
|
|
|
|
|
|
print STDERR "# reporting $f header:\n# ==========================\n"; |
62
|
0
|
0
|
|
|
|
|
my ($base_d, $in) = (-f "t/sinl.t" ? '.' : '..', ''); |
63
|
0
|
0
|
|
|
|
|
open M, "< $f" or die "Can't open $f"; |
64
|
0
|
|
0
|
|
|
|
$in = while defined $in and $in !~ /MakeMaker \s+ Parameters/xi; |
65
|
0
|
|
|
|
|
|
$in = ; |
66
|
0
|
|
0
|
|
|
|
$in = while defined $in and $in !~ /\S/; |
67
|
0
|
|
0
|
|
|
|
print STDERR $in and $in = while defined $in and $in =~ /^#/; |
|
|
|
0
|
|
|
|
|
68
|
0
|
|
|
|
|
|
close M; |
69
|
0
|
|
|
|
|
|
print STDERR "# ==========================\n"; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# We assume that MANIFEST contains no filenames with spaces |
73
|
|
|
|
|
|
|
chdir '..' or die "chdir ..: $!" |
74
|
|
|
|
|
|
|
if not -f 'MANIFEST' and -f '../MANIFEST'; # we may be in ./t |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Try to avoid debugging a code failing by some other reason than crashing. |
77
|
|
|
|
|
|
|
# In principle, it is easier to do in the "trigger" code with proper BEGIN/END; |
78
|
|
|
|
|
|
|
# just be extra careful, and recheck. (And we can be used standalone as well!) |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# There are 4 cases detected below, with !@ARGV thrown in, one covers 8 types. |
81
|
|
|
|
|
|
|
my($skip_makefiles, $mod_load_out); |
82
|
|
|
|
|
|
|
if ($chk_module) { |
83
|
|
|
|
|
|
|
# Using blib may give a false positive (blib fails) unless distribution |
84
|
|
|
|
|
|
|
# is already built; but the cost is small: just a useless rebuild+test |
85
|
|
|
|
|
|
|
if (system $^X, q(-wle), q(use blib)) { |
86
|
|
|
|
|
|
|
warn <
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Given that -Mblib fails, `perl Makefile.PL; make' was not run here yet... |
89
|
|
|
|
|
|
|
I can't do any intelligent pre-flight testing now; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
EOW |
92
|
|
|
|
|
|
|
die "Having no FAILING-SCRIPT makes no sense when -Mblib fails" |
93
|
|
|
|
|
|
|
unless @ARGV; |
94
|
|
|
|
|
|
|
warn <
|
95
|
|
|
|
|
|
|
... so I just presume YOU know that machine-code debugging IS needed... |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
EOW |
98
|
|
|
|
|
|
|
$skip_makefiles = 1; |
99
|
|
|
|
|
|
|
} else { #` |
100
|
|
|
|
|
|
|
# The most common "perpendicular" problem is that a loader would not load DLL ==> no crash. |
101
|
|
|
|
|
|
|
# Then there is no point in running machine code debugging; try to detect this: |
102
|
|
|
|
|
|
|
my $mod_load = `$^X -wle "use blib; print(eval q(use $chk_module; 1) ? 123456789 : 987654321)" 2>&1`; |
103
|
|
|
|
|
|
|
# Crashes ==> no "digits" output; DO debug. Do not debug if no crash, and no load |
104
|
|
|
|
|
|
|
if ($mod_load =~ /987654321/) { # DLL does not load, no crash |
105
|
|
|
|
|
|
|
$mod_load_out = `$^X -wle "use blib; use $chk_module" 2>&1`; |
106
|
|
|
|
|
|
|
warn "Module $chk_module won't load: $mod_load_out"; |
107
|
|
|
|
|
|
|
@ARGV = (); # machine-code debugging won't help |
108
|
|
|
|
|
|
|
} elsif ($mod_load =~ /123456789/) { # Loads OK |
109
|
|
|
|
|
|
|
# a (suspected) failure has a chance to be helped by machine-code debug |
110
|
|
|
|
|
|
|
($opt{'q'} or warn(<
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Module loads without a problem. (No FAILING-SCRIPT, so I skip debugging step.) |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
EOW |
115
|
|
|
|
|
|
|
} # else: Crash during DLL load. Do debug |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
unless ($skip_makefiles) { |
119
|
|
|
|
|
|
|
report_Makefile($_) for grep -f "$_.PL" && -f, map "$_/Makefile", '.', <*>; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
exit 0 unless @ARGV; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my $gdb = `gdb --version` unless $opt{d}; |
124
|
|
|
|
|
|
|
my $dbx = `dbx -V -c quit` unless $gdb; |
125
|
|
|
|
|
|
|
$gdb = `gdb --version` unless $gdb or $dbx; |
126
|
|
|
|
|
|
|
die "Can't find gdb or dbx" unless defined $gdb or defined $dbx; |
127
|
|
|
|
|
|
|
die "Can't parse output of gdb --version" |
128
|
|
|
|
|
|
|
unless $dbx or $gdb =~ /\b GDB \b | \b Copyright \b .* \b Free Software \b/x; |
129
|
|
|
|
|
|
|
die "Can't parse output of `dbx -V -c quit'" |
130
|
|
|
|
|
|
|
unless $gdb or $dbx =~ /\b dbx \s+ debugger \b/xi; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
die "Directory $bd exist; won't overwrite" if -d $bd; |
133
|
|
|
|
|
|
|
mkdir $bd or die "mkdir $bd: $!"; |
134
|
|
|
|
|
|
|
chdir $bd or die "chdir $bd: $!"; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
open MF, '../MANIFEST' or die "Can't read MANIFEST: $!"; |
137
|
|
|
|
|
|
|
while () { |
138
|
|
|
|
|
|
|
next unless /^\S/; |
139
|
|
|
|
|
|
|
s/\s.*//; |
140
|
|
|
|
|
|
|
my ($f, $d) = m[^((.*/)?.*)]; |
141
|
|
|
|
|
|
|
-d $d or mkpath $d if defined $d; # croak()s itself |
142
|
|
|
|
|
|
|
copy "../$f", $f or die "copy `../$f' to `$f' (inside $bd): $!"; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
close MF or die "Can't close MANIFEST: $!"; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
system $^X, qw(Makefile.PL OPTIMIZE=-g) and die "system(Makefile.PL OPTIMIZE=-g): rc=$?"; |
147
|
|
|
|
|
|
|
system 'make' and die "system(make): rc=$?"; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
my $p = ($^X =~ m([\\/]) ? $^X : `which perl`) || $^X; |
150
|
|
|
|
|
|
|
chomp $p unless $p eq $^X; |
151
|
|
|
|
|
|
|
my(@cmd, $ver); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
for my $script (@ARGV) { |
154
|
|
|
|
|
|
|
if ($gdb) { |
155
|
|
|
|
|
|
|
$ver = $gdb; |
156
|
|
|
|
|
|
|
my $gdb_in = 'gdb-in'; |
157
|
|
|
|
|
|
|
open TT, ">$gdb_in" or die "Can't open $gdb_in for write: $!"; |
158
|
|
|
|
|
|
|
# bt full: include local vars (not in 5.0; is in 6.5; is in 6.1, but crashes on 6.3: |
159
|
|
|
|
|
|
|
# http://www.cpantesters.org/cpan/report/2fffc390-afd2-11df-834b-ae20f5ac70d3) |
160
|
|
|
|
|
|
|
# disas /m : with source lines (FULL function?!) (not in 6.5; is in 7.0.1) |
161
|
|
|
|
|
|
|
# XXX all-registers may take 6K on amd64; maybe put at end? |
162
|
|
|
|
|
|
|
my $proc = (-d "/proc/$$" ? <
|
163
|
|
|
|
|
|
|
info proc mapping |
164
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
165
|
|
|
|
|
|
|
EOP |
166
|
|
|
|
|
|
|
print TT <
|
167
|
|
|
|
|
|
|
run -Mblib $script |
168
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
169
|
|
|
|
|
|
|
bt |
170
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
171
|
|
|
|
|
|
|
info all-registers |
172
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
173
|
|
|
|
|
|
|
disassemble |
174
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
175
|
|
|
|
|
|
|
bt 5 full |
176
|
|
|
|
|
|
|
echo \\n=====================================\\n\\n |
177
|
|
|
|
|
|
|
$proc disassemble /m |
178
|
|
|
|
|
|
|
quit |
179
|
|
|
|
|
|
|
EOP |
180
|
|
|
|
|
|
|
close TT or die "Can't close $gdb_in for write: $!"; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
#open STDIN, $gdb_in or die "cannot open STDIN from $gdb_in: $!"; |
183
|
|
|
|
|
|
|
@cmd = (qw(gdb -batch), "--command=$gdb_in", $p); |
184
|
|
|
|
|
|
|
} else { # Assume $script has no spaces or metachars |
185
|
|
|
|
|
|
|
# Linux: /proc/$proc/maps has the text map |
186
|
|
|
|
|
|
|
# Solaris: /proc/$proc/map & /proc/$proc/rmap: binary used/reserved |
187
|
|
|
|
|
|
|
# /usr/proc/bin/pmap $proc (>= 2.5) needs -F (force) inside dbx |
188
|
|
|
|
|
|
|
$ver = $dbx; |
189
|
|
|
|
|
|
|
# where -v # Verbose traceback (include function args and line info) |
190
|
|
|
|
|
|
|
# dump # Print all variables local to the current procedure |
191
|
|
|
|
|
|
|
# regs [-f] [-F] # Print value of registers (-f/-F: SPARC only) |
192
|
|
|
|
|
|
|
# list - # List previous lines (next with +) |
193
|
|
|
|
|
|
|
# -i or -instr # Intermix source lines and assembly code |
194
|
|
|
|
|
|
|
@cmd = (qw(dbx -c), # We do not do non-integer registers... |
195
|
|
|
|
|
|
|
qq(run -Mblib $script; echo; echo =================================; echo; where -v; echo; echo =================================; echo; dump; echo; echo =================================; echo; regs; echo; echo =================================; echo; list -i +1; echo; echo =================================; echo; list -i -10; echo; echo =================================; echo; echo ============== up 1:; up; dump; echo; echo ============== up 2:; up; dump; echo; echo ============== up 3:; up; dump; echo; echo ============== up 4:; up; dump; echo ==============; /usr/proc/bin/pmap -F \$proc; quit), |
196
|
|
|
|
|
|
|
$p); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
system @cmd and die "Running @cmd: rc=$?"; |
199
|
|
|
|
|
|
|
print $ver; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
1; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
__END__ |