JMCR 之约束求解原理

简述

本文将会介绍 JMCR 中约束求解的算法。

提纲

  • 约束求解的定义
  • 约束求解的目的
  • JMCR 中基本的概念
  • JMCR 中约束的实现

本文中的图片和文字参考于源码在 github Readme中提供的论文。本文仅供学习参考使用,如有侵权,请联系删除。

系列文章:
1. JMCR 简介
2. JMCR 字节码插桩(一)
3. JMCR 字节码插桩(二)
4. JMCR 约束求解原理
5. JMCR 线程调度

一、什么是约束求解

约束求解是通过一个已知的程序运行记录(以下简称 trace),生成约束并通过 smt 求解技术得出新的线程可行的调度序列的过程。

二、约束求解的目标

回顾一下在 JMCR 学习笔记 中的内容

如果要测试多线程的程序,我们需要 “多跑几遍”,将程序中所有可能的操作序列复现一遍,看看是否可以触发错误,这样才能保证我们的测试不会漏过每一种可能。随之而来的问题是,如何计算出来所有可能的操作集合序列?对于每一次执行,怎么驱动程序按照制定操作序列进行调度,又如对于多线程程序爆炸性增长的探索空间进行剪枝呢?

首先,为了保证测试的 completeness ,要覆盖程序过程中每一个可能的状态。其次,由于多线程程序的测试具有随着测试规模增长而爆炸性增长的趋势,对于不合法的和冗余的测试样例在约束阶段就删除掉,提高测试程序的运行效率

三、一些基本的概念

  1. trace:程序调度序列,由一系列 event 组成。
  2. event:多线程程序中发生的事件,包括:
    • begin(t)/end(t):线程 t 中的第一个/最后一个事件
    • read(t, x, v)/write(t, x, v):线程 t 读到/写变量 x 值为 v
    • lock(t, l)/unlock(t, l):线程 t 获取/释放锁 l
    • fork(t, t'):线程 t 创建新线程 t'
    • join(t, t'):线程 t 阻塞直到 t' 运行结束

运行程序,通过之前提到过的字节码插装技术,我们可以获取到整个测试程序中的变量、线程以及trace 信息。

四、约束

程序中每一行都是一个 event,我们对其进行了标号,在本小节将对以下程序增加约束来产生新的调度序列


例子

(一)保证生成合法的调度序列

合法在这里有三个部分:读一致性、锁互斥以及 Must Happens-Before 原则。

  1. 读一致性

A read event contains the value written
by the most recent write event on the same memory location.

指的是每一个 read(t, x, v0) 事件和之前距离其最近的 write(t, x, v1) 事件,v0 = v1。

2.锁互斥原则

Each release event is preceded by an acquire event on the same lock by the same thread, and each pair is not interleaved by any other acquire or release event on the same lock

每个 unlock(t, l) 事件之前都应该有一个 lock(t, l) 事件,且两个不同线程对于同一个锁 l 的 lock unlock 对之间不能有交错的时间序列。

  1. Must Happens-Before 原则

A begin event can happen only as a first event in a thread and only after the thread is forked by another thread. An end event can happen only as the last event in a thread, and a join event can happen only after the end event of the joined thread.

begin 事件只能作为线程中的第一个事件发生,并且只能在该线程被另一个线程 fork 之后发生。 end 事件只能作为线程中的最后一个事件发生,而 join 事件只能在被 join 线程的 end 事件之后发生。

  1. 线程内串行
    对于线程内的事件顺序,序号为n的事件一定 happens-before 于序号 n+1 的事件。

  2. 线程间的顺序以及 happens-before 传递性
    这里借用了 Lamport 算法思想:因为begin 事件只能在该线程被另一个线程 fork 之后发生。所以 fork 是因,begin是果。而 end 事件只能在被 join 线程的 end 事件之后发生,所以 end 是因,join 是果。而每个线程内的事件可以把前一个事件作为因,后面的是果, 这样一来,我们可以参照分布式系统因果一致性,来对于程序的线程之间的通信(fork、start、end、join)进行建模。而因果关系是具有传递性的,同样的happens-before 也具有传递性。

(二)程序状态模型

来给程序执行的过程建立一个模型:

  • 程序状态:
    程序的状态是由当前的所有线程的局部变量和所有线程的共享的全局变量的集合。特别的,每个线程的 pc 计数器也是局部变量的一部分。
  • 程序执行:
    程序执行的过程就是程序从初始状态经过一系列的事件,也就是 trace,程序的状态状态不断地变化的一个过程。


    程序执行模型

当一个事件的操作对象是局部变量的时候,取决于当前程序的状态,事件所在线程的局部变量到达了一个新的状态,而不会改变全局变量;而当一个时间的操作对象是全局变量 s 的时候,取决于当前程序的状态,s 和事件所在线程的局部状态会发生改变。

(三) Race 约束

基于之前的程序状
态模型和之前提出的程序合法性。

不同的程序状态会导致程序中某些直接判断语句if 和间接判断 (例如全局变量 x 作为数组的下标) 产生不一样的新的程序状态。所以,我们的目标就是探究所有可能的程序状态序列。提出了 Race 约束

定义:COP(a,b) 是不同线程中对于同一个全局变量的读-写对。

对于每一个存在 COP 的变量,对于该变量的每一个读事件R,定义以下约束:

  • 所有 happens-before 于 R 的读事件读到和之前一样的值
  • 读事件 R 读到和之前不一样的值。

以下面这个程序为例


TIM截图20191117141610.png

图中的每一个线程中的代码都会循环两次执行,给出了事件的编号,以及事件在 trace 中的表示方法,其中 L_i^j 中上标j表示的是第几次循环,而下标i给出的是在图中的行号(1-14)。

假设我们第一次运行中读写的状态如图:


这个图的含义为:第一个黑点表示 T2 线程中第八行第一次循环 if(x>0) 读到的值是 T2 线程第一次循环第六行写 x = 0 的值。第二个黑点表示T2 线程第二次循环中第八行 if(x>0) 读到的值是 T2 线程第二次循环第六行写 x = 0 的值。后面两个黑点表示 T3 线程第一次循环和第二次循环中第十一行 if(x>1) 都是读到的T2 线程第二次循环第六行写 x = 0 的值。同时,由于两次 T3 中 if(x>1) 的判断结

果都是false,因此没有对于 y 的读时间,y 的读写表为空。

接下来对于R_8^1,添加 Race 约束,使R_8^1读到一个与之前不同的值 1。并保证所有 happens-before 于这个事件的读事件(此处为空集合)都可以读到。运行的程序可得到以下的读写表。

不断读到一个新值,可以获得一颗树。


这颗树便覆盖了程序中所有可能的程序状态。

(四)示例的约束求解:

例子
  1. 锁互斥
    O_5 < O_7 ∨ O_9 < O_2
  2. Must Happens-Before 与线程内串行
    O_1 < O_2 < . . . < O5
    O_{14} < . . . < O_{16}
    O_6 < O_7 < . . . < O_{13}
  3. Race 约束(满足其中之一即可)
    使第八行的 y 读到 0

    使第十行的 x 读到 0

通过这一系列约束,我们可以通过 z3 来进行求解,得到新的事件顺序(这里满足了 Race 约束中的第二个):
O_1 O_6O_7O_8O_9O_2O_3O_4O_5O_{10} ....
转换到线程,我们也得到了一个调度序列前缀:
t1,t2,t2,t2,t2,t1,t1,t1,t1,t2...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容