solr 高并发_并发类的覆盖驱动测试代码生成

1c29d6a1332811cc8898191fbf8c660f.png

摘要:

以往的并发测试技术,主要是在探索手动编写测试代码的交织空间,以暴露共享内存访问的错误交织。这些技术假定了故障诱发测试的可用性。在本文中,我们介绍了 AutoConTest,这是一种由覆盖率驱动的方法,用于生成有效的并发测试代码,以实现高交织覆盖率。 AutoConTest 由三个组件组成。首先,它使用捕获共享内存访问的执行上下文的覆盖率指标,在顺序测试代码生成过程中动态、迭代地计算覆盖率要求。其次,它根据计算结果明智地选择这些顺序代码,并将它们组合起来进行并发测试,从而实现了增加上下文相关的交错覆盖范围。第三,探讨了新覆盖的交错。我们已经将 AutoConTest 实现为自动化工具,并使用 6 个现实世界中的并发 Java 主题对其进行了评估。结果表明,AutoConTest 能够生成有效的并发测试,从而实现高交织覆盖率并快速暴露并发故障。 AutoConTest 仅花费了不到 65 秒(包括程序分析,测试生成和执行)的时间来暴露程序主体中的错误。

简介

多核芯片技术的飞速发展让并发编程被普遍采用,并发编程由共享内存空间中的多个线程共同执行。由于线程同步的内在复杂性,并发程序很容易出错

尽管编写引发故障的测试既麻烦又费力,但它们的可用性对于暴露软件故障至关重要。 因此,针对顺序程序的测试代码的自动生成是一个活跃的研究主题。将这些技术用于并发测试,既带来机遇,也带来挑战

对顺序程序采用自动测试代码生成的主要障碍是有效测试预言的自动派生,它可以准确地区分失败测试和成功测试。幸运的是,并发测试大大减轻了这一障碍。最近对真实故障的特征研究表明,检查的并发故障中有 56-70%导致可见的 oracle 违规,例如崩溃或挂起。此外,使用有效的并发正确性标准(例如,可序列化性)可以帮助检测那些没有表现出明显的 Oracle 违规行为的并发错误。

与顺序测试不同,并发测试的挑战在于线程调度的不确定性。即使对于可能触发并发故障的测试,我们也经常需要执行多次测试才能暴露出错误的(即违反 oracle 的)线程交织。 尽管已经提出了一些有助于交错探索的技术,但是由于交错空间的巨大性,我们实际上只能探索少量测试的交错空间。由于有效的并发故障检测通常需要大量随机生成的测试,因此这对使用随机测试生成技术施加了严格的约束。

现有的关于并发测试的著作大多研究如何根据交织覆盖标准指导给定并发测试的交织空间探索。然而,很少有研究用于交织覆盖标准的并发测试代码的生成。确实,这是一个重要的问题,因为如果我们能够生成并发测试代码,从而有效触发与给定交织覆盖范围标准相关的共享内存访问的交织,则可以大大提高测试效率。在探索大量与并发故障检测无关或对提高交错覆盖率无效的交错方面,它节省了多余的精力。

自动生成交织覆盖率的测试会带来两个主要挑战。首先,它需要估计关于所有可能的并行测试的覆盖要求(即交织)。 然而,计算交织覆盖标准的可执行域恰好需要上下文敏感和同步敏感的分析,这对于并行程序来说是机器无法确定的。其次,它需要估计生成的并发测试可以覆盖的一组交织,以避免生成多余地探索交织的测试。但是,由于需要进行线程敏感的分析,因此精确估算的成本等于实际探索测试的交织空间的成本,这一点很棘手。先前的工作通过使用上下文无关的分析来估计交织覆盖范围,从而解决了挑战。但是,这种近似会导致需求的不可行或缺失。

问题公式化

09e08858c5f8cad298704cf786d1991d.png

2.1 面向对象的并发测试

76081d3cbc2758208003ea90f1fdeb70.png

