带你走进Cflow (一)

目录

​编辑

1、cflow 简介

2、使用 cflow 分析程序的简要方法

3、两种类型的流图


1、cflow 简介

        cflow 工具用于分析 C 语言实现的源文件集并输出各个函数之间的依赖关系图。cflow 可以生成两种类型的图:正向图和逆向图。正向图从 main()函数开始,递归显示 main()函数调用的所有函数。相反,逆向图是一个子图的集合,使用递归命令时,为每个函数列出它的调用者。

        除了这两种输出模式,cflow 也可以为输入文件中的所有遇到的符号生成一个交叉引用列表。

        该程序还提供了对将出现在输出中出现的符号的详细控制,允许忽略用户不感兴趣的符号。详细的输出格式也是可配置的。

2、使用 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() :显示 main 函数在源文件 whoami.c 中的第 25 行定义,原型是 int main (intargc,char **argv),最后的冒号表示 main 函数调用了其他函数。这一行后面的是被 main 函数调用的函数。每一行都会根据嵌套关系有相应的缩进。

        通常 cflow 会显示完整的函数原型。然而有时你希望忽略原型的一部分。一些选项可以用来完成这个功能。使用--omit-symbol-names 选项来打印没有函数名的原型。使用--omit-arguments 选项来忽略参数列表。这些选项可以被用做很多用途,其中一个就是使结果图更加的紧凑。为了显示他们的作用,下面是使用上述两种--omit-选项的结果:main() :默认情况下,cflow 从 main()函数开始输出正向图。当分析一个完整的 C 程序集的时候这是非常的便利的。但是有时候用户可能只想看到从特定函数出发的部分图。使用—main(-m)选项,Cflow 允许选择这样的功能调用。因此,运行$cflow--main who_am_i whoami.c,将会得到下面的结果:

who_am_i() :
 getpwuid()
 geteuid()
 getenv()
 fprintf()
 printf()

3、两种类型的流图

        在前面我们讨论了正向图,用于展示调用者-被调用者的依赖关系。另一种 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 选项对正向图和逆向图都起作用。

带你走进Cflow (一)_第1张图片

你可能感兴趣的:(Cflow,算法,图论,cflow,java,gitee,spring)