GraphvizでC関数のcallgraphを描く

ちょっと必要だったので、gcc専用ですが、実行中のC関数が呼び出される様子(callgraph)をいろいろな組み合せてつくってみました。こんな図が描けます。(これはサンプルなので、実際のモノによってはもっと巨大な図が生成されます)


使い方

まずこの辺からダウンロードします。

http://github.com/takuma104/hacktools/tree/99ea8c9e71a4eae67f34f44f0054c466c78520ba/callgraph

実行には、GNU binutils が必要です。MacOSX で確認しているのですが、インストールされていないので、MacPort でインストールしました。

$ sudo port install binutils

とかでいけます。あとは make してOKであれば*1

$ ./sample
$ ruby trace2dot.rb sample > sample.dot

とかして、Graphviz (Mac版はこれがよさそうです) に sample.dot を読み込ませるだけです。sample.c が callgraph を記録している対象です。

しくみとか

実行時のすべてのC関数に対して gcc 拡張を使って hook し、それが trace.txt に記録されます。これは、

http://www.ibm.com/developerworks/jp/linux/library/l-graphvis/index.html

の instrument.c を使っています。何か関数が呼ばれる or その関数から抜けると、trace.txtに、その関数のアドレスを書き出します。

このアドレスだと何の関数だか分らないので、通常 GNU binutils に含まれる addr2line を使うようなのですが、どうも上手く動いてくれないのと -g オプションが必要だったりするみたいなので、binutilsを使った別の実装で addr2name というのを作りました。といってもこれも

http://homepage1.nifty.com/herumi/prog/binary.html

の bfd.tgz から GetBFDSymbol 関数を拝借して、それを改造しています。addr2name は、実行形式中に含まれるすべての関数のアドレスとシンボルの対を標準出力に出力するだけのものです。

これらを組みあわせて、trace.txt から Graphviz で読める dot ファイルを作っているのが trace2dot.rb です。出力される dot ファイルの label には call された順番が入っているので、1、2、3と読み進めていくと、どの順番で call されたかが分ると思います。

あと、この sample 以外の図を書きたい場合ですが、instrument.o を、コピーして、通常 configure するところを、

$ CFLAGS=-finstrument-functions LDFLAGS=instrument.o ./configure

とかして make してあげると*2、実行形式で実行したときに、trace.txtが出力されるようになります。ちなみに、-O2 とかで最適化されていると、関数名が抜けたりするので、最適化は切っておいたほうが無難なようです。あとは trace2dot.rb の引数に、その実行形式を指定してあげると dot で出力してくれます。

おまけ

これを使って Ruby 1.9.1p0 の miniruby で main.c の ruby_run_node() からを出力してみました。

Ruby 1.9.1 (miniruby) callgraph

$ miniruby -e'p 1+2'

の実行結果です。オリジナルのサイズはこちらから。
http://www.flickr.com/photos/takuma104/3373460048/sizes/o/

Dotファイルはこちらから
https://dl.getdropbox.com/u/136792/ruby_191_miniruby_p_1_plus_2.dot

*1:MacPortでない場合は、おそらくbinutilsのパスがダメだと思うので、適宜 Makefile を修正してください

*2:要は、-finstrument-functionsオプションを付けてコンパイルし、なおかつ instrument.o をリンクしてあげればOK