β 和 γ 序列将由两个不同的线程同时执行。β 和 γ 序列的示例可以在图 2 中找到。像现有的作品一样,我们对每个测试采用两个并发线程的最小配置。 这是因为以前的研究表明,如果在两个线程之间强制执行一定的部分顺序,则可以保证检出的并发错误中有 96%会显露出来。

为了允许 P(t)触发共享内存访问,我们将 β 和 γ 中 CUT 类型的方法调用参数(包括对象接收器)限制为单个 CUT 类型的“被测对象”(SOUT),以便由 这两个序列可能会访问 SOUT 字段并触发共享内存访问。α 的作用是在同时执行 β 和 γ 之前初始化 SOUT。请注意,调用序列可以创建和变异其他对象,例如,那些与 CUT 类型不同的方法参数引用的对象。这些对象将不会在线程之间共享。

2.2 覆盖驱动的测试代码生成

现有的技术已经提出了各种交织覆盖标准以帮助从由测试引起的交织空间中选择代表性的交织。交织覆盖标准规定了交织必须满足的一组属性才能被视为测试覆盖要求。这些属性主要是根据特定的故障模型或错误特征得出的。基于交织的要求的示例包括那些构成数据竞赛,覆盖特定位置对或匹配有问题的访问模式的交织,这违反了某种形式的可序列化性。

5510a6a9962440c0a9a9b2bc0778460c.png
40ae8d1ee9ce5ce1ee9ab48219e143a2.png

图 1 激励示例的源代码

我们的研究问题是要生成能够在给定时间预算内共同实现最高覆盖率的测试。

激励示例

挑战 1:通常(在测试之前)静态地得出 RQ 的覆盖范围要求是不可行的。虽然通常针对给定的测试输入来计算交织覆盖标准,但是覆盖指导的测试输入生成需要针对所有可能的测试输入来计算覆盖要求。然而,计算交织覆盖标准的可执行域恰好需要上下文敏感和同步敏感的分析,这对于并行程序来说是机器无法确定的。

a2f2e5fcf3ba70b02e91ed3652c3772f.png

最近,Steenbuck 和 Fraser 提出了 ConSuite,这是一种针对并发类的覆盖率指导的测试生成技术。首先,通过根据与部分交织类似的给定参数化交织覆盖,对访问 CUT 字段的字节码指令进行置换,通过上下文无关和同步不敏感的分析得出近似的覆盖要求 RQ 集。与交错覆盖率的计算不同,可以通过跟踪调用序列执行了哪些指令来精确而有效地计算调用序列的结构覆盖率。进行并发测试以确定并发测试所涵盖的覆盖范围要求是否可行并引起故障。

4b9f95910cadcc76385486dd7adffaf5.png
accbab11d57355606c62673d7cc404ab.png

图 2 并行测试示例

419cff9270c62248ba1165294d210758.png

其次,忽略上下文敏感度也可能导致无法触发错误的交错,即使它们被测试覆盖也是如此。因此,在生成并发测试时,我们需要考虑同步的上下文。

方法

AutoConTest 是一个由三个主要组件组成的迭代过程(图 3)

ced69f3565a7d78d245037b1bc6cf17e.png

图 3 第 n 次迭代中的 AutoConTest 过程概述。 迭代直到达到时间预算 B

4.1 顺序覆盖

本节介绍了有关呼叫序列上下文相关覆盖率度量 M,称为顺序覆盖。令 E 表示呼叫序列 δ 的轨迹,即由顺序执行(单线程)执行的 δ 显示的事件的有序序列。事件可以是以下之一:

152e14dea003da4e497edace59b70016.png
395a0e3d80f8c7dcb55b806fb1b7d01d.png

为了将方法调用 ci∈δ 与 ci 直接或间接调用的方法区别开来,在以下讨论中,我们将 ci 称为最外层方法调用。 M(δ)的定义利用了这样的观察,即可以通过忽略 δ 中最外层方法调用的顺序执行顺序来计算覆盖率,而不会影响并发故障检测能力。这是因为线程安全性的一般形式不能保证在同一线程中原子地执行对线程安全类的共享库的多次调用。这些多次调用需要外部同步,以保持原子性。Pradel 等人通过检查每个捕获的异常是否也可以通过调用的线性化来触发,从而在并发测试中修剪所有此类交错。顺序调用序列执行中忽略最外层方法调用的顺序的优点是,覆盖率 M 可能会更快达到饱和。

