K.Sasada's Home Page

Diary - 2017 November

研究日記

霜月

_17(Fri)

パンダ可愛い。毎日可愛い。すごい。


C 言語の時、リファクタリングなどを通して、ある関数が使われている / いないをチェックする方法が欲しいことは時々あります。static 関数であれば、コンパイラが「誰も使っていない static 関数があるけど、いいの?」と warning を出してくれますが、static でなければ、別のファイルが利用するかもしれないので、特に何も言いません。リンク時に、visibility が default でないもの(ELF とかで、外部から見えないシンボル)なのに、参照がない場合に警告でも出してくれればいいんですが、今ちょっと見てみても、そういうオプションは無さそう。

というわけで、調べてみようと思ったんだけど、多分必要なのは、「ある関数から呼ばれた関数一覧」があれば、どの関数が参照を持っていないかわかるんじゃないか、というもの。まぁ、やれば出来るんだろうけど、面倒なので、もちろん書きたくないし、ソフトとかインストールしたくない。

Twitter で聞いてみたところ、https://searchcode.com/codesearch/view/17596782/ を教えてもらいました。要点は次の通り:

(1) nm を使うことで、「あるファイルから呼ばれる、他のファイルの関数一覧」一覧を得ることが出来る。 (2) 「あるファイルにある export された関数一覧」を得ることができる。

これを比較すれば、「export された関数一覧だが、外から誰も参照していない関数」、つまり「static でいいんじゃないの?」という提案ができる、と。そして、static 化すれば、その後でもし使っていなければ、「誰も使ってないんじゃ無いの?」ということがわかる。まぁ、その前に grep してしまえばいいのですが。

誰も使ってないけど、拡張ライブラリのために一応置いてある関数もあるので、一概に「誰も使ってない」とも言えないのだけど、まぁ grep すればいいのでヒントにはなる。

で、Perl script は動かなかったので、Ruby でちょろっと書き直した。

#
# This tool is inspired by https://searchcode.com/codesearch/view/17596782/

funcs = Hash.new{|h, k| h[k] = Hash.new(0)} # [type => [name]]
ARGV.each{|file|
  `nm #{file}`.each_line{|line|
      # 0000000000000190 t call_cfunc_5
    if /................ (.) (.+)/ =~ line
      type = $1
      func = $2
      funcs[type][func] += 1
    else
      raise line.dump
    end
  }
}
require 'pp'

exported_functions = funcs['T'].keys
undefined_functions = funcs['U']
include_file_data_ary = Dir.glob(File.join(__dir__, '../include/ruby/*.h')).map{|include_file|
  [include_file, File.read(include_file)]
}

ref_func = []
found_func = []
unfound_func = []
exported_functions.each{|f|
  if undefined_functions.has_key? f
    ref_func << [undefined_functions[f], f]
  else
    found = false
    include_file_data_ary.each{|(include_file, data)|
      if !data.scan("#{f}(").empty?
        found_func << "#{f} in #{include_file}"
        found = true; break
      end
    }
    unless found
      unfound_func << "static?: #{f}"
    end
  end
}
ref_func.sort_by{|(n, f)| -n}.each{|(n, f)|
  puts "#{f} is reffered from #{n} files"
}
puts found_func
puts unfound_func

便利。最後のほう、include/ にあれば公開関数に違いない、と想像している。

Sasada Koichi / ko1 at atdot dot net
$Date: 2003/04/28 10:27:51 $