JMCR 簡(jiǎn)介

本系列記錄了我研究JMCR的學(xué)習(xí)筆記和一些自己的想法偿曙,JMCR 是一個(gè)多線程測(cè)試工具的實(shí)現(xiàn),其源代碼地址為:鏈接屋讶。歡迎大家一起探討交流~
系列文章:
1. JMCR 簡(jiǎn)介
2. JMCR 字節(jié)碼插樁(一)
3. JMCR 字節(jié)碼插樁(二)
4. JMCR 約束求解原理
5. JMCR 線程調(diào)度

簡(jiǎn)述

本文介紹了多線程測(cè)試相關(guān)概念辞友、JMCR 的簡(jiǎn)介和 JMCR 的使用。

一溃睹、多線程測(cè)試

多線程程序的錯(cuò)誤往往是不易被發(fā)現(xiàn)的而账。而每當(dāng)我們想復(fù)現(xiàn)這個(gè)錯(cuò)誤的時(shí)候,我們重新運(yùn)行這個(gè)程序或者使用 JUnit 框架進(jìn)行測(cè)試因篇,這個(gè)錯(cuò)誤往往又會(huì)消失不見(jiàn)(或者概率性出現(xiàn))泞辐。因此我們需要一個(gè)工具來(lái)幫我們排查多線程程序中的錯(cuò)誤笔横。

多線程程序測(cè)試區(qū)別于單線程程序測(cè)試的地方是,如果我們將一個(gè)程序的運(yùn)行過(guò)程抽象成操作的序列的話咐吼,單線程程序中的每個(gè)操作(包括操作的類型吹缔、操作的值)以及操作的順序是確定的,而多線程程序由于線程調(diào)度的不確定性以及可見(jiàn)性等問(wèn)題锯茄,其操作序列幾乎總是在變化的厢塘。例如,對(duì)于單線程程序1來(lái)講肌幽,其操作序列總是 [(Read, a, 0), (Write, b, 2), (Read, b, 2)]

單線程程序1:

public class Test {
    static int a = 0;
    static int b = 1;
    public static void main(String []args){
        if(a == 0){ //Read  a 0
            b = 2;   //Write b 2   
        }  else {
            System.err.println("Error! Read a != 0");
            System.exit(-1);
        }
        System.out.println(b); // Read b 2
    }
}

但是對(duì)于多線程而言晚碾,就沒(méi)有這么簡(jiǎn)單了,對(duì)于如下的程序喂急,其操作序列就是不確定的格嘁,并且有一定的概率觸發(fā)錯(cuò)誤 case。

多線程程序1:

public class Test {
    static int a = 0;
    static int b = 1;
    public static void main(String []args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Operation 1
                a = 1; //Write a 1 
            }
        }).start();
        Thread.sleep(10);
        //Operation 2
        if(a == 0){ //Read  a ?
            //Operation 3
            b = 2;   //Write b 2
        } else {
            System.err.println("Error! Read a != 0");
            System.exit(-1);
        }
        //Operation 4
        System.out.println(b); // Read b 2
    }
}

上述代碼中 Operation 1 和 Operation 2 兩個(gè)操作由于調(diào)度的不確定性廊移,其先后順序是無(wú)法確定的糕簿,當(dāng) Operation 2 (Read, a, 0) 發(fā)生在 Operation 1 (Write, a, 1) 之前時(shí),if 語(yǔ)句進(jìn)入了第一個(gè)分支狡孔,執(zhí)行 (Write, b 2 )懂诗,再執(zhí)行 Operation 3 和 Operation 4;而當(dāng) Operation 1 (Write, a, 1) 發(fā)生在 Operation 2 (Read, a, 1) 之前時(shí)苗膝,if 語(yǔ)句進(jìn)入了第二個(gè)分支殃恒,出發(fā)了錯(cuò)誤,程序輸出錯(cuò)誤信息荚醒,調(diào)用System.exit()芋类,此時(shí)程序崩潰,操作序列中也不會(huì)出現(xiàn) Operation 4界阁。

從上面這個(gè)例子可以看出侯繁,如果要測(cè)試多線程的程序,我們需要“多跑幾遍”,將程序中所有可能的操作序列復(fù)現(xiàn)一遍,看看是否可以觸發(fā)錯(cuò)誤失尖,這樣才能保證我們的測(cè)試不會(huì)漏過(guò)每一種可能。隨之而來(lái)的問(wèn)題是咕别,如何計(jì)算出來(lái)所有可能的操作集合序列?對(duì)于每一次執(zhí)行写穴,怎么驅(qū)動(dòng)程序按照制定操作序列進(jìn)行調(diào)度惰拱,又如對(duì)于多線程程序爆炸性增長(zhǎng)的探索空間進(jìn)行剪枝呢?

How can we do that in Java啊送?—— By JMCR

二偿短、JMCR簡(jiǎn)介

Maximal-Causality-Reduction (MCR) Java version
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.

