使用egypt+graphviz分析ffplay的函数调用关系图

在《Ffplay视频播放流程》文章中我给出了一个ffplay的函数调用关系图,在分析代码上会有不小的帮助。那么本文就详细的描述如何从源码中一步步的得到我们想要的函数调用关系图。

前置条件

下载ffmpeg源码

安装graphviz:sudo>http://www.gson.org/egypt/

编译整个ffmepg

我采用的是默认配置+直接编译的方式,即./configure &&>ffmpeg$ makeCC ffplay.oLD ffplay_gCP ffplaySTRIP ffplay
我们从上述输出中可以看到,编译ffplay主要有四步:编译(CC),链接(LD),重命名(CP),去除符号表操作(STRIP),其中编译阶段是我们重点要分析的,因为编译是对源码的直接分析和处理。

生成RTL文件

确定了需要在编译ffplay的步骤后,我们在makefile中找到具体的编译函数:

define COMPILE
       $(call $(1)DEP,$(1))
       $($(1)) $($(1)FLAGS) $($(1)_DEPFLAGS) $($(1)_C) $($(1)_O) $<
endef

因为是编译ffplay.c文件,即此处的$(1)指的是CC,对应的$($(1)FLAGS)就是$(CCFLAGS),而CCFLAGS的定义中包含$(CFLAGS),即按照 egypt中的说明,我们在$(CFLAGS)的定义中添加-fdump-rtl-expand参数即可在make的时候成成RTL文件:

CFLAGS     += $(ECFLAGS) -fdump-rtl-expand
并且在COMPILE函数中将$($(1)FLAGS)的值打印出来,那么再次修改ffplay.c并编译后的输出如下:

-I. -I./ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -std=c99 -fomit-frame-pointer -pthread -D_GNU_SOURCE=1 -D_REENTRANT -I/usr/include/SDL -g -Wdeclaration-after-statement -Wall -Wno-parentheses -Wno-switch -Wno-format-zero-length -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wno-pointer-sign -Wwrite-strings -Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -Wstrict-prototypes -O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=return-type -Werror=vla  -fdump-rtl-expand -D_GNU_SOURCE=1 -D_REENTRANT -I/usr/include/SDL
CC	ffplay.o
LD	ffplay_g
CP	ffplay
STRIP	ffplay

并且在当前目录下生成ffplay.c.144r.expand文件,即我们需要的RTL文件。

生成DOT文件

生成完RTL文件后,我们可以使用现成的一个工具来分析它:egypt,具体命令如下:

egypt ffplay.c.144r.expand > ffplay.dot

生成函数调用图

有了dot文件,使用graphviz提供的工具就可以直接生成图形了:

dot ffplay.dot -Tpng -o ffplay.png
其中ffplay.dot是输入文件,-Tpng表示生成png格式的文件,-o>

文件有点大,请自行保存下来进行查看。

ffplay调用流程图

去除编译优化

这个图看上去有些奇怪,比如在main函数中非常显眼的event_loop函数,哪里去了呢?

神奇之处就在于CFLAYGS中的-O3参数,gcc的man手册中如是说:

-O3 Optimize yet more.  -O3 turns on all optimizations specified by -O2
    and also turns on the -finline-functions, -funswitch-loops,
    -fpredictive-commoning, -fgcse-after-reload, -ftree-vectorize and
    -fipa-cp-clone options.
我们发现-O3是在-O2优化的基础上,又添加了一系列的优化参数。那么同理,-O2优化肯定也是在-O1优化基础上新增一些优化参数。

因此,为了保持生成的RTL文件和源代码保持一致性,我们去除所有的编译优化选项,即在config.mak文件中,从CFLAGS定义中删除-O3字符串:

CFLAGS=   -std=c99 -fomit-frame-pointer -pthread -D_GNU_SOURCE=1 -D_REENTRANT -I/usr/include/SDL -g -Wdeclaration-after-statement -Wall -Wno-parentheses -Wno-switch -Wno-format-zero-length -Wdisabled-optimization -Wpointer-arith -Wredundant-decls -Wno-pointer-sign -Wwrite-strings -Wtype-limits -Wundef -Wmissing-prototypes -Wno-pointer-to-int-cast -Wstrict-prototypes -O3 -fno-math-errno -fno-signed-zeros -fno-tree-vectorize -Werror=implicit-function-declaration -Werror=missing-prototypes -Werror=return-type -Werror=vla
然后重新修改编译ffplay.c,并生成RTL文件和png图片。生成的图形如下:

未优化的函数调用流程图

哇塞,通过这两个图形对比,我们发现编译优化选项做了多大的工作啊,或者说原始代码为了可阅读行,是多么的烂啊!

手动调整

未优化的函数调用关系图太复杂了,在分析问题时,感觉有些老虎吃刺猬无处下嘴啊!

那么我们就可以手动打开刚才处理RTL后生成的dot文件,比如去除一些孤立的点,去除一些细节处理等等。最后,我们得到一个比较概要的函数调用关系图。

使用egypt+graphviz分析ffplay的函数调用关系图_第1张图片

PS:和上述图片对应的dot文件如下:

http://download.csdn.net/detail/nfer_cn/6772321




你可能感兴趣的:(ffmpeg)