4.2 调用顺序生成

在每次迭代中,调用序列生成组件都会系统地探索可能的调用序列的空间,并返回一个由 β 表示的,在当前迭代中探索的序列覆盖率最高的序列。 此序列将用于组装新的并发测试。由于交织探索的成本随执行长度的增加而增加,因此当组件比较覆盖度提高程度相同的序列时,它会选择最短的序列(以最外面的方法调用的数量来衡量)。 更正式地说,让我们在调用序列上定义以下关系。 给定两个序列 δ1 和 δ2,在改善顺序覆盖率方面,δ1“优于”δ2,表示为 δ1≻δ2,当且仅当 | ∆M(δ1)|> | ∆M(δ2)|或(| ∆M(δ1 )| = | ∆M(δ2)|∧|δ1|

d28dcf337bf15c5ad6eca97efa955f4e.png

搜索空间表示为一棵有根,有向,可能是无限的树 T,其根节点是实例化对象 SOUT 的调用序列 α。 实例化包括进行构造函数调用和其他必要的方法调用,以设置非原始参数。 边缘代表方法调用。树中的每个节点代表一个调用序列,该调用序列对应于沿着从根到节点的路径的方法调用的有序序列。图 4 显示了搜索空间中代表图 1 中激励示例的四个序列 δA,δB,δC 和 δD 的部分。

扩展序列(节点)的候选方法(CM)是 P 中的那些方法,使得它们具有至少一个 CUT 类型的参数 p,可以将其绑定到共享对象 SOUT(请参阅第 2.1 节)。AutoConTest 通过参数值的不同组合来系统地探索节点的可能扩展。由 Randoop 在每次迭代中伪确定性地(使用相同的随机种子)构造原始和非原始参数值的池。AutoConTest 在迭代中使用不同的随机种子来实现多样性。结果,每个节点的出站边缘在每次迭代时都是相同的,此属性对于我们的树遍历至关重要。

一个关键的挑战是如何有效构建可能的呼叫序列的搜索空间并比较其覆盖范围的改善。 但是,即使将调用序列的长度限制为给定值,也不可能完全构建这样的搜索空间。 为了应对这一挑战,AutoConTest 部署了覆盖范围驱动的搜索遍历,该遍历可在将节点扩展到其子级之前进行预测,如果这些子级可以具有代表最佳调用序列 β 的后代。如果没有,这些孩子将被跳过。 通过对程序状态和每个拜访调用序列所达到的覆盖范围进行推理来进行预测。

调用序列 δ= 的每次执行都会引发一系列状态转换

26ee9439e4279d8a16988e5fe4ffcb43.png

。令 Si-1 表示在调用 ci 之前 ci 的参数状态,而 Si 表示之后的状态。我们假设给定状态为 Si-1,方法调用 ci 的顺序执行是确定的。在这种假设下,如果方法调用 cn + 1 附加在两个不同的调用的末尾,则会触发相同的方法调用跟踪达到相同状态 Sn 的序列。因此,如果我们已经探索了到达相同状态并具有更好覆盖范围的另一个呼叫序列的可能扩展,那么探索一个呼叫序列的可能扩展是多余的。

定义 5.呼叫序列 δ 是冗余当且仅当 ∃δ∗∈Q,使得 S(δ)= S(δ∗)∧(∆M(δ)⊂∆M(δ∗)∨(∆M(δ)= ∆ M(δ∗)∧|δ∗ |≤|δ|))。

定理 1.无需探求代表冗余呼叫序列的节点的未探索后代即可获得最佳解 β。

6ebfe5078d6d886e552742cdaea80366.png

图 5 调用顺序生成算法

现在,我们描述调用序列生成算法(图 5)和搜索空间修剪策略(图 6)。

我们选择使用深度优先搜索(DFS)遍历树。由于覆盖范围倾向于随序列长度的增加而增加,因此 DFS 策略可能比广度优先搜索(BFS)更快地找到更高的覆盖范围序列。

可能存在一些可以连续扩展以提供新的非冗余序列的序列。 因此,如果没有明确定义的停止标准,该算法很容易耗尽所有可用的测试时间预算。一种可能的解决方案是在序列长度上施加上限。 这种方法的问题在于,没有一种实用的方法为给定的 CUT 选择最佳界限。相反,该算法采用基于饱和度的停止准则。令 δ′表示通过方法的 δ 的扩展调用序列。如果 δ'的最新 k 个扩展均未显示出覆盖率改善,则该算法将停止扩展 δ',但使用另一种未决方法继续扩展 δ。 如果算法在未找到可改善顺序覆盖范围的序列 β 的情况下终止,则会以增加的饱和度 k 重新启动。

aa5c526e183a7e2e80baec87ee026d8d.png

图 6 搜索修剪策略

收集运行时数据。 执行每个新探索的序列以收集以下信息。

  1. δ.excp,一个二进制值,如果执行 δ 引发捕获或未捕获的异常,则为真; 否则为假。
  2. ∆M(δ),通过执行被测程序的仪器版本来计算。
  3. S(δ),执行 δ 之后对象 SOUT 的状态。 它是通过以深层复制语义序列化 SOUT 来获得的。

深度修剪如果满足以下任一条件,该算法将停止扩展 δ',但继续使用另一种挂起方法扩展 δ。D1:δ′.excp =真。 D2:| ∆M(δ')| = | ∆M(δ)| ∧S(δ')= S(δ)。D3:| ∆M(δ')| = | ∆M(δ)| ∧δ'是多余的。

