JavaPathFinderの備忘録

名前しか知らなかったjavaPathFinderを数時間いじってみた。

ダウンロード、セットアップ

JavaPathFinderから最新版をダウンロード。解凍すれば問題なく動作する。

できること

Javaバイトコードを読み込んで、デッドロックや競合状態の有無などを検査する。
このPDFがわかりやすい。

最初期はJavaソースコードSPINのコードに変換するものだった。すぐにバイトコードを読み込み独自の検査ツールで検証をおこなうようになったが、マルチスレッドによる並行処理の問題点を検出するのが、おそらく最も適した使い方。

サンプル

複数スレッドで競合状態を起こしてみる。

public class test implements Runnable{
    static int shared_data = 0;

    public static void main(String args[]){
        int size = Integer.parseInt(args[0]);

        test mt = new test();
        Thread[] th = new Thread[size];

        for (int i = 0; i < size; i++) {
            th[i] = new Thread(mt);
            th[i].start();
        }
    }

    void add_one() {
        int tmp = shared_data;
        shared_data += 1;
        assert(tmp + 1 == shared_data);
    }

    public void run() {
        add_one();
    }
}

これをコンパイルしてjpfで実行する。

$javac test.java
$ time ../bin/jpf +classpath=. test 2

... 略 ...

====================================================== error #1
gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty
java.lang.AssertionError
        at test.add_one(test.java:20)
        at test.run(test.java:24)
====================================================== snapshot #1
thread index=2,name=Thread-1,status=RUNNING,this=java.lang.Thread@216,target=test@289,priority=5,lockCount=0,suspendCount=0
  call stack:
        at test.add_one(test.java:20)
        at test.run(test.java:24)
====================================================== results
error #1: gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty "java.lang.AssertionError  at test.add_one(test..."
====================================================== statistics
elapsed time:       0:00:01
states:             new=18, visited=3, backtracked=8, end=4
search:             maxDepth=13, constraints=0
choice generators:  thread=17, data=0
heap:               gc=22, new=309, free=66
instructions:       3367
max memory:         6MB
loaded code:        classes=76, methods=1063
====================================================== search finished: 11/03/06 13:01

real    0m1.475s
user    0m1.197s
sys     0m0.052s

ということで、2つのスレッドが同時に関数add_one()を呼び出すと、変数shared_dataも書き換えてしまうので、shared_dataの値がassert()内で期待する値にならないことを検出した。

プログラムを修正し、関数add_one()にsynchronized宣言をつける。これで関数add_one()は同時に実行されることがなくなるので、assert文も成立するはずである。

......
    synchronized void add_one() {
       int tmp = shared_data;
       shared_data += 1;
        assert(tmp + 1 == shared_data);
    }
......

これをコンパイルしてjpfで実行する。

$javac test.java
$ time ../bin/jpf +classpath=. test 2
JavaPathfinder v5.x - (C) RIACS/NASA Ames Research Center
====================================================== system under test
application: test.java
arguments:   2 
====================================================== search started: 11/03/06 13:06
====================================================== results
no errors detected
====================================================== statistics
elapsed time:       0:00:01
states:             new=73, visited=55, backtracked=127, end=15
search:             maxDepth=15, constraints=0
choice generators:  thread=73, data=0
heap:               gc=122, new=336, free=479
instructions:       4878
max memory:         6MB
loaded code:        classes=73, methods=1033

====================================================== search finished: 11/03/06 13:06

real    0m1.761s
user    0m1.248s
sys     0m0.071s

確かに競合状態がなくなった。

実行時間

JavaPathFinderは実行可能なすべてのパスを試すので、実行に非常に時間がかかる。
例えば、上の例ではスレッドが2つだったが、3つ、4つ、、、と増えるにしたがって実行可能なパスも組み合わせの数のように増えていく。

$ time ../bin/jpf +classpath=. test 3
real    0m2.037s
user    0m1.366s
sys     0m0.050s

$ time ../bin/jpf +classpath=. test 4
real    0m4.534s
user    0m2.456s
sys     0m0.053s

$ time ../bin/jpf +classpath=. test 5
real    0m6.658s
user    0m5.331s
sys     0m0.078s

$ time ../bin/jpf +classpath=. test 6
real    0m21.848s
user    0m16.953s
sys     0m0.117s

$ time ../bin/jpf +classpath=. test 7
real    1m21.716s
user    1m8.190s
sys     0m0.263s

大規模なシステムへの適用は難しいだろうが*1、適度な規模のシステムならば非常に役にたつと思う。


JavaPathFinderの検査方式である全パターン検査は、モデル検証の世界では最初期の手法で、今やもっとよい方式も出ているらしい。ちょっと最近のモデル検査ツールを調べてみたい。

その他

設定ファイルなど

JavaPathFinderの検証は"*.jpf"ファイルなどで制御できる。ファイルの書き方はここにある*2

サーバプログラム

実は、サーバプログラムjgtmをチェックしたかったのだが、jpfに載せると、クライアントからの入力待ちになってエラーを検出し終了してしまう。念のためクライアントを接続しようとしても接続拒否になる。

$ time ../bin/jpf +classpath=./jgtm-0.3.3.jar jgtm -p 6666 -D . -x 100

... 略 ...

====================================================== error #1
gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty
java.lang.UnsatisfiedLinkError: java.net.PlainSocketImpl.initProto()V (no peer)
        at java.net.PlainSocketImpl.<clinit>(PlainSocketImpl.java:84)
        at java.net.ServerSocket.setImpl(ServerSocket.java:236)
        at java.net.ServerSocket.<init>(ServerSocket.java:178)
        at java.net.ServerSocket.<init>(ServerSocket.java:97)
        at jgtm.main(jgtm.java:141)
====================================================== snapshot #1
thread index=0,name=main,status=RUNNING,this=java.lang.Thread@0,target=null,priority=5,lockCount=0,suspendCount=0
  owned locks:java.lang.Class@20946
  call stack:
        at java.net.PlainSocketImpl.<clinit>(PlainSocketImpl.java:84)
        at java.net.ServerSocket.setImpl(ServerSocket.java:236)
        at java.net.ServerSocket.<init>(ServerSocket.java:178)
        at java.net.ServerSocket.<init>(ServerSocket.java:97)
        at jgtm.main(jgtm.java:141)
====================================================== results
error #1: gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty "java.lang.UnsatisfiedLinkError: java.net.PlainSock..."
====================================================== statistics
elapsed time:       0:00:18
states:             new=0, visited=1, backtracked=0, end=0
search:             maxDepth=0, constraints=0
choice generators:  thread=1, data=0
heap:               gc=40, new=20976, free=26
instructions:       223359
max memory:         17MB
loaded code:        classes=113, methods=1729
====================================================== search finished: 11/03/06 14:50

原理からすると、通信するプログラムのチェックには使えないのが当たり前だ。
WEBをさまよってみたら、これを見つけた。

現実的なシステムのプログラムは、通常GUIや通信機能のための
標準ライブラリを使用しているが、この場合、そのままではJavaPathFinder を
使用して検証することができないため、... 略 ....
実際のシステム開発に適用するのが難しい。

現実的なシステムのプログラムの検証に必要な、複数プロセスの
プログラムを1 プロセスのものに変換するノウハウ、また抽象化により
ライブラリを使用しないプログラムに変換するノウハウを.... 略 ....

うまく使えるように変換しろということか。

*1:富士通のプレスリリースによれば、数々の工夫をこらして並列処理で数万ステップだそうである。

*2:まだ把握できていない