[论文解读] Cross-Entropy Based Testing

Cross-Entropy Based Testing

简介

论文标题

  • Cross-Entropy Based Testing
  • 基于交叉熵的测试

简介

  • 将代码的执行顺序分解成图,
  • 不同的线程在节点上有其离散的概率分布表.
  • 根据概率选择某个线程执行,收集足够多的样本,根据数据样本更新概率分布
  • 迭代,直到达到性能函数最优,或者标准差低于某个阈值

交叉熵应用在:假定每个节点有个理想分布,该分布最容易发现问题.然后从原始分布开始,利用交叉熵逐步逼近理想分布.因为理想分布是未知的,所以是通过采样的方式去求每次的分布

以上为个人理解,其实我也没搞懂到底怎么回事

摘要

主要问题: 增加测试的穷尽性减少未检测错误的数量是软件测试的主要问题。

在本文中,我们提出了一种基于交叉熵方法的新的软件测试方法.我们定义了一个性能函数,它在我们寻找的错误或模式的邻域中更高。然后,程序被执行多次,从一些随机分布中选择输入向量。起始分布通常是均匀的,并且它在每次迭代中根据在前一次迭代中性能函数的最高值的向量而改变。

交叉熵方法被证明在估计罕见事件的概率寻找困难优化问题的解决方案方面非常有效。

基于交叉熵(Cross-Entropy )CE:又称KL距离或KL散度

实现工具: ConCEnter

介绍

CE 测试方法流程

CE 测试方法包括一个迭代过程,其中每个迭 代包括两个阶段:

1)根据指定的机制生成随机数据样本。

2)基于数据更新随机机制的参数,以在下一次迭代中产生“更好”的样本,其中根据预定义的性能函数

选择“更好”。

根据预定义的性能函数评估样本。当生成“最佳”样本,即具有性能函数最大值的样本(或者,如果全局

最大值事先未知,则具有足够小的相对偏差)时,该过程终止。

用途举例

为了使 CE 方法适用,我们将焦点从搜索错误转移到关注程序中最容易出错的地方。下面的例 子有助于澄清这两种方法之间的区别。假设我们有一个程序,我们想测试它的缓冲区溢出错误。面向错 误的测试工具搜索缓冲区溢出的执行。这些执行可能非常罕见,可能会逃过我们的测试。在交叉熵测试 中,我们将执行指向缓冲区达到最大容量的区域。这种方法的优点是,这个区域虽然可以很小,但不是可以忽略的,并且可以在随机测试中发现。此外,如果存在缓冲区溢出的错误执行,它肯定发生在这个区域。

我们认为许多常见的bug和可能包含bug的模式都有一个自然的性能函数。例如,在缓冲区溢出的情况下,执行的自然性能函数给出的值等于该执行中缓冲区的最大大小。我们将描述更多常见错误和错误模式及其相关性能函数的例子。

背景

优化问题中的交叉熵方法

交叉熵公式

D ( g , h ) = E g ln g ( X ) h ( X ) = g ( x ) ln g ( x ) d x g ( x ) ln h ( x ) d x \mathcal{D}(g, h)=E_{g} \ln \frac{g(X)}{h(X)}=\int g(x) \ln g(x) d x-\int g(x) \ln h(x) d x

衡量两个分布的差异,注意不是严格意义的距离,因为不是对称的

在迭代i中,我们根据当前概率分布fi绘制了一个随机样本,并基于该样本计算 f i f_{i} g l g l 之间的(近似)交叉熵。然后我们根据交叉熵的结果更新 f i f_i ,从而构造 f i + 1 f_{i+1}

基于CE方法对图形优化问题的应用

我们将测试中的程序表示为图。然后,该图中的路径对应于程序的执行,并且概率被分配给边。

性能函数

我们定义S,以便S(X)在使系统进入测试状态的路径上达到其全局最大值,许多常见的错误和错误模式都具有自然的性能功能。

