|
本文将介绍 OpenGrok 的故事。OpenGrok 是一款极为快速的源代码浏览器,可与 OpenSolaris 源代码浏览器 相提并论。 文章将详细阐述它的内部技术。本文将是一篇受大家欢迎的博客条目。也就是说,它将得到及时更新。暂时还不提供其他技术文档,如果您喜欢阅读 PDF 格式的文章,我们将尽快提供。
二进制的报复
作为所有 Sun 产品的安全监视人员,我一直密切关注着有关最近发现的安全漏洞的报道,然后检查 Sun 软件否存在这方面的隐患。典型的例子就是,我使用 cscope 查找任何易受攻击的代码片段。在 Solaris 中,大多数 ON(操作系统/网络)的门户每夜提前建立 cscope 索引。Sloaris 不仅仅是 ON,它还是 WOS(Wad Of Stuff);代码和二进制从 10 多个网关流入;还有许多软件产品,我还没有准备去访问,以获得索引和源代码。
我在 Sun 系统内收集 Solaris 安装图像、JES 安装图像、Sun Cluster 和 N1 软件等许多重要内容。我编写了一个 Perl 脚本。它将针对文件和目录内容递归检索。它将尽可能地提取文本信息。例如,它可以将信息流包转换成目录,解压缩,提取 tar、zip、gz、bzip 等文件或者在 ELF (3) 文件上运行 dis(1),在二进制文件上运行 strings(1)。所有这些文本信息将存储在一个单独的目录中。这是 GB 数量级的字符。最初的 perl 脚本被命名为 rob.pl。要花不止一天的时间去完成这个过程。但是它可以工作了。
我需要一个非常好的文本搜索引擎,用于搜索所有由 rob.pl 生成的文本文件。我对大量的搜索引擎作了评估,它们中的大部分都用于搜索一般的纯文本或者 html 文档。它们要么性能很差,要么无法给出准确的结果。Lucene 最后从这次评估中脱颖而出。它不是这样一个搜索引擎。它是一个库,用于创建一个反向索引并搜索它。您可以使用它构建自己的搜索引擎,以适应自己的文件域。最初,我只使用 Lucene 的范例搜索引擎。我被它的速度惊呆了。然后我看了 Lucene 网站,发现 Doug Cutting 是 Lucene 的作者。对于任何一个学习信息检索(即搜索引擎研究)的学生而言,这是一个熟悉的名字。我认为 Lucene 就是一款值得一用的软件。
搜索引擎
搜索引擎技术不是一门新兴科学。它早就出现了,并且远在现代计算机出现之前。拿一本书为例,翻到最后几页,很可能会发现一个索引章节。那是一个按字母顺序分类的关于特定术语和相应页码的列表。如果您要在全书中查找一些词,应该首先浏览索引中的页码,然后找到该页,最后浏览这一页中您要寻找的单词。这要比一页一页地阅读每一行去寻找那个词要快得多。

所有现代搜索引擎都是一样的原理。如 Google、Yahoo 等,Internet 是一本宏篇巨著,每个网页都是其中的一页。搜索引擎生成了一个索引,给定一个单词,您就能够找到相应的页面列表。这称为反向索引,因为不像查找文档中的单词一样,搜索引擎查找一组文档,以获得给定单词。
程序
Lucene 的一个优点就是它不需要理解文档内容。您必须为自己的内容编写分析程序。所以您能够根据需要解释不同类型的文件。Lucene 在存储解释和搜索内容方面做得很好。为了用各种语言解释某些通用的术语,我想出一种非常简单的方法,可适应各种语言,并且是可执行的。程序有“符号定义”、“符号引用”、“易于阅读的文本”、“路径”,还可能有“修订记录”。作为一个最初用 0xcafebabe 或者 C 程序编写的 java 类文件,它可以提取定义、符号、文本、路径和历史。

