ctagsやetagsなんてすごいものがあるんだから,これぐらいできるだろうと思っていたら,やっぱりあった.cflowはある関数がどんな関数をコールするかを解析して表示してくれる.すなわちプログラムのフローを表示してくれる.cflowは結構古くからあるらしく,いろんな実装があるが,今回もcflow_2.0-15_i386.deb(これで一意に特定できるのかな?)を使用する.
さっそく使ってみる
解析階層の指定
解析対象関数の指定
登場関数一覧
デバッグ用
さっそく使ってみる
試しにNetPIPE-2.4でcflowしてみた.
$ cflow netpipe.c
In file included from netpipe.c:21:
netpipe.h:53: #error "One of TCP, MPI, or PVM must be defined during compilation"
1 main {netpipe.c 31}
2 strcpy {}
3 PrintUsage {netpipe.c 517}
4 printf {}
5 exit {}
6 getopt {}
7 atoi {}
...以下省略...
冒頭でエラーが出ている.TCP,MPI,PVMのどれかをdefineしないとダメな雰囲気.これにはcppなどと同様-Dオプションを使用する.やりなおし.
cflow -DTCP netpipe.c
1 main {netpipe.c 31}
2 strcpy {}
3 PrintUsage {netpipe.c 517}
4 printf {}
5 exit {}
6 getopt {}
7 atoi {}
8 fprintf {}
9 exit {}
10 strlen {}
11 malloc {}
12 Setup {}
13 Establish {}
14 fopen {}
15 PrepareToReceive {}
16 Sync {}
17 When {netpipe.c 510}
18 gettimeofday {}
19 SendData {}
20 RecvData {}
21 free {}
22 SendTime {}
23 RecvTime {}
24 SendRepeat {}
25 RecvRepeat {}
26 fflush {}
27 fclose {}
28 CleanUp {}
{}内にはその関数が定義されているファイルと行番号が書かれている.{}内に何も書いていない関数は,定義場所は不明だがとにかくcallしているということだろう.試しに解析対象にTCP.cを加えてみた.
$ cflow -DTCP netpipe.c TCP.c
/usr/lib/cflow/prcc: cannot redefine: __ctype_b {v /usr/include/ctype.h 75}
/usr/lib/cflow/prcc: cannot redefine: __ctype_tolower {v /usr/include/ctype.h 76}
/usr/lib/cflow/prcc: cannot redefine: __ctype_toupper {v /usr/include/ctype.h 77}
...何かいろいろ怒られるが省略...
1 main {netpipe.c 31}
2 strcpy {}
3 PrintUsage {netpipe.c 517}
4 printf {}
5 exit {}
6 getopt {}
7 atoi {}
8 fprintf {}
9 exit {}
10 strlen {}
11 malloc {}
12 Setup {TCP.c 22}
13 bzero {}
14 socket {}
15 __errno_location {}
16 printf {}
17 exit {}
18 getprotobyname {}
19 setsockopt {}
20 atoi {}
21 inet_addr {}
22 gethostbyname {}
23 bcopy {}
24 htons {}
25 htonl {}
26 bind {}
27 Establish {TCP.c 292}
28 connect {}
29 __errno_location {}
30 printf {}
...省略...
先の結果に加えて,いくつかの関数の定義場所(今回はどれもTCP.c内)が解決され,その中でどんな関数がcallされているかも表示されている.
解析階層の指定
この調子で解析対象ファイルをどんどん追加して行くととんでもないことになる.そういうときは,解析レベルを指定することができる.-iオプションで関数呼び出しの解析階層を2としてみた.
$ cflow -DTCP -d 2 netpipe.c TCP.c
...何かいろいろ怒られるが省略...
1 main {netpipe.c 31}
2 strcpy {}
3 PrintUsage {netpipe.c 517}
4 getopt {}
5 atoi {}
6 fprintf {}
7 exit {}
8 strlen {}
9 malloc {}
10 Setup {TCP.c 22}
11 Establish {TCP.c 292}
12 fopen {}
13 PrepareToReceive {TCP.c 159}
14 Sync {TCP.c 141}
15 When {netpipe.c 510}
16 SendData {TCP.c 167}
17 RecvData {TCP.c 188}
18 free {}
19 SendTime {TCP.c 214}
20 RecvTime {TCP.c 233}
21 SendRepeat {TCP.c 256}
22 RecvRepeat {TCP.c 270}
23 fflush {}
24 fclose {}
25 CleanUp {TCP.c 354}
まずmainが呼ばれて(階層1),そこからいろんな関数が呼ばれている(階層2).-iで2と指定しているので,さらにそこから呼ばれている関数(階層3)は解析しない.
解析対象関数の指定
今まではmainから解析をしていたが,-rオプションで解析する関数を指定することもできる.SendDataという関数を対象にしてみる.
$ cflow -DTCP -r SendData netpipe.c TCP.c
...何かいろいろ怒られるが省略...
1 SendData {TCP.c 167}
2 write {}
3 __errno_location {}
4 printf {}
5 exit {}
-rで指定したSendData関数だけが解析されている.
登場関数一覧
-aオプションを指定すると,指定したファイルで登場する関数とその関数がcallする関数という一覧を表示してくれる.
$ cflow -DTCP -a netpipe.c TCP.c
...何かいろいろ怒られるが省略...
1 CleanUp {TCP.c 354}
2 write {}
3 read {}
4 close {}
5 Establish {TCP.c 292}
6 connect {}
7 __errno_location {}
8 printf {}
9 exit {}
10 listen {}
11 accept {}
12 getprotobyname {}
13 setsockopt {}
14 PrepareToReceive {TCP.c 159}
15 PrintUsage {netpipe.c 517}
...省略...
102 main {netpipe.c 31}
103 strcpy {}
104 PrintUsage ... {15}
105 getopt {}
106 atoi {}
107 fprintf {}
108 exit {}
109 strlen {}
110 malloc {}
...省略...
出力順は関数名のasciiコード順のようだ.mainの中のPrintUsageは" ... {15}"となっている.これは出力の左の番号が15となっている部分でPrintUsageの結果があるよ,という意味らしい.
デバッグ用
cflowは以下にもプログラムのデバッグに使えそうなツールだが,-nオプションを使うとよりデバッグに有用な出力が得られるとUsageにある.
cflow -V -DTCP -n netpipe.c
cpp -DTCP netpipe.c
/usr/lib/cflow/prcc
cat
main main {netpipe.c 31}
main strcpy {}
main PrintUsage {}
main getopt {}
main atoi {}
main fprintf {}
main exit {}
main strlen {}
main malloc {}
main Setup {}
main Establish {}
main fopen {}
main PrepareToReceive {}
main Sync {}
main When {}
main SendData {}
main RecvData {}
main free {}
main SendTime {}
main RecvTime {}
main SendRepeat {}
main RecvRepeat {}
main fflush {}
main fclose {}
main CleanUp {}
When When {netpipe.c 510}
When gettimeofday {}
PrintUsage PrintUsage {netpipe.c 517}
PrintUsage printf {}
PrintUsage exit {}
うーん,どうなんだろ...もしかしてcflow自身のデバッグに有用ということか...いや,そういうわけでもないような.