基于Maximal Causality Reduction的并发程序验证

Stateless Model Checking Concurrent Programs with Maximal Causality Reduction

 

# Remarks

Publication: ACM SIGPLAN Notices June 2015

Full Paper: https://dl.acm.org/doi/abs/10.1145/2813885.2737975

Artifact: https://github.com/parasol-aser/JMCR

 

# Summary

MCR is a stateless model checker powered by an efficient reduction algorithm. It systematically explores the state-space of the program by collecting runtime traces of the program executions and constructing ordering constraints over the traces to generate other possible schedules. It captures the values of the writes and reads to prune redundant explorations. By enforcing at least one read to return a different value, it generates a new schedule which drives the program to reach a new state.

 

# Introduction

并发程序验证中的一个基本挑战是线程交错爆炸:可能的交错数随线程数和程序执行长度呈指数增长。缓解这个问题的一般思路是忽略产生等效程序状态的冗余交错。在无状态模型检查中,技术通过一个特殊的调度程序来驱动程序的执行,从而系统地探索给定程序的状态空间,该调度决定只探索非冗余交错。

无状态模型检查的一个关键任务是如何识别冗余的交错。经典的方法是partial order reduction(POR),它基于这样的观察:如果交织可以通过交换来自不同线程的相邻非冲突事件而从另一个线程获得,则交织是多余的。所有交错都可以分为许多不同的等价类,称为Mazurkiewicz traces。POR探索了每个Mazurkiewicz traces中的一个交错,并被证明足以检查最有趣的安全属性,例如不存在断言冲突和数据竞赛。

POR的一个主要限制因素是,Mazurkiewicz traces的特征是基于与happens-before关系相关的严格事件依赖性。对于具有锁和共享变量的共享内存系统,在关系强制依赖于所有锁获取/释放和冲突读/写事件之前发生。这就错过了识别多余交错的许多机会。考虑三个线程p、q和r,对共享变量x执行读写访问:

//Thread 1
p: write x; 


//Thread 2
q: write x; 


//Thread 3
r : read x;

根据之前发生的情况,这三个访问都是相互依赖的。因此,这六个交错{p.q.r,p.r.q,q.p.r,q.r.p,r.p.q,r.q.p}属于六个不同的Mazurkiewicz traces,POR必须探索所有这些trace。从表面上看,这是必要的,因为它们都不是多余的。然而,如果我们仔细观察,只有一半是必要的探索。实际上,p.q.r相当于q.r.p,p.r.q到q.p.r,r.q.p到r.q.p。原因是r是唯一读取x的线程,其返回值可能会影响待验证属性。如果p和q的两次写入没有导致r读取不同的值,那么它们的顺序是多余的。更好的是,如果两个写操作都产生相同的值,那么p.q.r也等价于p.r.q,只产生两个要探索的交错(p.q.r和r.p.q)。

基于Maximal Causality Reduction的并发程序验证_第1张图片

 

为了进一步消除冗余的线程交错,本文提出的MCR是一个无状态的模型检查器,由一个有效的简化算法提供支持。它通过收集程序执行的运行时跟踪,并在跟踪上构造顺序约束来生成其他可能的调度,系统地探索程序的状态空间。它捕获写入和读取的值,以删除多余的探索。通过强制至少一次读取以返回不同的值,它生成一个新的调度,从而驱动程序到达一个新的状态。

 

基于Maximal Causality Reduction的并发程序验证_第2张图片

 

 

# The harm of concurrent errors

一个并发错误损失了1200万美元:https://stackoverflow.com/questions/16159203/why-does-this-java-program-terminate-despite-that-apparently-it-shouldnt-and-d

import java.util.*;


class A {
 static Point currentPos = new Point(1,2);
 static class Point {
 int x;
 int y;
 Point(int x, int y) {
 this.x = x;
 this.y = y;
 }
 }
 public static void main(String[] args) {
 new Thread() {
 void f(Point p) {
 synchronized(this) {}
 if (p.x+1 != p.y) {
                    System.out.println(p.x+" "+p.y);
                    System.exit(1);
 }
 }
            @Override
 public void run() {
 while (currentPos == null);
 while (true)
 f(currentPos);
 }
 }.start();
 while (true)
            currentPos = new Point(currentPos.x+1, currentPos.y+1);
 }
}

 

