阅读源码方法汇总
Table of Contents
1. 源码选择
如果只是单纯学习源码阅读,尽量学不需要太多背景的源码来做分析,因为选择需要有背景知识的源码成本就要高点,比如你要分析 Apache 的源码,就得学 Apache。有时候并不需要完整读完整套源码,可能只会分析一小块地方,比如你可能只会分析 Linux 文件系统怎么实现,而不会去读完整个源码。
带有目的去阅读源码效果为佳,比如:
去深入理解项目中用到的库; 解决遇到一些坑; 结合到理论知识学习,比如学习操作系统、算法、RFC 文档等; 学习新的编程模式; 学习别人的编程风格; 满足某些开发需求; 了解工具实现原理; 挖漏洞; ...
2. 让源码能编译并工作起来
源码分析的第一步不是用编辑器硬读代码,最重要的起步就是:先学会用它。
如果是软件,就先学会怎么用(成功编译、使用);如果是库,一般通过以下几个方式就能学会使用:
- 官方文档
- 相关书籍。并不是每个库都有清晰的文档,如果文档看不懂,最好能借助相关书籍。
- 代码仓库里的一些示例代码
- 单元测试
3. 寻找入口
先从入口开始看,比如 C 语言的 main 函数,如果是 API 的话,就直接看 API 的代码。
有个技巧——运行时打印出函数调用的过程,比如 Python 可通过下面方式查看函数调用流程:
python -m trace –trackcalls [文件名]
不同的语言和库有不同的方式,甚至可以用单步断点等方法。
另外,同时观察:
- 包含了哪些头文件、导入的哪些库,从而了解文件大概实现原理;
- 全局变量;
- 使用方法(Usage)和参数处理。
4. 工具
Source Insight 是一款运行在 Windows 上的商业源码检索工具,由于不跨平台,并且费用昂贵,很长一段时间我都在研究开源的代替方案,测试中发现 OpenGrok 和 GNU GLOBAL 很好用。
4.1. OpenGrok 和 GNU GLOBAL
有一些源码检索工具对于 Linux 内核这类规模的源码检索就存在性能问题,而 OpenGrok 和 GNU GLOBAL 这两款开源的源码检索十分好用,大大小小的项目都适用。
OpenGrok 有一个 Web 界面,把源码索引搭在服务器上用 Web 来看源码非常方便,甚至有时在客厅用 iPad 看源码。详细的请见:https://github.com/OpenGrok/OpenGrok。
GNU GLOBAL 生成索引数据,很多编辑器都有支持的插件,如 Emacs 和 Vim。同时它也可以在命令行下使用。官网:http://www.gnu.org/software/global/。
GNU GLOBAL 使用举例:
在项目目录下运行 gtags 命令生成索引。 global 命令负责符号检索: 例,要查找 main 函数的位置:global main 例,查找 main 函数在哪些文件中被引用过:global -r main 例,更新索引:global -u,这个命令可以配置在 Emacs 中间隔一段时间就运行一次
4.2. Universal Ctags
一个支持语言多、开发活跃的 ctags 的实现,官网:https://ctags.io/。
生成索引:
$ ctags -eR
参数说明:
-e:生成可用于 Emacs 的格式。
-R:递归目录。
Emacs 中操作说明可以参考官方文档:
https://www.gnu.org/software/emacs/manual/html_node/emacs/Looking-Up-Identifiers.html
https://www.gnu.org/software/emacs/manual/html_node/emacs/Identifier-Search.html
4.3. 其他工具
- 小项目用 Ctags 和 Emacs 自带的 Etags
- IDE:PyCharm 社区版,IDEA 社区版
- 思维导图
- 纸和笔,边看代码边画流程图,这是最顺手也最快捷的方法。
其他还有一些:
- Doxygen,生成函数调用关系图
- Argouml,UML绘制工具
运行时的工具:
- GDB
- Strace/Dtrace
- SystemTap
5. 了解架构
好的软件都有好的架构,要学了解常用的软件架构。Project有别于脚本,脚本代码相对来说较随意,Project 更有组织性。
- 设计模式和 OOP,分析类之间是如何组织的;
- 自动构建,比如 Makefile,还有 Java 的 Ant 等等,并且分析它们在项目里是如何组织代码的;
- 模块划分,根据项目文档或者目录组织,了解划分的模块;
- 编程风格约定;
- 常见的数据结构,如链表、队列等。
有时候不要纠结在某个函数的算法上,否则浪费很多时间的,清楚这个函数是干嘛的,输入什么,输出什么就 OK 了。后面追究细节的时候再来看这个具体实现也不错。项目太大可以不用看完整体源码,看关注的那部分模块即可。
6. 了解一个函数的功能
- 最基本的,从函数名是否能读出函数的功能;
- 看函数的头注释;
- 是否有文档;
- 测试用例;
- 看看哪些地方调用了它,以及是如何调用的。
对于代码行数很长的函数,去精简它,删掉多余的判断逻辑、不重要的逻辑路径。
7. 使用调试器
学会了解软件在运行时候的动态,代码是一个样,运行的时候又是另外一个样子,所以要学会用调试器,学会调试软件,在不懂的地方可以下断点,或者在某些地方打印某些东西,看源码也可以边修改边理解。
8. 遇到问题
- 记录下来,反复阅读源码,回头再看;
- 不要纠结细节,明白这里干嘛的即可,回头再看;
- 不懂的地方去 Google,如果遇到语言语法问题,去学习;
- 实在不行问别人吧。