OpenGrok 的最初版本是一个叫做 rob.pl 的 perl 脚本,它提取了上述 5 个流,并将它们送到 lucene 搜索引擎。rob.pl 已变得越来越智能。它现在通过 ctags 运行每个文件并提取定义。它还解析出程序标识符。它将在 ELF 文件上运行 dis(1),然后提取标签和调用语句符号。
我将它称为通用程序搜索引擎。我在自己的电脑上使用了很长时间。这个系统用于确认或者否认一些漏洞的存在。例如,我用它确认 Solaris 7 中没有代码调用 gzprintf(),这是造成 CVE-2003-0107 的原因。现在我可以找出 Solaris 中受每个新发现安全漏洞影响的区域。
Perl 与 Java
因为 Perl 能够非常容易且迅速地编码,所以我选择它。我使用它非常高效的数据结构。它可以非常迅速地创建设计原型并确保它正常工作。我意识到选择 Perl 作为长期解决方案是一个错误。Perl 在使用和抛出应用程序类型方面做得非常好。当我分析好流程,java 进程主要是等待 perl 解析文本。处理整个程序树源代码和二进制文件花费了我几乎半天的时间。在对 perl 代码进行分析和优化后,我可以将时间缩短到八九个小时。perl 消耗了太多的计算机资源,尽管我的脚本程序只有两百多行。

后来,我意识到可以在 Lucene 中编写自定义 Analyzers,Lucene 能够分析每种类型的程序,将标记流发给 Lucene 索引程序。它非常高效而快捷。现在只需 20 分钟就可以分解 ON 网关资源,并对它们进行索引。

