09
3月
2007

プログラムのフロー(cflow)

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自身のデバッグに有用ということか...いや,そういうわけでもないような.

You may also like...