File: deadcode.pl

package info (click to toggle)
arb 6.0.6-8
  • links: PTS, VCS
  • area: non-free
  • in suites: sid, trixie
  • size: 66,204 kB
  • sloc: ansic: 394,911; cpp: 250,290; makefile: 19,644; sh: 15,879; perl: 10,473; fortran: 6,019; ruby: 683; xml: 503; python: 53; awk: 32
file content (229 lines) | stat: -rwxr-xr-x 6,126 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/perl

use strict;
use warnings;

my @symbol_priority = (
                       '::', # class members
                       '^TEST_', # ARB unit tests
                       '^XS_', # perl xsub interface
                       '^main$',
                      );

my $reg_file_exclude = qr/\/(GDE|EISPACK|READSEQ|PERL2ARB)\//;


sub findObjects(\@) {
  my ($libs_r) = @_;

  my $cmd = 'find . -name "*.o"';
  open(FOUND,$cmd.'|') || die "can't execute '$cmd' (Reason: $?)";
  foreach (<FOUND>) {
    chomp $_;
    if (not $_ =~ $reg_file_exclude) {
      push @$libs_r, $_;
    }
  }
  close(FOUND);
}

sub is_weak($) {
  my ($type) = @_;
  return ($type =~ /^[vVwW]$/o);
}

# ------------------------------ store symbols

my %def_loc = (); # key = symbol, value=ref to array [ file, line, type ]
my %dupdef_loc = (); # key = symbol, value=ref to array of refs to array [ file, line, type ]

my %referenced = (); # key=symbol, value=1 -> has been referenced

sub set_definition($$$$) {
  my ($sym,$file,$line,$type) = @_;
  my @array = ($file,$line,$type);
  $def_loc{$sym} = \@array;
}

sub add_dup_definition($$$$) {
  my ($sym,$file,$line,$type) = @_;

  my @array = ($file,$line,$type);
  my $dups_r = $dupdef_loc{$sym};

  if (not defined $dups_r) {
    my @dups = ( \@array );
    $dupdef_loc{$sym} = \@dups;
  }
  else {
    my $add = 1;
  LOOKUP: foreach my $duploc_r (@$dups_r) {
      my ($dfile,$dline,$dtype) = @$duploc_r;
      if (($dfile eq $file) and ($dline eq $line)) { # already have that location
        $add = 0;
        last LOOKUP;
      }
    }
    if ($add==1) {
      push @$dups_r, \@array;
    }
  }
}

sub definesSymbol($$$$$) {
  my ($obj,$file,$line,$sym,$type) = @_;

  my $loc_r = $def_loc{$sym};
  if (not defined $loc_r) { set_definition($sym,$file,$line,$type); }
  else {
    my ($pfile,$pline,$ptype) = @$loc_r;
    if (($file ne $pfile) and ($line != $pline)) { # locations differ
      if (is_weak($ptype) and not is_weak($type)) {
        set_definition($sym,$file,$line,$type);
        add_dup_definition($sym,$pfile,$pline,$ptype);
      }
      else { add_dup_definition($sym,$file,$line,$type); }
    }
  }
}

sub referencesSymbol($$$) {
  my ($obj,$sym,$type) = @_;
  $referenced{$sym} = $obj;
}

# ------------------------------ analyse

sub list_unreferenced_symbols() {
  print "Checking unreferenced symbols:\n";

  my @undefs = ();
  foreach my $sym (keys %def_loc) {
    my $ref_r = $referenced{$sym};
    if (not defined $ref_r) {
      my $def_r = $def_loc{$sym};
      my ($file,$line,$type) = @$def_r;
      if (not is_weak($type) and # ignore weak unrefs
          not $file =~ /^\/usr\/include\//o # ignore unrefs if /usr/include
          ) { 
        push @undefs, $sym;
      }
    }
  }

  @undefs = sort {
    my $la_r = $def_loc{$a};
    my $lb_r = $def_loc{$b};
    my $cmp = $$la_r[0] cmp $$lb_r[0];
    if ($cmp==0) { $cmp = $$la_r[1] <=> $$lb_r[1]; }
    $cmp;
  } @undefs;

  my %importance = map { $_ => 1; } @undefs; # key=sym, value=importance (lower = more important)

  my $regs = scalar(@symbol_priority);
  for (my $r = 0; $r<$regs; $r++) {
    my $expr = $symbol_priority[$r];
    my $imp = $r+2;
    my $reg = qr/$expr/;
    foreach my $sym (@undefs) {
      if ($sym =~ $reg) {
        $importance{$sym} = $imp;
      }
    }
  }
  my $max_imp = $regs+1;
  for (my $i=1; $i<=$max_imp; $i++) {
    print "Symbols for importance==$i:\n";
    foreach my $sym (@undefs) {
      if ($importance{$sym} == $i) {
        my $def_r = $def_loc{$sym};
        my ($file,$line,$type) = @$def_r;
        print "$file:$line: unreferenced '$sym' [$type]\n";
      }
    }
  }
}

