To The Graduate School:

The members of the Committee approve the thesis of Qichang Chen presented on April 13, 2009.

Liqiang Wang, Chairman

James L. Caldwell

G. Eric Moorhouse

APPROVED:

Jerry C. Hamann, Head, Department of Computer Science

Don Roth, Dean, The Graduate School

Chen, Qichang,

Detecting Atomicity Violations via Integrated Dynamic and Static Analysis, M.S., Department of Computer Science, May, 2009

The reality of multi-core hardware has made concurrent programs pervasive. Unfortunately, writing correct concurrent programs is difficult. Atomicity violation, which is caused by concurrently executing code unexpectedly violating the atomicity of a code segment, is one of the most common concurrency errors. However, atomicity violations are hard to find using traditional testing and debugging techniques. This thesis presents a hybrid approach that integrates static and dynamic analyses to attack this problem. We first perform static analysis to obtain summaries of synchronizations and accesses to shared variables. The static summaries are then instantiated with runtime values during dynamic executions to speculatively approximate the behaviors of branches that are not taken. Compared to dynamic analysis, the hybrid approach is able to detect atomicity violations in unexecuted parts of the code. Compared to static analysis, the hybrid approach produces fewer false alarms. We implemented this hybrid analysis in a tool called HAVE that detects atomicity violations in multi-threaded Java programs and integrated it with the dynamic thread escape analysis to improve the performance and accuracy. Experimental evaluations on several benchmarks and real-world applications demonstrate promising results.

1

Detecting Atomicity Violations via Integrated Dynamic and Static Analysis

by Qichang Chen

A thesis submitted to the Department of Computer Science and the Graduate School of The University of Wyoming in partial fulfillment of the requirements for the degree of

Master of Science in Computer Science Laramie, Wyoming May, 2009

Acknowledgements

First of all, I would like to thank Prof. Liqiang Wang for taking me as his student. I really enjoy working with him for the past 3 years in which he not only gave me numerous pieces of advice on how to do research but also helped me on many personal issues. Thank David Weiser for his company in the pits in the first year. Thank Prof. James Caldwell and Prof. Eric Moorhouse for being on my committee. Thank Hongjiang Li and Lei Wu for their discussions and helps.

ii

Contents

List of Tables

vi

List of Figures

vii

1 Introduction

1

2 Background: Atomicity Violations

7

3 Integrating Dynamic and Static Analyses

9

3.1

The Static Analyzer

. . . . . . . . . . . . . . . . . . . . . . . . . . .

10

3.2

Instrumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

3.3

The Dynamic Monitor and Speculator . . . . . . . . . . . . . . . . .

12

4 The Conflict-Edge Algorithm

15

4.1

Building Conflict-Edges Between Hybrid Trees . . . . . . . . . . . . .

15

4.2

Detecting Atomicity violations . . . . . . . . . . . . . . . . . . . . . .

16

4.3

Unwrap Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

5 Optimization: Dynamic Escape Analysis

21

5.1

Introduction to Thread Escape Analysis . . . . . . . . . . . . . . . .

21

5.2

Two stage Dynamic Escape analysis . . . . . . . . . . . . . . . . . . .

22

iii

5.2.1

Dynamic Escape Detection . . . . . . . . . . . . . . . . . . . .

6 Experiments

24 25

6.1

Atomicity Violations in tsp . . . . . . . . . . . . . . . . . . . . . . . .

31

6.2

Atomicity Violations in other benchmarks . . . . . . . . . . . . . . .

31

7 Implementation

34

7.1

Eclipse and JDT . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

7.2

HAVE and Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

7.3

JDT and its AST . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36

7.3.1

Multi-pass instrumentation . . . . . . . . . . . . . . . . . . . .

39

7.4

Dynamic Monitoring . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

7.5

Runtime Optimization Techniques . . . . . . . . . . . . . . . . . . . .

40

7.5.1

Optimizations using Escape Information . . . . . . . . . . . .

40

7.5.2

Optimizations on Thread Monitor . . . . . . . . . . . . . . . .

44

7.6

Instrumentation Details . . . . . . . . . . . . . . . . . . . . . . . . .

46

7.7

A Brief HAVE Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . .

46

7.7.1

Command line . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

7.7.2

GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49

8 Related Work

50

8.1

Dynamic Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

8.2

Static Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

8.2.1

Hybrid Analysis . . . . . . . . . . . . . . . . . . . . . . . . . .

51

Escape Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52

8.3

9 Conclusions and Future Work

53

iv

Bibliography

54

v

List of Tables

vi

List of Figures 1.1

Examples in Java demonstrating data races and atomicity violations.

2

1.2

Problems of Static Analysis . . . . . . . . . . . . . . . . . . . . . . .

3

1.3

Problems of Dynamic Analysis . . . . . . . . . . . . . . . . . . . . . .

4

3.1

The architecture of the tool HAVE. . . . . . . . . . . . . . . . . . . . .

9

3.2

An example of a static summary tree (SST), where Account.c and Account.s denote Account.checking and Account.saving, and “R” or “W” denotes that the node is a read or write, respectively. . . . . .

3.3

11

An example of hybrid trees. Tree (a) corresponds to an execution of withdraw in Figure 3.2 when the else if is executed; tree (b) corresponds to the scenario when the first then is executed. The grey nodes are generated from the real executions; all the other nodes are speculated. The dotted lines denote conflict-edges introduced in Chapter 4. Only partial conflict-edges are marked out. . . . . . . . . . . . . . . .

14

4.1

The conflict-edge algorithm to detect atomicity violations . . . . . . .

17

5.1

Examples in Java demonstrating thread-escape objects and threadlocal objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

vii

23

6.1

Comparison of the purely dynamic commit node algorithm and the hybrid conflict-edge algorithm in performance and accuracy. The column “nAV” denotes the number of atomicity violations, which are counted based on the places in source code where the events involved in atomicity violations appear. The column “NA-methods” denotes the number of non-atomic methods with the categories being bug - benign - false positive. All times are measured in seconds. . . . . . . . . . . . . .

6.2

Overhead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #1 . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.3

25

27

Overhead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #2 . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

6.4

An code example that contains atomicity violations . . . . . . . . . .

29

6.5

An example showing the problem of counting atomicity violatioin valid pairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.6

29

An staightforward example illustrating the counting of the number of atomicity violations . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30

6.7

Comparison of Atomicity Violation Reports . . . . . . . . . . . . . .

30

6.8

An Atomicity Violation in tsp Discovered by HAVE . . . . . . . . . .

32

6.9

Method visit nodes in TspSolver.java . . . . . . . . . . . . . . . . . . .

33

7.1

Example:An sample package view of all projects under a workspace .

48

viii

Chapter 1 Introduction Today, multi-core hardware has become ubiquitous, which puts us at a fundamental turning point in software development. In order for software applications to benefit from the significant and continued throughput advances in new processors, the applications will need to be well-written multi-threaded programs. However, writing correct multi-threaded programs is difficult, because concurrency can introduce subtle errors that do not exist in sequential programs, if concurrent accesses to shared data are not properly synchronized. Two of the most common concurrency errors are data races and atomicity violations. A data race occurs when two concurrent threads perform conflicting accesses (i.e., accesses to the same shared variable and at least one access is a write) and the threads use no explicit mechanism to prevent the accesses from being simultaneous. In Program 1 of Figure 1.1, conflicting accesses to the shared variable bal can happen simultaneously without any protecting lock, hence a data race occurs. An atomicity violation occurs when an interleaved execution of a set of code blocks (expected to be atomic) by multiple threads is not equivalent to any serial execution of the same code blocks. Program 2 in Figure 1.1 eliminates the data race in Program 1 by adding a 1

Program 1 Thread 1 Thread 2 deposit(int deposit(int val){ val){ int tmp = bal; int tmp = bal; tmp = tmp + val; tmp = tmp + bal = tmp; val; } bal = tmp; }

Thread 1 deposit(int val){

Program 2 Thread 2 deposit(int val){

synchronized(o){ int tmp = bal; tmp = tmp + val; }

synchronized(o){ int tmp = bal; tmp = tmp + val; }

synchronized(o){ bal = tmp; } }

synchronized(o){ bal = tmp; } }

Figure 1.1: Examples in Java demonstrating data races and atomicity violations. lock o. However, Program 2 is still incorrect if the deposit method is required to be atomic. An atomicity violation occurs in Program 2 when the two synchronization blocks in thread 2 execute between the two synchronization blocks in thread 1. The traditional testing and debugging techniques are inappropriate for multithreaded programs, which may behave differently from one run to another, because threads are scheduled indeterminately. For most systems, the number of possible schedules is enormous, and testing the system’s behavior for each possible schedule is infeasible. Specialized techniques are needed to ensure that multi-threaded programs do not contain concurrency-related errors. Static analysis is a fundamental technique for analyzing and verifying programs. Static analysis reasons about source code without actually executing it. The strength of static analysis is that it can consider all possible executions of a program. However, it may produce false positives (i.e., false alarms), because some aspects of a program’s behavior, such as alias relationships, values of array indices and happensbefore relationships, are very difficult to analyze statically. Moreover, many static analyses, such as the type system for atomicity in [13], would require either a manual annotation of the program or a rewriting of the program into a special language.

2

Thread-2 synchronized(this){ Bank.a.f = 0; }

Thread-1 Account a1 = new Account(); Bank.a = a1; (new Thread-2()).start(); synchronized(this){ a1.bal += 200; }

Figure 1.2: Problems of Static Analysis Figure 1.2 shows the deficiencies of static analysis. In figure 1.2, thread 1 first creates an Account objects a1, assigns it to the static field a of class Bank. After that it starts the Thread 2 which will run concurrently with it. During the subsequent execution, thread 1 would add 200 to the bal field of a and thread 2 would empty the bal field of a to 0. Although both the operations for thread 1 and thread 2 are performed inside a synchronzied block respectively, it is difficult to determine whether both threads can synchronize on the same this object within static analysis. In addition, it is almost impossible to determine whether a and a1 are still aliased to the same object when the operations are executed. It could be the case that some thread changes the object that Bank.a refers to before thread 2 executes its synchronized block. If thread 1 and 2 are indeed not synchronized on the same object and Bank.a and a1 are still referring to the same object by the time thread 2 executes the synchronized block, then an atomicity violation could happen during the execution. However, as in the example, due to so many uncertainties in static analysis, we are unable to accurately report atomicity violations. Dynamic (runtime) analysis observes and analyzes the actual behaviors of the program by executing it. Generally, dynamic analysis is unsound compared to static analysis, because it does not analyze unobserved behaviors of programs, but may be more precise (i.e., give fewer false positives) for the explored behaviors. Furthermore,

