在《Ffplay视频播放流程》文章中我给出了一个ffplay的函数调用关系图,在分析代码上会有不小的帮助。那么本文就详细的描述如何从源码中一步步的得到我们想要的函数调用关系图。
下载ffmpeg源码
安装graphviz:sudo>http://www.gson.org/egypt/
我采用的是默认配置+直接编译的方式,即./configure &&>ffmpeg$ makeCC ffplay.oLD ffplay_gCP ffplaySTRIP ffplay
我们从上述输出中可以看到,编译ffplay主要有四步:编译(CC),链接(LD),重命名(CP),去除符号表操作(STRIP),其中编译阶段是我们重点要分析的,因为编译是对源码的直接分析和处理。
确定了需要在编译ffplay的步骤后,我们在makefile中找到具体的编译函数:
define COMPILE $(call $(1)DEP,$(1)) $($(1)) $($(1)FLAGS) $($(1)_DEPFLAGS) $($(1)_C) $($(1)_O) $< endef
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
生成完RTL文件后,我们可以使用现成的一个工具来分析它:egypt,具体命令如下:
egypt ffplay.c.144r.expand > ffplay.dot
有了dot文件,使用graphviz提供的工具就可以直接生成图形了:
dot ffplay.dot -Tpng -o ffplay.png其中ffplay.dot是输入文件,-Tpng表示生成png格式的文件,-o>
文件有点大,请自行保存下来进行查看。
这个图看上去有些奇怪,比如在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文件,比如去除一些孤立的点,去除一些细节处理等等。最后,我们得到一个比较概要的函数调用关系图。
PS:和上述图片对应的dot文件如下:
http://download.csdn.net/detail/nfer_cn/6772321