パンダ可愛い。毎日可愛い。すごい。
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/ にあれば公開関数に違いない、と想像している。