3

Thread-1

Thread-2

Initialize(){ Account a1 = new Account(); Account a2 = new Account(); acctVector.add(a1); acctvector.add(a2); (new Thread-2(acctVector)).start(); }

Withdraw(int givenID, float val){ for(Account a: acctVector) { if (a.ID == givenID) if (a.bal >= val) { a.bal -= val; break; } } }

Deposit(int givenID, float val){ for(Account a: acctVector) { if (a.ID == givenID){ a.bal += val; break; } } }

Figure 1.3: Problems of Dynamic Analysis dynamic analysis generally does not require manual annotation of the code that is often required in static analysis; this is a significant practical advantage. Figure 1.3 shows the deficiencies of static analysis. In figure 1.2, thread 1 first creates the two Account objects a1, a2 and passes them to thread 2 through the container acctVector. Then thread 1 invokes the method Deposit and thread 2 invokes Withdraw simultaneously without proper synchronizations. For a given test case, the condition guarding the if statement might not be true for both threads, thus there is no atomicity violation happening in this given execution. However, for another test case, both threads could reach the then branch with the condition a.ID = givenID being true for the same givenID. This would cause a potential atomicity violation since the two threads are accessing the same object a1 or a2 without synchronization. Generally speaking, the task of generating various test cases to cover all possible behaviors of the target program is undecided. 4

Most existing approaches to detect atomicity violations are either purely dynamic (e.g. [10, 31, 30, 29]) or purely static (e.g. [13, 11]). The strength of static analysis is that it can consider all possible behaviors of a program. However, it may produce false positives (i.e., false alarms), because some aspects of a program’s behavior, such as alias relationships, values of array indices, and happens-before relationships, are very difficult to analyze statically. Moreover, many static analyses, such as the type system for atomicity in [13], require either manual annotation of the program or rewriting of the program into a special language. Dynamic analysis observes and analyzes the actual behaviors of a program by executing it. Generally, dynamic analysis is unsound compared to static analysis, because it does not analyze unobserved behaviors of programs. On the positive side, it generally produces fewer false positives. Furthermore, dynamic analysis generally does not require manual annotation of code that is often required in static analysis; this is a significant practical advantage. In order to exploit the complementary benefits of static and dynamic analyses, we propose a hybrid approach to detect atomicity violations. In this approach, we perform a conservative intraprocedural static analysis to generate a summary for each method in the program. The runtime system tracks and records the values of reference variables during execution. When we observe an unexecuted branch during dynamic analysis, the static summary of that unexplored branch is retrieved and instantiated using the recorded values. Thus, the instantiated summary speculatively approximates what would have happened if the branch had been executed. We implemented the hybrid approach in a tool called Hybrid Atomicity Violation Explorer (HAVE) [4] for detecting atomicity violations in multi-threaded Java programs and evaluated it on several benchmarks and real-world applications. The experiments show that the hybrid approach reports fewer false positives than the

5

previous static approaches [13, 1], and fewer false negatives (i.e., missed errors) than the previous dynamic approaches [10, 30, 29]. This thesis is mainly adapted from the paper [4] that is coauthored with Liqiang Wang, Zijiang Yang and Scott Stoller. In addition, we have incorporated new materials which are not present in the paper. Chapter 5 contains a thorough discussion about our optimization technique. Chapter 6 includes detailed results from the experimental evaluation. Chapter 7 is completely new and provides our implementation details. Chapter 8 includes a few more related works on escape anlysis.

6

Chapter 2 Background: Atomicity Violations An execution σ = hs1 , . . . , sn i is a sequence of accesses to shared variables, lock acquire, lock release, thread start, thread join, and barrier synchronization operations. A transactional unit (or transaction) is an execution of a code block expected to be atomic. A non-transactional unit is an execution of a code block not expected to be atomic. For an event or transaction x, let th(x) be the thread that performed x. As in [29], we assume that transaction boundaries are chosen so that thread start and join operations and barrier operations occur at transaction boundaries, not in the middle of transactions. Thus, thread and barrier operations induce a partial order on transactions: given an execution σ, and two transactional or non-transactional units u1 and u2 , u1 happens-before u2 , denoted u1
T ∪ A that is consistent with the happens-before relation
8

Chapter 3 Integrating Dynamic and Static Analyses This chapter gives an overview of this hybrid approach to check atomicity violations. Figure 3.1 shows the architecture of our tool, HAVE, which consists of five components. 1. A static analyzer, which parses the source code to generate static summary trees (SSTs). 2. An instrumentation tool, which inserts event interception code. source code

static analyzer

instrumentation tool instrumented code

static summary trees

speculator

dynamic monitor

dynamic trees

hybrid conflict-edge algorithm

hybrid trees

atomicity violation warnings

Figure 3.1: The architecture of the tool HAVE.

9

3. A dynamic monitor, which intercepts events and builds dynamic trees during execution. 4. A speculator, which generates speculations for the unexecuted branches from SSTs and combines them with dynamic trees to form hybrid trees. 5. A detector, which analyzes the hybrid trees for atomicity violations using the hybrid conflict-edge algorithm described in Chapter 4.

3.1

The Static Analyzer

The static analyzer parses source code to construct static summary trees (SSTs). Each SST corresponds to a method in a certain class. Specifically, a static tree may contain nodes representing: (1) read/write to non-final and non-volatile fields; or (2) entrance and exit of synchronized blocks, including synchronized methods (which represent lock acquire and release operations); or (3) control-flow structures, namely, if, for/while, and switch/case; or (4) assignments to reference variables, which are used to speculate reference changes for the unexecuted code blocks. SSTs do not contain interprocedural information, i.e., method calls are ignored. Unlike the dynamic monitor, the static analyzer ignores thread start, join and barrier synchronizations. Accesses to array elements are ignored in this thesis due to the difficulty of statically resolving the indices of array elements. Figure 3.2 shows an example of a code block and its SST.

10

class Account { int checking, saving;

The corresponding SST withdraw

public void withdraw(int w) { if ((this.checking + this.saving) < w) print("Not enough balance"); else if (this.checking >= w) synchronized(this) this.checking -= w; else synchronized(this) { this.saving -= w - this.checking; this.checking = 0; } } }

IF cond

THEN

R R Account.c Account.s

ELSE IF

cond

THEN

ELSE

R Account.c

Synchronized (Account)

Synchronized (Account)

R W R R W W Account.c Account.c Account.c Account.s Account.s Account.c

Figure 3.2: An example of a static summary tree (SST), where Account.c and Account.s denote Account.checking and Account.saving, and “R” or “W” denotes that the node is a read or write, respectively.

3.2

Instrumentation

The instrumentation component instruments source code in order to intercept specific events during execution. The intercepted events include program control-flow structures, reads and writes to non-final and non-volatile fields, synchronization (including lock acquire and release, barrier operation, thread start and join), assignments to reference variables, and transaction boundaries. Similar to [29], executions of the following code fragments are considered as transactions by default because their executions are often expected to be atomic by programmers: non-private methods, synchronized private methods, and synchronized blocks inside non-synchronized private methods. With exceptions, the executions of the main() method in which the program starts and the executions of run() methods of classes that implement Runnable are not considered as transactions, because these executions represent the entire executions of threads and are often not expected to be atomic. Moreover, start, join and barrier operations are treated as boundaries, i.e., they separate the preceding events and following events into different units, and 11

are not contained in any unit. We adopt this heuristic because execution fragments containing these operations are typically not atomic and hence are not expected to be transactions. The events not in transactions form non-transactional units. Note that for nested transactions, we check atomicity only for the outmost transactions, since they contain the inner transactions. The defaults can be overridden using a configuration file.

3.3

The Dynamic Monitor and Speculator

When an instrumented program runs, the dynamic monitor receives events issued by the instrumented code. The events of each unit (including transactional and non-transactional units) are stored in a structure called a hybrid tree, which consists of events observed in the execution and speculations based on static summary trees. Each leaf node represents a read or write to a shared variable and contains the runtime identifier for the shared variable. For example, R(320.checking) denotes a read to the field “checking” of an object identified by its hashcode 320. Each non-leaf node except for the root represents a lock-based synchronization block or control-flow structure (e.g. if/then/else, for/while loop, switch/case). Each synchronization node contains the runtime identifier for the current lock (i.e., synchronization object). The root node simply identifies the unit. For each unexecuted branch in the unit, we instantiate the corresponding part of the method’s SST by simulating its execution using the runtime context at the associated branch point, and add the resulting concrete events (e.g. synchronization nodes, reads and writes) to the hybrid trees. We instantiate symbolic names in the SST by querying binding tables. A binding table is maintained for each object; it stores the mappings between symbolic names and runtime values of all reference 12

fields and local reference variables under the context of the object. A binding table is maintained for each class with static reference fields. Binding tables are updated when assignments to reference variables are executed. During speculative execution, assignments to reference variables in SSTs trigger updates on temporary copies of binding tables, instead of the original ones. Since there might be unresolved symbolic names left during speculation, the speculation may be not as accurate as its runtime equivalent observed in the dynamic analysis if it can be executed. This speculative technique may lead to false positives. Our experiments show that such kind of false positives are very rare in practice. Our speculative execution also constructs subtrees corresponding to speculative iterations of loop bodies. According to Theorem 4.3.1 in Section 4.3, if all iterations of a loop perform the same accesses, then at most two iterations are needed to detect atomicity violations. We use this as a heuristic, without attempting to verify the hypothesis of the theorem. Specifically, when the control flow reaches a loop, if the execution contains no iterations of the loop at that point, we add two speculative iterations; if the execution contains only one iteration of the loop at that point, we add one speculative iteration. Two examples of hybrid trees are shown in Figure 3.3. Tree (a) and Tree (b) are generated by two threads of an execution that call the method withdraw() in Figure 3.2. The hashcode of the instance of Account is assumed to be 320.

