GNU cflow实现调用关系分析

GNU cflow是一个GNU的开源项目,有关网站(官网)如下:
官网-GNU cflow
下载-http://ftp.gnu.org/gnu/cflow/
手册-GNU cflow manual

cflow安装(Linux):
除了cflow之外,还需要安装软件tree2dotx和graphviz。
cflow -打印文本格式的函数调用关系
tree2dotx -将文本格式转化为dot格式
graphviz -将dot格式转化为图片格式
推荐安装xdot,该软件可以把文本格式的dot文件显示为图形,函数调用关系就可以用图形来显示。

安装命令等如下:
安装cflow:
$ sudo apt-get install cflow
$ cflow --version

cflow (GNU cflow) 1.4
Copyright (C) 2005, 2006, 2009, 2010, 2011 2009 Sergey Poznyakoff
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Sergey Poznyakoff.

安装tree2dotx:
该软件应该在这里:
linux-cd/tree2dotx at master · peterx/linux-cd · GitHub
$ sudo wget -c https://github.com/peterx/linux-cd/blob/master/linux-0.11/tools/tree2dotx -O /usr/bin/tree2dotx
$ sudo chmod +x /usr/bin/tree2dotx
以上安装似乎是有问题的,需要把文件/usr/bin/tree2dotx的内容改为:

#!/bin/bash
# tree2dot.sh --- transfer a "tree"(such as the result of tree,calltree)
#                 to a p_w_picpath discribed by DOT language(provided by Graphviz)
# author: falcon<zhangjinw@gmail.com>
# update: 2007-11-14
# usage: 
#       tree -L 2 -d /path/to/a/directory | bash tree2dot.sh > tree.dot
#       cd /path/to/a/c/project/; calltree -gb -np -m *.c | bash tree2dot.sh > tree.dot

# indicate the symbols you not concern with space as decollator here
filterstr="";

# transfer the tree result to a file described in DOT language
grep -v ^$ | grep -v "^[0-9]* director" \
| awk '{if(NR==1) system("basename "$0); else printf("%s\n", $0);}' |\
awk -v fstr="$filterstr" '# function for filter the symbols you not concern
        function need_filter(node) {
                for( i in farr ) { 
                if(match(node,farr[i]" ") == 1 || match(node,"^"farr[i]"$") == 1) {
                        return 1;
                }
                }
                return 0;
        }
        BEGIN{ # filternode array are used to record the symbols who have been filtered.
                oldnodedepth=-1; oldnode=""; nodep[-1]=""; filternode[nodep[-1]]=0;
                # store the symbols to an array farr
                split(fstr,farr," ");
                # print some setting info
                printf("digraph G{\n"); 
                printf("\trankdir=LR;\n");
                printf("\tsize=\"800,600\";\n");
                printf("\tnode [fontsize=10,fontcolor=red,style=filled,fillcolor=lightblue];\n");
        }{
                # get the node, and its depth(nodedepth)
                nodedepth=match($0, "[^| `]");
                node=substr($0,nodedepth); 
                nodedepth=int(nodedepth/4)
                # if whose depth is 1 less than him, who is his parent
                if(nodedepth-oldnodedepth == 1) {
                        nodep[nodedepth-1]=oldnode;
                }
                # for debugging
                #printf("%d %s\n", nodedepth, node);
                #printf("\t\"%s\";\n",node);
                # print the vectors
                if (oldnodedepth != -1) {
                        # if need filter or whose parent have been filter, not print it, and set the flat of filter to 1
                        if(need_filter(node) || filternode[nodep[nodedepth-1]]==1) {
                                filter[node]=1;
			} else if (nodep[nodedepth-1] != "") {
                                printf("\t\"%s\" -> \"%s\";\n", nodep[nodedepth-1], node, nodep[nodedepth-1], node);
                        #       printf("\t\"%s\" -> \"%s\"[label=\"%s>%s\"];\n", nodep[nodedepth-1], node, nodep[nodedepth-1], node);
                        }
                }
                # save the old depth and the old node
                oldnodedepth=nodedepth;
                oldnode=node;
        }END{
                printf("}");
        }'

