Login | Register
My pages Projects Community openCollabNet

jbRAID uSER MANUAL

vERSION 1.0

 

TABLE OF CONTENTS

 

·         What is JBraid?

·         How to use JBraid

·         Types of problems found by JBraid

·         Tips

·         Limitations and know issues

·         Command line arguments


JBraid is a runtime analysis tool that finds multi-threading bugs in Java programs. JBraid monitors memory accesses and thread synchronizations in a running program, and detects two types of concurrency bugs:

·         Race conditions, where the output and/or result of the program depends on the sequence or timing of events;

·         Deadlocks, where two or more competing actions are waiting for the other to finish, and thus neither ever does.

Concurrency bugs are often non-deterministic because their occurrence depends on the way threads are scheduled, which varies from an execution to another. However JBraid is deterministic and only one run is necessary to find all potential problems. JBraid does not require the bugs to actually occur during this run; by mixing and matching memory accesses and thread synchronizations, JBraid can anticipate problems that could occur in all possible executions.

JBraid consists of two parts:

·         The listener (jbraidlist.jar), a standalone application that monitors the running test program.

·         A runtime library (jbraidrt.jar), to be linked with the test program, which contains an API that the test program invokes to connect to the listener and start the monitoring.


The steps for using JBraid are:

1.     Write a test program that contains the code where you want to find concurrency bugs.

2.     Start the JBraid listener, which then waits for the test program to start.

3.     Run the test program, which then connects to the listener and notifies execution events that are analyzed by JBraid.

4.     Read the JBraid report generated by the listener, and which lists the problems found by JBraid in the analyzed code.

These steps are explained in this section.

Writing a test program


A JBraid test program is a Java application that contains one or several JBraid test series of the form:

Braid.beginSeries(seriesName);

 

Braid.beginPseudoThread(firstThreadName);

<first piece of code to be analyzed>

Braid.endPseudoThread();

 

Braid. beginPseudoThread(secondThreadName);

<second piece of code to be analyzed>

Braid.endPseudoThread();

 

Braid.endSeries();

 

The pieces of code enclosed between the startPseudoThread and endPseudoThread pairs, although executed sequentially in the test program, are analyzed by JBraid as if they were executed concurrently – hence the name pseudo thread. During the execution of pseudo threads, JBraid records relevant events such as field accesses. When the series is finished, events that occurred in the pseudo-threads are analyzed in order to detect patterns revealing potential concurrency bugs.

It is recommended to compile the test program in debug mode, so JBraid can give precise source code location information when signaling problems.

Here is an example of a complete test program. This program defines a singleton class called A and checks if the method A.getA can be safely invoked by two concurrent threads:

 

1

package jbraidtest;

2

 

3

import net.jbraid.runtime.Braid;

4

 

5

public class SingletonTest {

6

 

7

   static class A {

8

 

9

      static A singleton = null;

10

 

11

      static A getA() {

12

         if (singleton == null) {

13

            singleton = new A();

14

         }

15

         return singleton;

16

      }

17

 

18

   }

19

 

20

   public static void main(String[] args) {

21

 

22

      Braid.beginSeries("Singleton");

23

 

24

      Braid.beginPseudoThread("Thread 1");

25

      A.getA();

26

      Braid.endPseudoThread();

27

 

28

      Braid.beginPseudoThread("Thread 2");

29

      A.getA();

30

      Braid.endPseudoThread();

31

 

32

      Braid.endSeries();

33

 

34

   }

35

 

36

}

Singleton test program


A test program can contain several series, and a series can contain more than two pseudo-threads. In this case, JBraid will mix and match all the pseudo-threads pairwise.

Starting the JBraid listener


To start the JBraid listener, run the jbraidlist.jar file. Memorizing and analyzing the execution events is very memory consuming, so you should run JBraid with a fair amount of memory, for example 512 Mbytes:

java -Xmx512M -jar jbraidlist.jar

 

Once the listener is started, it waits for the test program to connect.

