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