37
PHALANX Parallel Checking of Expressive Heap Assertions Greta Yorsh Martin Vechev Eran Yahav Bard Bloom IBM T.J. Watson Research Center

Parallel Checking of Expressive Heap Assertions Greta YorshMartin VechevEran YahavBard Bloom IBM T.J. Watson Research Center

Embed Size (px)

Citation preview

PHALANXParallel Checking of Expressive Heap Assertions

Greta Yorsh Martin VechevEran Yahav Bard Bloom

IBM T.J. Watson Research Center

Motivation

Unrestricted use of aliasing is evil Unrestricted use of aliasing in the

presence of concurrency is ultimate evil

Motivating Example: Azureus

Over 360 million downloads

Runtime Errororg.eclipse.swt.SWTException: Graphic is disposed

    at org.eclipse.swt.SWT.error(SWT.java:3744)    at org.eclipse.swt.SWT.error(SWT.java:3662)    at org.eclipse.swt.SWT.error(SWT.java:3633)    at org.eclipse.swt.graphics.GC.getClipping(GC.java:2266)    at com.aelitis.azureus.ui.swt.views.list.ListRow.doPaint(ListRow.java:260)    at com.aelitis.azureus.ui.swt.views.list.ListRow.doPaint(ListRow.java:237)    at com.aelitis.azureus.ui.swt.views.list.ListView.handleResize(ListView.java:867)    at com.aelitis.azureus.ui.swt.views.list.ListView$5$2.runSupport(ListView.java:406)    at org.gudy.azureus2.core3.util.AERunnable.run(AERunnable.java:38)    at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)    at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:130)    at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3323)    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2985)    at org.gudy.azureus2.ui.swt.mainwindow.SWTThread.<init>(SWTThread.java:183)    at org.gudy.azureus2.ui.swt.mainwindow.SWTThread.createInstance(SWTThread.java:67)….

What Happened?