JMCR 是一個(gè)使用高效約簡(jiǎn)算法的無(wú)狀態(tài)的模型測(cè)試工具欣孤。它通過(guò)記錄程序運(yùn)行時(shí)的事件序列并在記錄上構(gòu)建新序列的約束,系統(tǒng)的探索了程序的整個(gè)狀態(tài)空間昔逗。JMCR捕獲寫操作和讀操作的值以去除冗余探索降传。 通過(guò)強(qiáng)制執(zhí)行至少一個(gè)讀操作返回不同的值,它會(huì)生成一個(gè)新的調(diào)度序列勾怒,該調(diào)度序列將驅(qū)動(dòng)程序達(dá)到新的狀態(tài)婆排。

從這份 JMCR 在 Readme 中的簡(jiǎn)短描述,以上提出的幾個(gè)問(wèn)題已經(jīng)初見(jiàn)端倪笔链,同時(shí)也了解到了 JMCR 主要工作可以分為以下三個(gè)部分:

  • 程序運(yùn)行時(shí)的操作序列 (trace) 信息捕獲
  • 通過(guò)一個(gè)現(xiàn)有的操作序列生成一個(gè)新的操作序列的約束段只,并通過(guò)約束生成新的操作序列信息(線程調(diào)度序列前綴)
  • 使程序按照新的(線程調(diào)度)序列執(zhí)行循環(huán)直至不再產(chǎn)生新的狀態(tài)空間序列。

這些內(nèi)容都會(huì)在隨后的文章中有詳細(xì)講解卡乾,此處不再展開(kāi)翼悴。總而言之幔妨,JMCR可以幫我們探尋一個(gè)多線程程序內(nèi)所有的狀態(tài)空間,作為我們多線程測(cè)試的工具谍椅。

三误堡、JMCR 的使用

首先我們需要去 GitHub 上將 JMCR 的源代碼拉到本地,使用 Intelij IDEA 導(dǎo)入功能打開(kāi)這個(gè)項(xiàng)目

使用 Import Project 打開(kāi)項(xiàng)目

隨后點(diǎn)擊項(xiàng)目根目錄下的pom.xml進(jìn)行導(dǎo)入雏吭。
pom.xml

隨后一路確認(rèn)锁施,進(jìn)入項(xiàng)目后,將根目錄下的 default.properties 文件拷貝到 mcr-test 模塊下的 \target\classes 文件夾內(nèi)杖们。

文件

隨后點(diǎn)擊 EditConfigure 配置參數(shù)悉抵,為 JUnit 項(xiàng)目添加 vm參數(shù) -ea -javaagent:../libs/agent.jar

vm 參數(shù)配置

運(yùn)行 mcr-test 模塊下 src 中的 TestDeadLock.java 測(cè)試可以得到以下輸出:


或者我們可以自己寫自己的多線程測(cè)試樣例:

  1. 在測(cè)試類上加上注解 @RunWith(JUnit4MCRRunner.class)
  2. 像 JUnit 一樣編寫測(cè)試樣例

代碼如下:

package edu.tamu.aser.tests;

import edu.tamu.aser.reex.JUnit4MCRRunner;
import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith(JUnit4MCRRunner.class)
public class MyTest {
    static int a = 0;
    static int b = 1;

    @Test
    public void test() throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                a = 1;
                System.out.println("b : " + b);
            }
        }).start();
        Thread.sleep(10);
        if(a == 0){ //Read  a 0
            b = 2;   //Write b 2
        } else {
            System.err.println("Error! Read a != 0");
            System.exit(-1);
        }
        System.out.println(b); // Read b 2
    }
}

運(yùn)行結(jié)果:


TIM截圖20191106002324.png

可以看到,程序執(zhí)行了兩次摘完,覆蓋到了我們提到的兩種情況姥饰,使用成功~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孝治,隨后出現(xiàn)的幾起案子列粪,更是在濱河造成了極大的恐慌,老刑警劉巖谈飒,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岂座,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杭措,警方通過(guò)查閱死者的電腦和手機(jī)费什,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)手素,“玉大人鸳址,你說(shuō)我怎么就攤上這事赘那。” “怎么了氯质?”我有些...
    開(kāi)封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵募舟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我闻察,道長(zhǎng)拱礁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任辕漂,我火速辦了婚禮呢灶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钉嘹。我一直安慰自己鸯乃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布跋涣。 她就那樣靜靜地躺著缨睡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陈辱。 梳的紋絲不亂的頭發(fā)上奖年,一...
    開(kāi)封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音沛贪,去河邊找鬼陋守。 笑死,一個(gè)胖子當(dāng)著我的面吹牛利赋,可吹牛的內(nèi)容都是我干的水评。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼媚送,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼中燥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起季希,我...
    開(kāi)封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤褪那,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后式塌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體博敬,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年峰尝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偏窝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祭往,靈堂內(nèi)的尸體忽然破棺而出伦意,到底是詐尸還是另有隱情,我是刑警寧澤硼补,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布驮肉,位于F島的核電站,受9級(jí)特大地震影響已骇,放射性物質(zhì)發(fā)生泄漏离钝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一褪储、第九天 我趴在偏房一處隱蔽的房頂上張望卵渴。 院中可真熱鬧,春花似錦鲤竹、人聲如沸浪读。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)碘橘。三九已至,卻和暖如春揩尸,著一層夾襖步出監(jiān)牢的瞬間蛹屿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工岩榆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坟瓢。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓勇边,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親折联。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粒褒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容