Page 172 - 《软件学报》2026年第7期
P. 172

宋壹 等: 基于异常检查点植入的软件缺陷定位方法                                                        2857


                 图  2  中的第  2、7  行) 的形式记录对应独立受检中是否触发异常, 当该值为               1  时, 表示检查点  Checkpoint#i 中捕获
                 到了异常, 为   0  表示未捕获.
                    在程序执行跟踪中, 以“Statements_Begin”开头的行表示程序在进入对应独立受检前执行的语句, 以“Statements_
                 End”开头的行表示程序在进入独立受检后、退出独立受检前执行的语句, 上述两组语句间不存在交集. 例如, 在
                 表  1  中, “Statements_Begin_1”代表程序在第  1  次进入检查点前执行的所有语句, “Statements_End_1”代表程序在
                 第  1  次进入检查点后、退出该检查点前执行的所有语句. 特别地, “Statements_Ending”表示程序执行退出最后一
                 个独立受检后执行的所有语句.
                    以图  3  的错误函数为例对异常触发流进行演示. 用图              2  所示的规则为其植入检查点, 植入后的效果如图              4  所
                 示 (其中下划线语句为植入的异常处理语句, 无下划线语句为函数自身语句), 其中第                         477  行、第  486  行的语句分
                 别对应表   1 中一条以“Begin”开头的语句和一条以“End”开头的语句 (即异常触发信息). 用以“Begin”开头和以“End”
                 开头的语句作为分界, 可形成表           1  中以“Statements_Begin”“Statements_End”开头的行所代表的程序语句块和
                 “Statements_Ending”语句块 (即程序执行跟踪).

                   475. public static int lcm(int a, int b) throws MathArithmeticException{
                   476.   String triggeredOrNot = “0” ;
                   477.   System.out.println(“Begin:ArithmeticUtils.java_475” );
                   478.   try{
                   479.      if (a == 0 || b == 0){
                   480.        return 0;}
                   481.      int lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));
                   482.      if (lcm != Integer.MIN_VALUE) {  // 缺陷语句: “!=”应为 “==”
                   483.        throw new MathArithmeticException(LocalizedFormats.LCM_OVERFLOW_32_BITS, a, b);}
                   484.      return lcm;}
                   485.   catch (Exception triggered) { triggeredOrNot = “1”; throw triggered; }
                   486.   finally { System.out.println(“End:ArithmeticUtils.java_475_” + triggeredOrNot); }}
                                              图 4 检查点植入及异常触发流示例

                    为便于后续的形式化描述, 在具有           n  个失败测试用例     f i (i = 1,2,...,n) 的待测程序中, 将为  f i 生成的伪正确版
                 本记为   pcv i , 在待测错误程序上的失败执行和在        pcv i 上的通过执行分别记为      e i 和  pe i , 在  e i 和  pe i 上收集到的异常
                 触发流分别记为      ets_fail i 和  ets_pass i .
                    INSPECT  将收集到的异常触发流作为缺陷定位任务的源信息, 在所瞄准缺陷的触发对异常捕获与异常分析
                 敏感的情况下, 将收到更好的效果. 如果缺陷的执行过程不涉及异常产生及传播, 则在异常触发流收集环节可能难
                 以得到富含缺陷指示意义的数据. 我们认为, 程序异常信息与其他各种缺陷定位源信息一样, 难以完全覆盖所有缺
                 陷场景. 例如, 当前得到最广泛使用之一的程序执行覆盖信息可能面临偶然性正确问题, 因为根据                              PIE  模型, 即便
                 真实的缺陷语句被覆盖, 错误也可能不被传导到中间的执行状态, 导致覆盖信息在定位缺陷方面存在本质不足. 为
                 了更全面地度量      INSPECT  在真实环境下面向不同种类缺陷的定位能力, 在实验中基于                  Defects4J 这一缺陷定位领
                 域最流行数据集之一中常用的           Chart、Time、 Math  和  Lang  工程, 生成了  480  个突变缺陷版本, 并选取了   60  个真
                 实缺陷版本, 以开展更加全面的实验 (详见第             3  节和第  4  节).
                  2.3.2    分歧点确定
                    根据  PIE  模型, 底层代码中含有的缺陷传导到中间状态, 才能外在体现为表层的执行失效. 本文将程序异常触
                 发流作为观测程序运行中间状态的窗口, 因此, 需要比对在失败执行                     e i 和通过执行   pe i 上分别收集到的异常触发
                 流  ets_fail i 和  ets_pass i , 将二者首次显现执行状态差异的位置记为分歧点     bp i , 以此作为计算程序语句风险值, 进而
                 定位底层代码缺陷的依据.
                    对比  ets_fail i 和  ets_pass i 时, 首先将两套异常触发流中以“End”开头的信息对齐, 以便成对比较. 设         ets_fail i 中
                 有  count_fail i 条以“End”开头的信息, ets_pass i 中有  count_pass i 条以“End”开头的信息. 按收集顺序依次检查
                 ets_fail i 和  ets_pass i 中以“End”开头的信息, ets_fail i 中第  k (k = 1,2,...,min(count_ fail i ,count_pass i ))  条信息将与
   167   168   169   170   171   172   173   174   175   176   177