13

withdraw

IF

IF

THEN

cond R 320.c

withdraw

ELSE

R 320.s

IF

cond

THEN

ELSE

R 320.c

Synchronized (320)

Synchronized (320)

R 320.c

W 320.c

R 320.c

THEN

cond R 320.c

R 320.s

W 320.s

W 320.c

IF

cond

THEN

ELSE

R 320.c

Synchronized (320)

Synchronized (320)

R 320.c

(a)

ELSE

R 320.s

W 320.c

R 320.c

R 320.s

W 320.s

W 320.c

(b)

Figure 3.3: An example of hybrid trees. Tree (a) corresponds to an execution of withdraw in Figure 3.2 when the else if is executed; tree (b) corresponds to the scenario when the first then is executed. The grey nodes are generated from the real executions; all the other nodes are speculated. The dotted lines denote conflict-edges introduced in Chapter 4. Only partial conflict-edges are marked out.

14

Chapter 4 The Conflict-Edge Algorithm This chapter presents the conflict-edge algorithm that detects atomicity violations based on hybrid trees. The algorithm adds edges, called conflict-edges, between hybrid trees, to connect two conflicting nodes (which is discussed in details in Chapter 4.1). The algorithm then generates all valid pairs of conflict-edges; informally, “valid” means that all nodes involved in the pair can coexist in some execution. The algorithm detects and reports atomicity violations by analyzing each valid conflict-edge pair. Note that the conflict-edge algorithm does not merely look for violations of atomicity in the observed execution, but also determines whether atomicity violations exist in feasible permutations of the observed execution.

4.1

Building Conflict-Edges Between Hybrid Trees

Two nodes n1 and n2 conflict if (1) they are in different hybrid trees, and (2) they represent accesses to the same variable and at least one of them is a write, and (3) thread start, join, and barrier operations do not induce a happens-before relation on them (i.e., do not prevent them from occurring simultaneously). Let held(ni ) denote 15

the set of locks held when ni is executed, which is determined by the synchronization nodes that are ancestors of ni in the tree. For each pair (n1 , n2 ) of conflicting nodes, if held(n1 ) ∩ held(n2 ) = ∅, we add a conflict-edge between n1 and n2 ; otherwise, we add a conflict-edge between the highest ancestors of n1 and n2 that are synchronization nodes for the same lock. The highest synchronization nodes represent the outmost common lock held during the executions of n1 and n2 . The conflict-edge reflects the granularity at which the code blocks containing conflicting accesses can be interleaved. For example, Figure 3.3 shows partial conflict-edges between the two hybrid trees.

4.2

Detecting Atomicity violations

A hybrid tree t represents a set [[t]] of possible (transactional or non-transactional) execution units, corresponding to different choices of the branches of the if, switch, and loop statements that appear in it. For simplicity, our speculative analysis assumes that each branch could be taken, independently of other choices; in other words, the conditions guarding the branches are ignored. Given a set T = {t1 , . . . , tn } of hybrid trees representing transactions, a set A = {a1 , . . . , am } of hybrid trees representing non-transactional units, and a happens-before relation
CheckAtomicityViolations() { AV Scenarios := ∅; for each transactional hybrid tree t do for each valid conflict-edge pair (e, e0 ) of t do if only two hybrid trees including t are connected by e and e0 then /* find an atomicity violation scenario */

AVScenarios := AVScenarios ∪{(e, e0 )}; else if ∃ a valid cycle c of conflict-edges involving (e, e0 ) then AVScenario := AVScenario ∪{c}; }

Figure 4.1: The conflict-edge algorithm to detect atomicity violations Conflict-edge e is an ancestor of conflict-edge e0 in hybrid tree t if an endpoint of e is an ancestor of an endpoint of e0 in t. A pair (e, e0 ) of conflict-edges is valid for hybrid tree t, if (1) e and e0 are compatible, (2) e is not an ancestor of e0 in t, and vice versa, and (3) e and e0 are incident on different nodes of t. In the rest of the thesis, all pairs mentioned are valid by default if without explicit indication. We have the following theorem to determine atomicity for a transactional hybrid tree. Let Fσ be a hybrid forest generated from an execution σ. Given a transactional hybrid tree t contained in Fσ , let Fσ \ {t} be the set of all the other units. Theorem 4.2.1 Suppose a hybrid forest Fσ has no potential for deadlock. If t has no valid pair in Fπ , h{t}, Fσ \ {t},
Given a valid pair of conflict-edges (e, e0 ), a sequence of conflict-edges involving e and e0 may form into a cycle, where two conflict-edges connected to the same tree are considered linked. A cycle involving (e, e0 ) is valid if (1) neither e nor e0 is an ancestor of the other; and (2) all conflict-edges on the cycle are compatible; and (3) for each node n on the cycle, held(n) ∩ held(nte ) = ∅∧ held(n) ∩ held(nte0 ) = ∅, where nte and nte0 are the end nodes of e and e0 in t, respectively; and (4) all involved transactional and non-transactional units are concurrent (i.e., the happens-before relation enforces no order on them). We have the following theorems to check atomicity violations. Theorem 4.2.2 Suppose a hybrid forest Fσ has no potential for deadlock. If a valid pair of conflict-edges (e, e0 ) on a transactional hybrid tree t is involved in a valid cycle of Fσ , then t has an atomicity violation with the scenario indicated by the cycle. Proof Sketch: Suppose the valid cycle consists of conflict-edges e0 , e1 , . . . , en , where e0 = e and en = e0 . Let ui and ui+1 be the execution units containing the endpoints of ei , for i = 0..n.

Note that u0 = t and un+1 = t.

The conditions

in the definition of valid cycle imply that u0 , . . . , un are distinct and there exist u00 ∈ [[u0 ]] , . . . , u0n ∈ [[un ]] and an interleaving σ 0 for hT 0 , A0 , <0H i that contains events in the order hendpoint(e0 , t), endpoint(e0 , u1 ), endpoint(e1 , u1 ), endpoint(e1 , u2 ), . . . , endpoint(en , un ), endpoint(en , t)i (i.e., all of the other nodes on the cycle occur between the two nodes of t on the cycle), where T 0 and A0 contain the transactional and non-transactional units, respectively, in {u00 , . . . , u0n }, and u0i <0H u0j iff ui
18

Corollary 4.2.3 Suppose a hybrid forest has no potential for deadlock. If a valid pair of conflict-edges on a transactional hybrid tree t is incident to only two transactions (including t), then t has an atomicity violation with the scenario indicated by the pair. Proof Sketch: The valid pair forms into a cycle. Thus, the conclusion is simply implied by Theorem 4.2.2.  For each hybrid tree t, we detect atomicity violations by checking valid pairs of conflict-edges as shown by CheckAtomicityViolations() in Figure 4.1. Given a valid pair (e, e0 ) of t, if e and e0 involve only two hybrid trees, this pair implies an atomicity violation by Corollary 4.2.3. If e and e0 involve three hybrid trees (recall that e and e0 are already incident to t), we check atomicity violations based on Theorem 4.2.2. Our current implementation does not use Theorem 1, because our system looks for potential atomicity violations; it does not try to verify atomicity. Corollary 4.2.3 is applied first because it is cheaper. For example, in Figure 3.3, an atomicity violation is revealed by a valid pair (< a.W(320.c), b.R(320.c) >, < a.W(320.s), b.R(320.s) >). The atomicity violation cannot be discovered by a purely dynamic approach because the valid pair connects a speculative branch in tree a with an executed branch in tree b. Let P be a program under test and tr be an execution trace of P . Let S (mnemonic for Size of trees) denote the maximum number of nodes in any hybrid tree induced by tr. Note that the number of trees is |T ∪ A|. The worst-case time complexity of constructing conflict-edges is O((|T ∪ A| · S)2 ). Let nc denote the maximum number of conflict-edges incident on any hybrid tree. Usually nc is much less than |T ∪ A| · S 2 . Theorem 4.2.2 requires finding a valid cycle, which is O((|T ∪ A| · nc )2 ). The total number of valid pairs is O(|T | · n2c ). Hence, the worst-case time complexity of checking atomicity violations is O(|T | · |T ∪ A|2 · n4c ). 19

4.3

Unwrap Loops

The following theorem shows that executing a loop twice is sufficient to find atomicity violations, if all iterations perform the same accesses. Consider a loop such that every iteration contains the same sequence of access events. Let σ2 denote an execution in which, at some point, a thread performs exactly two iterations of the loop. Let t2 be the corresponding transaction containing the two iterations. Let σm be an execution that differs from σ2 only in that, at the same point, the thread performs more than two iterations. Let tm be the corresponding transaction containing the m iterations. Suppose t2 and tm differ only on the number of iterations. Theorem 4.3.1 ht2 , A,
20

Chapter 5 Optimization: Dynamic Escape Analysis This chapter describes the dynamic escape analysis that we adopt to improve the performance and accuracy of our analysis.

5.1

Introduction to Thread Escape Analysis

Thread escape analysis determines if an object is reachable only from a single thread during its lifetime, allowing the synchronization on the object to be removed, permitting any dynamic concurrent program analysis to reduce the runtime overhead by removing the observations on these thread-local objects. When an object o is created, o is owned by the creating thread. Object o threadescapes when it may be accessed by two or more threads. Thus, o may have multiple thread-owners. A thread-owner of o is also the owner of its every instance field. For a static field, all threads are its owner. Thread-ownership can be transferred. The thread ownership of a field o.f is said to be transferred from a thread t to another 21