概率更新方法
  1. 我们从统一的概率分布开始。在每个迭代i中,我们将按其性能函数值的升序对在此迭代中生成的样本Xi
    = {X1,…,XN}进行排序。即,S(X1)≤S(X2)≤…≤S(XN)。

  2. q < = 1 q <= 1
    Q ( X i ) = { X ( 1 q ) N , X ( 1 q ) N + 1 , , X N } Q\left(\mathcal{X}_{i}\right)=\left\{X_{\lfloor(1-q) N\rfloor}, X_{\lfloor(1-q) N+1\rfloor}, \ldots, X_{N}\right\}
    是其中最好的q部分,

    有小到大排序后,选取后百分之x的样本

  3. 则概率更新公式如下
    f ( e ) = Q ( e ) Q ( v ) f^{\prime}(e)=\frac{|Q(e)|}{|Q(v)|}
    其中 e E e∈E 是起源于顶点 v v G G 的边, Q v Q(v )是 Q X i Q(\mathcal{X}_{i}) 中经过v的路径,而 Q ( e ) Q(e) Q X i Q(\mathcal{X}_{i}) 中经过e的路径。

  4. 当样本的相对标准偏差低于预定义的阈值参数(通常在1%和5%之间)时,该过程终止。

备注3.1(平滑更新):在涉及离散随机变量的优化问题(例如图形优化问题)中,以下方程式用于更新概率函数,而不是方程式1:
f ( e ) = α f ( e ) + ( 1 α ) f ( e ) f^{\prime \prime}(e)=\alpha f^{\prime}(e)+(1-\alpha) f(e)
其中 0 < α 1 0 <α≤1 是平滑参数。显然,对于 α = 1 α= 1 ,我们有原始的更新方程。通常,使用介于0.4和0.9之间的 α α 值。平滑更新效果更好的主要原因是,它可以防止永远丢失好样本(如果在一次迭代中将其边缘之一分配为0)。

预备定义

目标程序: 有限的多线程程序

有限

  1. 线程数有限:t代表程序中的线程数
  2. 程序的所有执行有限:每个循环的代码都被复制了最大次数

定义3.2(PL): Program location

程序位置(PL)是程序的展开代码中的行号。