运行这个程序得到的一些结果

$ java A
145281 145282
$ java A
141373 141374
$ java A
49251 49252
$ java A
47007 47008
$ java A
47427 47428
$ java A
154800 154801
$ java A
34822 34823
$ java A
127271 127272
$ java A
63650 63651

 

问题的关键出现在

currentPos = new Point(currentPos.x+1, currentPos.y+1)

 

基于Maximal Causality Reduction的并发程序验证_第3张图片

 

new Point实际上会执行一些操作,包括将默认值写入x和y(0),然后将它们的初始值写入构造函数。由于这条语句没有加锁,编译器/JVM可以自由地重新排序这4个写操作。因此,从读取线程的角度来看,以x的新值读取x,而y的默认值为0是合法的。当程序到达println语句时(顺便说一下,println语句是同步的,因此会影响读取操作),变量就有了它们的初始值,程序会打印出预期的值。

# Example

下图中的示例程序启动三个并发线程T1、T2和T3,每个线程循环两次,并访问两个共享变量x和y以及一个锁l。如果满足这两个条件x>1和y==3,则在第13行会触发一个错误。然而,这个错误很难找到,因为它隐藏在复杂的线程交互中。如果条件为真,则第2行必须在第7行和第8行之间执行;如果条件为真,则第9行必须在第14行之后执行。此外,第11行和第12行必须分别在第10行和第9行之后以及在x和y的值被任何其他写入更改之前执行。为了发生错误,每个线程必须在交错T2-T2-T2-T1之后循环两次。-T1-T3-、T3如图2底部所示。但是,考虑到这个程序中所有可能的交错,总数超过1000万!这对现有的错误查找技术提出了重大挑战。

 

 

基于Maximal Causality Reduction的并发程序验证_第4张图片

 

本文使用在这项工作中开发的无状态模型检查器运行这个程序,并实现了各种算法。基本模式(深度优先搜索,直到覆盖所有交错)在一小时内超时之前搜索3293931个交错。ICB模式(实现了与CHESS中相同的ICB算法,这通常是查找并发错误的最有效方法)执行了77322次,直到在探索20秒后遇到错误。ICB可以与DPOR结合,以进一步减少探索的执行。我们运行了ICB+DPOR模式,在3秒内发现错误之前,它仍然执行了3782次。相比之下,我们的MCR方法只执行了46次,在2秒内发现了错误,将ICB+DPOR探索的执行次数减少了两个数量级,ICB减少了三个数量级,因为大多数执行都是因果等价的。

为了使这个例子更有趣,我们让第3行(重复地将y设置为1)循环N次,并比较随着N的增加不同算法的性能。由于第3行的每次执行都对应于y上的一个新的写入事件,因此程序的交错空间以N为指数增加。对于每一个较大的N,ICB和DPOR都必须探索更多的交错才能找到错误。但是,MCR没有,因为第3行的重复写入事件只会创建冗余的交错,这些交错由最大因果关系捕获。图3显示了N从1到10的比较结果。请注意,垂直轴在对数刻度上。对于ICB和ICB+DPOR,执行次数和发现错误的总时间都随着N的增加而急剧增加。ICB+DPOR甚至比ICB单独增加得更快,因为DPOR无法减少由于第3行写入而导致的冗余交错。但MCR对N几乎不敏感,当N大于3时,MCR在50次执行时趋于稳定,且总时间增长不明显。

 

基于Maximal Causality Reduction的并发程序验证_第5张图片

 

# Maximal Causality Reduction

 

从概念上讲,MCR不同于现有的技术,它为每一次执行覆盖一组交错。在最大因果模型(MCM)的支持下,MCR能够分析从每个执行跟踪中导出的指数且可证明的最大交织数。