选择 Java 的另一个原因是在发布 1.5 版本之后,它在数据结构和编程简单性方面与 Perl 非常类似,但是更高效而快捷。
OpenSolaris
最初我考虑了一个基于 LXR 的解决方案,用于托管 OpenSolaris 代码。由于 LXR 存在许多问题,一个 cscope Web 前端已经成型。我们内部广泛使用 cscope。cscope 具有一个很好的特性,这个特性是其他的代码搜索工具(如 LXR)所缺少的。当搜索结果出来的时候,LXR 转储行编号,但不给出任何相关的信息。但出,cscope 能够使用符号或函数显示行。Ctags 不显示符号引用,cscope 显示。Cscope 可以很好地用于小项目中,但是对于像 OpenSolaris 这样的大项目却不行。
Cscope 具有许多小缺点。它的全文搜索速度太慢。搜索与上例相似的一本书,它会对每页进行线性搜索,以寻找需要的单词。它无法同时进行两个条件的搜索。如果您想在源代码树层次结构中进行搜索,这是一个严重的限制。为了解决这一限制,我们在每个主要树分支上(例如库或者内核代码)提前建立 cscope 索引,但这是昂贵且低效的。还有就是,它只能理解 C 语言或者类 C 语言,并且不能在 Makefiles 中搜索定义。
下面我们总结一下针对大型开源项目(如 http://lxr.mozilla.org/ 和 http://cvs.gnome.org/)所部署的代码检查和版本控制 Web 界面的最新情况。
| 特性 |
--- LXR --- |
--- ctags --- |
--- cscope --- |
--- CVSview/web --- |
| 全文搜索 |
Y |
|
# |
|
| 定义搜索 |
# |
Y |
Y |
|
| 标识符搜索 |
Y |
|
Y |
|
| 路径搜索 |
Y |
|
Y |
|
| 历史搜索 |
|
|
|
|
| 显示匹配行 |
|
Y |
Y |
|
| 分层搜索 |
|
|
|
|
| 查询语法如 AND、OR、field: |
|
|
|
|
| 增量更新 |
|
|
|
|
| 语法高亮-Xref |
Y |
|
|
# |
| SCM 接口 |
|
|
|
Y |
| 开放源代码 |
|
Y |
Y |
Y |
| 可用的 URL |
Y |
- |
- |
| 单个文件下载 |
|
- |
- |
Y |
| 目录层次更改 |
|
- |
- |
# |
| 多语言支持 |
# |
Y |
# |
- |
符号:
Y:具有该特性
#:部分部分该特性
-:不适用
注意:OpenGrok 具有以上所有特性
基于 Lucene 的程序搜索引擎已经具有上述的所有缺失的搜索特性。它正确地进行全文搜索。它可以识别任何用 ctags 能够理解的语言所做的定义。它可以限制对源代码树分支的搜索(即分层搜索)。它显示搜索匹配词而不只是行号。而且它对它的索引进行增量更新。
OpenGrok 唯一缺少的特性就是针对源代码的良好颜色标识和显示版本控制信息。而这些并不难以实现。我评估了许多 Lex,如代码生成器和速度最快的 JFlex。JFlex 现在用于标记和引用超文本链接。
今年的早些时候,部署 OpenGrok 用于托管 opensolaris.org 上的 OpenSolaris 源代码,并且仅针对 OpenSolaris 社区成员开放。通过社区成员的讨论和反馈,它的许多方面都得到了改善。今年 6 月 14 日,它已经用于向全球发布数百万行的 OpenSolaris 源代码。现在有些人认为 OpenGrok 是 OpenSolaris 最重要的 10 件事情之一。
“让通用事情更快更简单”
在考虑用于 OpenSolaris 之前,OpenGrok 仅仅是一个我个人使用的程序。如果它用于 OpenSolaris 的部署,并且公开可用,那么将有数百人和机器人一直冲击它。它应该是安全的、快速的、高效的、适宜的、可用的。OpenGrok 的目标是成为用于源代码搜索和浏览的完全集成的可用解决方法。
在考虑 OpenSolaris 之前,它仅仅是一个为我自己使用写的程序。如果它将为 OpenSolaris 而配置并且公开可用,那将有数以百计的人们和机器人一直冲击它。它应该是安全的、快速的、高效的、适合的、可用的。OpenGrok 的目标在于成为源代码搜索和浏览的完全集成和可用的解决方案。
OpenGrok 成功的秘诀在于“让通用事情更快更简单”这一原则,这种原则称为 Chandan 法则(因为 Google 说我是第一个这样说的人)。它结合了计算机体系结构的基本原则(即“让通用事情更快”,也称为 Amdahl 法则)和人与计算机交互时的基本原则(即“让通用事情更简单”)。要构建自己的软件,就要选用最好的可用工具,如 Lucene 和 Java。结果就是现在您在 http://cvs.opensolaris.org 上所看到的。相信我,下点功夫让事情变得更简单而不是更复杂。
还有更多的特性和注意事项,表明了它在可用性和速度上的与众不同。
重构
最初的工作原型对于 OpenSolaris 社区成员是可用的。在 OpenSolaris 启动后,它将能轻松抵抗发布当天的攻击。Web 服务器的所有者 Allen 说,CPU 使用率非常低,尽管那天我们有数万次的访问量。
然后,我开始重构代码,以保持代码的有序,从而帮助他人扩展功能或轻松添加对更多的语言类型和版本控制系统的支持。我使用 Netbeans 执行重构。Netbeans 相比于我很多年前第一次使用已经发生了很大变化。它使 Java 编程成为更加舒适的体验。我可以轻易地四处移动代码,安全地删除未使用的方法和类,更符合逻辑地进行对象重命名,或做很多其他事情。重构很费时,核心分析引擎在比较的情况下发生变化。结束后,它以 OpenGrok 的名字公开源代码。
OpenGrok 可从 此处下载。它的标志有一个开放的花括号。在 C 语言和 Java 语言中,它是一个通用的程序块开始符号。它没有闭合的括号,指示它是开放源代码的。

OpenGrok 内幕
有关 OpenGrok 内部机制的详细信息,请访问 OpenGrok Internals 页面。
结束语
如果人们无法轻松理解一段软件源代码的工作原理,或者如果人们无法轻松而准确地搜索它,那么它就像任何让普通大众难于理解的专有软件或者数据格式一样糟糕。
从这个意义上来讲,OpenGrok 是开放源代码的真正推进器。它让人们简单而快速地找到源代码,检查源代码,理解源代码的历史和变更。它让开发人员的生活更轻松。当我在寻找软件安全漏洞时,我会觉得我的生活非常轻松。
|