sub list_duplicate_defines() {
  print "Checking duplicate definitions:\n";
  foreach my $sym (keys %dupdef_loc) {
    my $main_def_r = $def_loc{$sym};
    my ($file,$line,$type) = @$main_def_r;
    if (not is_weak($type)) { # dont warn about weak symbols
      my $dup_def_r = $dupdef_loc{$sym};
      my $onlyWeakDups = 1;
      foreach my $dup_r (@$dup_def_r) {
        my ($dfile,$dline,$dtype) = @$dup_r;
        if (not is_weak($dtype)) {
          if ($onlyWeakDups==1) { # first non-weak dup -> start
            print "$file:$line: Multiple definition of '$sym' [$type]\n";
            $onlyWeakDups = 0;
          }
          print "$dfile:$dline: duplicate definition [$dtype]\n";
        }
      }
    }
  }
}

# ------------------------------ parse

my $reg_def = qr/^(.*)\s([BDRTVW])\s([0-9a-f]+)\s([0-9a-f]+)\s+([^\s]+):([0-9]+)$/;
my $reg_def_noloc = qr/^(.*)\s([BDRTVW])\s([0-9a-f]+)\s([0-9a-f]+)$/;
my $reg_def_noloc_oneadd = qr/^(.*)\s([AT])\s([0-9a-f]+)\s+$/;

my $reg_refer = qr/^(.*)\s([Uw])\s+([^\s]+):([0-9]+)$/;
my $reg_refer_noloc = qr/^(.*)\s([Uw])\s+$/;

sub scanObj($) {
  my ($obj) = @_;

  my $cmd = 'nm -l -P -p -C -g '.$obj;
  open(SYMBOLS,$cmd.'|') || die "can't execute '$cmd' (Reason: $?)";
  my $line;
  while (defined($line=<SYMBOLS>)) {
    chomp($line);

    if ($line =~ $reg_def) {
      my ($sym,$type,$add1,$add2,$file,$line) = ($1,$2,$3,$4,$5,$6);
      definesSymbol($obj,$file,$line,$sym,$type);
    }
    elsif ($line =~ $reg_def_noloc) { ; } # ignore atm
    elsif ($line =~ $reg_def_noloc_oneadd) { ; } # ignore atm
    elsif ($line =~ $reg_refer) {
      my ($sym,$type,$file,$line) = ($1,$2,$3,$4);
      referencesSymbol($obj,$sym,$type);
    }
    elsif ($line =~ $reg_refer_noloc) {
      my ($sym,$type) = ($1,$2);
      referencesSymbol($obj,$sym,$type);
    }
    else {
      die "can't parse line '$line'\n";
    }
  }
  close(SYMBOLS);
}

sub main() {
  print "DeadCode detector\n";
  print "- detects useless external linkage, that could go static\n";
  print "  (then the compiler will warn if code/data is unused)\n";
  print "- needs compilation with DEBUG information\n";
  print "- also lists\n";
  print "  - useless stuff like class-members, xsub-syms\n";
  print "  - duplicated global symbols\n";

  my @objs;
  findObjects(@objs);
  print 'Examining '.scalar(@objs)." objs\n";

  foreach my $obj (@objs) {
    scanObj($obj);
  }

  list_unreferenced_symbols();
  list_duplicate_defines();
}
main();