目录
编辑
1、cflow 简介
2、使用 cflow 分析程序的简要方法
3、两种类型的流图
cflow 工具用于分析 C 语言实现的源文件集并输出各个函数之间的依赖关系图。cflow 可以生成两种类型的图:正向图和逆向图。正向图从 main()函数开始,递归显示 main()函数调用的所有函数。相反,逆向图是一个子图的集合,使用递归命令时,为每个函数列出它的调用者。
除了这两种输出模式,cflow 也可以为输入文件中的所有遇到的符号生成一个交叉引用列表。
该程序还提供了对将出现在输出中出现的符号的详细控制,允许忽略用户不感兴趣的符号。详细的输出格式也是可配置的。
我们先从一个例子开始熟悉 GNU cflow 的用法。假设你已经有了一个 whoami命令的简单实现,并且你想获得它的函数依赖关系图。下面的是程序:
#include
#include
#include
#include
int
who_am_i (void)
{
struct passwd *pw;
char *user = NULL;
pw = getpwuid (geteuid ());
if (pw)
user = pw->pw_name;
else if ((user = getenv ("USER")) == NULL)
{
fprintf (stderr, "I don't know!\n");
return 1;
}
printf ("%s\n", user);
return 0;
}
int
main (int argc, char **argv)
{
if (argc > 1)
{
fprintf (stderr, "usage: whoami\n");
return 1;
}
return who_am_i ();
}
运行 cflow 产生下面的输出:
$ cflow whoami.c
main() :
fprintf()
who_am_i() :
getpwuid()
geteuid()
getenv()
fprintf()
printf()
这是一个正向调用图显示了输入文件中的调用者-被调用者依赖关系。每一行以一个函数名开始,紧跟后面有一对括号来指明这是一个函数。如果这个函数在某一个输入文件中定义,这一行会使用一对尖括号,其中显示函数的原型和它被定义的位置。这一行将会以:结尾来表示这个函数调用了其它函数。比如,这一行main()
通常 cflow 会显示完整的函数原型。然而有时你希望忽略原型的一部分。一些选项可以用来完成这个功能。使用--omit-symbol-names 选项来打印没有函数名的原型。使用--omit-arguments 选项来忽略参数列表。这些选项可以被用做很多用途,其中一个就是使结果图更加的紧凑。为了显示他们的作用,下面是使用上述两种--omit-选项的结果:main()
who_am_i() :
getpwuid()
geteuid()
getenv()
fprintf()
printf()
在前面我们讨论了正向图,用于展示调用者-被调用者的依赖关系。另一种 cflow输出是逆向图,列举被调用者-调用者的依赖关系。,为了生成逆向图,运行 cflow的时候要使用--reverse(-r)选项。比如使用下面的例子:
fprintf():
who_am_i() :
main()
main()
getenv():
who_am_i() :
main()
geteuid():
who_am_i() :
main()
getpwuid():
who_am_i() :
main()
main()
printf():
who_am_i() :
main()
who_am_i() :
main()
这个输出包含了几个子图,每一个字图描述了一个特定的函数的调用者。因此,第一个子图说明函数 fprintf 被两个函数调用:who_am_i 和 main。同时他也被 main函数直接调用。
第一个值得注意的地方是在输出中 who_am_i 重复出现了多次。这是一个详细的输出,为了让输出显得更加简洁,可以使用--brief(-b)选项。比如:
$ cflow --brief --reverse whoami.c
fprintf():
who_am_i() :
main()
main() [see 3]
getenv():
who_am_i() : [see 2]
geteuid():
who_am_i() : [see 2]
getpwuid():
who_am_i() : [see 2]
main() [see 3]
printf():
who_am_i() : [see 2]
who_am_i() : [see 2]
在简要输出中,一旦某个给定的函数被写入,随后的关于该函数的调用实例中将会只包含它的定义和第一次输出行的引用。
如果输出图比较大,查找需要的行数就会比较麻烦(除非你使用 Emacs 的cflow-mode)。这种情况下可以使用特殊的选项--number (-n),这样就可以在输出时显示每一行的顺序标号。使用这个选项,上面的输出将变成这样:
$ cflow --number --brief --reverse whoami.c
1 fprintf():
2 who_am_i() :
3 main()
4 main() [see 3]
5 getenv():
6 who_am_i() : [see 2]
7 geteuid():
8 who_am_i() : [see 2]
9 getpwuid():
10 who_am_i() : [see 2]
11 main() [see 3]
12 printf():
13 who_am_i() : [see 2]
14 who_am_i() : [see 2]
当然,--brief 和--number 选项对正向图和逆向图都起作用。