实际上,tree2dotx就是一个bash脚本程序文件,其内容如上。直接创建一个内容如上的/usr/bin/tree2dotx文件,并且把用chmod命令将该文件更改为可执行即可。
该脚本程序中,使用了grep和awk等来实现。

安装graphviz:
$ sudo apt-get install graphviz
$ dot -?

Usage: dot [-Vv?] [-(GNE)name=val] [-(KTlso)<val>] <dot files>
(additional options for neato)    [-x] [-n<v>]
(additional options for fdp)      [-L(gO)] [-L(nUCT)<val>]
(additional options for memtest)  [-m<v>]
(additional options for config)  [-cv]

 -V          - Print version and exit
 -v          - Enable verbose mode 
 -Gname=val  - Set graph attribute 'name' to 'val'
 -Nname=val  - Set node attribute 'name' to 'val'
 -Ename=val  - Set edge attribute 'name' to 'val'
 -Tv         - Set output format to 'v'
 -Kv         - Set layout engine to 'v' (overrides default based on command name)
 -lv         - Use external library 'v'
 -ofile      - Write output to 'file'
 -O          - Automatically generate an output filename based on the input filename with a .'format' appended. (Causes all -ofile options to be ignored.) 
 -P          - Internally generate a graph of the current plugins. 
 -q[l]       - Set level of message suppression (=1)
 -s[v]       - Scale input by 'v' (=72)
 -y          - Invert y coordinate in output

 -n[v]       - No layout mode 'v' (=1)
 -x          - Reduce graph

 -Lg         - Don't use grid
 -LO         - Use old attractive force
 -Ln<i>      - Set number of iterations to i
 -LU<i>      - Set unscaled factor to i
 -LC<v>      - Set overlap expansion factor to v
 -LT[*]<v>   - Set temperature (temperature factor) to v

 -m          - Memory test (Observe no growth with top. Kill when done.)
 -m[v]       - Memory test - v iterations.

 -c          - Configure plugins (Writes $prefix/lib/graphviz/config 
               with available plugin information.  Needs write privilege.)
 -?          - Print usage and exit

安装xdot:
$ sudo apt-get install xdot
$ xdot -h

usage: xdot [-h] [-f FILTER] [-n] [-g GEOMETRY] [file]

xdot.py is an interactive viewer for graphs written in Graphviz's dot language.

positional arguments:
  file                  input file to be viewed

optional arguments:
  -h, --help            show this help message and exit
  -f FILTER, --filter FILTER
                        graphviz filter: dot, neato, twopi, circo, or fdp
                        [default: dot]
  -n, --no-filter       assume input is already filtered into xdot format (use
                        e.g. dot -Txdot)
  -g GEOMETRY, --geometry GEOMETRY
                        default window size in form WxH

Shortcuts:
  Up, Down, Left, Right     scroll
  PageUp, +, =              zoom in
  PageDown, -               zoom out
  R                         reload dot file
  F                         find
  Q                         quit
  P                         print
  Escape                    halt animation
  Ctrl-drag                 zoom in/out
  Shift-drag                zooms an area

cflow命令:
$ cflow --help