广度修剪。该算法停止节点的“广度”访问,即,如果满足以下任一条件,则可以避免使用任何待处理方法扩展 δ。B1:| ∆M(δ')|> | ∆M(δ)| ∧S(δ')= S(δ)。B2:δ 是多余的。此检查类似于 D3。

4.3 并发测试组装者

给定由调用序列生成器组件在当前迭代中返回的调用序列 β 和在先前迭代中返回的序列集合 L = {γi},并发测试组装器组件将按以下方式创建新的并发测试。它用 β 更新 L,然后创建| L |。通过将 β 与 L 中的每个 γi 组装在一起,即{ti =(β||γi)∀γi∈L},进行新的并行测试。结果,通过构造,β 覆盖的所有新方法调用迹线都与 L 中的所有方法调用迹线同时进行成对测试。这有利于改进交织覆盖率,因为覆盖一对新方法调用迹线是覆盖一种新的可行的和引起故障的交错。

2b7bee6a62d6027f2a5a057af158d33d.png

可以对每个新对执行附加检查,以推断它们是否与给定的交织覆盖范围标准相关。 为了使 AutoConTest 适用于任何条件,我们选择不执行此检查。 然而,不管考虑的标准是什么,覆盖所有可能的对都是测试充分性的充分条件。

4.4 交织探查器

并发测试可以表现出许多交错。给定在给定迭代中组装的并发测试,因此,Interleaving Explorer 组件将识别并测试与指定交织覆盖范围标准相匹配的交织。一个关键的挑战是识别并测试每个并行测试所涵盖的所有覆盖范围要求。这是因为,当一个调用序列与其他序列并发执行时,它可以表现出与其顺序序列不同的行为(即,触发不同的方法调用跟踪)。这种现象称为并发干扰,当多个序列同时访问和更改共享对象 SOUT 时发生。发生此现象时,可能不会执行可能增加交织覆盖率的方法调用跟踪。如果在并发测试中调用 SOUT 的状态不同于在顺序执行中调用该方法时的状态,则该方法会受到并发干扰。

c773f51ba61f1cbd973ac368d398c14f.png

图 7 AutoConTest 如何解决并发执行期间的干扰问题

为了应对这一挑战,我们采用预测跟踪分析(PTA)来确定覆盖范围要求,并且在测试这些要求时使用专用线程调度程序算法来避免并发干扰。

