1 | #!/usr/bin/perl |
---|
2 | |
---|
3 | use strict; |
---|
4 | use warnings; |
---|
5 | |
---|
6 | my @symbol_priority = ( |
---|
7 | '::', # class members |
---|
8 | '^TEST_', # ARB unit tests |
---|
9 | '^publishTEST_', # ARB unit tests (published) |
---|
10 | '^GBP_', # perl interface functions |
---|
11 | '^(create|destroy|copy|save|load)_(AN|PT|dll)_', # AISC generated functions |
---|
12 | '^main$', |
---|
13 | ); |
---|
14 | |
---|
15 | my $reg_file_exclude = qr/\/(GDE|EISPACK|READSEQ|PERL2ARB)\//; |
---|
16 | |
---|
17 | sub findObjects(\@) { |
---|
18 | my ($libs_r) = @_; |
---|
19 | |
---|
20 | my $cmd = 'find . -name "*.o"'; |
---|
21 | open(FOUND,$cmd.'|') || die "failed to fork '$cmd' (Reason: $!)"; |
---|
22 | foreach (<FOUND>) { |
---|
23 | chomp $_; |
---|
24 | if (not $_ =~ $reg_file_exclude) { |
---|
25 | push @$libs_r, $_; |
---|
26 | } |
---|
27 | } |
---|
28 | close(FOUND) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)"; |
---|
29 | } |
---|
30 | |
---|
31 | sub is_weak($) { |
---|
32 | my ($type) = @_; |
---|
33 | return ($type =~ /^[vVwW]$/o); |
---|
34 | } |
---|
35 | |
---|
36 | # ------------------------------ store symbols |
---|
37 | |
---|
38 | my %def_loc = (); # key = symbol, value=ref to array [ file, line, type ] |
---|
39 | my %dupdef_loc = (); # key = symbol, value=ref to array of refs to array [ file, line, type ] |
---|
40 | |
---|
41 | my %referenced = (); # key=symbol, value=1 -> has been referenced |
---|
42 | |
---|
43 | sub set_definition($$$$) { |
---|
44 | my ($sym,$file,$line,$type) = @_; |
---|
45 | my @array = ($file,$line,$type); |
---|
46 | $def_loc{$sym} = \@array; |
---|
47 | } |
---|
48 | |
---|
49 | sub add_dup_definition($$$$) { |
---|
50 | my ($sym,$file,$line,$type) = @_; |
---|
51 | |
---|
52 | my @array = ($file,$line,$type); |
---|
53 | my $dups_r = $dupdef_loc{$sym}; |
---|
54 | |
---|
55 | if (not defined $dups_r) { |
---|
56 | my @dups = ( \@array ); |
---|
57 | $dupdef_loc{$sym} = \@dups; |
---|
58 | } |
---|
59 | else { |
---|
60 | my $add = 1; |
---|
61 | LOOKUP: foreach my $duploc_r (@$dups_r) { |
---|
62 | my ($dfile,$dline,$dtype) = @$duploc_r; |
---|
63 | if (($dfile eq $file) and ($dline eq $line)) { # already have that location |
---|
64 | $add = 0; |
---|
65 | last LOOKUP; |
---|
66 | } |
---|
67 | } |
---|
68 | if ($add==1) { |
---|
69 | push @$dups_r, \@array; |
---|
70 | } |
---|
71 | } |
---|
72 | } |
---|
73 | |
---|
74 | sub definesSymbol($$$$$) { |
---|
75 | my ($obj,$file,$line,$sym,$type) = @_; |
---|
76 | |
---|
77 | my $loc_r = $def_loc{$sym}; |
---|
78 | if (not defined $loc_r) { set_definition($sym,$file,$line,$type); } |
---|
79 | else { |
---|
80 | my ($pfile,$pline,$ptype) = @$loc_r; |
---|
81 | if (($file ne $pfile) and ($line != $pline)) { # locations differ |
---|
82 | if (is_weak($ptype) and not is_weak($type)) { |
---|
83 | set_definition($sym,$file,$line,$type); |
---|
84 | add_dup_definition($sym,$pfile,$pline,$ptype); |
---|
85 | } |
---|
86 | else { add_dup_definition($sym,$file,$line,$type); } |
---|
87 | } |
---|
88 | } |
---|
89 | } |
---|
90 | |
---|
91 | sub referencesSymbol($$$) { |
---|
92 | my ($obj,$sym,$type) = @_; |
---|
93 | $referenced{$sym} = $obj; |
---|
94 | } |
---|
95 | |
---|
96 | # ------------------------------ analyse |
---|
97 | |
---|
98 | sub list_unreferenced_symbols() { |
---|
99 | print "Checking unreferenced symbols:\n"; |
---|
100 | |
---|
101 | my @undefs = (); |
---|
102 | foreach my $sym (keys %def_loc) { |
---|
103 | my $ref_r = $referenced{$sym}; |
---|
104 | if (not defined $ref_r) { |
---|
105 | my $def_r = $def_loc{$sym}; |
---|
106 | my ($file,$line,$type) = @$def_r; |
---|
107 | if (not is_weak($type) and # ignore weak unrefs |
---|
108 | not $file =~ /^\/usr\/include\//o # ignore unrefs if /usr/include |
---|
109 | ) { |
---|
110 | push @undefs, $sym; |
---|
111 | } |
---|
112 | } |
---|
113 | } |
---|
114 | |
---|
115 | @undefs = sort { |
---|
116 | my $la_r = $def_loc{$a}; |
---|
117 | my $lb_r = $def_loc{$b}; |
---|
118 | my $cmp = $$la_r[0] cmp $$lb_r[0]; |
---|
119 | if ($cmp==0) { $cmp = $$la_r[1] <=> $$lb_r[1]; } |
---|
120 | $cmp; |
---|
121 | } @undefs; |
---|
122 | |
---|
123 | my %importance = map { $_ => 1; } @undefs; # key=sym, value=importance (lower = more important) |
---|
124 | |
---|
125 | my $regs = scalar(@symbol_priority); |
---|
126 | for (my $r = 0; $r<$regs; $r++) { |
---|
127 | my $expr = $symbol_priority[$r]; |
---|
128 | my $imp = $r+2; |
---|
129 | my $reg = qr/$expr/; |
---|
130 | foreach my $sym (@undefs) { |
---|
131 | if ($sym =~ $reg) { |
---|
132 | $importance{$sym} = $imp; |
---|
133 | } |
---|
134 | } |
---|
135 | } |
---|
136 | my $max_imp = $regs+1; |
---|
137 | for (my $i=1; $i<=$max_imp; $i++) { |
---|
138 | print "Symbols for importance==$i:\n"; |
---|
139 | foreach my $sym (@undefs) { |
---|
140 | if ($importance{$sym} == $i) { |
---|
141 | my $def_r = $def_loc{$sym}; |
---|
142 | my ($file,$line,$type) = @$def_r; |
---|
143 | print "$file:$line: unreferenced '$sym' [$type]\n"; |
---|
144 | } |
---|
145 | } |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | sub list_duplicate_defines() { |
---|
150 | print "Checking duplicate definitions :\n"; |
---|
151 | foreach my $sym (keys %dupdef_loc) { |
---|
152 | my $main_def_r = $def_loc{$sym}; |
---|
153 | my ($file,$line,$type) = @$main_def_r; |
---|
154 | if (not is_weak($type)) { # dont warn about weak symbols |
---|
155 | my $dup_def_r = $dupdef_loc{$sym}; |
---|
156 | my $onlyWeakDups = 1; |
---|
157 | foreach my $dup_r (@$dup_def_r) { |
---|
158 | my ($dfile,$dline,$dtype) = @$dup_r; |
---|
159 | if (not is_weak($dtype)) { |
---|
160 | if ($onlyWeakDups==1) { # first non-weak dup -> start |
---|
161 | print "$file:$line: Multiple definition of '$sym' [$type]\n"; |
---|
162 | $onlyWeakDups = 0; |
---|
163 | } |
---|
164 | print "$dfile:$dline: duplicate definition [$dtype]\n"; |
---|
165 | } |
---|
166 | } |
---|
167 | } |
---|
168 | } |
---|
169 | } |
---|
170 | |
---|
171 | # ------------------------------ parse |
---|
172 | |
---|
173 | my $reg_def = qr/^(.*)\s([BDRTVW])\s([0-9a-f]+)\s([0-9a-f]+)\s+([^\s]+):([0-9]+)$/; |
---|
174 | my $reg_def_noloc = qr/^(.*)\s([BDRTVW])\s([0-9a-f]+)\s([0-9a-f]+)$/; |
---|
175 | my $reg_def_noloc_oneadd = qr/^(.*)\s([AT])\s([0-9a-f]+)\s+$/; |
---|
176 | |
---|
177 | my $reg_refer = qr/^(.*)\s([Uw])\s+([^\s]+):([0-9]+)$/; |
---|
178 | my $reg_refer_noloc = qr/^(.*)\s([Uw])\s+$/; |
---|
179 | |
---|
180 | sub dump_unhandled_line($$) { |
---|
181 | my ($tag,$line) = @_; |
---|
182 | if (0) { |
---|
183 | print "unhandled [$tag] : $line\n"; |
---|
184 | } |
---|
185 | } |
---|
186 | |
---|
187 | sub scanObj($) { |
---|
188 | my ($obj) = @_; |
---|
189 | |
---|
190 | my $cmd = 'nm -l -P -p -g '.$obj.' 2>&1 | c++filt '; |
---|
191 | open(SYMBOLS,$cmd.'|') || die "failed to fork '$cmd' (Reason: $!)"; |
---|
192 | my $line; |
---|
193 | while (defined($line=<SYMBOLS>)) { |
---|
194 | chomp($line); |
---|
195 | |
---|
196 | if ($line =~ $reg_def) { |
---|
197 | my ($sym,$type,$add1,$add2,$file,$line) = ($1,$2,$3,$4,$5,$6); |
---|
198 | definesSymbol($obj,$file,$line,$sym,$type); |
---|
199 | } |
---|
200 | elsif ($line =~ $reg_def_noloc) { dump_unhandled_line("noloc1", $line); } |
---|
201 | elsif ($line =~ $reg_def_noloc_oneadd) { dump_unhandled_line("noloc2", $line); } |
---|
202 | elsif ($line =~ $reg_refer) { |
---|
203 | my ($sym,$type,$file,$line) = ($1,$2,$3,$4); |
---|
204 | referencesSymbol($obj,$sym,$type); |
---|
205 | } |
---|
206 | elsif ($line =~ $reg_refer_noloc) { |
---|
207 | my ($sym,$type) = ($1,$2); |
---|
208 | referencesSymbol($obj,$sym,$type); |
---|
209 | } |
---|
210 | else { |
---|
211 | die "can't parse line '$line'\n"; |
---|
212 | } |
---|
213 | } |
---|
214 | close(SYMBOLS) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)"; |
---|
215 | } |
---|
216 | |
---|
217 | sub main() { |
---|
218 | print "DeadCode detector\n"; |
---|
219 | print "- detects useless external linkage, that could go static\n"; |
---|
220 | print " (then the compiler will warn if code/data is unused)\n"; |
---|
221 | print "- needs compilation with DEBUG information\n"; |
---|
222 | print "- also lists\n"; |
---|
223 | print " - useless stuff like class-members, xsub-syms\n"; |
---|
224 | print " - duplicated global symbols\n"; |
---|
225 | |
---|
226 | my @objs; |
---|
227 | findObjects(@objs); |
---|
228 | print 'Examining '.scalar(@objs)." objs\n"; |
---|
229 | |
---|
230 | foreach my $obj (@objs) { |
---|
231 | scanObj($obj); |
---|
232 | } |
---|
233 | |
---|
234 | print "Summary :\n"; |
---|
235 | print " - found ".scalar(keys %def_loc)." defined symbols\n"; |
---|
236 | print " - found ".scalar(keys %referenced)." referenced symbols\n"; |
---|
237 | |
---|
238 | list_unreferenced_symbols(); |
---|
239 | list_duplicate_defines(); |
---|
240 | } |
---|
241 | main(); |
---|