用法:  cflow [选项...] [文件]...
生成一张程序流程图

 通用选项: 
  -d, --depth=NUMBER         设置流程图的绘制深度
  -f, --format=NAME
                             使用指定的输出格式名。有效名称是‘gnu’(默认)和‘posix’
  -i, --include=CLASSES      包含指定的符号类(见下)。在 CLASSES
                             之前放上 ^ 或 - 将它们从输出中省去
  -o, --output=FILE          设置输出文件名(默认为
                             -,即标准输出)
  -r, --reverse              * 打印反向调用树
  -x, --xref                 仅生成交叉引用列表

 --include 参数的符号类

    _                        以下划线开始的符号名
    s                        静态符号
    t                        typedefs(仅针对交叉引用)
    x                        所有的数据符号(外部的和静态的)

 句法分析控制: 

  -a, --ansi                 * 仅接受 ANSI C 标准的源码
  -D, --define=NAME[=DEFN]   将 NAME 预定义为一个宏
  -I, --include-dir=DIR      将 DIR
                             目录添加至可被头文件搜索到的目录列表
  -m, --main=NAME            假定主函数是个叫 NAME 的函数
  -p, --pushdown=NUMBER      设置初始标识栈大小为 NUMBER
      --preprocess[=COMMAND], --cpp[=COMMAND]
                             * 执行指定的预处理命令
  -s, --symbol=SYMBOL:[=]TYPE   Register SYMBOL with given TYPE, or define an
                             alias (if := is used). Valid types are: keyword
                             (or kw), modifier, qualifier, identifier, type,
                             wrapper. Any unambiguous abbreviation of the above
                             is also accepted
  -S, --use-indentation      * 依赖缩进风格
  -U, --undefine=NAME        取消前面所有的 NAME 预定义

 输出控制: 

  -b, --brief                * 简洁输出
      --emacs                * 为与 GNU Emacs
                             联合使用而显示额外的格式输出
  -l, --print-level          * 打印调用关系树的嵌套结构
      --level-indent=ELEMENT 控制图显示
  -n, --number               * 打印行号
      --omit-arguments       * 不在函数声明部分打印参数列表
      --omit-symbol-names    * 不在声明字符串中打印符号名
  -T, --tree                 * 绘制 ASCII 形式的树

 输出信息选项: 

      --debug[=NUMBER]       设定调试级别
  -v, --verbose              * 详细的错误诊断报告

  -?, --help                 显示此帮助列表
      --usage                显示一份简洁的用法信息
  -V, --version              打印程序版本

选项完整形式所必须用的或是可选的参数,在使用选项缩写形式时也是必须的或是可选的。

*
每个带有星号标记的选项是相反操作,就像前面带有‘no-’的长选项名一样。例如,--no-cpp
就是取消 --cpp 选项用的。

请向 <bug-cflow@gnu.org> 报告错误。

cflow主要参数
-T 绘制ASCII形式的树
-m 指定主函数名,即根函数名
-n 打印函数的行号
-r 打印反向调用树
–cpp[=COMMAND] 执行指定的预处理命令

cflow分析C语言程序的函数调用关系
C语言程序whoami.c(来自官网手册)如下:

/* whoami.c - a simple implementation of whoami utility */
#include <pwd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

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 whoami.c

main() <int main (int argc, char **argv) at whoami.c:24>:
    fprintf()
    who_am_i() <int who_am_i (void) at whoami.c:7>:
        getpwuid()
        geteuid()
        getenv()
        fprintf()
        printf()

分析C语言程序输出dot文件
运行tree2dotx时,出现错误:
/bin/sh: 1: Syntax error: “(” unexpected
需要修改tree2dotx中的下述语句:
awk ‘{if(NR==1) system(“basename “$0); else printf(”%s\n”, $0);}’
改为:
awk ‘{printf(“%s\n”, $0);}’

运行以下命令:
$ cflow whoami.c | tree2dotx > whoami.dot
$ cat whoami.dot

digraph G{
	rankdir=LR;
	size="800,600";
	node [fontsize=10,fontcolor=red,style=filled,fillcolor=lightblue];
	"main() <int main (int argc, char **argv) at whoami.c:24>:" -> "fprintf()";
	"main() <int main (int argc, char **argv) at whoami.c:24>:" -> "who_am_i() <int who_am_i (void) at whoami.c:7>:";
	"who_am_i() <int who_am_i (void) at whoami.c:7>:" -> "getpwuid()";
	"who_am_i() <int who_am_i (void) at whoami.c:7>:" -> "geteuid()";
	"who_am_i() <int who_am_i (void) at whoami.c:7>:" -> "getenv()";
	"who_am_i() <int who_am_i (void) at whoami.c:7>:" -> "fprintf()";
	"who_am_i() <int who_am_i (void) at whoami.c:7>:" -> "printf()";
}

显示图形结果
可以用xdot打开文本文件whoami.dot,显示效果如下:
在这里插入图片描述也可以将dot文件转换为gif文件,命令如下:
$ dot -Tgif whoami.dot -o whoami.gif
打开whoami.gif文件,效果如下:
在这里插入图片描述
其他
其他值得关注的GNU项目有GNU Binutils,其中包含若干子项目gprof,可以用于分析各个函数使用的时间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值