370 likes | 386 Views
Motivating examples of runtime errors in software and how to improve heap query checking using parallel algorithms leveraging the garbage collector.
E N D
Parallel Checking of Expressive Heap Assertions Phalanx Greta Yorsh EranYahav Martin Vechev 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 Error org.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(booleanbForce) { // ... • gc.dispose(); } GC • gc • myGC Nativeresource protected void handleResize(booleanbForce) { // ... myGC.getClipping(…) }
If only I could check… protected void handleRefresh(booleanbForce) { // ... // @assert object pointed to by gc is not shared • gc.dispose(); } GC • gc • myGC Nativeresource protected void handleResize(booleanbForce) { // ... myGC.getClipping(…) }
Motivating Example II: jdbf public 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(...); }
Motivating Example II: jdbf Root ct Running Running Static Thread Database Thread If only I could check… @invariant: every conncetion is only reachable from one thread (avoiding connection manager) Stack Connection Manager Stack HashMap Connection Source Connection Source Connection Source Connection Connection Connection
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 trace-step(s; t) if (o = t) tm.sourcestm.sources { s } isShared(tm, o) tm.sources; mark-threads(tm, Ta) trace(tm) lock(allsources) allsourcesallsourcestm.sources unlock(allsources) if barrier-and-release-master() if |allsources| > 1 result true else result false release-blocked-evaluators()
Parallel Checking of isShared A r1 r2 B Is shared? Thread 1 t1.sources = { A } allsources = { A, B } Thread 2 t2.sources = { 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) target r1 source r2 r3 Phase 1: tag source
isObjectOwned(source,target) target r1 source r2 r3 Phase 2: mark roots (except target)
isObjectOwned(source,target) target r1 source r2 r3 Phase 3: trace from roots (except from source)
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
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) ; ... }