基于Maximal Causality Reduction的并发程序验证_第6张图片

算法1概述了我本文的基本算法。它以迭代的方式工作。每次迭代都会在线探索一个交织,并离线检查派生交织的最大因果集。此外,每个迭代可以生成更多的种子交错,这些种子交错将用于驱动其他迭代。更具体地说,每个迭代有三个步骤:

 

Online exploring and tracing for one execution

此步骤执行程序,首先执行种子交织(最初为空),然后继续(使用任意交织)直到执行结束。除了执行程序外,此步骤还收集一个跟踪,其中包括构造MCM所需的信息。请注意,此处收集的跟踪不需要命中错误。我们将在下一节中提供MCM的详细信息。

 

Offline checking for maximal causal interleavings

此步骤构造MCM并脱机检查属性。每个MCM包含一组交错,maxcausive(s),这是从对应于交错的跟踪推断出的唯一且最大可行交错集。为了检查属性,我们将maxcausive(s)编码为一组顺序变量上的公式f,使得公式的任何解都对应于由拓扑排序的顺序变量表示的合法交错。通过将属性编码为附加约束并求解它们与f的连接,我们可以确定属性是否适用于maxcousal(s)中的所有交错。

 

Generating seed interleavings

此步骤生成种子交错,将在第一步中用于生成新交错。对于每个新的交织,maxcausual(s)覆盖交织的唯一子空间。这一步很关键,因为要最小化执行次数,我们必须确保没有两个子空间重叠,并且所有子空间一起覆盖整个交织空间。

 

基于Maximal Causality Reduction的并发程序验证_第7张图片

当探索所有种子交织并且没有新的种子交织可以生成时,表明整个状态空间已被覆盖,我们的方法将终止。

 

# Approach

本节主要介绍MCM以及如何对其进行编码。然后详细介绍了种子交织生成算法。本文的方法将冗余交错的推理转换为离线约束求解。由此产生的过程可以很容易地并行化,在理论上,如果有足够的内核,就可以将MCR扩展到任意大型程序。

基于Maximal Causality Reduction的并发程序验证_第8张图片

基于Maximal Causality Reduction的并发程序验证_第9张图片

 

基于Maximal Causality Reduction的并发程序验证_第10张图片

 

# Running Example

下图展示了前文中的示例的种子交织生成算法。s0是初始跟踪的种子交织。在第一次迭代中,我们生成四个种子交错{s1、s2、s3、s4},它们分别强制四个读取R读取值1(而不是初始跟踪中的0)。在第二次迭代中,我们继续研究si(i=1,2,3,4)对应的轨迹,并生成s1.1,s1.2。,s2.1,s2.2等。所有交错构成一个层次结构,每个子交错强制不同的读取值。同样,每个新的交织可以产生包含新的读取事件和/或写入值的跟踪,该跟踪可以产生新的子交织。例如,我们的方法最终将生成交织s1.1.2.2,它强制R13读取3并在第13行触发错误。

基于Maximal Causality Reduction的并发程序验证_第11张图片

 

# Evaluation

在本节中,本文重点回答三个问题:

  • 1. MCR查找并发错误的效率和效果如何?
  • 2. MCR在探索状态空间方面的效率和效果如何?
  • 3. 实际程序的MCR可伸缩性如何?

基于Maximal Causality Reduction的并发程序验证_第12张图片

基于Maximal Causality Reduction的并发程序验证_第13张图片

 

基于Maximal Causality Reduction的并发程序验证_第14张图片

 

# Useful Documents

  • [ECOOP'17] Speeding Up Maximal Causality Reduction with Static Dependency Analysis
  • [OOPSLA'16] Maximal Causality Reduction for TSO and PSO
  • [PLDI'15] Stateless Model Checking Concurrent Programs with Maximal Causality Reduction
  • [PLDI'14] Maximal Sound Predictive Race Detection with Control Flow Abstraction

你可能感兴趣的:(基于Maximal Causality Reduction的并发程序验证)