然后用实际执行测试保留的交错,以检查它们是否表现出错误的行为。使用线程调度程序算法(即,主动测试)来实施交织。在调用触发交错测试中涉及的共享内存访问的方法时,该算法可确保当关联时,SOUT 的状态为观察到的状态调用顺序按顺序执行。这是为了避免同时干扰任意绑定 SOUT 的状态。直观地讲,有两个 SOUT 状态的候选人。SOUT 的状态是在调用 sout.m3(10)之前通过顺序执行 δ3 达到的,而状态是在调用 sout.m1(1)之前通过顺序执行 δ1 达到的状态。因此,对于每个要测试的交错,我们在两个并发的测试计划上运行主动测试。图 7 示出了交织帽

320552e9c0e9274bcc10449ba6ad2382.png

的两个时间表。在图 7 的运行示例中,第一个调度成功触发了错误的交织。

评估

我们评估 AutoConTest 是否有效地暴露并发错误并能够实现高交错覆盖率。我们的实验中考虑的交织覆盖范围是一组有问题的交织,它们与违反原子集可序列化性的访问模式相匹配。我们进行了一系列实验来回答以下研究问题:

RQ1:在有时间预算的情况下,AutoConTest 在检测并发故障和实现高交错覆盖率方面有多大作用?

RQ2:AutoConTest 是否比基于随机测试的生成更有效?

RQ3:AutoConTest 的搜索修剪策略在探索搜索空间方面是否有效? 他们能否基本实现无损修剪?

为了评估我们的技术,我们将 AutoConTest 实施为 Java 类的自动化工具。测试生成阶段使 Randoop 适应扩展调用序列并生成可运行的 Java 测试代码。我们还对来自四个流行代码库的六个已知的现实世界并发错误进行了实验,这些错误已用于评估相关工作。对于每种故障,表 1 给出以下信息:本文其余部分中使用的主题 ID,代码库的名称和版本,被测类(CUT),源代码行数(SLOC)(包括 CUT 的非抽象超类,如果有的话),以及来自问题跟踪系统的错误报告链接。

b576a56aa646c967f82395d11d87e878.png

表 1 一小时的时间预算,主题描述和实验结果

5.1 RQ1 – 消耗/有效性

对于每个主题,我们以一小时的时间预算运行了 AutoConTest。测得的经过时间包括测试生成和交错探索的时间。在实验中,由 AutoConTest 为每个对象生成的第一个测试成功地揭示了第一个错误,表明 AutoConTest 在生成少量公开并发错误有效并发测试方面是有效的。

5.2 RQ2 – 与 ConTeGen 对比

我们将 AutoConTest 与 ConTeGen(一种用于并行生成并发测试代码的最新技术)进行了比较。ConTeGen 的所有运行均未在时间预算内检测到任何并发故障。我们进一步地比较 AutoConTest 和一种使用 AutoConTest 的相同交错探索组件来探索由 ConTeGen 生成的测试的交错空间的方法。(ConTeGen ∗)平均而言,AutoConTest 在不到 40 秒的时间内即可达到 ConTeGen *在 1 小时内实现的覆盖率的百分比。这表明 AutoConTest 的有效性主要来自于掩盖角落程序行为的能力,而这些行为很难通过随机生成更多的测试来掩盖。实际上,随机生成的测试会受到冗余的影响。

5.3 RQ3 – 搜查修剪策略

3cc0c3ec3b4bb4a30955877b330718bc.png

表 2 搜查修剪策略评估

该研究问题的目的是评估呼叫序列生成组件。对于这组实验,我们隔离运行组件,即不探索生成的测试的交错空间。我们运行了该组件的两个版本。我们使用相同的初始饱和度停止标准(即饱和度 k = 3,请参见第 4.2 节)在一个小时的时间预算上对两个版本进行测试。我们还通过将它们与时间预算期满后禁用版本返回的最佳序列进行比较,来评估启用版本在第一次迭代中返回的序列是否最佳。对于仅两个主题,启用版本返回的序列次优。

致谢

本文由南京大学软件学院 2020 级硕士王旭翻译转述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值