thread t0 if there exists a program state after which t will not access field o.f any more, and t0 does not access o.f until that program state. A field o.f is thread-local if it does not have multiple thread-owner simultaneously; otherwise, it is shared. For a field o.f , its escape point is the earliest program state where it becomes shared. Figure 5.1 shows an example of thread-local objects and thread-escape objects. In this example, the local container variable acctVector is created inside thread 1 and later passed to the Thread-2 as an argument to Thread-2’s constructor. There are also two objects a1 and a2 of class Account that are created within thread 1 and saved in the later thread-escape vector acctVector. Thus they all become accessible to both thread 1 and 2. When thread-2 is created and started, it executes the method Withdraw which would access some element that is stored in acctVector. Depending on the runtime value of the integer parameter givenID for Withdraw and Deposit, either a1 or a2 can be accessed by both threads. In this example, acctVector is a thread-escape object and a1 or a2 can be a threadescape object while accountList is a thread-local object which is only visible within thread 1. Using our dynamic escape analysis, we can precisely tell which object (a1 or a2) that is shared in the runtime thus reducing the unnecessary observation on the other thread-local object.

5.2

Two stage Dynamic Escape analysis

To reduce the runtime overhead of monitoring, we restrict monitoring to shared variables. Before an object becomes shared (i.e., escapes from the thread that created it), all events involving it can be ignored. We designed and implemented dynamic escape analysis to accurately determine the sharing property of each variable. This analysis extends our previous dynamic escape analysis [30] and introduces an additional 22

Thread-1

Thread-2

Initialize(){ Vector acctVector = new Vector(); Vector accountList = new Vector(); Account a1 = new Account(); Account a2 = new Account(); acctVector.add(a1); acctvector.add(a2); accountList.add(a1); accountList.add(a2); (new Thread-2(acctVector)).start(); }

Withdraw(int givenID, float val){ for(Account a: acctVector) { if (a.ID == givenID) if (a.bal >= val) { a.bal -= val; break; } } }

Deposit(int givenID, float val){ for(Account a: acctVector) { if (a.ID == givenID){ a.bal += val; break; } } }

Figure 5.1: Examples in Java demonstrating thread-escape objects and thread-local objects execution on the same input before the atomicity analysis. The first execution is used to determine whether each field of every class ever becomes shared during the entire run. Note that we do not construct and analyze hybrid trees during this execution. Each field of a class is processed independently, since some fields might be always accessed by a single thread even if the owner object is shared by multiple threads. Specifically, for each field, if that field in some instance has ever been accessed by multiple threads, the field of the corresponding class is marked as shared; otherwise, it is considered unshared.

23

5.2.1

Dynamic Escape Detection

During the second execution with the same input, we keep track of when an object (instead of field) becomes shared while constructing hybrid trees and analyzing atomicity violations. Fields classified as unshared from the first execution are not monitored. When an object becomes shared, all its monitored fields are marked as shared. To indicate whether an object has escaped from its creating thread, we add a boolean instance field to every class with the initial value false. We use Java reflection mechanism to dynamically update the field. An object o becomes shared in the following scenarios: (1) o is stored in a static field or a field of a shared object; (2) o is an instance of a thread and the thread is started; (3) o is referenced by a field of another object o0 , and o0 becomes shared (this leads to cascading sharing); (4) o is passed as an argument to a native method that may cause it to be shared. See 7.5.1 for implementation details. The dynamic escape analysis is based on an assumption that given the same input, the sharings of a variable are the same during different executions, which is true in our experiment of Chapter 6. The dynamic sharing analysis has improved performance significantly. For example, it reduces the overall runtime by 40% on the benchmarks Tsp and Jigsaw compared to the executions without the dynamic sharing analysis. Another optimization is that, for access nodes with the same parent node, we preserve only the first two accesses in the same type (read or write) to each shared variable, because the first two accesses can represent all discarded accesses for checking atomicity. This is justified by Theorem 7.1 in [29].

24

Chapter 6 Experiments This chapter discusses our evaluation of HAVE and experimental results on several multi-threaded Java programs ranging from small benchmark programs to large-scale web servers.

Program

Elevator Tsp Sor Hedc Jigsaw Tomcat Vector1.4 Stack1.4 HashTable1.4

LOC

339 519 8253 4267 100846 168297 383 418 597

Threads Base

3 3 3 3 68 3 2 2 2

0.1 0.4 0.8 0.3 1.4 3.3 0.1 0.1 0.2

Dummy

0.2 3.5 1.2 0.4 1.7 4.1 0.2 0.2 0.3

Purely Dynamic Time nAV 0.5 18 14 28 1.6 0 0.5 7 2.7 3 7.7 10 0.6 10 0.7 10 0.6 4

Hybrid

NA NA methods Time nAV methods 0-2-0 1 18 0-2-0 2-0-0 66.9 54 2-0-0 0-0-0 3.1 0 0-0-0 1-0-0 0.6 7 1-0-0 1-0-0 118.3 24 2-0-0 0-2-0 38.5 18 1-2-0 4-4-0 0.8 10 4-4-0 3-4-0 0.8 10 3-4-0 0-4-0 0.9 4 0-4-0

Code Coverage

89.2% 79.7% 74.9% 35.1% 8.1% 13.7% 69.2% 85.7% 47.5%

Figure 6.1: Comparison of the purely dynamic commit node algorithm and the hybrid conflict-edge algorithm in performance and accuracy. The column “nAV” denotes the number of atomicity violations, which are counted based on the places in source code where the events involved in atomicity violations appear. The column “NA-methods” denotes the number of non-atomic methods with the categories being bug - benign false positive. All times are measured in seconds.

25

We tested our tool on the following programs: Elevator, Tsp, Sor, and Hedc from [25], Jigsaw 2.2.6 from [17], Apache tomcat 6.0.16, and Vector, Stack, and Hashtable from JDK 1.4.2. Elevator is a program that uses 2 threads to simulate the elevator. tsp is the multi-threaded Travel Sales Person problem solver which comes with a standard set of TSP problems. Given a specific TSP test harness, the user can additionally specify the number of threads employed to solve that problem. sor stands for Successive Over-Relaxation which performs 100 iterations of successive over-relaxation on a N × N grid as specified in the test harness. Hedc is a multithreaded web crawler. Jigsaw 2.2.6 is a basic HTTP server that supports both secure and unsecure communications. Apache tomcat is an HTTP and J2EE application server that supports the dynamic content generation for server-side web pages. [23] has extensive coverages on the Java Grande benchmarks(namely, Elevator, Tsp, Sor). We performed the experiments on a commodity Desktop with 1.8 GHz Intel dualcore CPU, 2 GiB memory, Windows XP SP3, and Sun JDK 1.6. Figure 6 compares the running time and results of our hybrid algorithm with the purely dynamic commit node algorithm for conflict-atomicity in [29]. “Base” is the original program’s running time without instrumentation. “Dummy” is the instrumented program’s running time without analyzing atomicity violations (i.e., analysis is not performed after intercepting the events). “Purely Dynamic” is the instrumented program’s running time using the purely dynamic commit node algorithm in [29]. “Hybrid” is the running time of our hybrid algorithm. “Code Coverage” is the coverage of statements in the current execution, which is obtained using an Eclipse plugin EclEmma. We implicitly assume public or synchronized methods are expected to be atomic.

26

6

Oveahead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #1

3 0

1

2

Normalized Time

4

5

Base Dummy Purely Dynamic Hybrid

elevator

sor

hedc

Vector.1.4

Stack.1.4

HashTable.1.4

Benchmarks

Figure 6.2: Overhead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #1 Because this is where usually a transaction boundary lies in. Figure 6.2 and 6.3 gives a graphical view of the runtime overheads for purely dynamic and hybrid approach. From this figure, we can see that HAVE scale almost as well as the purely dynamic approach for most benchmarks except tsp, jigsaw and tomcat. We count the number of atomicity violations as the number of different places from the source code that are involved in the atomicity violation. This is because by simply counting the number of atomicity violation valid pairs we might disguise the fact that how many coding mistakes the programmer might have only made. The fact that many of the valid pairs are symmetric and exchangeable between the involved threads has prompted us to come up with this alternative counting method. Since the bug-fixers or programmers are more concerned about which part of code is buggy in causing the atomicity violation, we have adopted the metric of counting the actual 27

120

Oveahead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #2

60 0

20

40

Normalized Time

80

100

Base Dummy Purely Dynamic Hybrid

tsp

jigsaw

tomcat

Benchmarks

Figure 6.3: Overhead Comparison of Purely Dynamic and Hybrid Atomicity Violation Analysis #2 code places are that part of any atomicity violation. Figure 6.4 and 6.5 illustrates the problem of counting atomicity violation pairs. In figure 6.4, thead 11 executes the then branch of if statement while thread 12 takes the else branch of its if statement. Figure 6.5 shows the hybrid tree with conflict edges built by HAVE for the above execution. If we are counting the valid pairs as the number of atomicity violations, then we would have 3 valid pairs {e2, e3}, {e3, e4}, {e2, e4}. The pairs {e2, e3} and {e3, e4} are symmetric and they essentially indicate the same atomicity violation. But here we have counted them twice. Figure 6.6 illustrates our new atomicity violation counting approach. In the figure, the field this.checkingBal is involved in multiple atomicity violations between thread 10 and thread 11. Thus for each unique involved place, we record it once. The total number of atomicity violations is 3 and this corresponds to the number of code places that the programmer needs to examine and fix. 28

Thread:11

Thread:10

executed speculation