The listener takes a number of command line options. Some of them are described in this manual. For a complete list and description, please see the reference documentation.

Running the test program


The test program must be run with an option that will make it connect to the listener. The class path must include jbraidrt.jar, which contains the runtime part of JBraid:

java -agentlib:jdwp=transport=dt_shmem,address=javadebug -cp jbraidrt.jar jbraidtest.SingletonTest

 

Once the test program is connected to the listener, it starts sending execution events that are analyzed by JBraid. When a series ends, JBraid generates a report for this series. When the test program ends, the JBraid listener exits unless it was started with the –loop command line option.

Reading the JBraid report


By default, reports are displayed on the standard output. You can redirect them to a file using the -report-file option.

Here is the report generated for the Singleton test program:

 

=================================================

Series 'Singleton'

=================================================

Braiding pseudo-threads 'Thread 1' and 'Thread 2'

Field accesses in 'Thread 1': 1052

Field accesses in 'Thread 2': 2

Potential race conditions: 1

Potential synchronization deadlocks: 0

----------------------------------

*** PRC for static field jbraidtest.SingletonTest$A.singleton

    * Location in 'Thread 1' (write) : jbraidtest.SingletonTest$A.getA()+13 (SingletonTest.java:13)

           at jbraidtest.SingletonTest.main(java.lang.String[])+10 (SingletonTest.java:25)

      Location in 'Thread 2' (read) : jbraidtest.SingletonTest$A.getA()+16 (SingletonTest.java:15)

           at jbraidtest.SingletonTest.main(java.lang.String[])+22 (SingletonTest.java:29)

      Accessed objects: 1

      Occurrences: 1

      Proximity: 0.00

    * Location in 'Thread 1' (write) : jbraidtest.SingletonTest$A.getA()+13 (SingletonTest.java:13)

           at jbraidtest.SingletonTest.main(java.lang.String[])+10 (SingletonTest.java:25)

      Location in 'Thread 2' (read) : jbraidtest.SingletonTest$A.getA()+0 (SingletonTest.java:12)

           at jbraidtest.SingletonTest.main(java.lang.String[])+22 (SingletonTest.java:29)

      Accessed objects: 1

      Occurrences: 1

      Proximity: 0.00

Report for the singleton test program execution

 

The header of the report gives general information about the execution of the series. JBraid was notified of 1004 accesses to object fields (either static or dynamic) during the execution of the first pseudo-thread, and of 2 during the execution of the second. The reason for this is that the class A was referenced for the first time during the execution of the first pseudo-thread, and was automatically loaded by the Java class loader at this point, which generated a lot of field accesses. During the execution of the second thread, the class was already loaded and JBraid was only notified of the 2 field accesses corresponding to the execution of the getA method.

 

The body of the report lists the problems that JBraid found in the program. Here, it found one PRC (Potential Race Condition). The next section explains which types of problems JBraid can find, and how it reports them.


JBraid finds two types of problems:

·         Potential race conditions;

·         Potential synchronization deadlocks.

Potential race conditions

What is a race condition?


