Page 37 - 《软件学报》2024年第6期
P. 37

沈天琪 等: DDoop: 基于差分式     Datalog  求解的增量指针分析框架                                    2613


                    从整体架构上, DDoop     框架主要可分为前端和后端两个部分, 这两个部分都拥有增量特性.
                    (1) 增量生成输入事实的前端. 其自动化处理待分析程序, 从中抽取变更信息并转化为后端增量评估所需的增
                 量输入.
                    (2) 自动化生成    DDlog  增量分析程序的后端. 根据输入的分析选项, 后端会将原始                Doop  框架的  Soufflé 规则
                 自动化重写为支持增量评估的           DDlog  规则, 并生成相应的增量分析程序.
                    首先, 我们对图     1  所示的  DDoop  框架的整体运行流程概述如下: 根据分析输入, 我们可以确定分析使用的
                 Soufflé 指针分析规则, DDoop  后端会将这份分析规则自动化重写为              DDlog  版本的增量分析程序. 随后, DDoop     前
                 端会收到待分析程序对应的一系列代码提交, 其将会按照顺序, 将代码变更转化为                          DDlog  增量分析程序可接受的
                 增量输入事实形式. 增量分析程序接收到代码变更的增量输入事实后, 增量评估产生分析结果的增量输出.
                    接下来, 我们将详细介绍        DDoop  框架前端和后端这两个部分.

                 2.1   前端: 增量输入事实构建
                    DDoop  框架和  Doop  框架都将指针分析问题归约为         Datalog  程序评估问题. Datalog  引擎接受一组事实和规则
                 模块视为“增量单元”. 增量单元越大, 每一次出现变更时需要重做的范围就会越大, 但是增量单元越小, 用来判断
                 作为输入. 在   Doop  框架中, 规则对应指针分析算法, 而输入事实则对应待分析程序的一组语义性质. 为此, 我们需
                 要一个前端来对待分析程序进行预处理, 从中抽取与指针分析相关的程序语义性质作为                             Datalog  引擎的输入事实.
                 前端的存在是为了实现分析框架的端到端过程: 将不能直接被                     Datalog  引擎处理的  jar 包转换为其可接受的输入
                 事实形式.
                    Doop  框架中已经提供了一个前端, 能够将一个完整的               Java 程序  (jar 包) 转换为  Soufflé 引擎的输入事实. 其
                 前端的输入生成器抽取语义信息时不涉及类之间的相互依赖信息. 它使用                        Soot 框架加载   jar 包中所有的类字节码
                 文件, 然后对已加载的每个类并行化的抽取其中的语义信息. 在具体处理时, 每个类中的方法在逻辑上都是独立
                 的. 但是  Doop 中的前端在我们的增量场景下存在一些问题.
                    ● 第  1  个问题是, Soufflé 引擎和  DDlog  引擎的输入在形式上存在细微差异: DDlog        引擎是为增量评估设计的,
                 输入时需要标记每条输入事实是插入还是删除                 (特别地, 某一条事实在增量过程中的更新会被处理为一条删除旧
                 值和一条插入新值的组合); 而        Soufflé 引擎将所有输入事实均视为插入.
                    ● 第  2  个问题是, Doop  框架的前端并未考虑到增量设计: 对于待分析程序的每一个版本, 其都会从头开始处
                 理整个待分析程序. 但在连续输入的增量场景中, 绝大多数的                   Java 类文件都是不变的, 对应的语义信息是完全相
                 同的. 这就导致了重复计算问题.
                    解决第   1  个问题相对容易, 这只是格式上的区别. 我们只需要: 1) 标记每条输入事实是插入还是删除; 2) 格式
                 转换. 然而, 对第   2  个问题, 我们需要一个支持增量事实生成的全新前端. 这也是我们在此处的最主要挑战.

                 2.1.1    框架前端架构
                    在我们的增量指针分析框架中, 我们希望避免重复计算: 即对于那些在代码变更中未发生变化的类和方法, 我
                 们可以复用此前生成的输入事实. 为此, 对于给定两个版本的                   jar 包, 我们需要一种方案来识别出在代码变更中未
                 发生变化的类文件, 并在处理时跳过它们. 为此, 我们需要解决以下                   3  个主要问题.
                    (1) 划分合适的基本“增量单元”, 其粒度必须适当. 在实现增量分析时, 选择适当的“增量单元”是至关重要的.
                 该单元应能在保证分析精度的同时, 最大程度地降低实现和计算的复杂度. 我们需要确定, 是将方法、类还是编译


                 增量的范围就相对更加复杂.
                    (2) 找出能够标识增量单元未发生变化的元素, 或者说一种“摘要”. 为了实现快速的增量变更检测, 我们需要
                 一种能够快速且准确地反映“增量单元”变化的“摘要”机制. 这种机制可以采用“增量单元”哈希值, 或者是结合“增
                 量单元”的某些性质, 如代码行数, 方法数量等. 选择哪种“摘要”机制取决于它们能否提供足够的信息来判断代码
                 的变化, 同时又能在短时间内完成计算.
                    (3) 维护我们在增量过程中的必要信息. 在增量分析过程中, 有效地管理和存储分析信息是一个挑战. 我们需
   32   33   34   35   36   37   38   39   40   41   42