protected void handleRefresh(boolean bForce) {

// ... gc.dispose(); }

protected void handleResize(boolean bForce) {

// ... myGC.getClipping(…) }

GC

gc myGC

Nativeresourc

e

If only I could check…

protected void handleRefresh(boolean bForce) { // ...

// @assert object pointed to by gc is not shared gc.dispose(); }

protected void handleResize(boolean bForce) {

// ... myGC.getClipping(…) }

GC

gc myGC

Nativeresourc

e

Motivating Example II: jdbfpublic class Database {

private ConnectionManager cm; public int insert(...) throws MappingEx { Connection c = cm.getConnection(...); ... } ...}public class ConnectionManager { private Map conns = Collections.synchronizedMap(new HashMap()); public Connection getConnection(String s) throws MappingException { try { ConnectionSource c = conns.get(s); if (c != null) return c.getConnection(); throw new MappingException(...); } catch (SQLEx e) { ... } }}}

public class ConnectionSource { private Connection conn; private boolean used; public Connection getConnection() throws SQLEx { if (!used) { used = true; return conn; } throw new SQLEx(...); }

Running

Thread

Stack

Database

Root

Running

Thread

Stack

HashMap

ConnectionSource

ConnectionSource

Connection Connection

Static

ConnectionSource

Connection

ConnectionManager

ct

Motivating Example II: jdbf

If only I could check…

@invariant: every conncetion is only

reachable from one thread

(avoiding connection manager)

Phalanx Challenges

Expressing heap queries Is object shared? Is object reachable? Is object reachable when avoiding paths

through some other objects? Is object owned? …

Checking heap queries at runtime

Expressing Heap Queries

Use JML Extended with additional primitives

reach(Object o, Object[] avoiding) pred(Object o) dom(Object o1,Object o2) …

Examples

Object o is shared

pred(o).size() > 1

Set of threads that can reach o, while avoiding objects in avoid:

{ Thread t | running().has(t) && (reach(t,avoid).has(o) || reach(stack(t),avoid).has(o)) }

Checking Heap Queries: Wish List

Support wide range of queries We have a nice extended JML +

primitives Overhead low enough to permit

running realistic applications Debugging Program understanding Maybe even production

Checking Heap Queries: First Attempt

We need to traverse the heap to answer our queries

The garbage collector (GC) already traverses the heap periodically

GC can be parallel and leverage available system cores

Piggyback the GC !

Crash Course: Tracing GC

r1

r2

Crash Course: Parallel Tracing GC

r1

r2

Thread 1

Thread 2

Checking Heap Queries:How can we use the GC? reach(o)

Know that objects are reachable, but not whether they are reachable from o

reach(o1,o2) With GC I would only know o1,o2 are

reachable from roots Now what?

reach(o1) reach(o2) = Requires two marked sets Now what?

Supported Primitives?

reach(Object o) pred(Object o) reach(Object o, Object[] avoiding) dom(Object o1,Object o2)

Some primitives cannot be computed by piggybacking a GC traversal

What can we do in parallel? Is object o shared?

pred(o).size() > 1

Disjoint reach set? reach(o1) reach(o2) =

Checking Heap Queries:Second Attempt

We need new parallel algorithms

We can use components of a parallel GC as building blocks for our parallel algorithms

New Algorithms Based on GC components

New parallel algorithms for common queries

New operations performed on GC steps New synchronization structures for

computing answers to heap queries

Leverage available system cores

Modified JMLC maps common queries to parallel implementations

Back to our example: isShared

isShared(tm, o)

tm.sources ;

mark-threads(tm, Ta)

trace(tm)

lock(allsources)

allsources allsources tm.sources

unlock(allsources)

if barrier-and-release-master()

if |allsources| > 1 result true

else result false

release-blocked-evaluators()

trace-step(s; t) if (o = t) tm.sources tm.sources { s }

Parallel Checking of isShared

r1

r2

Thread 1

Thread 2

Is shared?

t1.sources = { A }

A

B

t2.sources = { B }

allsources = { A, B }

isShared = true

isObjectOwned(source,target)

isObjectOwned(tm, source, target) { tag-object(tm, source) result false phase skip barrier() mark-roots(tm, Ta) barrier() phase none trace(tm) barrier() if (target Marked) barrier() push-object(tm; source) trace(tm) if barrier-and-release-master() if (target Marked) result true release-blocked-evaluators()}

tag-step(t) if (phase = skip t = target) return false

isObjectOwned(source,target)

source

r2

r1

r3

target

Phase 1: tag source

isObjectOwned(source,target)

source

r2

r1

r3

target

Phase 2: mark roots (except target)

isObjectOwned(source,target)

source

r2

r1

r3

target

Phase 3: trace from roots (except from source)

Parallel Algorithms Query Description Probe

pred(o).size() > 0 Is o pointed to by a heap object?

isHeap(Object o)

pred(o).size() > 1 Is o pointed to by two or more heap objects?

isShared(Object o)

reach(src).has(dst) Is dst reachable from src?

isReachable(Object src, Object dst)

!(exists Object v; reach(o1).has(v) ; reach(o2).has(v))

Is there an object reachable from both o1 and o2?

isDisjoint(Object o1, Object o2)

!(exists Object v ; reach(o).has(v) ; !dom(o,v))

Does o dominate all objects reachable from it?

isUniqueOwner(Object o)

!reach(o1,cut).has(o2) Does every path from o1 to o2 go through an object in cut

reachThrough(Object o1, o2, Object[] cut)

dom(Thread.currentThread(), o)

Does the current thread dominate o?

isObjectOwned(Object o1, Object o2)

Experimental Evaluation

Implemented on top of QVM platform IBM J9 production virtual machine Can leverage QVM adaptive overhead

manager (not in this talk) Provide a portable reference

implementation based on JVMTI Less efficient, no parallel algorithms Still useful in some cases

Modified JML Compiler

Speedup / #objects

0 2,000,000 4,000,000 6,000,000 8,000,000 10,000,000 0.00

1.00

2.00

3.00

4.00

5.00

6.00

7.00

JVMTI

QVM 1CPU

QVM 2CPU

QVM 3CPU

QVM 4CPU

QVM 5 CPU

QVM 6 CPU

QVM 7 CPU

QVM 8 CPU

# Objects

Speedup

Time / #cores

1 2 3 4 5 6 7 8 -

2,000

4,000

6,000

8,000

10,000

12,000

10,539

4,906

3,474

2,757 2,301

2,007 1,786 1,631

# Cores

Time (ms)

Speedup / #cores

1 2 3 4 5 6 7 8

1.00

2.15

3.03

3.82

4.58

5.25

5.90

6.46

# Cores

Speedup

Probes in Real Applications Disposal of Shared SWT Resources replace code of the form:

exp.dispose();

with code of the form

if (Phalanx.isShared(exp)) Phalanx.warning(”disposal of \ shared resource”+exp) ;

exp.dispose();

Probes in Real Applications Redundant Synchronization

replace code of the form:

synchronized(exp) { ... }

with code of the form

synchronized(exp) {

if(Phalanx.dom(Thread.currentThread(),exp)) Phalanx.warning(”synchronziation on \ an owned object”+exp) ;

...

}

Probes in Real ApplicationsApplicati

on LOC Probe

sViolatio

ns

AOI111,33

3 10 0

Azureus425,36

7 334 16

Freemind 70,483 16 2

Frostwire245,95

9 184 2

JEdit 93,790 66 0

jrisk 20,807 45 12

rssowl 74,280 95 3

tvbrowser105,47

1 40 1

TVLA 57,594 10 0

Summary

GC Details

The End