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,可以用于分析各个函数使用的时间。