if (isUpdated) { this.checkingBal = w; System.out.println(" Amount successfully updated "); if (isUpdated) { }else { this.checkingBal = w; this.checkingBal += w; System.out.println(" Amount } successfully updated "); }else { this.checkingBal += w; } executed

speculation

Figure 6.4: An code example that contains atomicity violations hybrid tree for thread 11

hybrid tree for thread 10

IF THEN R 320.c

IF ELSE

R 320.c

THEN W 320.c

R 320.c

e1

ELSE R 320.c

W 320.c

e2 e3 e4 e5

checkingBal is abbreviated as c

Figure 6.5: An example showing the problem of counting atomicity violatioin valid pairs Figure 6.7 provides the graphical comparison on the number of atomicity violations reported by purely dynamic and hybrid atomicity violation analysis respectively. As we can see clearly from this graph, HAVE reveals more atomicity violation places than its dynamic counterpart on the benchmarks tsp, jigsaw and tomcat.

29

Thread:11

Thread:10 executed

speculation

av place #1

if (isUpdated) { this.checkingBal = w; System.out.println(" Amount successfully updated "); }else { this.checkingBal += w; }

if (isUpdated) { this.checkingBal = w; System.out.println(" Amount successfully updated "); }else { this.checkingBal += w; } executed

av place #3

speculation

av place #2

Figure 6.6: An staightforward example illustrating the counting of the number of atomicity violations

60

Comparison of Atomicity Violation Reports

40 30 20 10 0

Number of Atomicity Violations

50

nAV by Purely Dynamic nAV by Hybrid

elevator

tsp

sor

hedc

jigsaw

tomcat

Vector.1.4

Stack.1.4

Benchmarks

Figure 6.7: Comparison of Atomicity Violation Reports 30

HashTable.1.4

6.1

Atomicity Violations in tsp

For Tsp, HAVE discovers more potential atomicity violations because of speculation. For example, we found that an atomicity violation involves a read on the static field TspSolver.MinTourLen in the speculative branch in the method split tour and two writes on the same field in the executed code of the method set best called by the method recursive solve. Figure 6.8 shows the scenario how this atomicity violation would happen. Thread 11 executes the method visit nodes (shown in Figure 6.9 which would repeatedly call the method setbest to update the best path that is currently found. Between the invocations of setbest, thread 10 calls split tour which could potential modify the value of TspSolver.MinTourLen. This is not observed in the execution with the given test case.

6.2

Atomicity Violations in other benchmarks

For Jigsaw, HAVE also reveals more atomicity violations than the purely dynamic approach. HAVE reports that the non-atomic method perform in httpd.java has multiple atomicity violations regarding several fields such as the instance field LRUNode.next and the instance field ResourceStoreImpl.resources. The previous purely dynamic approach missed this because some field accesses occur in speculatively executed branches. For Tomcat, the static field StringCache.accessCount in the method toString (ByteChunk bc) of StringCache.java has the potential for atomicity violation when at least two threads find StringCache.bcCache != null and execute the else branch. The same risk exists for the static field StringCache.hitCount in the same method,

31

Thread:11 static void set_best(int best, int[] path) { .... synchronized (TspSolver.MinLock) { if (best < TspSolver.MinTourLen) { ... TspSolver.MinTourLen = best; ... } } executed } static void set_best(int best, int[] path) { .... synchronized (TspSolver.MinLock) { if (best < TspSolver.MinTourLen) { ... TspSolver.MinTourLen = best; ... } } executed }

Thread:10 static void split_tour(int curr_ind) { ... synchronized (TspSolver.TourLock) { ... if (curr.last != Tsp.TspSize - 1) { ... speculation for (i = 0; i < Tsp.TspSize; i++) { ... t3 = curr.lower_bound + wt <= TspSolver.MinTourLen; TspSolver.MinTourLen; ... } } } } [TspSolver.split_tour(TspSolver.java:2224) TspSolver.find_solvable_tour(TspSolver.java:1135) TspSolver.get_tour(TspSolver.java) synchronized@10736847@line:path=n/a@object:TspSolver.TourLock TspSolver.get_tour(TspSolver.java:1287) TspSolver.Worker(TspSolver.java:2784) TspSolver.run(TspSolver.java:2518)]

[TspSolver.set_best(TspSolver.java) synchronized@6658066@line:path=n/a@object:TspSolver.MinL ock TspSolver.set_best(TspSolver.java:1745) TspSolver.visit_nodes(TspSolver.java:2674) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.visit_nodes(TspSolver.java:2739) TspSolver.recursive_solve(TspSolver.java:2504) TspSolver.Worker(TspSolver.java:2791) TspSolver.run(TspSolver.java:2518)]

Figure 6.8: An Atomicity Violation in tsp Discovered by HAVE if both threads fail the condition test before it. We classify this atomicity violation as a bug, because it may cause the statistics to be inaccurate, even though this inaccuracy does not cause other incorrect behavior.

32

void visit_nodes ( int from ) { int i ; int dist , last ; this . visitNodes ++; for ( i = 1; i < Tsp . TspSize ; i ++) { if ( this . Visit [ i ] != 0) { continue ; /* Already visited . */ } if (( dist = TspSolver . weights [ from ][ i ]) == 0) { continue ; /* Not connected . */ } if ( this . CurDist + dist > TspSolver . MinTourLen ) { continue ; /* Path too long . */ } /* Try next node . */ this . Visit [ i ] = 1; this . Path [ this . PathLen ++] = i ; this . CurDist += dist ; if ( this . PathLen == Tsp . TspSize ) { /* Visiting last node - determine if path is min length . */ if (( last = TspSolver . weights [ i ][ Tsp . StartNode ]) != 0 && ( this . CurDist += last ) < TspSolver . MinTourLen ) { TspSolver . set_best ( this . CurDist , this . Path ) ; } this . CurDist -= last ; } /* if visiting last node */ else if ( this . CurDist < TspSolver . MinTourLen ) { this . visit_nodes ( i ) ; /* Visit on . */ } /* Remove current try and loop again at this level . */ this . CurDist -= dist ; this . PathLen - -; this . Visit [ i ] = 0; } }}

Figure 6.9: Method visit nodes in TspSolver.java

33

Chapter 7 Implementation This chapter discusses the Java instrumentation framework we implemented.

7.1

Eclipse and JDT

Eclipse is an Open Source software developing platform initiated by the Eclipse Foundation. It provides an IDE(Integrated Developing Environment) and a plug-in system which aim to improve the productivity of large-scale application developers. It is written mainly in Java [15] and is initially used for Java developments although Eclipse now supports most of popular imperative program languages such as C/C++, Cobol, Python, Perl, PHP. JDT(Java Development Toolkit) is one of the earliest plug-ins shipped with Eclipse. It is a Java development tool plug-in that provides a Java IDE and an internal Java compiler that compiles Java source codes to standardcompliant byte codes. It is part of the Eclipse standard release. JDT supports syntax highlighting, code refactoring, hierarchical view of Java elements, call graph generation, polymorphic type hierarchy view, indexed search infrastructure, context specific content-assist, dead code removal and many other debugging facilities. Most of its fea34

tures fall into the static analysis category which are dependent on the AST(Abstract Syntax Tree).

7.2

HAVE and Eclipse

We developed HAVE as an Eclipse plug-in on top of its plug-in system. HAVE is dependent on several existing plug-ins besides the Eclipse platform itself. The static part of HAVE operates on the source code of Java programs. The reason we choose source level instrumentation is because that source level preserves the control flow structure much more concisely than its byte code counterpart although the byte code level instrumentation might offer some performance gain. We mitigate the performance overhead by selectively observing core events only and performing redundant states detection and removal at runtime. Specifically, HAVE explicitly requires the plug-ins org.eclipse.jdt.core to provide the AST of Java source for later analysis and instrumentation, org.eclipse.core.resources to provide underlying file manipulations, org.eclipse.ui to provide some UI(User Interface) interactions with end-users. The detailed plug-in dependency of HAVE can be found in the file ”plugin.xml” which is in the root directory of HAVE plug-in. It serves the bridge between Eclipse platform and HAVE. HAVE is released and distributed as a plug-in jar file. Like other plug-ins, HAVE needs to be in the ”plug-in” directory of Eclipse installation path in order to use it. Here we briefly explain how plug-in and Eclipse platform works together. When Eclipse starts, it scans its ”plug-in” directory and reads the ”plugin.xml” for each plug-in under that ”plug-in” directory. After resolving the dependency of each plug-in and make sure that the required plug-ins are available, Eclipse initializes the plug-ins and they become functional parts of the Eclipse platform. This composable plug-in 35

architecture enables different developers to develop their own Eclipse tools without knowing much about others’ irrelevant technicalities besides providing the standard ”plug-in.xml”. In one word, most of Eclipse features are implemented as plug-ins to its platform. HAVE is one of such plug-ins. It is upward compatible starting from Eclipse 3.3(Europa release).

7.3

JDT and its AST

The JDT plug-in org.eclipse.jdt.core provides an API for clients to build an AST for each source code file inside the current Eclipse workspace. To use HAVE, One has to create an Eclipse project for the target benchmark program. The following code illustrates how to obtain the Java project named projectName programmatically.

final final final final

IWorkspace current_ws = ResourcesPlugin . getWorkspace () ; IWorkspaceRoot root = current_ws . getRoot () ; IProject pproject = root . getProject ( projectName ) ; IJavaProject javaProject = JavaCore . create ( pproject ) ;

The ResourcePlugin is the starting point for all workspace and resource manipulation. ResourcesPlugin.getWorkspace() returns the workspace that is currently opened by Eclipse. A workspace is a logical and hierarchical organization of resources(i.e., projects, directories, files). All the projects are immediate children nodes of the IWorspaceRoot which is returned by current ws.getRoot(). Because Eclipse supports many IDE’s such as C++, Python, Java, we need to specifically cast the pproject into a java project(IJavaProject) before we can operate on it. Within a java project, we can iteratively visit each resource(i.e., Java packages 36

and files) using the Visitor[14] pattern which is provided by IResourceVisitor. The code snippet below shows how we implement this:

public class HaveFileVisitor implements IResourceVisitor { public boolean visit ( IResource resource ) { final boolean isJavaSourceFile = resource . getName () . endsWith ( " . java " ) ; if ( resource instanceof IFile && isJavaSourceFile ) { // create the AST for IFile and perform changes on it } } }

For each file under visiting, we create the AST from it, perform changes on the AST, and flush the changes back finally. The following code is excerpted from our implementation.

final ICompilationUnit i _co mp il at io n_ un it = JavaCore . c r e a t e C o m p i l a t i o n U n i t F r o m ( ifile ) ; final ASTParser parser = ASTParser . newParser ( AST . JLS3 ) ; parser . setKind ( ASTParser . K_ COM PI LA TI ON _U NI T ) ; parser . setSource ( i_c om pi la ti on _u ni t ) ; parser . s et B i nd i n gs R e co v e ry ( true ) ; parser . set Re so lv eB in di ng s ( true ) ; final CompilationUnit compilationUnit = ( CompilationUnit ) parser . createAST ( null ) ;

The compilationUnit declared in the last statement is the root of an AST on which we will operate. The AST can contain a total of 82 different types of nodes which are all subclasses of the Abstract superclass ASTNode. The class AST is the umbrella owner of all AST nodes and is an abstract syntax tree node factory which allows clients to create new nodes using the AbstractFactory 37

pattern [14]. To manipulate the AST, we again resort to the Visitor pattern that is supported by the class ASTVisitor. A sample code is shown below:

public class HaveAstVisitor extends ASTVisitor { public boolean visit ( A n n o t a t i o n T y p e D e c l a r a t i o n node ) { return true ; } ...... public boolean visit ( WildcardType node ) { return true ; } public boolean endVisit ( A n n o t a t i o n T y p e D e c l a r a t i o n node ) { return true ; } ...... public boolean endVisit ( WildcardType node ) { return true ; } }

To create a new ASTNode, one can invoke the methods from the AbstractFactory class AST and provide necessary arguments. There are 2 ways to insert, replace and delete a node in the AST. One way is to directly modify the AST using the utility methods provided by each AST node. The other approach is to use the class ASTRewrite which remembers the changes to the AST and rewrite the changes back to the AST at once at the end of manipulation. The latter way is preferred because it does not modify the original AST until the end of manipulation. With the ASTRewrite, we can continue to have access to the untampered AST while the first approach would reflect the modification immediately on the AST. For detailed Eclipse API documentation, one can refer to [7]. 38

7.3.1

Multi-pass instrumentation

Even with the ability to remember the modifications made to the AST using ASTRewrite, it is still infeasible to instrument all the necessary event monitoring code in a single traversal of the AST. The reason is there are many conflicting operations on the AST and many changes to the AST would be invalidated by some later changes on the related regions. For example, suppose we want to intercept the field access events in the assignment expression a.b.c.f = g.h. There are three field reads (namely, a.b, (a.b).c, g.h) and one field write ((a.b.c).f) in this expression. We use a top-down AST traversal to iteratively instrument the expressions a.b, (a.b).c, g.h and (a.b.c).f = g.h. However, by the time we instrument the last field write (a.b.c).f, the changes made by our previous steps have destroyed the old expression (a.b.c).f = g.h into a form such that the ASTNode we are manipulating is no longer valid. This situation occurs if we use the direct AST modification or ASTRewrite. To get around this problem, we instead adopt the incremental instrumentation scheme that incrementally makes the changes to the AST, updates the source, and rebuilds the up-to-date AST. To instrument the expression mentioned above (a.b.c.f = g.h), we would restrict one traversal of the AST to instrumenting field reads such that a.b, (a.b).c and g.h are instrumented. On the other traversal, we build an up-to-date AST from the source after the previous instrumentation and traverse it to make the instrumentations on field writes. The same idea applies to the instrumentation on the method entrances and exits, synchronization events, control flow structures.

39

7.4

Dynamic Monitoring

After instrumenting the source code files, we recompile them together with our runtime dynamic library into an executable program. Then we execute the instrumented program within which our instrumented code emits various events to the dynamic monitor. The dynamic monitor receives the relevant events emitted from the target program, records them into a specific data structure and performs the off-line analysis when the program terminates. In order to differentiate the events from different threads, we maintain a thread monitor for each running thread(including the Main thread) from the program in the run time. To enable HAVE to perform the off-line analysis after the target program’s termination, we use the Java shutdown hook facility provided by JVM. We implement our analysis framework as a Java class that extends the Thread class and insert an instance of the framework class into the Java runtime using the library API Runtime.getRuntime().addShutdownHook(Have.getSingletonHave()) before the program runs. By the time the program terminates, the Java runtime notifies all the shutdown hooks that are registered within it so that all those shutdown hooks will have a final chance to clean up or perform the last step operation. It is through this stage that we perform off-line analysis.

7.5

Runtime Optimization Techniques

7.5.1

Optimizations using Escape Information

We instrument a boolean field called isEscaped to every declared class in the source and initialize it to false. In the runtime, we use the reflection mechanism provided by 40

the library java.lang.reflect.* to dynamically retrieve and update its value. Every time the dynamic monitor encounters a instance field access, it checks the object instance’s isEscaped field first to see if it is true. If isEscaped is false, then we ignore such field access. The code for checking it is provided below

public static boolean isEscaped ( Object object ) { boolean result = false ; try { final Field field = object . getClass () . getField ( ASTUtility . isEscaped ) ; field . setAccessible ( true ) ; result = field . getBoolean ( object ) ; } catch ( final Exception e ) { return result ; } return result ; }

By default, no object instance escapes at the program’s start. As the program executes, we dynamically update the escape information when the scenarios described in 5.2.1 happen. The sample code for updating the isEscaped field of an object is provided below:

public static void markObje ctEsca ped ( Object object ) { if ( object == null ) { return ; } if ( object . getClass () . isArray () ) { D y n a m i c E s c a p e D e t e c t o r . markArrayEscaped ( object ) ; } else if ( object instanceof Collection ) { if ( Re flecti onUtil ity . setFieldEscaped ( object , ASTUtility . isEscaped ) ) { return ; }

41

D y n a m i c E s c a p e D e t e c t o r . markMemberFields ( object ) ; D y n a m i c E s c a p e D e t e c t o r . m a r k C o l l e c t i o n E l e m e n t s E s c a p e d ( object ) ; } else if ( object instanceof Map ) { if ( Re flecti onUtil ity . setFieldEscaped ( object , ASTUtility . isEscaped ) ) { return ; } D y n a m i c E s c a p e D e t e c t o r . markMemberFields ( object ) ; D y n a m i c E s c a p e D e t e c t o r . m a r k M a p E l e m e n t s E s c a p e d ( object ) ; } else { try { final Field field = object . getClass () . getField ( ASTUtility . isEscaped ) ; field . setAccessible ( true ) ; final boolean isEscaped = field . getBoolean ( object ) ; if ( isEscaped ) { // System . out . println (" already escaped ") ; return ; } field . set ( object , new Boolean ( true ) ) ; } catch ( final N o S u c h F i e l d E x c e p t i o n e ) { // e . printStackTrace () ; return ; } catch ( final Il l e g a l A c c e s s E x c e p t i o n e ) { // e . printStackTrace () ; return ; } catch ( final St ac kO ve rf lo wE rr or e ) { // e . printStackTrace () ; return ; } // this includes inherited fields D y n a m i c E s c a p e D e t e c t o r . markMemberFields ( object ) ; } }

For any array whose elements are of non-primitive(i.e., not the one of the following types boolean, double, float, char, short, long, void), if the array object escapes, then all its elements escape, too. The following code recursively marks the isEscaped fields of all its element objects.

42

public static void markArrayEscaped ( Object object ) { final int arrayLength = Array . getLength ( object ) ; boolean isPrimitive = object . getClass () . getComponentType () . isPrimitive () ; final boolean isArray = object . getClass () . getComponentType () . isArray () ; if (! isPrimitive ) { if ( isArray ) { for ( int i = 0; i < arrayLength ; i ++) { final Object arrayElement = Array . get ( object , i ) ; D y n a m i c E s c a p e D e t e c t o r . markArrayEscaped ( arrayElement ) ; } } else { for ( int i = 0; i < arrayLength ; i ++) { final Object arrayElement = Array . get ( object , i ) ; D y n a m i c E s c a p e D e t e c t o r . mark Object Escap ed ( arrayElement ) ; } } } }

The same concept applies to the objects of Container types(i.e., any class that implements the Collection or Map interface such as HashMap, Vector, List) The following two methods recursively marks the elements inside the Container.

public static void m a r k C o l l e c t i o n E l e m e n t s E s c a p e d ( Object object ) { if ( object == null ) { return ; } synchronized ( object ) { final Collection collection = ( Collection ) object ; final Iterator collIt = collection . iterator () ; while ( collIt . hasNext () ) { final Object elementObject = collIt . next () ; D y n a m i c E s c a p e D e t e c t o r . mark Objec tEscap ed ( elementObject ) ; } } } public static void m a r k M a p E l e m e n t s E s c a p e d ( Object object ) { if ( object == null ) { return ; }

43

synchronized ( object ) { final Map collection = ( Map ) object ; final Iterator keyIterator = collection . keySet () . iterator () ; while ( keyIterator . hasNext () ) { final Object keyObject = keyIterator . next () ; D y n a m i c E s c a p e D e t e c t o r . mark Objec tEscap ed ( keyObject ) ; final Object valueObject = collection . get ( keyObject ) ; D y n a m i c E s c a p e D e t e c t o r . mark Objec tEscap ed ( valueObject ) ; } } }

7.5.2

Optimizations on Thread Monitor

One naive approach to implement the thread monitor is to maintain a map of threads and their thread monitors. Whenever the dynamic monitor receives a event, we know the event’s thread ID and look up this thread ID in the map to obtain its corresponding thread monitor. This approach has proved to be computationally expensive in our experience. The reason is that map lookups involve many instructions and thus are expensive. It is expected that a program may generate thousands of events and these lookups produce unacceptable overhead to the running time. We address this problem by embedding the thread monitor into the thread. We create our own ThreadWrapper class that extends Thread and contains the thread monitor. Then we rewrite any source Thread class (i.e., any that class implements the Runnable interface or extends the Thread in the source) to extend our ThreadWrapper. For example, the original source class might look like the following

public class TspSolver extends Thread { ... }

44

After the rewriting, it becomes

public class TspSolver extends ThreadWrapper { ... }

Here is some excerpted code from the ThreadWrapper we implemented in HAVE

public class ThreadWrapper extends Thread { private static ThreadWrapper m ainThr eadWra pper = new ThreadWrapper () ; public static ThreadWrapper getCurrentThread () { final Thread currentThread = Thread . currentThread () ; if ( currentThread instanceof ThreadWrapper ) { return ( ThreadWrapper ) currentThread ; } return ThreadWrapper . ma inThre adWrap per ; } Th re ad Ev en tM on it or tem = new T hre ad Ev en tM on it or () ; public ThreadWrapper () { super () ; } ... public ThreadWrapper ( Runnable command ) { super ( command ) ; } public ThreadWrapper ( Runnable target , String name ) { super ( target , name ) ; } public void start () { super . start () ; } }

Every Java program always has a Main thread from which other threads are created. We are unable to rewrite the source to create a ThreadWrapper for the Main thread. Instead, we create a MainThreadWrapper for the Main thread dynamically. Every

45

time our dynamic monitor receives an event, we invoke getCurrentThread() to see whether it is from the Main thread or other threads. If it is not from Main thread, we reissue the event to the current thread’s thread monitor. If the event is from the Main thread, we issue it to the particular MainThreadWrapper we create for Main thread. This approach only involves an additional method call and a single conditional testing. This optimization technique has reduced the runtime overhead significantly that most programs do not experience any sharp performance drop. The programs monitored using the first approach almost never terminate in a reasonable amount of time. (i.e., 2 hours).

7.6

Instrumentation Details

We ignore the field reads/writes on the following fields: 1. The length field of every array object. It is a read-only field. 2. Fields that are not defined in the source (i.e., fields that are defined from the library). 3. Fields that are declared to volatile or final. We ignore the instrumentation on the run method of every class that implements Runnable or extends Thread.

7.7

A Brief HAVE Tutorial

HAVE comes with 2 flavors of user interface: GUI and command line. The latter is mainly for the impatient programmer who frequently uses a script and shell to avoid 46

the repetitive nature of typing. In order to use HAVE, one must complete the following steps: 1. Copy the HAVE plug-in ”edu.uwyo.have.jar” into the ”plug-in” directory of the Eclipse one will use. 2. Choose a workspace that Eclipse will use.

Eclipse must operate on some

workspace since it is used to store user preferences and local data . An user can have multiple workspaces for different developments. (e.g., one workspace for C++, another one for Java) 3. Create a Java project for the target program on which the user will perform the atomicity violation analysis. Then the user can either use the command or the GUI to perform the static analysis on the project.

7.7.1

Command line

The command line access allows the user to pass some additional arguments to start Eclipse and to perform the atomicity analysis. We

achieve

it

by

declaring

the

application

extension

point

org.eclipse.core.runtime.applications in ”plugin.xml” and implementing the interface org.eclipse.equinox.app.IApplication. To quote from Eclipse documentation [7]: Platform runtime supports plug-ins which would like to declare main entry points. That is, programs which would like to run using the platform runtime but yet control all aspects of execution can declare themselves 47

Figure 7.1: Example:An sample package view of all projects under a workspace as an application. Declared applications can be run directly from the main platform launcher by specifying the -application argument where the parameter is the id of an extension supplied to the applications extension point described here. This application is instantiated and run by the platform. Platform clients can also use the platform to lookup and run multiple applications. An usage example is shown below: eclipse -application edu.uwyo.have.SelectiveInstrumentorApps project=instrumented-BankExample -verbose -data ..\..\projects\eclipse-workspace\debug 48

The example assumes that the executable eclipse is already in your Operating System’s PATH environment variable. The option ”-application” specifies the application (We have different flavors of HAVE for varying purposes) which you want to use, for our case, it is ”edu.uwyo.have.SelectiveInstrumentorApps”. The ”-data” and ”project” provides the workspace path and the target project name under that workspace.

7.7.2

GUI

We create the simple GUI for HAVE by declaring the extension point org.eclipse.ui.actionSets and implementing the interface org.eclipse.ui.IWorkbenchWindowActionDelegate. The GUI can be quite intuitive to even the most amateur programmers. One just needs to start Eclipse as usual and make some preference selection in HAVE preference selection dialog from ”Preferences” in Eclipse. Finally, the user can click the small icon

on the toolbar and specify the target project name to proceed.

49

Chapter 8 Related Work 8.1

Dynamic Analysis

The most closely related work is Wang’s commit-node algorithm in [29], which is purely dynamic. The main contribution of this thesis is to extend it to a hybrid algorithm that combines static and dynamic analyses. This thesis also presents a new optimization to the algorithm. Dynamic algorithms to detect atomicity violations can be classified into two categories, based on whether they aim to detect potential atomicity violations (i.e., whether any feasible permutation of an observed trace is unserializable), or actual atomicity violations (i.e., whether an observed trace is unserializable). The algorithms to detect potential atomicity violations include this thesis, Wang and Stoller’s reduction-based, block-based algorithms, commit-node algorithms, [27, 30, 29], and Flanagan and Freund’s reduction-based algorithm [10], which is similar to Wang and Stoller’s reduction-based algorithm. Xu et al. infer computation units (subcomputations that the programmer might expect to be atomic) based on data and control dependencies and report an atomicity violation when an unserializable write by an50

other thread is interleaved in a computation unit [31]. Lu et al.’s AVIO system learns access interleaving invariants as indications of programmers’ likely expectations about atomicity and reports an atomicity violation when an observed interleaving violates an access interleaving invariant [19]. Flanagan et al. developed a sound and complete atomicity violation detector based on analysis of exact dependencies between operations [12]. Farzan and Madhusudan developed a space-efficient algorithm for detecting atomicity violations [8]. Park and Sen propose a randomized dynamic analysis technique that greatly increases the probability that a special class of potential atomicity violations will manifest as actual atomicity violations [20].

8.2

Static Analysis

Static analyses have been developed to infer or verify atomicity of code segments, e.g., [26, 13, 11, 28]. Static analysis gives stronger guarantees, because it considers all possible behaviors of a program, but is typically more restrictive or reports more false alarms than dynamic analysis. Model checking can also be used to check atomicity [9, 16, 8]. Model checking also provides strong guarantees but is feasible only for programs with relatively small state spaces.

8.2.1

Hybrid Analysis

Static and dynamic analyses can be combined in various ways for atomicity checking. Agarwal, Sasturkar, Wang, and Stoller used static analysis to reduce the overhead of the reduction-based algorithm [22] and the block-based algorithm [1]. JPredictor uses static analysis to improve the accuracy of the dependency relation used in dynamic

51

checking for potential concurrency errors, including atomicity violations [3]. Those techniques, in contrast to ours, do not use speculative execution.

8.3

Escape Analysis

Most existing static escape analyses [5], [24] and [21] apply points-to and interprocedural analysis on program source code or byte code to identify thread-local objects and fields. They are usually very expensive and tend to report many false positives due to the difficulty of reconciling the symbolic references with the actual memory locations. To the best of our knowledge, none of them can deal with the container(i.e., Collections and Maps) escape case. For example, if a container object escapes, then all objects contained inside this object are considered escaped by static analysis, which might be false positives, since some objects may be never accessed by other threads. Dynamic escape analyses (e.g.[6] and [18]) monitor accesses to objects and fields during execution and identify the escape objects and fields if they have been observed to be accessed by multiple threads. This is more accurate but suffers from the incompleteness due to the fact that not all code will be executed.

52

Chapter 9 Conclusions and Future Work This thesis describes a new approach to enhance dynamic analysis with results from static analysis to make the dynamic analysis more effective at finding subtle atomicity violations, by augmenting the dynamic analysis to consider some of the behavior of unexecuted branches in the program. This is significant because software testing rarely achieves full code coverage in practice. In our experiments, our hybrid conflict-edge algorithm scales almost as well as our previous dynamic algorithm [29]. Comparing our results in Figure 6 with results for those benchmarks in other papers [29, 30, 13, 10, 8, 12], our system detects all the atomicity violations detected by the purely dynamic algorithms described in those other papers and, for some benchmarks, detects additional atomicity violations. Directions for future work include extending the static analysis to be inter-procedural, taking the predicates guarding branches into account, incorporating more sophisticated approaches to identify transaction boundaries, , using a test case generator to generate inputs that lead to execution of speculative events involved in atomicity violations to verify that they are not false alarms, employ more accurate and less expensive escape analysis to mitigate the overhead of our analysis. 53

Bibliography [1] Rahul Agarwal, Amit Sasturkar, Liqiang Wang, and Scott D. Stoller. Optimized run-time race detection and atomicity checking using partial discovered types. In Proc. 20th IEEE/ACM International Conference on Automated Software Engineering (ASE). ACM Press, November 2005. [2] Rahul Agarwal, Liqiang Wang, and Scott D. Stoller. Detecting potential deadlocks with static analysis and runtime monitoring. In Proceedings of the Parallel and Distributed Systems: Testing and Debugging (PADTAD). Springer-Verlag, November 2005. [3] Feng Chen, Traian Florin Serbanuta, and Grigore Rosu. jpredictor: a predictive runtime analysis tool for java. In ICSE ’08: Proceedings of the 30th international conference on Software engineering, pages 221–230, New York, NY, USA, 2008. ACM. [4] Qichang Chen, Liqiang Wang, Zijiang Yang, and Scott Stoller. HAVE: Detecting Atomicity Violations via Integrated Dynamic and Static Analysis. In International Conference on Fundamental Approaches to Software Engineering (FASE), European Joint Conferences on Theory and Practice of Software (ETAPS), York, UK, March 2009. Springer-Verlag. (Acceptance Rate: 24%).

54

[5] Jong-Deok Choi, Manish Gupta, Mauricio J. Serrano, Vugranam C. Sreedhar, and Samuel P. Midkiff. Stack allocation and synchronization optimizations for java using escape analysis. ACM Trans. Program. Lang. Syst., 25(6):876–910, 2003. [6] Matthew B. Dwyer, John Hatcliff, Robby, and Venkatesh Prasad Ranganath. Exploiting object escape and locking information in partial-order reductions for concurrent object-oriented programs. Formal Methods in System Design, 25(23):199–240, 2004. [7] Eclipse

api

documentation,

3.4

ganymede.

Available

from

http://help.eclipse.org/ganymede/index.jsp. [8] Azadeh Farzan and P. Madhusudan. Monitoring atomicity in concurrent programs. In CAV ’08: Proceedings of the 20th international conference on Computer Aided Verification, pages 52–65, Berlin, Heidelberg, 2008. Springer-Verlag. [9] Cormac Flanagan. Verifying commit-atomicity using model-checking. In Proc. 11th Int’l. SPIN Workshop on Model Checking of Software, volume 2989 of LNCS, pages 252–266. Springer-Verlag, 2004. [10] Cormac Flanagan and Stephen N. Freund. Atomizer: A dynamic atomicity checker for multithreaded programs. In Proc. ACM Symposium on Principles of Programming Languages (POPL), pages 256–267. ACM Press, 2004. [11] Cormac Flanagan, Stephen N. Freund, and Shaz Qadeer. Exploiting purity for atomicity. IEEE Transactions on Software Engineering, 31(4), April 2005. [12] Cormac Flanagan, Stephen N. Freund, and Jaeheon Yi. Velodrome: a sound and complete dynamic atomicity checker for multithreaded programs. In Proceedings 55

of the 2008 ACM SIGPLAN conference on Programming language design and implementation (PLDI ’08), pages 293–303, New York, NY, USA, 2008. ACM. [13] Cormac Flanagan and Shaz Qadeer. A type and effect system for atomicity. In Proc. ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). ACM Press, 2003. [14] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design patterns: elements of reusable object-oriented software. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1995. [15] James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. Java Language Specification, Third Edition: The Java Series. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2005. [16] John Hatcliff, Robby, and Matthew B. Dwyer. Verifying atomicity specifications for concurrent object-oriented software using model-checking. In VMCAI2004, volume 2937 of LNCS. Springer-Verlag, January 2004. [17] Jigsaw, version 2.2.6. Available from http://www.w3c.org. [18] Kyungwoo Lee, Xing Fang, and Samuel P. Midkiff. Practical escape analyses: how good are they? In VEE ’07: Proceedings of the 3rd international conference on Virtual execution environments, pages 180–190, New York, NY, USA, 2007. ACM. [19] Shan Lu, Joseph Tucek, Feng Qin, and Yuanyuan Zhou. Avio: detecting atomicity violations via access interleaving invariants. In Twelfth International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS). ACM Press, 2006. 56

[20] Chang-Seo Park and Koushik Sen. Randomized active atomicity violation detection in concurrent programs. In Proceedings of the 16th ACM SIGSOFT International Symposium on Foundations of software engineering (FSE’08), pages 135–145, New York, NY, USA, 2008. ACM. [21] Alexandru Salcianu and Martin Rinard. Pointer and escape analysis for multithreaded programs. In Proc. ACM SIGPLAN 2001 Symposium on Principles and Practice of Parallel Programming (PPoPP). ACM Press, 2001. [22] Amit Sasturkar, Rahul Agarwal, Liqiang Wang, and Scott D. Stoller. Automated type-based analysis of data races and atomicity. In Proc. ACM SIGPLAN 2005 Symposium on Principles and Practice of Parallel Programming (PPoPP). ACM Press, June 2005. [23] L. A. Smith and J. M. Bull. A multithreaded java grande benchmark suite. In In Proceedings of the Third Workshop on Java for High Performance Computing, pages 97–105, 2001. [24] Zehra Sura, Xing Fang, Chi-Leung Wong, Samuel P. Midkiff, Jaejin Lee, and David Padua. Compiler techniques for high performance sequentially consistent java programs. In PPoPP ’05: Proceedings of the tenth ACM SIGPLAN symposium on Principles and practice of parallel programming, pages 2–13, New York, NY, USA, 2005. ACM. [25] Christoph von Praun and Thomas R. Gross. Object race detection. In Proc. 16th ACM Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA), volume 36(11) of SIGPLAN Notices, pages 70–82. ACM Press, October 2001.

57

[26] Christoph von Praun and Thomas R. Gross. Static detection of atomicity violations in object-oriented programs. In Journal of Object Technology, vol.3, no. 6, June 2004. [27] Liqiang Wang and Scott D. Stoller. Run-time analysis for atomicity. In Third Workshop on Runtime Verification (RV03), volume 89(2) of Electronic Notes in Theoretical Computer Science. Elsevier, 2003. [28] Liqiang Wang and Scott D. Stoller. Static analysis for programs with nonblocking synchronization. In Proc. ACM SIGPLAN 2005 Symposium on Principles and Practice of Parallel Programming (PPoPP). ACM Press, June 2005. [29] Liqiang Wang and Scott D. Stoller. Accurate and efficient runtime detection of atomicity errors in concurrent programs. In Proc. ACM SIGPLAN 2006 Symposium on Principles and Practice of Parallel Programming (PPoPP). ACM Press, March 2006. [30] Liqiang Wang and Scott D. Stoller. Runtime analysis of atomicity for multithreaded programs. IEEE Transactions on Software Engineering, 32(2):93–110, February 2006. [31] Min Xu, Rastislav Bodik, and Mark D. Hill. A serializability violation detector for shared-memory server programs. In Proc. ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI). ACM Press, 2005.

58

Detecting Atomicity Violations via Integrated Dynamic ...

olations in multi-threaded Java programs and integrated it with the dynamic thread escape analysis to improve the performance and accuracy. Experimental evaluations ... 31. 7 Implementation. 34. 7.1 Eclipse and JDT . ..... hybrid conflict-edge tool instrumented code dynamic trees dynamic monitor trees algorithm atomicity ...

658KB Sizes 1 Downloads 152 Views

Recommend Documents

Traffic Violations
People are different in their nature, behaviour and their rasing, so they differ by the degree of following laws and rules. However, to stop people from breaking laws and rules, some different penalties are made to protect people and saves their mone

Detecting Recompression of JPEG Images via ...
have been developed for detecting tampering in digital images. Some methods ... spatial or frequency domain, as an inherent signature for JPEG images [14-18] ...

of superiority violations
fronting languages, and how this might best be accounted for. For example .... both fixed and free argument structure within the vP/VPs of a single language, it.

Asymptotic Tracking for Uncertain Dynamic Systems via ...
pp. 1973–1991, 2006. Asymptotic Tracking for Uncertain Dynamic Systems. Via a Multilayer Neural Network Feedforward and. RISE Feedback Control Structure.

Sensorimotor coupling via Dynamic Bayesian Networks
hand movements and a high degree of voluntary attentional control in directing fixations .... a function α(d) that associates the data with an hypothesis. A ..... Computer Science Division (2002). [6] A. De ... movements. Science, 265 540-543, 1994.

Parallel Dynamic Tree Contraction via Self-Adjusting ...
rithm for the dynamic trees problem, which requires computing ... This problem requires computing various prop- ..... Symposium on Cloud Computing, 2011.

An Integrated Static and Dynamic Approach for Thread ...
Department of Computer Science. University of ..... Software engineering, pages 221–230, New York, NY,. USA, 2008. ACM. 7 ... In VM'04: Pro- ceedings of the ...

Supporting Transactional Atomicity in Flash Storage ...
way. X-FTL drastically improves the transactional throughput almost for free without resorting to costly journaling schemes. .... SQLite operates usually in either rollback mode [3] or write-ahead ... SQLite invokes fsync system calls more often when

Weak Atomicity Under the x86 Memory Consistency ...
Feb 16, 2011 - Programming Techniques Concurrent Programming. General Terms ... In contrast to SLA work in these language level mem- ory models, there ...

Testing for Violations of Microscopic Reversibility in ...
CA) was cotransfected as a marker with the cDNA of interest using the FuGENE .... likelihood fitting of each file (∼2.1 × 105 events) is 6.5 ( 0.1. (N ) 5, S.D.) with ...

On the k-Atomicity-Verification Problem - Research at Google
For example, in a social network, a user may still ..... Proof: We maintain H as a doubly linked list sorted ... linked list of all of w's dictated reads; for each of these.

Updating Beliefs With Causal Models: Violations of ...
that it does not trace back in one way or another to Gordon Bower. Gordon is like ... Call that set of relevant states R. Then mental states prior to the critical states ...

Weak Atomicity Under the x86 Memory Consistency ...
Feb 16, 2011 - Keywords Software Transactional Memory, x86 Memory Model. 1. Introduction ... C++: Catch fire due to data race, any result allowed ... clude only programs with Transactional Reads Unprotected Writes. Copyright is held by ...

Finding Software License Violations Through Binary Code ... - NixOS
May 21, 2011 - 1The gpl-violations.org Project & Tjaldur Software Governance Solutions ..... Apply this to the Apple App Store / Android Marketplace.

State GST Authorities Can't Look Into Violations Of Lottery Regulations ...
Apr 13, 2018 - DISTRICT POLICE CHIEF/SUPERINTENDENT OF POLICE,. PALAKKAD - 679 001. 7. CIRCLE INSPECTOR OF POLICE,. KASABA POLICE STATION, PALAKKAD - 679 001 ... ....2/-. Page 3 of 47. Main menu. Displaying State GST Authorities Can't Look Into Viola

Detecting Electricity Theft - Patrick GLAUNER
Imbalance of the data, meaning that there are significantly more regular customers than ... machine learning, deep learning, anomaly detection and big data.

Detecting Wikipedia Vandalism using WikiTrust
Abstract WikiTrust is a reputation system for Wikipedia authors and content. WikiTrust ... or USB keys, the only way to remedy the vandalism is to publish new compilations — incurring both ..... call agaist precision. The models with β .... In: SI

Via 1617(a) @ via ac97 enhanced audio controller driver download ...
Hp laserjet p1005 drivers windows 7 free download.Intrudactoun tuVia ... Download printer driver for hp officejet pro 8500.288489471588365.Dell 968 aio printer driver windows 7.Download Via ... Canon dr-3060 scsiscanner device driver.

Via Valeriana.pdf
... integratori alimentari, macchina fotografica,. maglietta tecnica di ricambio, occhiali e cappello da sole. Ai partecipanti iscritti verranno forniti i contatti telefonici.

HOW DYNAMIC ARE DYNAMIC CAPABILITIES? 1 Abstract ...
Mar 11, 2012 - superior performance. The leading hypothesis on performance is deemed to be that of sustainable competitive advantage, (Barney 1997).

Isomorphism via full groups
Suppose that X is a Polish space and E is a countable Borel equivalence relation on X. The full group of E is the group [E] of Borel automorphisms f : X → X such that graph(f) ⊆ E. The full semigroup of E is the semigroup [E] of Borel isomorphism