定义3.3 ( C F G i CFG_i :control flow graph

线程 i i [ t ] i(i∈[t]) 的控制流图( C F G i CFG_i )是有向图 G i = L i , E i , μ i G_{i}=\left\langle L_{i}, E_{i}, \mu_{i}\right\rangle
其中Li是线程的展开代码中所有程序位置的集合.
E i E_{i} ( v , u ) E i (v, u) \in E_{i} 的边集如果可以在位置v的语句之后立即执行位置u的语句,
µ i L µi∈L 是线程的初始程序位置。

定义3.4(PLV):Program location vector

程序位置向量(PLV) v是一个t维矢量,因此对于每个 i [ t ] v i L i i \in[t] \quad v_{i} \in L_{i} 。如果每个 i [ t ] v i i \in[t] \quad v_{i} 是线程i中要执行的下一个程序位置,则在程序执行期间的每个时刻m,我们都说执行是在PL V 的 v处进行的。

显然,所有PLVs的集合等于 L i S L_{i} S 的叉积

定义3.5(JCG):joint control graph

被测程序的联合控制图(JCG)是图V,E。其顶点为PLVs。如果存在执行路径,其中w是u的直接后继,则顶点u和w之间的JCG中会有一条边。

请注意,每时每刻只有一个线程可以移动。因此,每个顶点的分支度最大为t。由于展开了代码,因此其中的每个语句最多执行一次,并且这些语句按其程序位置的升序执行。因此,JCG是有限有向无环图(DAG)。
JCG的源顶点是PLV,它由每个线程i的初始PL µi组成。

定义3.6(PF)

概率函数 P F : V ( J C G ) × P F: V(J C G) \times [ t ] [ 0 , 1 ] [t] \mapsto[0,1] ,以使每个顶点出射边的概率总和为1。

该函数为每个顶点v及其每个输出边缘定义了线程i在执行达到v时前进的概率。如果并非所有线程都在v处启用,我们将采用已启用线程的相对概率。如果此时 T e n [ t ] T_{e n} \subseteq[t] 是已启用线程的集合,则相对概率为:
R P ( v , i ) { P F ( v , i ) j T e n P F ( v , j )  if  i T e n 0  otherwise  R P(v, i) \doteq\left\{\begin{array}{ll} {\frac{P F(v, i)}{\sum_{j \in T_{e n}} P F(v, j)}} & {\text { if } i \in T_{e n}} \\ {0} & {\text { otherwise }} \end{array}\right.
就是把0的不要,把剩下的重新平均概率

定义3.7(可见线程状态):Visible thread state

线程i的可见线程状态(VTS)s是对 v , σ , τ \langle v, \sigma, \tau\rangle 。其中 v L i v∈Li 表示当前PL,σ表示所有局部变量的值,τ表示该语句在程序位置 v v 处访问的全局变量的值。

直观地,可见线程状态集是系统访问线程系统各个部分时可见的系统所有可能状态。

我们假设程序中实施了正确的锁定策略(可以静态检查)。因此,当仅在锁定或解锁语句处允许上下文切换时,任何程序执行可以达到的任何VTS,某些程序执行也可以达到的任何VTS。因此,在这种上下文切换策略下,可能具有多个后继对象的JCG的唯一顶点是那些具有锁定或解锁语句程序位置的PLV。然后,可以折叠具有单个后继的所有顶点而不会丢失任何同步信息,因此,我们可以假定JCG图仅包含锁定/解锁语句的程序位置

对于程序的单次执行,我们称之为JCG的顶点序列,它访问JCG中的执行路径。

将交叉熵用于测试

算法

算法流程

  1. 概率分布表包含边缘上的概率分布,并在每次迭代时更新。最初,边缘是未知的,因此假设分布均匀,表为空。
  2. 通过在同步点添加回调来检测该程序。这使算法可以在这些点处停止执行,并确定接下来要遍历哪个边沿(即,哪个线程进行了移动)。在这一点上,该算法收集并存储了JCG的边缘知识。
  3. 在每次迭代中,该算法执行以下任务:
    1. 被分解的程序被执行多次,足以收集有意义的样本(执行次数取决于JCG的大小)。执行被迫根据边缘上的当前概率分布执行调度决策。停止执行并决定下一步允许哪个线程运行是由我们实现中的单独组件执行的。
    2. 这些执行用来计算新的概率分布(之间计算f的公式)。参数q和α的选择随后讨论
  4. 当前迭代的采样样本具有足够小的相对标准偏差(在1%和5%之间)时,算法终止。

各部分组成

  • Instrumenter : Instrumenter是一种检测工具,可在同步点即每个同步块之前和之后立即添加回调
  • Decider: Decider接收被测程序的JCG的节点v,并根据控制图边缘上的当前相对概率RP(v)选择允许下一步运行的线程。
  • Stopper: Stopper在已检测代码的回调中,它使用为此线程指定的互斥体来停止当前正在运行的线程。然后,它在可以进行下一步(基于Decider的决定)的线程的互斥对象上调用notify
  • Evaluator: Evaluator收集执行路径所遍历的JCG的边缘。在每次执行结束时,它都会计算执行的S值
  • Updater:Updater 更新程序根据评估程序的计算为下一次迭代更新概率分布表。

在执行期间,Decider和Evaluator收集大量数据。为了最小化此数据的内存缓冲区的大小,它会定期写入文件中。

改进性能的启发式

处理展开(Dealing with unwinding):要生成展开程序的完整JCG(实际上不生成展开代码),程序位置由原始(未展开)代码中的行号和一个反映展开的附加参数组成。该参数是程序当前所在的所有循环的循环计数器值的向量。

减少模数(Modulo reduction):实际上,即使对于中型程序,展开程序的JCG也会很大。这个问题是双重的。

  • 首先,图本身可能要大两倍才能适合内存并有效搜索。
  • 其次,更重要的是,当投影到JCG上时,在算法的每次迭代中生成的执行样本集可能太稀疏。在这种情况下,仅针对一小部分节点更新概率。结果是该算法收敛到任意局部最大值,而不是目标全局最大值。

我们通过引入模归约来解决这两个问题,在模归约中,程序位置中的计数器以一些小整数为模来计算。模减少可从原始JCG创建模联合控制图(MJCG)。通过取其所有计数器的模值,JCG的每个顶点都映射到MJCG的顶点。模减少会显着减小图形的大小,但是,它没有保留所需的1-1映射到展开的程序位置。我们的实验表明,通过一些微调,可以找到最佳的模参数,其中多对一映射不会混淆CE计算,但是MJCG足够密集以允许CE方法收敛达到全局(而非局部)最大值。

应用

  • 发现数据竞争:函数是执行期间访问的共享资源的数量。
  • 测试错误路径:函数是执行期间采用的错误路径的数量。
  • 不正确使用同步原语:函数是执行中对互斥函数的调用数。
  • 引起或与等待通知情况有关的错误:此功能是丢失通知的数量。
  • 测试线程/任务多次失败的恢复:该函数是取消的线程/任务的数量。
  • 触发许多异常的环境的仿真:该函数是生成的异常的数量。
  • 无法正确释放资源或资源池耗尽:此功能是分配的资源数量减去已释放资源的数量。
发布了71 篇原创文章 · 获赞 21 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33935895/article/details/104585347
今日推荐