To quote Wikipedia, a race condition is “A flaw in a system or process whereby the output and/or result of the process is unexpectedly and critically dependent on the sequence or timing of other events.” (http://en.wikipedia.org/wiki/Race_condition).

Race conditions occur in concurrent Java programs as a result of the non-deterministic way in which threads are interlaced. However, a non-deterministic behavior is not always a race condition: it only is one if it affects the program in a way that is not the intent of the programmer. This intent cannot be determined automatically by any analysis tool. For this reason, JBraid doesn’t claim to find actual race conditions, but Potential Race Conditions (PRCs). JBraid uses heuristics to detect non-deterministic behaviors that are the most likely to be actual race conditions, but heuristics are necessarily incomplete and JBraid can report on false positives, or, on the contrary, fail to report actual bugs.

The lines 7 to 18 in the Singleton test program give a common example of a race condition due to an unsafe implementation of the singleton design pattern. This implementation is unsafe because it critically depends on the order of execution in concurrent threads. If the getA method is called for the first time in two concurrent threads, then both threads might pass the test (singleton == null) and enter the body of the if statement at about the same time. This would result in creating two instances of class A, which is not the intent of the program. The common fix is to make the getA method synchronized.

How JBraid reports Potential Race Conditions


For each PRC, JBraid reports:

·         The field (either static or not) that was subject to non-deterministic access during the execution of the series;

·         The locations where the concurrent accesses have occurred in the Java test program.

For example, the report for the singleton test program contains one PRC for the singleton field of the SingletonTest$A inner class:

 

*** PRC for static field jbraidtest.SingletonTest$A.singleton

 

Concurrent accesses occurred in two cases. The first case is:

 

    * Location in 'Thread 1' (write) : jbraidtest.SingletonTest$A.getA()+13 (SingletonTest.java:13)

           at jbraidtest.SingletonTest.main(java.lang.String[])+10 (SingletonTest.java:25)

      Location in 'Thread 2' (read) : jbraidtest.SingletonTest$A.getA()+16 (SingletonTest.java:15)

           at jbraidtest.SingletonTest.main(java.lang.String[])+22 (SingletonTest.java:29)

      Accessed objects: 1

      Occurrences: 1

      Proximity: 0.00

 

The second case is:

 

    * Location in 'Thread 1' (write) : jbraidtest.SingletonTest$A.getA()+13 (SingletonTest.java:13)

           at jbraidtest.SingletonTest.main(java.lang.String[])+10 (SingletonTest.java:25)

      Location in 'Thread 2' (read) : jbraidtest.SingletonTest$A.getA()+0 (SingletonTest.java:12)

           at jbraidtest.SingletonTest.main(java.lang.String[])+22 (SingletonTest.java:29)

      Accessed objects: 1

      Occurrences: 1

      Proximity: 0.00

For each case, JBraid gives two locations in the two pseudo-threads where the field is accessed and whose execution order would impact the result of the program. For each location, JBraid also gives the call stack at the access point.

 

For instance, in the first case, JBraid says that the order in which line 13 and line 15 would be executed in two concurrent threads would change the result of the program. Indeed, assume that the first thread is just about to execute line 13, and the second thread is just about to execute line 15. If line 13 is executed after line 15, then the second thread will return the singleton. If line 13 is executed before line 15, then the second thread will return the newly created object.

 

The second case of concurrent access follows a similar logic.

 

If you synchronize the getA method and rerun JBraid, it will not report any more PRCs, because it will see that the accesses to the fields are synchronized.


Potential synchronization deadlocks

What is a synchronization deadlock?


To quote Wikipedia, a deadlock is “a situation wherein two or more competing actions are waiting for the other to finish, and thus neither ever does.” (http://en.wikipedia.org/wiki/Deadlock).

Deadlocks occur in concurrent Java programs when two threads concurrently wait for each other to release a lock on an object. This typically happens when two threads acquire locks on the same two objects in a reverse order using the synchronized construct. JBraid detects this pattern, called Potential Synchronization Deadlock (PSD).

The program below illustrates a common case of PSD:

 

1

package jbraidtest;

2

 

3

import net.jbraid.runtime.Braid;

4

 

5

public class ModelViewTest {

6

 

7

   static class Model {

8

 

9

      View myView;

10

 

11

      int data = 0;

12

 

13

      synchronized void updateModel(int newData) {

14

         setData(newData);

15

         myView.refresh();

16

      }

17

 

18

      synchronized void setData(int newData) {        

19

         data = newData;

20

      }

21

 

22

      int getData() {

23

         return data;

24

      }

25

 

26

   }

27

 

28

   static class View {

29

 

30

      Model myModel;

31

 

32

      synchronized void refresh() {

33

         myModel.getData(); // Do something with the data

34

      }

35

 

36

      synchronized void updateView(int newData) {

37

         myModel.setData(newData);

38

      }

39

 

40

   }

41

 

42

   public static void main(String[] args) {

43

 

44

      Model model = new Model();

45

      View view = new View();

46

 

47

      model.myView = view;

48

      view.myModel = model;

49

 

50

      Braid.beginSeries("Model View");

51

 

52

      Braid.beginPseudoThread("Model thread");

53

      model.updateModel(1);

54

      Braid.endPseudoThread();

55

 

56

      Braid.beginPseudoThread("View thread");

57

      view.updateView(2);

58

      Braid.endPseudoThread();

59

 

60

      Braid.endSeries();

61

 

62

   }

63

 

64

}

Model/View test program

 

The Model and View classes implement a model/view pattern, where an update of the model triggers a refresh of the view, and where an update of the view triggers a modification of the model. The program tests the concurrent execution of an update of the model and of an update of the view. This can lead to a deadlock because the Model and View instances are synchronized in a reverse order, as we can see by running JBraid.

How JBraid reports Potential Synchronization Deadlocks


When you run the Model/View example, JBraid generates the following report:

=================================================

Series 'Model View'

=================================================

Braiding pseudo-threads 'Model thread' and 'View thread'

Field accesses in 'Model thread': 4

Field accesses in 'View thread': 2

Potential race conditions: 0

Potential synchronization deadlocks: 1

----------------------------------

*** PSD with obj1 (jbraidtest.ModelViewTest$View) and obj2 (jbraidtest.ModelViewTest$Model)

      Call stack in 'Model thread':

           -> synchronized(obj1) in jbraidtest.ModelViewTest$View.refresh()

           at jbraidtest.ModelViewTest$Model.updateModel(int)+9 (ModelViewTest.java:15)

           -> synchronized(obj2) in jbraidtest.ModelViewTest$Model.updateModel(int)

           at jbraidtest.ModelViewTest.main(java.lang.String[])+38 (ModelViewTest.java:53)

      Call stack in 'View thread':

           -> synchronized(obj2) in jbraidtest.ModelViewTest$Model.setData(int)

           at jbraidtest.ModelViewTest$View.updateView(int)+5 (ModelViewTest.java:37)

           -> synchronized(obj1) in jbraidtest.ModelViewTest$View.updateView(int)

           at jbraidtest.ModelViewTest.main(java.lang.String[])+51 (ModelViewTest.java:57)

 

 

Report for the model/view test program execution


This report signals a PSD due to the reverse locking of an instance of View and of an instance of Model. For each pseudo-thread, the report gives the call stack showing where these objects were locked.

In the Model thread, the Model instance (obj2) was first locked in the updateModel method called from main. Then updateModel called the refresh method, where the View instance (obj1) was locked.

In the View thread, the View instance (obj1) was first locked in the updateView method called from main. The updateView called the setData method, where the Model instance (obj2) was locked.

Due to JDI limitations, JBraid can only give the name of the method where objects are locked, not the line number of the synchronized construct.

 

Tips


Improving the performance


Executing a test program with JBraid can be very time and memory consuming:

·         The test program is slowed down by the monitoring. A program normally executing in a matter of seconds can take long minutes or even hours under JBraid supervision.

·         The amount of memory required by the JBraid listener to capture and analyze the execution event can be very high, which provoke out of memory exceptions.

There are several best practices and monitoring options that you can use to improve the performance.

Using small test programs


The quality of an analysis does not depend on the amount of data processed by a test, or by the length of its execution, but by the paths that are covered in your code. In general, executing a line of code once gives as much information to JBraid as executing it many times with different data. Therefore we recommend that you test your code with the smallest possible (and yet significant) input data. The best way to test many execution paths in your program is to have a set of bite-size tests, each covering one case, rather than one large and comprehensive test.

Another good practice is to put initialization code outside of series. JBraid only activates test program monitoring during the execution of series. Code outside of series is not monitored and is executed with normal performance. Therefore, only put the code that you want to test in series, and keep initialization code outside. A non-obvious example of this practice is class loading: if you don’t want the static initialization of a class to be part of your test, put a reference to the class before the execution of the series, to force its loading by the Java class loader.

Limiting monitored fields


By default, JBraid monitors all the fields accessed in the test program. The –watch-only and –watch-not server command line options allow you to restrict monitoring to the fields of specific classes. This can dramatically improve the performance of the test program, but can also make you miss some crucial PRCs.

For example, with the following command line option, the JBraid server only monitors the fields of the classes in the mypackage package and its sub-packages:

java -Xmx512M -jar jbraidlist.jar -watch-only mypackage

 

Note that this will make JBraid miss PRCs occurring in other packages as a result of an improper invocation from your code. For example, an improper use of the java.util.HashMap class in a concurrent context will not be detected. In order to avoid this specific problem, you can add java.util to the watched packages:

java -Xmx512M -jar jbraidlist.jar -watch-only mypackage java.util

 

For a more complete description of the –watch-only and –watch-not command line options, as well as other command line options mentioned in this guide, see Command line arguments.

Limiting stack trace memorization


Stack traces are provided in PRC reports to help understand the context in which PRCs occur. By default, JBraid memorizes stack traces at all the field access points during the execution of the test program, because it cannot anticipate which ones will be necessary for the report. This is very memory consuming. The –getstack-only option allows you to specify a limited list of locations where JBraid should memorize stack traces.

For example, with the following command line option, the JBraid server only memorizes stack traces for fields accessed during the execution of the methods myMethod1 and myMethod2 of the mypackage.MyClass class:

java -Xmx512M -jar jbraidlist.jar  \

-getstack-only mypackage.MyClass.myMethod1 mypackage.MyClass.myMethod2

 

When analyzing a large program, you can use the -getstack-only option with the following scenario:

·         Run JBraid with the -getstack-only option with no arguments, so JBraid doesn’t memorize any stack traces;

·         Re-run the test using –getstack-only with the PRC locations signaled in the previously generated report.

Embedded usage


Although JBraid works best with small tests, it is not restricted to testing small applications. You can test parts of a large application by adding series of pseudo-threads at the right places in the code. Outside of series, the application will run with normal performance. Also note that, if you don’t start the application with the –agentlib option, the invocation of the net.jbraid.runtime.Braid methods will have no effect. This allows you to keep your series in your code without hampering the normal execution of the application.

Use in automated tests


The Braid class methods getNumberOfErrors and getReport can be used to retrieve the result of the execution of a series in the test program. This allows writing automated tests that check that the analysis yields an expected result. See also the command line option -no-details.

Cascading PRCs


In a JBraid report, several PRCs can be rooted to the same problem. In general, you should start by fixing the PRCs that are the simplest to understand, and then rerun the test. This might eliminate several of the previously reported PRCs, and save you time.


No monitoring of array access


Due to a limitation of JDI (the Java Debugging Interface), JBraid doesn’t monitor array accesses. It will not signal race conditions occurring when accessing array elements.


Unawareness of concurrency utilities


JBraid only recognizes the synchronized construct as a way of protecting critical sections, and is not aware of the facilities provided by the java.util.concurrent package or by the thread APIs.

For instance, if you use the Lock.lock and Lock.unlock methods instead of the synchronized construct to protect critical sections, JBraid will not know that and might signal false potential race conditions or fail to signal potential synchronization deadlocks.

Similarly, if you use ConcurrentHashMap, which implement thread-safe hash maps without using any locking mechanism, JBraid will signal potential race conditions occurring in the code of this class. As workaround, you can use the option -watch-not java.util.concurrent to prevent JBraid from monitoring accesses in ConcurrentHashMap and similar classes.


Unawareness of nested synchronized blocks


Due to a limitation of JDI, JBraid cannot take synchronized blocks that are directly nested in the same method into account when detecting potential synchronization deadlocks. For example, it will not detect a problem with the following code:

Pseudo-thread 1:

 

synchronized (obj1) {

   synchronized (obj2) {

      …

   }

}

 

Pseudo-thread 2:

 

synchronized (obj2) {

   synchronized (obj1) {

      …

   }

}

In other words, there must be at least one method call in the call stack between two synchronized blocks for JBraid to detect that they could participate to a PSD, like in the following code:

 

Pseudo-thread 1:

 


         synchronized (obj1) {

      methodA();

   }

 

void methodA() {
         synchronized (obj2) {

      …

   }

}

 

Pseudo-thread 2:

 


         synchronized (obj2) {

      methodB();

   }

 

void methodB() {
         synchronized (obj1) {

      …

   }

}


Need for a field access for PSD detection


Due to a limitation of JDI, JBraid only looks up the synchronized object stack at points where a field is accessed. A PSD that occurs in code without any field accesses will not be detected.


Only one server per machine


You can only run one JBraid server per machine at the same time. This can be annoying when using a shared test machine.


Random JDWP error message


Sometimes, the test program displays the following error message when it exits:

ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2

JDWP exit error AGENT_ERROR_NO_JNI_ENV(183):  [../../../src/share/back/util.c:820]

 

This error message is harmless. This is a know JDI problem.

 

Command line arguments

 


Test program

 

The test program must be run with the following JVM option:

java -agentlib:jdwp=transport=dt_shmem,address=javadebug other_arguments

 

This option tells the VM to execute in debug mode, which is necessary to connect to the listener.



Listener


The JBraid listener is launched using the jbraidlist.jar file provided in the JBraid distribution. It is recommended to increase the memory size using the -Xmx option:

java -Xmx512M -jar jbraidlist.jar


The listener command line options are:

-report-file file

Writes the report in the specified file. By default, the listener writes the report to the standard output.

 

-watch-only pattern*

Specifies a list of patterns for the classes whose fields must be watched. By default, the listener watches the field of all the classes. The -watch-only option can be used to restrict the watched classes to those matching specific patterns.

If a pattern starts with a '/' (slash), it is interpreted as a regular expression written in the java.util.regex syntax. This regular expression is matched with the class's fully qualified name. If the pattern does not start with a '/', it is taken as a class prefix to be matched with the beginning of the class's fully qualified name.

For example, the following option specifies that the listener should watch the classes in the java.util package and its sub packages, and the classes of the mypackage package and sub packages whose name finishes with Impl:

-watch-only java.util /mypackage\..*Impl$

Note that passing -watch-only with no pattern arguments will prevent JBraid from watching any classes.

 

-watch-not pattern*

Specifies a list of patterns for classes to be excluded from watching. See the -watch-only option for a description of patterns.

For example, the following option specifies that the listener should not watch fields in the java.lang and mypackage packages, and their sub packages:

-watch-not java.lang mypackage

This option takes precedence over the -watch-only option.

 

-getstack-only pattern*

Specifies patterns of locations in the target VM where call stacks should be memorized during the execution. By default, the listener memorizes all call stacks.

The patterns specified with -getstack-only are matched with the PRC locations as reported in the PRC report, which are of the form:

fullyQualifiedMethodName(signature)+bytecodeIndex

For example, the following option specifices that the listener should memorize the call stack for all the field accesses performed in themypackage.MyClass.myMethod method:

 

-getstack-only mypackage.MyClass.myMethod

 

Note that passing -getstack-only with no pattern arguments will prevent JBraid from memorizing any call stacks.

 

-dump-trace

Tells the listener to write the traces collected during the execution of the test program in a file whose name is displayed in the listener log. These traces contain all the field accesses performed during the execution of the pseudo-threads. In some cases this can help understanding PRCs.

 

-loop

By default, the listener exits when the test program exits. With this option, it waits for new connections.

 

-report-synchronized

Makes JBraid report PRCs that occurred in synchronized blocks. By default, PRCs that occur in blocks synchronized with the same object are not reported, but in some cases this can filter out some real issues.

 

-no-details

With this option, the report doesn't contain detailed information that is sensitive to small source code changes, like line numbers in source code, number of field accesses, or indexes in byte code,. This makes the report more suitable for comparison with a baseline report in automated tests.