diff -r a37b7de54d5b src/com/enea/jcarder/agent/EventListener.java --- a/src/com/enea/jcarder/agent/EventListener.java Sat Feb 21 18:37:42 2009 +0100 +++ b/src/com/enea/jcarder/agent/EventListener.java Thu Oct 22 00:20:44 2009 +0200 @@ -21,7 +21,12 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; import net.jcip.annotations.ThreadSafe; @@ -41,7 +46,15 @@ private final LockingContextIdCache mContextCache; private final Logger mLogger; private final Counter mNumberOfEnteredMonitors; + + private final Map monitorInfoCache = new HashMap(); + private final List filterClassLevel = getFilterList("jcarder.classLevel", false); + private final List filterInclude = getFilterList("jcarder.include", true); + + private final static Object sentinelInstanceLevel = new Object(); + private final static Object sentinelIgnore = new Object(); + public static EventListener create(Logger logger, File outputdir) throws IOException { EventFileWriter eventWriter = @@ -51,6 +64,16 @@ new ContextFileWriter(logger, new File(outputdir, CONTEXTS_DB_FILENAME)); return new EventListener(logger, eventWriter, contextWriter); + } + + private List getFilterList(String key, boolean defaultAcceptAll) { + final List ret = new ArrayList(); + + final StringTokenizer tok = new StringTokenizer(System.getProperty(key, defaultAcceptAll ? " " : ""), ","); + while (tok.hasMoreTokens()) { + ret.add(tok.nextToken().trim()); + } + return ret; } public EventListener(Logger logger, @@ -68,23 +91,104 @@ public void beforeMonitorEnter(Object monitor, LockingContext context) throws Exception { mLogger.finest("EventListener.beforeMonitorEnter"); - Iterator iter = mEnteredMonitors.getIterator(); - while (iter.hasNext()) { - Object previousEnteredMonitor = iter.next().getMonitorIfStillHeld(); - if (previousEnteredMonitor == null) { - iter.remove(); - } else if (previousEnteredMonitor == monitor) { - return; // Monitor already entered. + + //check ignoreFilter and switch to class level if the monitor + //is matched by classLevelFilter results are cached + Object classifiedMonitor = checkMonitor(monitor); + + if (classifiedMonitor != sentinelIgnore) { + Iterator iter = mEnteredMonitors.getIterator(); + while (iter.hasNext()) { + Object previousEnteredMonitor = iter.next().getMonitorIfStillHeld(); + if (previousEnteredMonitor == null) { + iter.remove(); + } else if (previousEnteredMonitor == monitor) { + return; // Monitor already entered. + } + } + enteringNewMonitor(monitor, classifiedMonitor, context); + } + } + + private Object checkMonitor(Object monitor) { + //the default behaviour is to treat monitor objects on instance level + //this was the old behaviour + Object classifiedMonitor = monitor; + + if (monitor != null) { + final Class cl = monitor.getClass(); + + //Try to use cache + Object firstOccurrence = monitorInfoCache.get(cl); + if (firstOccurrence == null) { + firstOccurrence = checkFilters(monitor, cl); + } + + //firstOccurence may have three states at this point: + //1. sentinelInstanceLevel: (default) the monitor should be + // handled for each instance + //2. sentinelIgnore: the monitor is not of interesst + //3. any other instance: is the first representation of a + // monitor of its class. class level handling + + if (firstOccurrence != sentinelInstanceLevel) { + //By using only one single instance for monitors of a certain + //class we simulate group level handling. + classifiedMonitor = firstOccurrence; } } - enteringNewMonitor(monitor, context); + return classifiedMonitor; + } + + private Object checkFilters(Object monitor, final Class cl) { + Object firstOccurrence; + final String clName = cl.getName(); + + //check if include filter matches + if (checkIncludeFilter(clName)) { + //check if class level filter matches + firstOccurrence = checkGroupLevelFilter(monitor, clName); + } else { + firstOccurrence = sentinelIgnore; + mLogger.info("ignoring: " + clName); + } + monitorInfoCache.put(cl, firstOccurrence); + return firstOccurrence; + } + + private boolean checkIncludeFilter(final String clName) { + boolean includeFilterMatch = false; + for (String filter : filterInclude) { + if (clName.startsWith(filter)) { + includeFilterMatch = true; + break; + } + } + return includeFilterMatch; + } + + private Object checkGroupLevelFilter(Object monitor, final String clName) { + Object firstOccurrence; + boolean classLevelFilterMatch = false; + for (String filter : filterClassLevel) { + if (clName.startsWith(filter)) { + classLevelFilterMatch = true; + break; + } + } + firstOccurrence = classLevelFilterMatch ? monitor : sentinelInstanceLevel; + if (classLevelFilterMatch) { + mLogger.info("instrumenting on class level: " + clName); + } + return firstOccurrence; } private synchronized void enteringNewMonitor(Object monitor, + Object classifiedMonitor, LockingContext context) throws Exception { mNumberOfEnteredMonitors.increment(); - int newLockId = mLockIdGenerator.acquireLockId(monitor); + int newLockId = mLockIdGenerator.acquireLockId(classifiedMonitor); int newContextId = mContextCache.acquireContextId(context); EnteredMonitor lastMonitor = mEnteredMonitors.getFirst(); if (lastMonitor != null) { diff -r a37b7de54d5b src/com/enea/jcarder/analyzer/LockEdge.java --- a/src/com/enea/jcarder/analyzer/LockEdge.java Sat Feb 21 18:37:42 2009 +0100 +++ b/src/com/enea/jcarder/analyzer/LockEdge.java Thu Oct 22 00:20:44 2009 +0200 @@ -95,8 +95,14 @@ } public int hashCode() { - // TODO Improve hashCode algorithm to improve performance? - return mTarget.getLockId() + mSource.getLockId(); + final int prime = 31; + int result = 1; + result = prime + mSource.getLockId(); + result = prime * result + mSourceContextId; + result = prime * result + mTarget.getLockId(); + result = prime * result + mTargetContextId; + result = prime * result + (int) (mThreadId ^ (mThreadId >>> 32)); + return result; } LockNode getTarget() {