From: navid Date: Mon, 5 Jan 2009 18:19:39 +0000 (+0000) Subject: my version X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=21be2fb6bc78934837108cb7122c7e28fd79ffa4;p=IRC.git my version --- diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EnteredMonitor.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EnteredMonitor.java new file mode 100644 index 00000000..35b0772c --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EnteredMonitor.java @@ -0,0 +1,64 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import java.lang.ref.WeakReference; + +/** + * Each instance of this class represents an entered monitor. + * + * The reference to the monitor object is kept as a WeakReference internally in + * this class and won't prevent the monitor from being garbage collected. + * + * TODO Add basic test for the WeakReference handling. + */ +final class EnteredMonitor { + private final WeakReference mMonitorRef; + private final int mLockingContextId; + private final int mLockId; + + EnteredMonitor(Object monitor, + int lockId, + int lockingContextId) { + mLockId = lockId; + mLockingContextId = lockingContextId; + mMonitorRef = new WeakReference(monitor); + } + + Object getMonitorIfStillHeld() { + final Object monitor = mMonitorRef.get(); + /* + * TODO The call to Thread.holdsLock takes long time. It would be + * interesting to test how the performance would be affected if this + * code is removed and replaced by an event for each MonitorExit and + * each finished synchronized method. + */ + if (monitor != null && Thread.holdsLock(monitor)) { + return monitor; + } else { + return null; + } + } + + int getLockingContextId() { + return mLockingContextId; + } + + int getLockId() { + return mLockId; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListener.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListener.java new file mode 100644 index 00000000..7d4acdb3 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListener.java @@ -0,0 +1,159 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import static com.enea.jcarder.common.contexts.ContextFileReader.EVENT_DB_FILENAME; +import static com.enea.jcarder.common.contexts.ContextFileReader.CONTEXTS_DB_FILENAME; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +//import net.jcip.annotations.ThreadSafe; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextFileWriter; +import com.enea.jcarder.common.contexts.ContextWriterIfc; +import com.enea.jcarder.common.events.EventFileWriter; +import com.enea.jcarder.common.events.LockEventListenerIfc; +import com.enea.jcarder.util.Counter; +//import com.enea.jcarder.util.TransactionalCounter; +import com.enea.jcarder.util.TransactionalCounter; +import com.enea.jcarder.util.logging.Logger; + +import dstm2.Thread; + +//@ThreadSafe +import java.util.Vector; +import java.util.concurrent.Callable; +final class EventListener implements EventListenerIfc { + private final ThreadLocalEnteredMonitors mEnteredMonitors; + private final LockEventListenerIfc mLockEventListener; + private final LockIdGenerator mLockIdGenerator; + private final LockingContextIdCache mContextCache; + private final Logger mLogger; + private final Counter mNumberOfEnteredMonitors; + + + private final TransactionalCounter trmNumberOfEnteredMonitors; + + public static EventListener create(Logger logger, File outputdir) + throws IOException { + EventFileWriter eventWriter = + new EventFileWriter(logger, + new File(outputdir, EVENT_DB_FILENAME)); + ContextFileWriter contextWriter = + new ContextFileWriter(logger, + new File(outputdir, CONTEXTS_DB_FILENAME)); + return new EventListener(logger, eventWriter, contextWriter); + } + + public EventListener(Logger logger, + LockEventListenerIfc lockEventListener, + ContextWriterIfc contextWriter) { + mLogger = logger; + mEnteredMonitors = new ThreadLocalEnteredMonitors(); + mLockEventListener = lockEventListener; + mLockIdGenerator = new LockIdGenerator(mLogger, contextWriter); + mContextCache = new LockingContextIdCache(mLogger, contextWriter); + mNumberOfEnteredMonitors = + new Counter("Entered Monitors", mLogger, 100000); + + trmNumberOfEnteredMonitors = + new TransactionalCounter("Entered Monitors", mLogger, 100000); + } + + 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. + } + } + //enteringNewMonitor(monitor, context); + enteringNewMonitor(monitor, context); + + } + + private synchronized void enteringNewMonitor(Object monitor, + LockingContext context) + throws Exception { + mNumberOfEnteredMonitors.increment(); + int newLockId = mLockIdGenerator.acquireLockId(monitor); + int newContextId = mContextCache.acquireContextId(context); + + EnteredMonitor lastMonitor = mEnteredMonitors.getFirst(); + if (lastMonitor != null) { + java.lang.Thread performingThread = Thread.currentThread(); + mLockEventListener.onLockEvent(newLockId, + newContextId, + lastMonitor.getLockId(), + lastMonitor.getLockingContextId(), + performingThread.getId()); + } + mEnteredMonitors.addFirst(new EnteredMonitor(monitor, + newLockId, + newContextId)); + + } + + private synchronized void trenteringNewMonitor(Object monitor, + Vector context) + throws Exception { + mNumberOfEnteredMonitors.increment(); + int newLockId = mLockIdGenerator.acquireLockId(monitor); + int newContextId = mContextCache.acquireContextId((LockingContext)context.get(0)); + + EnteredMonitor lastMonitor = mEnteredMonitors.getFirst(); + if (lastMonitor != null) { + java.lang.Thread performingThread = Thread.currentThread(); + Vector args = new Vector(); + args.add(newLockId); + args.add(newContextId); + args.add(lastMonitor.getLockId()); + args.add(lastMonitor.getLockingContextId()); + args.add(performingThread.getId()); + + mLockEventListener.tronLockEvent(args); + } + mEnteredMonitors.addFirst(new EnteredMonitor(monitor, + newLockId, + newContextId)); + + } + + private void trenteringNewMonitorWrapper(Object monitor, + LockingContext context) + throws Exception { + final Vector arguments = new Vector(); + arguments.add(context); + Thread.doIt(new Callable() { + public Boolean call() throws Exception { + trenteringNewMonitor(mLogger, arguments); + return true; + } + }); + + + + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListenerIfc.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListenerIfc.java new file mode 100644 index 00000000..f62580b8 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListenerIfc.java @@ -0,0 +1,26 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import com.enea.jcarder.common.LockingContext; + +public interface EventListenerIfc { + + void beforeMonitorEnter(Object monitor, + LockingContext context) throws Exception; + +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/JavaAgent.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/JavaAgent.java new file mode 100644 index 00000000..62dc534d --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/JavaAgent.java @@ -0,0 +1,177 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import TransactionalIO.core.TransactionalFile; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.instrument.Instrumentation; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; + +import com.enea.jcarder.agent.instrument.ClassTransformer; +import com.enea.jcarder.agent.instrument.InstrumentConfig; +import com.enea.jcarder.util.BuildInformation; +import com.enea.jcarder.util.logging.AppendableHandler; +import com.enea.jcarder.util.logging.Handler; +import com.enea.jcarder.util.logging.Logger; +import dstm2.Init; +import java.io.RandomAccessFile; + +/** + * This is the main class of the JCarder Java agent. It will initialize JCarder + * and register a ClassTransformer that is called by the JVM each time a class + * is loaded. + */ +public final class JavaAgent { + + private static final String DUMP_PROPERTY = "jcarder.dump"; + private static final String LOGLEVEL_PROPERTY = "jcarder.loglevel"; + private static final String LOG_FILENAME = "jcarder.log"; + private static final String LOG2_FILENAME = "jcarder2ndlog.log"; + + private final InstrumentConfig mConfig = new InstrumentConfig(); + private Logger mLogger; + PrintWriter mLogWriter; + private File mOutputDir; + private Logger.Level mLogLevel; + private static final String OUTPUTDIR_PROPERTY = "jcarder.outputdir"; + + private JavaAgent() { } + + /** + * This method is called by the JVM when the JVM is started with the + * -javaagent command line parameter. + */ + public static void premain(final String args, + final Instrumentation instrumentation) + throws Exception { + Init.init(); + JavaAgent javaAgent = new JavaAgent(); + javaAgent.init(instrumentation); + } + + private void init(Instrumentation instrumentation) + throws Exception { + handleProperties(); + initLogger(); + mLogger.info("Starting " + BuildInformation.getShortInfo() + " agent"); + logJvmInfo(); + EventListener listener = EventListener.create(mLogger, mOutputDir); + ClassTransformer classTransformer = + new ClassTransformer(mLogger, mOutputDir, mConfig); + instrumentation.addTransformer(classTransformer); + StaticEventListener.setListener(listener); + mLogger.info("JCarder agent initialized\n"); + } + + private void initLogger() { + File logFile = new File(mOutputDir, LOG_FILENAME); + if (logFile.exists()) { + logFile.delete(); + } + //TransactionalFile traf = new TransactionalFile(logFile, "rw"); + FileWriter fileWriter; + try { + fileWriter = new FileWriter(logFile); + } catch (IOException e) { + System.err.println("Failed to open log file \"" + + logFile + "\": " + e.getMessage()); + return; + } + mLogWriter = new PrintWriter(new BufferedWriter(fileWriter)); + AppendableHandler fileHandler = new AppendableHandler(mLogWriter); + // AppendableHandler fileHandler = new AppendableHandler(mLogWriter, new TransactionalFile(LOG_FILENAME, "rw")); + AppendableHandler consoleHandler = + new AppendableHandler(System.err, Logger.Level.INFO, "{message}\n"); + + /* File logFile2 = new File(mOutputDir, LOG2_FILENAME); + if (logFile2.exists()) { + logFile2.delete(); + } + //TransactionalFile traf = new TransactionalFile(logFile, "rw"); + FileWriter fileWriter2; + try { + fileWriter2 = new FileWriter(logFile2); + } catch (IOException e) { + System.err.println("Failed to open log file \"" + + logFile2 + "\": " + e.getMessage()); + return; + } + + + AppendableHandler consoleHandler = + new AppendableHandler(new PrintWriter(new BufferedWriter(fileWriter)), Logger.Level.INFO, "{message}\n", new TransactionalFile(LOG2_FILENAME, "rw")); +*/ + Thread hook = new Thread() { + public void run() { + mLogWriter.flush(); + } + }; + Runtime.getRuntime().addShutdownHook(hook); + + Collection handlers = new ArrayList(); + handlers.add(fileHandler); + handlers.add(consoleHandler); + mLogger = new Logger(handlers, mLogLevel); + } + + private void logJvmInfo() { + Enumeration properties = System.getProperties().propertyNames(); + while (properties.hasMoreElements()) { + String key = (String) properties.nextElement(); + if (key.startsWith("java.vm.")) { + mLogger.config(key + ": " + System.getProperty(key)); + } + } + } + + private void handleProperties() throws IOException { + handleDumpProperty(); + handleLogLevelProperty(); + handleOutputDirProperty(); + } + + private void handleDumpProperty() { + mConfig.setDumpClassFiles(Boolean.getBoolean(DUMP_PROPERTY)); + } + + private void handleLogLevelProperty() { + String logLevelValue = System.getProperty(LOGLEVEL_PROPERTY, "fine"); + Logger.Level logLevel = Logger.Level.fromString(logLevelValue); + if (logLevel != null) { + mLogLevel = logLevel; + } else { + System.err.print("Bad loglevel; should be one of "); + System.err.println(Logger.Level.getEnumeration()); + System.err.println(); + System.exit(1); + } + } + + private void handleOutputDirProperty() throws IOException { + final String property = System.getProperty(OUTPUTDIR_PROPERTY, "."); + mOutputDir = new File(property).getCanonicalFile(); + if (!mOutputDir.isDirectory()) { + mOutputDir.mkdirs(); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockIdGenerator.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockIdGenerator.java new file mode 100644 index 00000000..3afe3a42 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockIdGenerator.java @@ -0,0 +1,67 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import java.io.IOException; +//import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.contexts.ContextWriterIfc; +import com.enea.jcarder.util.IdentityWeakHashMap; +import com.enea.jcarder.util.logging.Logger; + +/** + * This class is responsible for generating unique IDs for objects. + * + * We cannot use System.identityHashCode(o) since it returns random numbers, + * which are not guaranteed to be unique. + * + * TODO Add basic tests for this class. + */ +//@NotThreadSafe +final class LockIdGenerator { + private final IdentityWeakHashMap mIdMap; + private final ContextWriterIfc mContextWriter; + private final Logger mLogger; + + /** + * Create a LockIdGenerator backed by a ContextWriterIfc + */ + public LockIdGenerator(Logger logger, ContextWriterIfc writer) { + mLogger = logger; + mIdMap = new IdentityWeakHashMap(); + mContextWriter = writer; + } + + /** + * Return an ID for a given object. + * + * If the method is invoked with the same object instance more than once it + * is guaranteed that the same ID is returned each time. Two objects that + * are not identical (as compared with "==") will get different IDs. + */ + public int acquireLockId(Object o) throws IOException { + assert o != null; + Integer id = mIdMap.get(o); + if (id == null) { + id = mContextWriter.writeLock(new Lock(o)); + mIdMap.put(o, id); + mLogger.finest("Created new lock ID: " + id); + } + return id; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockingContextIdCache.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockingContextIdCache.java new file mode 100644 index 00000000..cb33b33f --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockingContextIdCache.java @@ -0,0 +1,158 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.HashMap; +//import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextWriterIfc; +import com.enea.jcarder.util.logging.Logger; + +/** + * This class is responsible for mapping LockingContext instances to locking + * context IDs. It maintains a cache to be able to return the same ID again if + * an ID is requested for the same or equal LockingContext more than once. + * + * This class is similar to the java.util.WeakHashMap but uses soft references + * instead of weak references in order to try to keep the entries in the cache + * as long as there is enough memory available. + * + * TODO An alternative implementation to consider could be to only store the + * hashCode in a map and perform a comparison with the Context file file + * (possibly memory mapped). I don't know how that would affect the performance. + * Another option to consider would be to use a plain HashMap without + * SoftReferences and accept the potential memory problem as a trade-of for + * better performance (?) and to avoid getting different IDs for duplicated + * LockingContexts. + * + * TODO Add basic tests for this class. + */ +//@NotThreadSafe +final class LockingContextIdCache { + private final HashMap mCache; + private final ReferenceQueue mReferenceQueue; + private final ContextWriterIfc mContextWriter; + private final Logger mLogger; + + /** + * Create a LockingContextIdCache backed by a ContextWriterIfc. + */ + public LockingContextIdCache(Logger logger, ContextWriterIfc writer) { + mLogger = logger; + mCache = new HashMap(); + mReferenceQueue = new ReferenceQueue(); + mContextWriter = writer; + } + + /** + * Acquire a unique ID for the provided LockingContext. The ID will be + * cached. If a provided LockingContext is equal to a previously provided + * LockingContext that is still in the cache, the same ID will be returned. + * + * The equality is checked with the LockingContext.equals(Object other) + * method. + */ + public int acquireContextId(LockingContext context) throws IOException { + assert context != null; + removeGarbageCollectedKeys(); + Integer id = mCache.get(new StrongKey(context)); + if (id == null) { + mLogger.finest("Creating new context ID"); + id = mContextWriter.writeContext(context); + mCache.put((new SoftKey(context, mReferenceQueue)), id); + } + return id; + } + + private void removeGarbageCollectedKeys() { + Reference e; + while ((e = mReferenceQueue.poll()) != null) { + mLogger.finest("Removing garbage-collected cached context"); + mCache.remove(e); + } + } + + private static interface EqualsComparableKey { + Object get(); + boolean equals(Object obj); + int hashCode(); + } + + private static class StrongKey implements EqualsComparableKey { + private final Object mReferent; + + StrongKey(Object referent) { + assert referent != null; + mReferent = referent; + } + + public Object get() { + return mReferent; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + try { + EqualsComparableKey reference = (EqualsComparableKey) obj; + return mReferent.equals(reference.get()); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return mReferent.hashCode(); + } + } + + private static class SoftKey extends SoftReference + implements EqualsComparableKey { + private final int mHash; + + SoftKey(LockingContext referent, ReferenceQueue queue) { + super(referent, queue); + assert referent != null; + mHash = referent.hashCode(); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + try { + Object otherReferent = ((EqualsComparableKey) obj).get(); + Object thisReferent = get(); + return (thisReferent != null + && otherReferent != null + && thisReferent.equals(otherReferent)); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return mHash; + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/StaticEventListener.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/StaticEventListener.java new file mode 100644 index 00000000..74010e90 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/StaticEventListener.java @@ -0,0 +1,78 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +//import net.jcip.annotations.ThreadSafe; +import com.enea.jcarder.common.LockingContext; + +/** + * This class provides static methods that are supposed to be invoked directly + * from the instrumented classes. + */ +//@ThreadSafe +public final class StaticEventListener { + + private StaticEventListener() { } + private static EventListenerIfc smListener; + + public synchronized static void setListener(EventListenerIfc listener) { + smListener = listener; + } + + public synchronized static EventListenerIfc getListener() { + return smListener; + } + + /** + * This method is expected to be called from the instrumented classes. + * + * @param monitor + * The monitor object that was acquired. This value is allowed to + * be null. + * + * @param lockReference + * A textual description of how the lock object was addressed. + * For example: "this", "com.enea.jcarder.Foo.mBar" or + * "com.enea.jcarder.Foo.getLock()". + * + * @param methodWithClass + * The method that acquired the lock, on the format + * "com.enea.jcarder.Foo.bar()". + */ + public static void beforeMonitorEnter(Object monitor, + String lockReference, + String methodWithClass) { + try { + EventListenerIfc listener = getListener(); + if (listener != null) { + final LockingContext lockingContext = + new LockingContext(Thread.currentThread(), + lockReference, + methodWithClass); + listener.beforeMonitorEnter(monitor, + lockingContext); + } + } catch (Throwable t) { + handleError(t); + } + } + + private static void handleError(Throwable t) { + setListener(null); + t.printStackTrace(); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/ThreadLocalEnteredMonitors.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/ThreadLocalEnteredMonitors.java new file mode 100644 index 00000000..e54e5d2c --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/ThreadLocalEnteredMonitors.java @@ -0,0 +1,54 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent; + +import java.util.ArrayList; +import java.util.Iterator; +//import net.jcip.annotations.ThreadSafe; + +/** + * Each instance of this class keeps a list of entered monitors for a thread. + * + * Note that this class is a ThreadLocal and therefore each thread will have its + * own instance. + */ + +//@ThreadSafe +final class ThreadLocalEnteredMonitors +extends ThreadLocal> { + + public ArrayList initialValue() { + return new ArrayList(); + } + + Iterator getIterator() { + return get().iterator(); + } + + EnteredMonitor getFirst() { + ArrayList list = get(); + if (list.isEmpty()) { + return null; + } else { + return list.get(0); + } + } + + void addFirst(EnteredMonitor enteredMonitor) { + get().add(0, enteredMonitor); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/ClassTransformer.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/ClassTransformer.java new file mode 100644 index 00000000..a278e901 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/ClassTransformer.java @@ -0,0 +1,222 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import com.enea.jcarder.org.objectweb.asm.ClassReader; +import com.enea.jcarder.org.objectweb.asm.ClassVisitor; +import com.enea.jcarder.org.objectweb.asm.ClassWriter; +import com.enea.jcarder.org.objectweb.asm.util.CheckClassAdapter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.security.ProtectionDomain; + + +import com.enea.jcarder.util.logging.Logger; + +/** + * This class is responsible for all instrumentations and handles related issues + * with class loaders. + * + * TODO Add basic test for this class. + */ +public class ClassTransformer implements ClassFileTransformer { + private static final String ORIGINAL_CLASSES_DIRNAME = + "jcarder_original_classes"; + private static final String INSTRUMENTED_CLASSES_DIRNAME = + "jcarder_instrumented_classes"; + private final Logger mLogger; + private final ClassLoader mAgentClassLoader; + private final InstrumentConfig mInstrumentConfig; + private File mOriginalClassesDir; + private File mInstrumentedClassesDir; + + public ClassTransformer(Logger logger, + File outputDirectory, + InstrumentConfig config) { + mLogger = logger; + mOriginalClassesDir = + new File(outputDirectory, ORIGINAL_CLASSES_DIRNAME); + mInstrumentedClassesDir = + new File(outputDirectory, INSTRUMENTED_CLASSES_DIRNAME); + mInstrumentConfig = config; + mAgentClassLoader = getClass().getClassLoader(); + mLogger.fine("JCarder loaded with " + + getClassLoaderName(mAgentClassLoader) + "."); + if (mAgentClassLoader == null) { + mLogger.info("Will instrument AWT and Swing classes"); + } else { + mLogger.info("Not instrumenting standard library classes " + + "(AWT, Swing, etc.)"); + } + deleteDirRecursively(mInstrumentedClassesDir); + deleteDirRecursively(mOriginalClassesDir); + } + + public byte[] transform(final ClassLoader classLoader, + final String jvmInternalClassName, + final Class classBeingRedefined, + final ProtectionDomain protectionDomain, + final byte[] originalClassBuffer) + throws IllegalClassFormatException { + String className = jvmInternalClassName.replace('/', '.'); + try { + return instrument(classLoader, originalClassBuffer, className); + } catch (Throwable t) { + mLogger.severe("Failed to transform the class " + + className + ": " + t.getMessage()); + dumpClassToFile(originalClassBuffer, + mOriginalClassesDir, + className); + return null; + } + } + + private byte[] instrument(final ClassLoader classLoader, + final byte[] originalClassBuffer, + final String className) { + if (className.startsWith("com.enea.jcarder") + && !className.startsWith("com.enea.jcarder.testclasses")) { + return null; // Don't instrument ourself. + } + final String reason = isInstrumentable(className); + if (reason != null) { + mLogger.finest( + "Won't instrument class " + className + ": " + reason); + return null; + } + if (!isCompatibleClassLoader(classLoader)) { + mLogger.finest("Can't instrument class " + className + + " loaded with " + getClassLoaderName(classLoader)); + return null; + } + final ClassReader reader = new ClassReader(originalClassBuffer); + final ClassWriter writer = new ClassWriter(true); + ClassVisitor visitor = writer; + if (mInstrumentConfig.getValidateTransfomedClasses()) { + visitor = new CheckClassAdapter(visitor); + } + visitor = new ClassAdapter(mLogger, visitor, className); + reader.accept(visitor, false); + byte[] instrumentedClassfileBuffer = writer.toByteArray(); + if (mInstrumentConfig.getDumpClassFiles()) { + dumpClassToFile(originalClassBuffer, + mOriginalClassesDir, + className); + dumpClassToFile(instrumentedClassfileBuffer, + mInstrumentedClassesDir, + className); + } + return instrumentedClassfileBuffer; + } + + /** + * Instrumented classes must use the same static members in the + * com.ena.jcarder.agent.StaticEventListener class as the Java agent and + * therefore they must be loaded with the same class loader as the agent was + * loaded with, or with a class loader that has the agent's class loader as + * a parent or ancestor. + * + * Note that the agentLoader may have been loaded with the bootstrap class + * loader (null) and then "null" is a compatible class loader. + */ + private boolean isCompatibleClassLoader(final ClassLoader classLoader) { + ClassLoader c = classLoader; + while (c != mAgentClassLoader) { + if (c == null) { + return false; + } + c = c.getParent(); + } + return true; + } + + private static String getClassLoaderName(final ClassLoader loader) { + if (loader == null) { + return "the bootstrap class loader"; + } else if (loader == ClassLoader.getSystemClassLoader()) { + return "the system class loader"; + } else { + return "the class loader \"" + loader + "\""; + } + } + + /** + * Check whether we want to instrument a class. + * + * @param className Name of the class, including package. + * @return null if the class should be instrumented, otherwise a + * string containing the reason of why the class shouldn't be + * instrumented. + */ + private static String isInstrumentable(String className) { + // AWK and Swing classes are OK. + if (className.startsWith("java.awt.") + || className.startsWith("javax.swing.")) { + return null; + } + + // Other standard library classes are not. + if (className.startsWith("java.") + || className.startsWith("javax.") + || className.startsWith("sun.") || className.startsWith("TransactionalIO.") + || className.startsWith("dstm2")) { + return "standard library class"; + } + + // All other classes should be instrumented. + return null; + } + + private static boolean deleteDirRecursively(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i = 0; i < children.length; i++) { + boolean success = + deleteDirRecursively(new File(dir, children[i])); + if (!success) { + return false; + } + } + } + return dir.delete(); + } + + /** + * The dumped file can be decompiled with javap or with gnu.bytecode.dump. + * The latter also prints detailed information about the constant pool, + * something which javap does not. + */ + private void dumpClassToFile(byte[] content, + File baseDir, + String className) { + try { + String separator = System.getProperty("file.separator"); + File file = new File(baseDir + separator + + className.replace(".", separator) + + ".class"); + file.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(content); + fos.close(); + } catch (IOException e) { + mLogger.severe("Failed to dump class to file: " + e.getMessage()); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentConfig.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentConfig.java new file mode 100644 index 00000000..8b2f4206 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentConfig.java @@ -0,0 +1,40 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +// TODO Is this config class needed? +public final class InstrumentConfig { + + private final boolean mValidateTransfomedClasses = true; + private boolean mDumpClassFiles; + + public InstrumentConfig() { + mDumpClassFiles = false; + } + + public void setDumpClassFiles(boolean dumpClassFiles) { + mDumpClassFiles = dumpClassFiles; + } + + public boolean getDumpClassFiles() { + return mDumpClassFiles; + } + + public boolean getValidateTransfomedClasses() { + return mValidateTransfomedClasses; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentationUtilities.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentationUtilities.java new file mode 100644 index 00000000..0751f592 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentationUtilities.java @@ -0,0 +1,52 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import com.enea.jcarder.org.objectweb.asm.MethodVisitor; +import com.enea.jcarder.org.objectweb.asm.Opcodes; + + +public final class InstrumentationUtilities { + + private InstrumentationUtilities() { } + + public static void pushClassReferenceToStack(MethodVisitor mv, + String className) { + /* + * It is not possible to use: + * + * mv.visitLdcInsn(RuleType.getType(mClassName)); + * + * for class versions before 49.0 (introduced with java 1.5). Therefore + * we use Class.forName instead. + * + * TODO It might be possible to do this more efficiently by caching the + * result from Class.forName. But note that adding a new field (where + * the cached class object can be stored) is only possible if the class + * has not already been loaded by the JVM. + */ + mv.visitLdcInsn(className); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, + "java/lang/Class", + "forName", + "(Ljava/lang/String;)Ljava/lang/Class;"); + } + + public static String getInternalName(Class c) { + return c.getName().replace('.', '/'); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentedAttribute.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentedAttribute.java new file mode 100644 index 00000000..a135ebce --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentedAttribute.java @@ -0,0 +1,46 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import com.enea.jcarder.org.objectweb.asm.Attribute; +import com.enea.jcarder.org.objectweb.asm.ByteVector; +import com.enea.jcarder.org.objectweb.asm.ClassWriter; + + +public final class InstrumentedAttribute extends Attribute { + private static final String PREFIX = "com.enea.jcarder.instrumented"; + + public InstrumentedAttribute() { + super(PREFIX); + } + + public InstrumentedAttribute(String attributeType) { + super(PREFIX + "." + attributeType); + } + + public static boolean matchAttribute(Attribute a) { + return a.type.startsWith(PREFIX); + } + + protected ByteVector write(ClassWriter arg0, + byte[] arg1, + int arg2, + int arg3, + int arg4) { + return new ByteVector(); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/MonitorEnterMethodAdapter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/MonitorEnterMethodAdapter.java new file mode 100644 index 00000000..595ce5ba --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/MonitorEnterMethodAdapter.java @@ -0,0 +1,75 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import net.jcip.annotations.NotThreadSafe; + + +import com.enea.jcarder.agent.StaticEventListener; + +import com.enea.jcarder.org.objectweb.asm.MethodAdapter; +import com.enea.jcarder.org.objectweb.asm.MethodVisitor; +import com.enea.jcarder.org.objectweb.asm.Opcodes; +import static com.enea.jcarder.agent.instrument.InstrumentationUtilities.getInternalName; + +@NotThreadSafe +class MonitorEnterMethodAdapter extends MethodAdapter { + private static final String CALLBACK_CLASS_NAME = + getInternalName(StaticEventListener.class); + private final String mClassAndMethodName; + private final String mClassName; + private StackAnalyzeMethodVisitor mStack; + + MonitorEnterMethodAdapter(final MethodVisitor visitor, + final String className, + final String methodName) { + super(visitor); + mClassAndMethodName = className + "." + methodName + "()"; + mClassName = className; + } + + public void visitInsn(int inst) { + if (inst == Opcodes.MONITORENTER) { + mv.visitInsn(Opcodes.DUP); + mv.visitLdcInsn(convertFromJvmInternalNames(mStack.peek())); + mv.visitLdcInsn(mClassAndMethodName); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, + CALLBACK_CLASS_NAME, + "beforeMonitorEnter", + "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V"); + } + super.visitInsn(inst); + } + + private String convertFromJvmInternalNames(String s) { + if (s == null) { + assert false; + return "null???"; + } else { + final String name = s.replace('/', '.'); + if (name.equals(mClassName + ".class")) { + return "class"; + } else { + return name; + } + } + } + + void setStackAnalyzer(StackAnalyzeMethodVisitor stack) { + mStack = stack; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/SimulateMethodSyncMethodAdapter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/SimulateMethodSyncMethodAdapter.java new file mode 100644 index 00000000..99392c1a --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/SimulateMethodSyncMethodAdapter.java @@ -0,0 +1,104 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import com.enea.jcarder.org.objectweb.asm.Label; +import com.enea.jcarder.org.objectweb.asm.MethodAdapter; +import com.enea.jcarder.org.objectweb.asm.MethodVisitor; +import com.enea.jcarder.org.objectweb.asm.Opcodes; +import net.jcip.annotations.NotThreadSafe; + +/** + * This Method Adapter simulates a synchronized declaration on a method by + * adding a MonitorEnter and MonitorExits. + */ +//@NotThreadSafe +class SimulateMethodSyncMethodAdapter extends MethodAdapter { + private final String mClassName; + private final boolean mIsStatic; + private final Label mTryLabel = new Label(); + private final Label mFinallyLabel = new Label(); + + SimulateMethodSyncMethodAdapter(final MethodVisitor visitor, + final String className, + final boolean isStatic) { + super(visitor); + mClassName = className; + mIsStatic = isStatic; + } + + public void visitCode() { + super.visitCode(); + /* + * This MethodAdapter will only be applied to synchronized methods, and + * constructors are not allowed to be declared synchronized. Therefore + * we can add instructions at the beginning of the method and do not + * have to find the place after the initial constructor byte codes: + * + * ALOAD 0 : this + * INVOKESPECIAL Object.() : void + * + */ + putMonitorObjectReferenceOnStack(); + mv.visitInsn(Opcodes.MONITORENTER); + mv.visitLabel(mTryLabel); + } + + /** + * This method is called just after the last code in the method. + */ + public void visitMaxs(int arg0, int arg1) { + /* + * This finally block is needed in order to exit the monitor even when + * the method exits by throwing an exception. + */ + mv.visitLabel(mFinallyLabel); + putMonitorObjectReferenceOnStack(); + mv.visitInsn(Opcodes.MONITOREXIT); + mv.visitInsn(Opcodes.ATHROW); + mv.visitTryCatchBlock(mTryLabel, + mFinallyLabel, + mFinallyLabel, + null); + super.visitMaxs(arg0, arg1); + } + + public void visitInsn(int inst) { + switch (inst) { + case Opcodes.IRETURN: + case Opcodes.LRETURN: + case Opcodes.FRETURN: + case Opcodes.DRETURN: + case Opcodes.ARETURN: + case Opcodes.RETURN: + putMonitorObjectReferenceOnStack(); + mv.visitInsn(Opcodes.MONITOREXIT); + break; + default: + // Do nothing. + } + super.visitInsn(inst); + } + + private void putMonitorObjectReferenceOnStack() { + if (mIsStatic) { + InstrumentationUtilities.pushClassReferenceToStack(mv, mClassName); + } else { + mv.visitVarInsn(Opcodes.ALOAD, 0); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/StackAnalyzeMethodVisitor.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/StackAnalyzeMethodVisitor.java new file mode 100644 index 00000000..761733af --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/StackAnalyzeMethodVisitor.java @@ -0,0 +1,313 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.agent.instrument; + +import com.enea.jcarder.org.objectweb.asm.AnnotationVisitor; +import com.enea.jcarder.org.objectweb.asm.Attribute; +import com.enea.jcarder.org.objectweb.asm.Label; +import com.enea.jcarder.org.objectweb.asm.MethodVisitor; +import com.enea.jcarder.org.objectweb.asm.Opcodes; +import com.enea.jcarder.org.objectweb.asm.Type; +import java.util.Stack; +import net.jcip.annotations.NotThreadSafe; + + + +import com.enea.jcarder.util.logging.Logger; + +/** + * This class tries to keep track of what is currently on the operand stack. It + * does not keep track of the actual values but from where the values + * originates. A value may for example originate from a specific field member in + * the class, a local variable, a return value from a specific method or + * something else. + * + * The analysis is done during the instrumentation. + */ +@NotThreadSafe +class StackAnalyzeMethodVisitor implements MethodVisitor { + private static final TextualDescription UNKOWN_VALUE = + new TextualDescription("???"); + private final Logger mLogger; + private final Stack mStack = new Stack(); + private final MethodVisitor mMethodVisitor; + private final boolean mIsStatic; + + StackAnalyzeMethodVisitor(final Logger logger, + final MethodVisitor methodVisitor, + final boolean isStatic) { + mLogger = logger; + mMethodVisitor = methodVisitor; + mIsStatic = isStatic; + } + + private static class TextualDescription { + private final String mDescription; + + TextualDescription(String description) { + mDescription = description; + } + + public String toString() { + return mDescription; + } + } + + /** + * @return A textual description of from where the current value of the + * stack originates. The string "???" is returned if the origin is + * unknown. + */ + String peek() { + if (mStack.isEmpty()) { + return UNKOWN_VALUE.toString(); + } else { + return mStack.peek().toString(); + } + } + + private String pop() { + return popObject().toString(); + } + + private Object popObject() { + if (mStack.isEmpty()) { + return UNKOWN_VALUE; + } else { + return mStack.pop(); + } + } + + private void pushTextualDescription(String s) { + mStack.push(new TextualDescription(s)); + } + + private void pushStringObject(String s) { + mStack.push(s); + } + + private void clear() { + mLogger.finest("Invalidating stack"); + mStack.clear(); + } + + public void visitCode() { + mMethodVisitor.visitCode(); + clear(); + } + + public void visitEnd() { + mMethodVisitor.visitEnd(); + clear(); + } + + public void visitFieldInsn(int opCode, + String owner, + String name, + String desc) { + mMethodVisitor.visitFieldInsn(opCode, owner, name, desc); + switch (opCode) { + case Opcodes.GETFIELD: + pop(); + pushTextualDescription(owner + "." + name); + break; + case Opcodes.GETSTATIC: + pushTextualDescription(owner + "." + name); + break; + default: + clear(); + } + } + + public void visitIincInsn(int arg0, int arg1) { + mMethodVisitor.visitIincInsn(arg0, arg1); + clear(); + } + + public void visitInsn(int opCode) { + mMethodVisitor.visitInsn(opCode); + switch (opCode) { + case Opcodes.DUP: + pushTextualDescription(peek()); + break; + default: + clear(); + } + } + + public void visitIntInsn(int opCode, int arg1) { + mMethodVisitor.visitIntInsn(opCode, arg1); + clear(); + } + + public void visitJumpInsn(int opCode, Label arg1) { + mMethodVisitor.visitJumpInsn(opCode, arg1); + clear(); + } + + public void visitLabel(Label arg0) { + mMethodVisitor.visitLabel(arg0); + // We have to invalidate the stack since we don't know how we arrived + // at this label. We might have jumped to this place from anywhere. + clear(); + } + + public void visitLdcInsn(Object cst) { + mMethodVisitor.visitLdcInsn(cst); + if (cst instanceof Type) { + Type t = (Type) cst; + pushTextualDescription(t.getClassName() + ".class"); + } else if (cst instanceof String) { + pushStringObject((String) cst); + } else { + clear(); + } + } + + public void visitLineNumber(int arg0, Label arg1) { + mMethodVisitor.visitLineNumber(arg0, arg1); + } + + public void visitLocalVariable(String name, + String desc, + String signature, + Label start, + Label end, + int index) { + mMethodVisitor.visitLocalVariable(name, + desc, + signature, + start, + end, + index); + } + + public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) { + mMethodVisitor.visitLookupSwitchInsn(arg0, arg1, arg2); + clear(); + } + + // TODO refactor this method + public void visitMethodInsn(int opCode, + String owner, + String name, + String desc) { + mMethodVisitor.visitMethodInsn(opCode, owner, name, desc); + switch (opCode) { + case Opcodes.INVOKEVIRTUAL: + // pass through to next case. + case Opcodes.INVOKESPECIAL: + // pass through to next case. + case Opcodes.INVOKESTATIC: + if ("forName".equals(name) + && "java/lang/Class".equals(owner) + && "(Ljava/lang/String;)Ljava/lang/Class;".equals(desc)) { + Object stackObject = popObject(); + if (stackObject instanceof String) { + String classDescription = ((String) stackObject) + ".class"; + pushTextualDescription(classDescription); + break; + } + } + // pass through to next case. + case Opcodes.INVOKEINTERFACE: + clear(); + if (isNonVoidMethod(name, desc)) { + pushTextualDescription(owner + "." + name + "()"); + } + break; + default: + clear(); + } + } + + private static boolean isNonVoidMethod(String name, String desc) { + return Type.getReturnType(desc) != Type.VOID_TYPE + || name.equals(""); + } + + public void visitMultiANewArrayInsn(String arg0, int arg1) { + mMethodVisitor.visitMultiANewArrayInsn(arg0, arg1); + clear(); + } + + public AnnotationVisitor visitParameterAnnotation(int arg0, + String arg1, + boolean arg2) { + return mMethodVisitor.visitParameterAnnotation(arg0, arg1, arg2); + } + + public void visitTableSwitchInsn(int arg0, + int arg1, + Label arg2, + Label[] arg3) { + mMethodVisitor.visitTableSwitchInsn(arg0, arg1, arg2, arg3); + clear(); + } + + public void visitTryCatchBlock(Label arg0, + Label arg1, + Label arg2, + String arg3) { + mMethodVisitor.visitTryCatchBlock(arg0, arg1, arg2, arg3); + clear(); + } + + public void visitTypeInsn(int opCode, String desc) { + mMethodVisitor.visitTypeInsn(opCode, desc); + } + + public void visitVarInsn(int opCode, int index) { + mMethodVisitor.visitVarInsn(opCode, index); + switch (opCode) { + case Opcodes.ALOAD: + if (index == 0 && !mIsStatic) { + pushTextualDescription("this"); + } else { + /* + * TODO Translate the index to a local variable name. To be able + * to do that we probably have to analyze the class in two steps + * since the visit method visitLocalVariable is not called until + * after all calls to visitVarInsn. + */ + pushTextualDescription(""); + } + break; + case Opcodes.ASTORE: + pop(); + break; + default: + clear(); + } + } + + public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) { + return mMethodVisitor.visitAnnotation(arg0, arg1); + } + + public AnnotationVisitor visitAnnotationDefault() { + return mMethodVisitor.visitAnnotationDefault(); + } + + public void visitAttribute(Attribute arg0) { + mMethodVisitor.visitAttribute(arg0); + } + + public void visitMaxs(int arg0, int arg1) { + mMethodVisitor.visitMaxs(arg0, arg1); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Analyzer.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Analyzer.java new file mode 100644 index 00000000..8b502db5 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Analyzer.java @@ -0,0 +1,342 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import TransactionalIO.core.TransactionalFile; +import static com.enea.jcarder.common.contexts.ContextFileReader.CONTEXTS_DB_FILENAME; +import static com.enea.jcarder.common.contexts.ContextFileReader.EVENT_DB_FILENAME; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextFileReader; +import com.enea.jcarder.common.contexts.ContextReaderIfc; +import com.enea.jcarder.common.events.EventFileReader; +import com.enea.jcarder.util.BuildInformation; +import com.enea.jcarder.util.InvalidOptionException; +import com.enea.jcarder.util.OptionParser; +import com.enea.jcarder.util.logging.AppendableHandler; +import com.enea.jcarder.util.logging.Handler; +import com.enea.jcarder.util.logging.Logger; +import com.enea.jcarder.util.logging.Logger.Level; + +/** + * The main class of the JCarder analyzer. + */ +public final class Analyzer { + + enum OutputMode { INCLUDE_ALL, + INCLUDE_CYCLES, + INCLUDE_ONLY_MULTI_THREADED_CYCLES }; + + /* + * Cycles with only one thread can never cause a deadlock, but it might be + * possible that basic tests of a single class are very simplified and use + * only a single thread where a real program might invoke the methods from + * several different threads. Therefore single-threaded cycles are also + * interesting to detect and include by default. + */ + private OutputMode mOutputMode = OutputMode.INCLUDE_CYCLES; + private boolean mIncludePackages = false; + private boolean mPrintDetails = false; + private Logger mLogger; + final private Level mLogLevel = Logger.Level.INFO; + private String mInputDirectory = "."; + + public static void main(String[] args) { + new Analyzer().start(args); + } + + public void start(String[] args) { + parseArguments(args); + initLogger(); + LockGraphBuilder graphBuilder = new LockGraphBuilder(); + final ContextReaderIfc contextReader; + + try { + contextReader = + new ContextFileReader(mLogger, new File(mInputDirectory, + CONTEXTS_DB_FILENAME)); + + EventFileReader eventReader = new EventFileReader(mLogger); + eventReader.parseFile(new File(mInputDirectory, EVENT_DB_FILENAME), + graphBuilder); + } + catch (IOException e) { + mLogger.severe("Error while reading result database: " + + e.getMessage()); + return; + } + printInitiallyLoadedStatistics(graphBuilder.getAllLocks()); + + CycleDetector cycleDetector = new CycleDetector(mLogger); + cycleDetector.analyzeLockNodes(graphBuilder.getAllLocks()); + printCycleAnalysisStatistics(cycleDetector); + + if (mOutputMode == OutputMode.INCLUDE_ALL) { + printDetailsIfEnabled(cycleDetector.getCycles(), contextReader); + try { + generatGraphvizFileForAllNodes(graphBuilder, contextReader); + } catch (IOException e) { + mLogger.severe("Error while generating Graphviz file: " + + e.getMessage()); + } + } else { + if (mOutputMode == OutputMode.INCLUDE_ONLY_MULTI_THREADED_CYCLES) { + cycleDetector.removeSingleThreadedCycles(); + } + if (cycleDetector.getCycles().isEmpty()) { + System.out.println("No cycles found!"); + return; + } + graphBuilder.clear(); // Help GC. + /* + * TODO Also clear all references in LockNode.mOutgoingEdges to + * avoid keeping references to a lot of LockEdge and LockNode + * objects in order to release as much memory as possible for the + * memory mapped file? + * + * It is not necessary to use the DuplicateEdgeshandler since those + * duplicates are removed anyway when cycles that are alike are + * removed. + */ + cycleDetector.removeAlikeCycles(contextReader); + + printDetailsIfEnabled(cycleDetector.getCycles(), contextReader); + try { + generateGraphvizFilesForCycles(contextReader, cycleDetector); + } catch (IOException e) { + mLogger.severe("Error while generating Graphviz file: " + + e.getMessage()); + } + } + } + + private void initLogger() { + Collection handlers = new ArrayList(); + // try { + handlers.add(new AppendableHandler(System.out, + Logger.Level.CONFIG, + "{message}\n")); + // handlers.add(new AppendableHandler(new PrintStream(new File("analyzer.log")), Logger.Level.CONFIG, "{message}\n", new TransactionalFile("analyzer.log", "rw"))); + /// } catch (FileNotFoundException ex) { + // java.util.logging.Logger.getLogger(Analyzer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + // } + mLogger = new Logger(handlers, mLogLevel); + } + + private void generateGraphvizFilesForCycles(ContextReaderIfc reader, + CycleDetector cycleDetector) + throws IOException { + System.out.println(); + int index = 0; + Collection> cycles = + cycleDetector.mergeCyclesWithIdenticalLocks(); + for (HashSet edges : cycles) { + if (index >= 100) { + System.out.println("Aborting. Too many cycles!"); + break; + } + GraphvizGenerator graphvizGenerator = new GraphvizGenerator(); + createGraphvizFile(graphvizGenerator.generate(edges, + reader, + mIncludePackages), + index++); + } + } + + private void printCycleAnalysisStatistics(CycleDetector cycleDetector) { + System.out.println("\nCycle analysis result: "); + System.out.println(" Cycles: " + + cycleDetector.getCycles().size()); + System.out.println(" Edges in cycles: " + + cycleDetector.getNumberOfEdges()); + System.out.println(" Nodes in cycles: " + + cycleDetector.getNumberOfNodes()); + System.out.println(" Max cycle depth: " + + cycleDetector.getMaxCycleDepth()); + System.out.println(" Max graph depth: " + + cycleDetector.getMaxDepth()); + System.out.println(); + } + + private void generatGraphvizFileForAllNodes(LockGraphBuilder graphBuilder, + ContextReaderIfc reader) + throws IOException { + DuplicatedEdgesHandler.mergeDuplicatedEdges(graphBuilder.getAllLocks(), + reader); + // TODO Print statistics about removed duplicates? + LinkedList allEdges = new LinkedList(); + for (LockNode node : graphBuilder.getAllLocks()) { + allEdges.addAll(node.getOutgoingEdges()); + } + GraphvizGenerator graphvizGenerator = new GraphvizGenerator(); + createGraphvizFile(graphvizGenerator.generate(allEdges, + reader, + mIncludePackages), + 0); + } + + private void parseArguments(String[] args) { + OptionParser op = new OptionParser(); + configureOptionParser(op); + + try { + op.parse(args); + } catch (InvalidOptionException e) { + handleBadOption(op, e.getMessage()); + } + + handleOptions(op); + } + + private void configureOptionParser(OptionParser op) { + /* + * TODO Add parameters for filtering (including & excluding) specific + * locks and edges for example by specifying thread names, object + * classes, method names or packages? + */ + + op.addOption("-help", + "Print this help text"); + op.addOption("-d ", + "Read results to analyze from (default:" + + " current directory)"); + op.addOption("-includepackages", + "Include packages (not only class names) in graph"); + op.addOption("-outputmode ", + "Set output mode to (one of ALL, CYCLES, MTCYCLES);" + + " ALL: include everything;" + + " CYCLES: only include cycles (this is the default);" + + " MTCYCLES: only include multi-thread cycles"); + op.addOption("-printdetails", + "Print details"); + op.addOption("-version", + "Print program version"); + } + + private void handleOptions(OptionParser op) { + Map options = op.getOptions(); + for (String option : options.keySet()) { + if (option.equals("-help")) { + printHelpText(System.out, op); + System.exit(0); + } else if (option.equals("-i")) { + mInputDirectory = options.get(option); + } else if (option.equals("-includepackages")) { + mIncludePackages = true; + } else if (option.equals("-outputmode")) { + String value = options.get(option); + if (value.equalsIgnoreCase("all")) { + mOutputMode = OutputMode.INCLUDE_ALL; + } else if (value.equalsIgnoreCase("cycles")) { + mOutputMode = OutputMode.INCLUDE_CYCLES; + } else if (value.equalsIgnoreCase("mtcycles")) { + mOutputMode = OutputMode.INCLUDE_ONLY_MULTI_THREADED_CYCLES; + } else { + handleBadOption(op, "bad output mode"); + } + } else if (option.equals("-printdetails")) { + mPrintDetails = true; + } else if (option.equals("-version")) { + BuildInformation.printLongBuildInformation(); + System.exit(0); + } + } + } + + private void printHelpText(PrintStream stream, OptionParser op) { + stream.print("Usage: java -jar jcarder.jar [options]\n\n"); + stream.print("Options:\n"); + stream.print(op.getOptionHelp()); + } + + private void handleBadOption(OptionParser optionParser, String message) { + System.err.println("JCarder: " + message); + printHelpText(System.err, optionParser); + System.exit(1); + } + + private void printDetailsIfEnabled(Iterable cycles, + ContextReaderIfc reader) { + if (!mPrintDetails) { + return; + } + SortedSet threads = new TreeSet(); + SortedSet methods = new TreeSet(); + for (Cycle cycle : cycles) { + for (LockEdge edge : cycle.getEdges()) { + LockingContext source = + reader.readContext(edge.getSourceLockingContextId()); + LockingContext target = + reader.readContext(edge.getTargetLockingContextId()); + threads.add(source.getThreadName()); + threads.add(target.getThreadName()); + methods.add(source.getMethodWithClass()); + methods.add(target.getMethodWithClass()); + } + } + System.out.println(); + System.out.println("Threads involved in cycles:"); + for (String thread : threads) { + System.out.println(" " + thread); + } + System.out.println(); + System.out.println("Methods involved in cycles:"); + for (String method : methods) { + System.out.println(" " + method); + } + System.out.println(); + } + + + private void printInitiallyLoadedStatistics(Iterable locks) { + int numberOfNodes = 0; + int numberOfUniqueEdges = 0; + int numberOfDuplicatedEdges = 0; + for (LockNode lock : locks) { + numberOfNodes++; + numberOfUniqueEdges += lock.numberOfUniqueEdges(); + numberOfDuplicatedEdges += lock.numberOfDuplicatedEdges(); + } + System.out.println("\nLoaded from database files:"); + System.out.println(" Nodes: " + numberOfNodes); + System.out.println(" Edges: " + numberOfUniqueEdges + + " (excluding " + numberOfDuplicatedEdges + + " duplicated)"); + } + + private void createGraphvizFile(String s, int index) throws IOException { + File file = new File("jcarder_result_" + index + ".dot"); + System.out.println("Writing Graphviz file: " + file.getAbsolutePath()); + FileWriter fw = new FileWriter(file); + fw.write(s); + fw.flush(); + fw.close(); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Cycle.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Cycle.java new file mode 100644 index 00000000..81fc0e0e --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Cycle.java @@ -0,0 +1,129 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; + +import com.enea.jcarder.common.contexts.ContextReaderIfc; + +/** + * An instance of this class represents a single cycle of edges. The edges must + * form a single circle witout any alternative paths. If there are alternative + * paths in a graph cycle, those cycles will be split into separate Cycle + * objects. + * + * TODO Write basic tests. + */ +class Cycle { + final HashSet mEdgesInCycle = new HashSet(); + + Cycle(Collection edgesInTheCycle) { + mEdgesInCycle.addAll(edgesInTheCycle); + assert mEdgesInCycle.size() >= 2; + } + + HashSet getEdges() { + return mEdgesInCycle; + } + + HashSet getNodes() { + HashSet nodes = new HashSet(); + for (LockEdge edge : mEdgesInCycle) { + /* + * All sources will be included if we get all the targets, since it + * is a cycle. + */ + nodes.add(edge.getTarget()); + } + return nodes; + } + + void updateNodeCycleStatus() { + final LockNode.CycleType type; + if (isSingleThreaded()) { + type = LockNode.CycleType.SINGLE_THREADED_CYCLE; + } else { + type = LockNode.CycleType.CYCLE; + } + for (LockEdge edge : mEdgesInCycle) { + edge.getSource().raiseCycleType(type); + edge.getTarget().raiseCycleType(type); + } + } + + boolean isSingleThreaded() { + // TODO Cache the result to improve performance? + final Iterator iter = mEdgesInCycle.iterator(); + if (iter.hasNext()) { + final long firstThreadId = iter.next().getThreadId(); + while (iter.hasNext()) { + if (firstThreadId != iter.next().getThreadId()) { + return false; + } + } + } + return true; + } + + boolean alike(Cycle other, ContextReaderIfc reader) { + if (this.equals(other)) { + return true; + } + if (mEdgesInCycle.size() != other.mEdgesInCycle.size()) { + return false; + } + if (isSingleThreaded() != other.isSingleThreaded()) { + return false; + } + LinkedList otherEdges = + new LinkedList(other.mEdgesInCycle); + // TODO Refactor the following code? + outerLoop: + for (LockEdge edge : mEdgesInCycle) { + Iterator iter = otherEdges.iterator(); + while (iter.hasNext()) { + if (edge.alike(iter.next(), reader)) { + iter.remove(); + continue outerLoop; + } + } + return false; + } + return true; + } + + public boolean equals(Object obj) { + try { + Cycle other = (Cycle) obj; + return mEdgesInCycle.equals(other.mEdgesInCycle); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return mEdgesInCycle.hashCode(); + } + + + public String toString() { + return mEdgesInCycle.toString(); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/CycleDetector.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/CycleDetector.java new file mode 100644 index 00000000..84edea9e --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/CycleDetector.java @@ -0,0 +1,248 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.contexts.ContextReaderIfc; +import com.enea.jcarder.util.Counter; +import com.enea.jcarder.util.MaxValueCounter; +import com.enea.jcarder.util.logging.Logger; + +/** + * This class is responsible for finding and managing cycles. + * + * TODO Add possibility to ignore cycles guarded by a common lock. + * + * TODO Add possibility to ignore cycles created by two threads that cannot + * possibly run at the same time. Is it possible to achieve that by tracking + * Thread.start() and Thread.join()? + * + * TODO Add more basic tests for this class. + */ +@NotThreadSafe +class CycleDetector { + private final HashSet mCycles; + private final Logger mLogger; + private final MaxValueCounter mMaxDepth; + private final MaxValueCounter mMaxCycleDepth; + private final MaxValueCounter mNoOfCycles; + private final Counter mNoOfCreatedCycleObjects; + + CycleDetector(Logger logger) { + mLogger = logger; + mCycles = new HashSet(); + mMaxDepth = new MaxValueCounter("Graph Depth", mLogger); + mMaxCycleDepth = new MaxValueCounter("Cycle Depth", mLogger); + mNoOfCycles = new MaxValueCounter("Found cycles", mLogger); + mNoOfCreatedCycleObjects = new Counter("Created cycle objects", + mLogger, + 100000); + } + + /** + * Analyze a set of LockNodes and LockEdges. All cycles they form will be + * stored within this class. + */ + void analyzeLockNodes(final Iterable nodes) { + ArrayList nodesOnStack = new ArrayList(10); + ArrayList edgesOnStack = new ArrayList(10); + HashSet visitedNodes = new HashSet(); + HashSet visitedEdges = new HashSet(); + for (LockNode lock : nodes) { + if (!visitedNodes.contains(lock)) { + analyzeNode(lock, nodesOnStack, edgesOnStack, visitedNodes, + visitedEdges); + } + } + for (Cycle cycle : mCycles) { + cycle.updateNodeCycleStatus(); + } + } + + MaxValueCounter getMaxDepth() { + return mMaxDepth; + } + + MaxValueCounter getMaxCycleDepth() { + return mMaxCycleDepth; + } + + private void analyzeNode(final LockNode node, + final ArrayList nodesOnStack, + final ArrayList edgesOnStack, + final HashSet visitedNodes, + final HashSet visitedEdges) { + visitedNodes.add(node); + nodesOnStack.add(node); + for (LockEdge edge : node.getOutgoingEdges()) { + edgesOnStack.add(edge); + analyzeEdge(edge, nodesOnStack, edgesOnStack, visitedNodes, + visitedEdges); + edgesOnStack.remove(edgesOnStack.size() - 1); + } + nodesOnStack.remove(nodesOnStack.size() - 1); + } + + private void analyzeEdge(final LockEdge edge, + final ArrayList nodesOnStack, + final ArrayList edgesOnStack, + final HashSet visitedNodes, + final HashSet visitedEdges) { + if (!visitedEdges.contains(edge)) { + mMaxDepth.set(nodesOnStack.size()); + final int index = nodesOnStack.indexOf(edge.getTarget()); + if (index >= 0) { + final List edgesInCycle = + edgesOnStack.subList(index, edgesOnStack.size()); + mNoOfCreatedCycleObjects.increment(); + mMaxCycleDepth.set(edgesInCycle.size()); + mCycles.add(new Cycle(edgesInCycle)); + mNoOfCycles.set(mCycles.size()); + /* + * Keeping the first edge from the cycle in the visitedEdges + * list is an optimization that avoids unnecessary (as I + * believe) repeated checks. The other edges have to be removed, + * otherwise all cycles won't be found. See the testcases for + * examples of such cases. + */ + List edgesToRemove = + edgesInCycle.subList(1, edgesInCycle.size()); + visitedEdges.removeAll(edgesToRemove); + } else { + visitedEdges.add(edge); + analyzeNode(edge.getTarget(), + nodesOnStack, + edgesOnStack, + visitedNodes, + visitedEdges); + } + } + } + + + HashSet getCycles() { + return mCycles; + } + + private static boolean containsAlike(Cycle cycle, Iterable others, + ContextReaderIfc reader) { + for (Cycle other : others) { + if (cycle.alike(other, reader)) { + return true; + } + } + return false; + } + + /** + * Reducing the number of cycles by ignoring those that are duplicates (very + * similar) to another cycle. + */ + void removeAlikeCycles(ContextReaderIfc reader) { + int removedCycles = 0; + ArrayList uniqueCycles = new ArrayList(); + Iterator iter = mCycles.iterator(); + while (iter.hasNext()) { + final Cycle cycle = iter.next(); + if (containsAlike(cycle, uniqueCycles, reader)) { + iter.remove(); + removedCycles++; + } else { + uniqueCycles.add(cycle); + } + } + mLogger.info("Ignoring " + removedCycles + + " almost identical cycle(s)."); + assert uniqueCycles.equals(new ArrayList(mCycles)); + } + + /** + * Remove cycles that are formed by a only one thread. + */ + void removeSingleThreadedCycles() { + int removedCycles = 0; + Iterator iter = mCycles.iterator(); + while (iter.hasNext()) { + final Cycle cycle = iter.next(); + if (cycle.isSingleThreaded()) { + iter.remove(); + removedCycles++; + } + } + mLogger.info("Ignoring " + + removedCycles + + " single threaded cycle(s)."); + } + + /** + * Get the total number of edges in all known cycles. + */ + int getNumberOfEdges() { + HashSet edges = new HashSet(); + for (Cycle cycle : mCycles) { + edges.addAll(cycle.getEdges()); + } + return edges.size(); + } + + /** + * Get the total number of nodes in all known cycles. + */ + int getNumberOfNodes() { + HashSet nodes = new HashSet(); + for (Cycle cycle : mCycles) { + for (LockEdge edge : cycle.getEdges()) { + nodes.add(edge.getSource()); + nodes.add(edge.getTarget()); + } + } + return nodes.size(); + } + + /** + * Find out the cycles that consist of identical locks and group them + * together. Then return all edges in each group. + * + * The data structure in the CycleDetector class is unaffected. + */ + Collection> mergeCyclesWithIdenticalLocks() { + /* + * TODO Refactor this method? The temporary data structure is too + * complex? + */ + HashMap, HashSet> setOfNodesToEdgesMap = + new HashMap, HashSet>(); + for (Cycle cycle : mCycles) { + final HashSet nodes = cycle.getNodes(); + HashSet edges = setOfNodesToEdgesMap.get(nodes); + if (edges == null) { + edges = new HashSet(); + setOfNodesToEdgesMap.put(nodes, edges); + } + edges.addAll(cycle.getEdges()); + } + return setOfNodesToEdgesMap.values(); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/DuplicatedEdgesHandler.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/DuplicatedEdgesHandler.java new file mode 100644 index 00000000..0cba0aaf --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/DuplicatedEdgesHandler.java @@ -0,0 +1,109 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextReaderIfc; + +/** + * This class contains functionality for merging edges that have the same source + * and target nodes and identical thread IDs and locking contexts content, but + * different locking context IDs. + * + * Such a merge might be desirable since the producer of the context file is not + * required to guarantee that identical locking contexts always get the same + * IDs. + * + * @TODO Add basic tests for this class. + */ +public final class DuplicatedEdgesHandler { + private final Iterable mLockNodes; + private final Map mContextIdTranslation; + private final Map> mContextToIdMap; + + /** + * The constructor is made private to prevent that someone creates an + * instance of this class and then forgets to release the reference to it. + * That would be undesirable since the mContextToIdMap structure in this + * class might be very large and should be garbage collected as soon as + * possible. + * + * @see DuplicatedEdgesHandler.mergeDuplicatedEdges() instead. + */ + private DuplicatedEdgesHandler(Iterable lockNodes, + ContextReaderIfc reader) { + mLockNodes = lockNodes; + mContextIdTranslation = populateTranslationMap(); + mContextToIdMap = createContextToIdMap(reader); + } + + public static void mergeDuplicatedEdges(Iterable lockNodes, + ContextReaderIfc reader) { + DuplicatedEdgesHandler handler = new DuplicatedEdgesHandler(lockNodes, + reader); + handler.updateContextIdTranslationMap(); + handler.updateEdgesWithTranslationMap(); + } + + private void updateEdgesWithTranslationMap() { + for (LockNode node : mLockNodes) { + node.translateContextIds(mContextIdTranslation); + } + } + + private Map populateTranslationMap() { + final HashMap contextIds = + new HashMap(); + for (LockNode node : mLockNodes) { + node.populateContextIdTranslationMap(contextIds); + } + return contextIds; + } + + private Map> + createContextToIdMap(ContextReaderIfc reader) { + final Map> contextToId = + new HashMap>(); + for (Integer id : mContextIdTranslation.values()) { + LockingContext context = reader.readContext(id); + TreeSet ids = contextToId.get(context); + if (ids == null) { + ids = new TreeSet(); + contextToId.put(context, ids); + } + ids.add(id); + } + return contextToId; + } + + private void updateContextIdTranslationMap() { + for (TreeSet ids : mContextToIdMap.values()) { + if (ids.size() > 1) { + Iterator iter = ids.iterator(); + Integer firstId = iter.next(); + while (iter.hasNext()) { + mContextIdTranslation.put(iter.next(), firstId); + } + } + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/GraphvizGenerator.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/GraphvizGenerator.java new file mode 100644 index 00000000..4d490096 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/GraphvizGenerator.java @@ -0,0 +1,166 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.HashSet; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextReaderIfc; + +/** + * This class can be used to generate a Graphviz graph + * as a string. + * + * TODO Add tooltips to the graph? The tooltips might for example contain the + * package names of classes. + * + * TODO If there are to many edges, merge them together in the graph and add an + * href link to an html page that describes them all? + * + * TODO Optionaly merge edges that are identical except for the threads? + */ +final class GraphvizGenerator { + private static final String EDGE_LABEL_FORMAT = + " [fontsize=10, label=<\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
" + + "Thread: %1$s
" + + "
" + + "holding: %2$s
" + + "
" + + "in: %3$s
" + + "
" + + "taking: %4$s
" + + "
" + + "in: %5$s
" + + "
\n" + + " >]"; + + public String generate(Iterable edgesToBePrinted, + ContextReaderIfc reader, + boolean includePackages) { + StringBuffer sb = new StringBuffer(); + sb.append("digraph G {\n"); + sb.append(" node [shape=ellipse, style=filled, fontsize=12];\n"); + final HashSet alreadyAppendedNodes = new HashSet(); + for (LockEdge edge : edgesToBePrinted) { + appendNodeIfNotAppended(reader, + sb, + alreadyAppendedNodes, + edge.getSource()); + appendNodeIfNotAppended(reader, + sb, + alreadyAppendedNodes, + edge.getTarget()); + sb.append(" " + edge.getSource().toString() + ""); + sb.append(" -> " + edge.getTarget().toString() + ""); + sb.append(createEdgeLabel(reader, edge, includePackages)); + sb.append(";\n"); + } + sb.append("}\n"); + return sb.toString(); + } + + private String createEdgeLabel(ContextReaderIfc reader, + LockEdge edge, + boolean includePackages) { + final LockingContext source = + reader.readContext(edge.getSourceLockingContextId()); + final LockingContext target = + reader.readContext(edge.getTargetLockingContextId()); + return String.format(EDGE_LABEL_FORMAT, + escape(handlePackage(target.getThreadName(), + includePackages)), + escape(handlePackage(source.getLockReference(), + includePackages)), + escape(handlePackage(source.getMethodWithClass(), + includePackages)), + escape(handlePackage(target.getLockReference(), + includePackages)), + escape(handlePackage(target.getMethodWithClass(), + includePackages))); + } + + private String handlePackage(String s, + boolean includePackages) { + String[] parts = s.split("\\."); + if (parts.length >= 2 && !includePackages) { + return parts[parts.length - 2] + "." + parts[parts.length - 1]; + } else { + return s; + } + } + + private static String escape(String s) { + return s.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("\'", "'"); + } + + private String getLockNodeString(LockNode node, + ContextReaderIfc reader) { + final String color; + switch (node.getCycleType()) { + case CYCLE: + color = "firebrick1"; + break; + case SINGLE_THREADED_CYCLE: + color = "yellow"; + break; + default: + color = "white"; + } + return " " + node.toString() + " [label = \"" + + escape(reader.readLock(node.getLockId()).toString()) + + "\" , fillcolor=" + color + "];\n"; + } + + private void appendNodeIfNotAppended(ContextReaderIfc reader, + StringBuffer sb, + HashSet alreadyAppendedNodes, + LockNode node) { + if (!alreadyAppendedNodes.contains(node)) { + alreadyAppendedNodes.add(node); + sb.append(getLockNodeString(node, reader)); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockEdge.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockEdge.java new file mode 100644 index 00000000..c3175734 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockEdge.java @@ -0,0 +1,140 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.Map; + +import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.common.contexts.ContextReaderIfc; + +/** + * A LockEdge instance represents a directed edge from a source LockNode to a + * target LockNode. + */ +@NotThreadSafe +class LockEdge { + private final LockNode mSource; + private final LockNode mTarget; + private final long mThreadId; // The thread that did the synchronization. + private int mSourceContextId; + private int mTargetContextId; + private long mNumberOfDuplicates; + + LockEdge(LockNode source, + LockNode target, + long threadId, + int sourceLockingContextId, + int targetLockingContextId) { + mSource = source; + mTarget = target; + mThreadId = threadId; + mSourceContextId = sourceLockingContextId; + mTargetContextId = targetLockingContextId; + mNumberOfDuplicates = 0; + } + + void merge(LockEdge other) { + assert this.equals(other); + mNumberOfDuplicates += (other.mNumberOfDuplicates + 1); + } + + long getDuplicates() { + return mNumberOfDuplicates; + } + + boolean alike(LockEdge other, ContextReaderIfc reader) { + /* + * TODO Some kind of cache to improve performance? Note that the context + * IDs are not declared final. + */ + LockingContext thisSourceContext = + reader.readContext(mSourceContextId); + LockingContext otherSourceContext = + reader.readContext(other.mSourceContextId); + LockingContext thisTargetContext = + reader.readContext(mTargetContextId); + LockingContext otherTargetContext = + reader.readContext(other.mTargetContextId); + return thisSourceContext.alike(otherSourceContext) + && thisTargetContext.alike(otherTargetContext) + && mSource.alike(other.mSource, reader) + && mTarget.alike(other.mTarget, reader); + } + + public boolean equals(Object obj) { + /* + * TODO It might be a potential problem to use LockEdges in HashMaps + * since they are mutable and this equals method depends on them? + */ + try { + LockEdge other = (LockEdge) obj; + return (mTarget.getLockId() == other.mTarget.getLockId()) + && (mSource.getLockId() == other.mSource.getLockId()) + && (mThreadId == other.mThreadId) + && (mSourceContextId == other.mSourceContextId) + && (mTargetContextId == other.mTargetContextId); + } catch (Exception e) { + return false; + } + } + + public int hashCode() { + // TODO Improve hashCode algorithm to improve performance? + return mTarget.getLockId() + mSource.getLockId(); + } + + LockNode getTarget() { + return mTarget; + } + + LockNode getSource() { + return mSource; + } + + int getSourceLockingContextId() { + return mSourceContextId; + } + + int getTargetLockingContextId() { + return mTargetContextId; + } + + /** + * Translate the source and target context ID according to a translation + * map. + */ + void translateContextIds(Map translation) { + final Integer newSourceId = translation.get(mSourceContextId); + if (newSourceId != null && newSourceId != mSourceContextId) { + mSourceContextId = newSourceId; + } + final Integer newTargetId = translation.get(mTargetContextId); + if (newTargetId != null && newSourceId != mTargetContextId) { + mTargetContextId = newTargetId; + } + } + + long getThreadId() { + return mThreadId; + } + + public String toString() { + return " " + mSource + "->" + mTarget; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockGraphBuilder.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockGraphBuilder.java new file mode 100644 index 00000000..1da9af7c --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockGraphBuilder.java @@ -0,0 +1,75 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.io.IOException; +import java.util.HashMap; + +import java.util.Vector; +import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.events.LockEventListenerIfc; + +/** + * This class is responsible for constructing a structure of LockNode and + * LockEdge objects from incoming lock events. + * + * TODO Add basic tests for this class. + */ +@NotThreadSafe +class LockGraphBuilder implements LockEventListenerIfc { + private HashMap mLocks = + new HashMap(); + + LockNode getLockNode(int lockId) { + LockNode lockNode = mLocks.get(lockId); + if (lockNode == null) { + lockNode = new LockNode(lockId); + mLocks.put(lockId, lockNode); + } + return lockNode; + } + + public void onLockEvent(int lockId, + int lockingContextId, + int lastTakenLockId, + int lastTakenLockingContectId, + long threadId) { + if (lastTakenLockId >= 0) { + final LockNode sourceLock = getLockNode(lastTakenLockId); + final LockNode targetLock = getLockNode(lockId); + final LockEdge edge = new LockEdge(sourceLock, + targetLock, + threadId, + lastTakenLockingContectId, + lockingContextId); + sourceLock.addOutgoingEdge(edge); + } + } + + void clear() { + mLocks.clear(); + } + + Iterable getAllLocks() { + return mLocks.values(); + } + + public void tronLockEvent(Vector srgs) throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockNode.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockNode.java new file mode 100644 index 00000000..06d3e722 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockNode.java @@ -0,0 +1,110 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.analyzer; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.contexts.ContextReaderIfc; + +/** + * A LockNode instance represents a lock in a graph. + */ +@NotThreadSafe +class LockNode { + enum CycleType { NO_CYCLE, SINGLE_THREADED_CYCLE, CYCLE }; + + private final int mLockId; + private Map mOutgoingEdges; + private CycleType mCycleType = CycleType.NO_CYCLE; + + LockNode(final int lockId) { + mLockId = lockId; + mOutgoingEdges = new HashMap(); + } + + int getLockId() { + return mLockId; + } + + CycleType getCycleType() { + return mCycleType; + } + + void raiseCycleType(CycleType newCycleType) { + if (newCycleType.compareTo(mCycleType) > 0) { + mCycleType = newCycleType; + } + } + + void addOutgoingEdge(LockEdge newEdge) { + LockEdge existingEdge = mOutgoingEdges.get(newEdge); + if (existingEdge == null) { + mOutgoingEdges.put(newEdge, newEdge); + } else { + existingEdge.merge(newEdge); + } + } + + void populateContextIdTranslationMap(Map translationMap) { + for (LockEdge edge : mOutgoingEdges.values()) { + translationMap.put(edge.getSourceLockingContextId(), + edge.getSourceLockingContextId()); + translationMap.put(edge.getTargetLockingContextId(), + edge.getTargetLockingContextId()); + } + } + + void translateContextIds(Map translation) { + Map oldEdges = mOutgoingEdges; + mOutgoingEdges = new HashMap(oldEdges.size()); + for (LockEdge edge : oldEdges.values()) { + edge.translateContextIds(translation); + addOutgoingEdge(edge); + } + } + + Set getOutgoingEdges() { + return mOutgoingEdges.keySet(); + } + + public String toString() { + return "L_" + mLockId; + } + + long numberOfUniqueEdges() { + return mOutgoingEdges.size(); + } + + long numberOfDuplicatedEdges() { + long numberOfDuplicatedEdges = 0; + for (LockEdge edge : mOutgoingEdges.values()) { + numberOfDuplicatedEdges += edge.getDuplicates(); + } + return numberOfDuplicatedEdges; + } + + boolean alike(LockNode other, ContextReaderIfc reader) { + // TODO Maybe introduce some kind of cache to improve performance? + String thisClassName = reader.readLock(mLockId).getClassName(); + String otherClassName = reader.readLock(other.mLockId).getClassName(); + return thisClassName.equals(otherClassName); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/Lock.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/Lock.java new file mode 100644 index 00000000..114997bc --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/Lock.java @@ -0,0 +1,69 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common; + +//import net.jcip.annotations.ThreadSafe; + +/** + * A Lock instance represents a Java monitor object. + */ +//@ThreadSafe +public final class Lock { + private final String mClassName; + private final int mObjectId; + + public Lock(Object lock) { + mClassName = lock.getClass().getName(); + mObjectId = System.identityHashCode(lock); + } + + public Lock(String className, int objectId) { + mClassName = className; + mObjectId = objectId; + } + + public String toString() { + return mClassName + '@' + Integer.toHexString(mObjectId).toUpperCase(); + } + + public int getObjectId() { + return mObjectId; + } + + public String getClassName() { + return mClassName; + } + + public int hashCode() { + return mObjectId; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Lock other = (Lock) obj; + return mObjectId == other.mObjectId + && mClassName.equals(other.mClassName); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/LockingContext.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/LockingContext.java new file mode 100644 index 00000000..7d5ed8a5 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/LockingContext.java @@ -0,0 +1,101 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common; + +//import net.jcip.annotations.ThreadSafe; + +/** + * An instance of this class represents the context for the acquiring of a lock. + */ +//@ThreadSafe +public final class LockingContext { + /** + * The name of the thread that acquired the lock. + */ + private final String mThreadName; + + /** + * A textual description of how the lock object was addressed. For example: + * "this", "com.enea.jcarder.Foo.mBar" or "com.enea.jcarder.Foo.getLock()" + */ + private final String mLockReference; + + /** + * The method that acquired a lock, on the + * format "com.enea.jcarder.Foo.bar()". + */ + private final String mMethodWithClass; + // TODO Include row number in MethodWithClass? + + public LockingContext(String threadName, + String lockReference, + String methodWithClass) { + mThreadName = threadName; + mLockReference = lockReference; + mMethodWithClass = methodWithClass; + } + + public LockingContext(Thread thread, + String lockReference, + String methodWithClass) { + this(thread.getName(), lockReference, methodWithClass); + } + + public String getLockReference() { + return mLockReference; + } + + public String getMethodWithClass() { + return mMethodWithClass; + } + + public String getThreadName() { + return mThreadName; + } + + public boolean alike(LockingContext other) { + return mLockReference.equals(other.mLockReference) + && mMethodWithClass.equals(other.mMethodWithClass); + } + + public boolean equals(Object other) { + try { + if (other == null) { + return false; + } + // TODO Maybe use interned strings to improve performance? + final LockingContext otherContext = (LockingContext) other; + return mThreadName.equals(otherContext.mThreadName) + && mLockReference.equals(otherContext.mLockReference) + && mMethodWithClass.equals(otherContext.mMethodWithClass); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return mThreadName.hashCode() + + mMethodWithClass.hashCode() + + mLockReference.hashCode(); + } + + public String toString() { + return "Thread: " + mThreadName + + " LockRef: " + mLockReference + + " Method: " + mMethodWithClass; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileReader.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileReader.java new file mode 100644 index 00000000..8afad30a --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileReader.java @@ -0,0 +1,97 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.contexts; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; + +//import net.jcip.annotations.NotThreadSafe; + +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.util.logging.Logger; + +//@NotThreadSafe +public final class ContextFileReader +implements ContextReaderIfc { + // TODO Make the directory of the database files configurable? + public static final String EVENT_DB_FILENAME = "jcarder_events.db"; + public static final String CONTEXTS_DB_FILENAME = "jcarder_contexts.db"; + static final long MAGIC_COOKIE = 3927194112434171438L; + static final int MAJOR_VERSION = 1; + static final int MINOR_VERSION = 0; + static final Charset CHARSET = Charset.forName("UTF-8"); + private final Logger mLogger; + private final ByteBuffer mBuffer; + + public ContextFileReader(Logger logger, File file) throws IOException { + mLogger = logger; + RandomAccessFile raFile = new RandomAccessFile(file, "r"); + final String path = file.getCanonicalPath(); + mLogger.info("Opening for reading: " + path); + FileChannel roChannel = raFile.getChannel(); + if (roChannel.size() > Integer.MAX_VALUE) { + throw new IOException("File too large: " + path); + } + mBuffer = roChannel.map(FileChannel.MapMode.READ_ONLY, + 0, + (int) roChannel.size()); + roChannel.close(); + raFile.close(); + validateHeader(path); + } + + private void validateHeader(String filename) throws IOException { + mBuffer.rewind(); + if (MAGIC_COOKIE != mBuffer.getLong()) { + throw new IOException("Invalid file contents in: " + filename); + } + final int majorVersion = mBuffer.getInt(); + final int minorVersion = mBuffer.getInt(); + if (majorVersion != MAJOR_VERSION) { + throw new IOException("Incompatible version: " + + majorVersion + "." + minorVersion + + " in: " + filename); + } + } + + private String readString() { + final int stringBytes = mBuffer.getInt(); + mBuffer.limit(stringBytes + mBuffer.position()); + final String result = CHARSET.decode(mBuffer).toString(); + mBuffer.limit(mBuffer.capacity()); + return result; + } + + public LockingContext readContext(int id) { + mBuffer.position(id); + return new LockingContext(readString(), + readString(), + readString()); + } + + public Lock readLock(int id) { + mBuffer.position(id); + String className = readString(); + int objectId = mBuffer.getInt(); + return new Lock(className, objectId); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileWriter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileWriter.java new file mode 100644 index 00000000..737c4fc1 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileWriter.java @@ -0,0 +1,514 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.contexts; + +import TransactionalIO.core.TransactionalFile; +import TransactionalIO.exceptions.GracefulException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +//import net.jcip.annotations.ThreadSafe; + +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.LockingContext; +import com.enea.jcarder.transactionalinterfaces.Bool; +import com.enea.jcarder.transactionalinterfaces.Intif; +import com.enea.jcarder.transactionalinterfaces.bytebuffer; +import com.enea.jcarder.transactionalinterfaces.bytebuffer.byteholder; +import com.enea.jcarder.util.logging.Logger; +import dstm2.Init; +import dstm2.atomic; +import dstm2.Thread; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.nio.LongBuffer; +import java.util.Vector; +import java.util.concurrent.Callable; + +import dstm2.AtomicArray; + +//@ThreadSafe +import java.util.logging.Level; +public final class ContextFileWriter +implements ContextWriterIfc { + + private final FileChannel mChannel; + private int mNextFilePosition = 0; + private final Logger mLogger; + private ByteBuffer mBuffer;// = ByteBuffer.allocateDirect(8192); + private byte[] data; + RandomAccessFile raFile; + + + private bytebuffer mbuff; + private Intif filePosition; + private Intif test; + private Bool ShutdownHookExecuted; + + TransactionalFile trraFile; + + + private boolean mShutdownHookExecuted = false; + + public ContextFileWriter(Logger logger, File file) throws IOException { + System.out.println("d"); + mbuff = new bytebuffer(); + mbuff.allocateDirect(8192); + filePosition = new Intif(); + filePosition.init(); + test = new Intif(); + test.init(); + ShutdownHookExecuted = new Bool(); + ShutdownHookExecuted.init(); + data = new byte[8192]; + mBuffer = ByteBuffer.wrap(data); + mLogger = logger; + mLogger.info("Opening for writing: " + file.getAbsolutePath()); + raFile = new RandomAccessFile(file, "rw"); + + trraFile = new TransactionalFile(file.getAbsolutePath(), "rw"); + trraFile.file.setLength(0); +// ShutdownHookExecuted.init(); + + + raFile.setLength(0); + mChannel = raFile.getChannel(); + + + // writeHeader(); + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { shutdownHook(); } + }); + } + + + + private void shutdownHook() { + // System.out.println(Thread.currentThread() + " ashut ddddddborted in committing"); + try{ + Thread.doIt(new Callable() { + + public Boolean call() { + + try { + if (trraFile.file.getChannel().isOpen()) { + writeBuffer(); + + } + } catch (IOException e) { + e.printStackTrace(); + } + + ShutdownHookExecuted.set(true); + return true; + } + }); + }catch(GracefulException e){ + // System.out.println(Thread.currentThread() + " shut graceful exc"); + } + } + + + + private void writeBuffer() throws IOException { + mbuff.flip(); + System.out.println(Thread.currentThread()); + + // System.out.println(mbuff.remaining()); + trraFile.write(mbuff.getBytes()); + while (mbuff.hasRemaining()) { + Thread.yield(); + trraFile.write(mbuff.getBytes()); + } + mbuff.clear(); + } + + + + private void writeHeader() throws IOException { + // System.out.println("head"); + ByteBuffer mBuffer = ByteBuffer.allocateDirect(16); + mBuffer.putLong(ContextFileReader.MAGIC_COOKIE); + mBuffer.putInt(ContextFileReader.MAJOR_VERSION); + mBuffer.putInt(ContextFileReader.MINOR_VERSION); + mBuffer.rewind(); + for (int i=0; i<16; i++){ + mbuff.put(mBuffer.get()); + } + //filePosition.get(); + test.increment(8+4+4); + //filePosition.increment(8+4+4); + } + + + + public void close() throws IOException { + // System.out.println("clo"); + try{ + Thread.doIt(new Callable() { + + public Boolean call() { + try { + // System.out.println(Thread.currentThread() + " closeaborted in committing"); + writeBuffer(); + trraFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return true; + }; + }); + }catch(GracefulException e){ + System.out.println(Thread.currentThread() + " close graceful exc"); + } + } + + + + private void writeString(String s) throws IOException { + // System.out.println("str"); + ByteBuffer encodedString = ContextFileReader.CHARSET.encode(s); + final int length = encodedString.remaining(); + assureBufferCapacity(4 + length); + ByteBuffer mBuffer = ByteBuffer.allocateDirect(length +4); + mBuffer.putInt(length); + mBuffer.put(encodedString); + mBuffer.rewind(); + System.out.println("---------------"); + System.out.println("int: " + length); + System.out.println("str: " + encodedString); + System.out.println("---------------"); + //test.increment(8+length); + + //for (int i=0; i<4+length; i++) + // mbuff.put(mBuffer.get()); + // while (mBuffer.hasRemaining()) + // mbuff.put(mBuffer.get()); + // mbuff.put(mBuffer); + // mbuff.put(encodedString); + //filePosition.get(); + // test.increment(4+length); + // test.increment(4); + //filePosition.increment(4 + length); + } + + + + private void writeInteger(int i) throws IOException { + // System.out.println("int"); + assureBufferCapacity(4); + ByteBuffer mBuffer = ByteBuffer.allocateDirect(4); + mBuffer.putInt(i); + System.out.println("---------------"); + System.out.println("int: " + i); + + + System.out.println("---------------"); + for (int j=0; j<4; j++){ + // mbuff.put(mBuffer.get()); + } + // filePosition.get(); + //test.increment(4); + //filePosition.increment(4); + } + + + + private void assureBufferCapacity(int size) throws IOException { + if (mbuff.remaining() < size){// || ShutdownHookExecuted.isTrue()) { + writeBuffer(); + } + + // Grow buffer if it can't hold the requested size. + while (mbuff.capacity() < size) { + mbuff = mbuff.allocateDirect(2 * mbuff.capacity()); + } + } + + + + + + + public int trwriteLock(Vector arg) throws IOException { + int startPosition = test.get(); + //writeString(((Lock)arg.get(0)).getClassName()); + //writeInteger(((Lock)arg.get(0)).getObjectId()); + + ByteBuffer encodedString = ContextFileReader.CHARSET.encode(((Lock)arg.get(0)).getClassName()); + final int length = encodedString.remaining(); + ByteBuffer mBuffer = ByteBuffer.allocateDirect(8+ length); + mBuffer.putInt(length); + mBuffer.put(encodedString); + mBuffer.putInt(((Lock)arg.get(0)).getObjectId()); + mBuffer.rewind(); + // assureBufferCapacity(8+length); + for (int j=0; j<(8+length); j++){ + byte value = mBuffer.get(); + //if (mbuff.mbuffer.getByteHolder().get(mbuff.mbuffer.getPosition()) == null) + // mbuff.mbuffer.getByteHolder().set(mbuff.mbuffer.getPosition(), mbuff.factory2.create()); + AtomicArray ar = mbuff.mbuffer.getByteHolder(); + byteholder bh = mbuff.factory2.create(); + bh.setByte(value); + mbuff.mbuffer.getByteHolder().set(mbuff.mbuffer.getPosition(),bh); + mbuff.mbuffer.setPosition(mbuff.mbuffer.getPosition()+1); + } + + /*System.out.println("---------------"); + System.out.println("int: " + length); + System.out.println("str: " + encodedString); + System.out.println("int: " + ((Lock)arg.get(0)).getObjectId()); + System.out.println("---------------");*/ + test.increment(8+length); + flushBufferIfNeeded(); + return startPosition; + } + + public int writeLock(Lock lock) throws IOException { + // System.out.println("lock"); + int result = 0; + try{ + final Vector arg = new Vector() ; + arg.add(lock); + result = Thread.doIt(new Callable() { + + public Integer call() { + try { + // System.out.println(Thread.currentThread() + " alockborted in committing"); + return trwriteLock(arg); + // System.out.println(Thread.currentThread() + " NO???? alockborted in committing"); + } catch (IOException ex) { + java.util.logging.Logger.getLogger(ContextFileWriter.class.getName()).log(Level.SEVERE, null, ex); + } + return -1; + } + }); + + }catch(GracefulException e){ + System.out.println(Thread.currentThread() + "lock graceful exc"); + + }finally{ + return result; + } + } + + + + public int trwriteContext(LockingContext context) + throws IOException { + int startPosition = test.get(); + + // writeString(context.getThreadName()); + // writeString(context.getLockReference()); + // writeString(context.getMethodWithClass()); + + ByteBuffer encodedString = ContextFileReader.CHARSET.encode(context.getThreadName()); + final int length = encodedString.remaining(); + ByteBuffer encodedString2 = ContextFileReader.CHARSET.encode(context.getLockReference()); + final int length2 = encodedString2.remaining(); + ByteBuffer encodedString3 = ContextFileReader.CHARSET.encode(context.getMethodWithClass()); + final int length3 = encodedString3.remaining(); + ByteBuffer mBuffer = ByteBuffer.allocateDirect(12+length+length2+length3); + + mBuffer.putInt(length); + mBuffer.put(encodedString); + mBuffer.putInt(length2); + mBuffer.put(encodedString2); + mBuffer.putInt(length3); + mBuffer.put(encodedString3); + mBuffer.rewind(); + /* System.out.println("------"); + System.out.println("int: " + length); + System.out.println("str: " + encodedString); + System.out.println("int: " + length2); + System.out.println("str: " + encodedString2); + System.out.println("int: " + length3); + System.out.println("str: " + encodedString3); + System.out.println("------");*/ + + + // assureBufferCapacity(12 + length + length2 + length3); + for (int j=0;j<(12+length +length2 +length3);j++){ + // mbuff.put(mBuffer.get()); + byte value = mBuffer.get(); + //if (mbuff.mbuffer.getByteHolder().get(mbuff.mbuffer.getPosition()) == null) + // mbuff.mbuffer.getByteHolder().set(mbuff.mbuffer.getPosition(), mbuff.factory2.create()); + AtomicArray ar = mbuff.mbuffer.getByteHolder(); + byteholder bh = mbuff.factory2.create(); + bh.setByte(value); + mbuff.mbuffer.getByteHolder().set(mbuff.mbuffer.getPosition(),bh); + mbuff.mbuffer.getByteHolder().get(mbuff.mbuffer.getPosition()).setByte(value); + mbuff.mbuffer.setPosition(mbuff.mbuffer.getPosition()+1); + } + + test.increment(12+length +length2 +length3); + flushBufferIfNeeded(); + + return startPosition; + } + + public int writeContext(LockingContext context) + { + + int startPosition = -2; + try{ + final LockingContext c = context; + startPosition = Thread.doIt(new Callable() { + + public Integer call() throws IOException { + return trwriteContext(c); + } + + }); + + + + }catch(GracefulException e){ + System.out.println(Thread.currentThread() + " context graceful exc"); + } + finally{ + // System.out.println(Thread.currentThread() + " con"); + return startPosition; + } + } + + private void flushBufferIfNeeded() throws IOException { + if (ShutdownHookExecuted.isTrue()) { + // System.out.println("fdddlush"); + writeBuffer(); + } + } + + + /* + private void writeString(String s) throws IOException { + + ByteBuffer encodedString = ContextFileReader.CHARSET.encode(s); + final int length = encodedString.remaining(); + + assureBufferCapacity(4 + length); + mBuffer.putInt(length); + mBuffer.put(encodedString); + System.out.println("------"); + System.out.println("int: " + length); + System.out.println("str: " + encodedString); + System.out.println("------"); + + mNextFilePosition += 4 + length; + } + + private synchronized void shutdownHook() { + System.out.println("shut"); + try { + if (mChannel.isOpen()) { + writeBuffer(); + } + } catch (IOException e) { + e.printStackTrace(); + } + mShutdownHookExecuted = true; + } + + + + public synchronized void close() throws IOException { + writeBuffer(); + mChannel.close(); + } + + private void writeInteger(int i) throws IOException { + assureBufferCapacity(4); + mBuffer.putInt(i); + System.out.println("------"); + System.out.println("int: " + i); + System.out.println("------"); + mNextFilePosition += 4; + + } + private void assureBufferCapacity(int size) throws IOException { + if (mBuffer.remaining() < size || mShutdownHookExecuted) { + writeBuffer(); + } + + // Grow buffer if it can't hold the requested size. + while (mBuffer.capacity() < size) { + mBuffer = ByteBuffer.allocateDirect(2 * mBuffer.capacity()); + } + } + + public synchronized int writeLock(Lock lock) throws IOException { + final int startPosition = mNextFilePosition; + writeString(lock.getClassName()); + writeInteger(lock.getObjectId()); + flushBufferIfNeeded(); + return startPosition; + } + + public synchronized int writeContext(LockingContext context) + throws IOException { + final int startPosition = mNextFilePosition; + writeString(context.getThreadName()); + writeString(context.getLockReference()); + writeString(context.getMethodWithClass()); + flushBufferIfNeeded(); + System.out.println(Thread.currentThread() + " con"); + return startPosition; + } + + private void flushBufferIfNeeded() throws IOException { + if (mShutdownHookExecuted) { + writeBuffer(); + // System.out.println("sssskk"); + } + } + + private void writeBuffer() throws IOException { + mBuffer.flip(); + System.out.println("here " + mBuffer.array().length); + System.out.println("Written" + mChannel.write(mBuffer)); + while (mBuffer.hasRemaining()) { + Thread.yield(); + raFile.write(data); + //mChannel.write(mBuffer); + } + mBuffer.clear(); + } + private void writeHeader() throws IOException { + mBuffer.putLong(ContextFileReader.MAGIC_COOKIE); + mBuffer.putInt(ContextFileReader.MAJOR_VERSION); + mBuffer.putInt(ContextFileReader.MINOR_VERSION); + mNextFilePosition += 8 + 4 + 4; + + // LongBuffer g; + // Long. + // Long.valueOf(ContextFileReader.MAGIC_COOKIE). + // mBuffer.putLong(ContextFileReader.MAGIC_COOKIE); + // mBuffer.putInt(ContextFileReader.MAJOR_VERSION); + // mBuffer.putInt(ContextFileReader.MINOR_VERSION); + // mNextFilePosition += 8 + 4 + 4; + }*/ + } + + + + + diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextMemory.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextMemory.java new file mode 100644 index 00000000..80fb9885 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextMemory.java @@ -0,0 +1,49 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.contexts; + +import java.util.LinkedList; +//import net.jcip.annotations.NotThreadSafe; +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.LockingContext; + +//@NotThreadSafe +public final class ContextMemory +implements ContextWriterIfc, ContextReaderIfc { + + private final LinkedList mLocks = new LinkedList(); + private final LinkedList mLockingContexts = + new LinkedList(); + + public int writeLock(Lock lock) { + mLocks.addLast(lock); + return mLocks.size() - 1; + } + + public int writeContext(LockingContext context) { + mLockingContexts.addLast(context); + return mLockingContexts.size() - 1; + } + + public Lock readLock(int id) { + return mLocks.get(id); + } + + public LockingContext readContext(int id) { + return mLockingContexts.get(id); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextReaderIfc.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextReaderIfc.java new file mode 100644 index 00000000..1dfb1e42 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextReaderIfc.java @@ -0,0 +1,25 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.contexts; + +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.LockingContext; + +public interface ContextReaderIfc { + LockingContext readContext(int id); + Lock readLock(int id); +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextWriterIfc.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextWriterIfc.java new file mode 100644 index 00000000..29999905 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextWriterIfc.java @@ -0,0 +1,29 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.contexts; + +import java.io.IOException; + +import com.enea.jcarder.common.Lock; +import com.enea.jcarder.common.LockingContext; + +public interface ContextWriterIfc { + + int writeLock(Lock lock) throws IOException; + + int writeContext(LockingContext context) throws IOException; +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileReader.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileReader.java new file mode 100644 index 00000000..c4a98518 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileReader.java @@ -0,0 +1,91 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.events; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import com.enea.jcarder.util.logging.Logger; + +public final class EventFileReader { + private static final int INT_LENGTH = 4; + private static final int LONG_LENGTH = 8; + private final Logger mLogger; + static final int EVENT_LENGTH = (INT_LENGTH * 4) + LONG_LENGTH; + static final long MAGIC_COOKIE = 2153191828159737167L; + static final int MAJOR_VERSION = 1; + static final int MINOR_VERSION = 0; + + public EventFileReader(Logger logger) { + mLogger = logger; + } + + public void parseFile(File file, + LockEventListenerIfc eventReceiver) + throws IOException { + int numberOfParsedEvents = 0; + FileInputStream fis = new FileInputStream(file); + final String path = file.getCanonicalPath(); + mLogger.info("Opening for reading: " + path); + FileChannel fileChannel = fis.getChannel(); + validateHeader(fileChannel, path); + final ByteBuffer buffer = ByteBuffer.allocate(EVENT_LENGTH); + while (fileChannel.read(buffer) == EVENT_LENGTH) { + buffer.rewind(); + parseLockEvent(buffer, eventReceiver); + buffer.rewind(); + numberOfParsedEvents++; + } + mLogger.fine("Loaded " + numberOfParsedEvents + + " lock events from file."); + } + + private void validateHeader(FileChannel channel, + String filename) throws IOException { + final ByteBuffer buffer = ByteBuffer.allocate(8 + 4 + 4); + channel.read(buffer); + buffer.flip(); + if (MAGIC_COOKIE != buffer.getLong()) { + throw new IOException("Invalid file contents in: " + filename); + } + final int majorVersion = buffer.getInt(); + final int minorVersion = buffer.getInt(); + if (majorVersion != MAJOR_VERSION) { + throw new IOException("Incompatible version: " + + majorVersion + "." + minorVersion + + " in: " + filename); + } + } + + private static void parseLockEvent(ByteBuffer lockEventBuffer, + LockEventListenerIfc eventReceiver) + throws IOException { + final int lockId = lockEventBuffer.getInt(); + final int lockingContextId = lockEventBuffer.getInt(); + final int lastTakenLockId = lockEventBuffer.getInt(); + final int lastTakenLockingContextId = lockEventBuffer.getInt(); + final long threadId = lockEventBuffer.getLong(); + eventReceiver.onLockEvent(lockId, + lockingContextId, + lastTakenLockId, + lastTakenLockingContextId, + threadId); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileWriter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileWriter.java new file mode 100644 index 00000000..976155eb --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileWriter.java @@ -0,0 +1,249 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.events; + +import TransactionalIO.core.TransactionalFile; +import TransactionalIO.exceptions.GracefulException; +import com.enea.jcarder.transactionalinterfaces.Bool; +import com.enea.jcarder.transactionalinterfaces.bytebuffer; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +//import net.jcip.annotations.ThreadSafe; + +import com.enea.jcarder.util.Counter; +import com.enea.jcarder.util.TransactionalCounter; +import com.enea.jcarder.util.logging.Logger; + +import java.util.concurrent.Callable; +import static com.enea.jcarder.common.events.EventFileReader.EVENT_LENGTH; + +import dstm2.Thread; + +//@ThreadSafe +import java.util.Vector; +public final class EventFileWriter implements LockEventListenerIfc { + private final ByteBuffer mBuffer = + ByteBuffer.allocateDirect(EVENT_LENGTH * 1024); + private final FileChannel mFileChannel; + private final Logger mLogger; + private final Counter mWrittenLockEvents; + private boolean mShutdownHookExecuted = false; + + private bytebuffer mbuff; + private Bool ShutdownHookExecuted; + TransactionalFile traf; + TransactionalCounter trmWrittenLockEvents; + + public EventFileWriter(Logger logger, File file) throws IOException { + + + + mLogger = logger; + mLogger.info("Opening for writing: " + file.getAbsolutePath()); + RandomAccessFile raFile = new RandomAccessFile(file, "rw"); + raFile.setLength(0); + mFileChannel = raFile.getChannel(); + mWrittenLockEvents = new Counter("Written Lock Events", + mLogger, + 100000); + + mbuff = new bytebuffer(); + mbuff.allocateDirect(8192); + traf = new TransactionalFile(file.getAbsolutePath(), "rw"); + ShutdownHookExecuted = new Bool(); + ShutdownHookExecuted.init(); + trmWrittenLockEvents = new TransactionalCounter("Written Lock Events", + mLogger, + 100000); + + writeHeader(); + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { shutdownHook(); } + }); + } + + private void writeHeader() throws IOException { + mBuffer.putLong(EventFileReader.MAGIC_COOKIE); + mBuffer.putInt(EventFileReader.MAJOR_VERSION); + mBuffer.putInt(EventFileReader.MINOR_VERSION); + writeBuffer(); + } + + private void trwriteHeader() throws IOException { + ByteBuffer mBuffer = ByteBuffer.allocateDirect(16); + mBuffer.putLong(EventFileReader.MAGIC_COOKIE); + mBuffer.putInt(EventFileReader.MAJOR_VERSION); + mBuffer.putInt(EventFileReader.MINOR_VERSION); + mBuffer.rewind(); + + for (int i=0; i<16; i++){ + mbuff.put(mBuffer.get()); + } + writeBuffer(); + } + + public synchronized void onLockEvent(int lockId, + int lockingContextId, + int lastTakenLockId, + int lastTakenLockingContextId, + long threadId) throws IOException { + + mBuffer.putInt(lockId); + mBuffer.putInt(lockingContextId); + mBuffer.putInt(lastTakenLockId); + mBuffer.putInt(lastTakenLockingContextId); + mBuffer.putLong(threadId); + mWrittenLockEvents.increment(); + if (mBuffer.remaining() < EVENT_LENGTH || mShutdownHookExecuted) { + writeBuffer(); + } + } + + public void tronLockEvent(Vector arg) throws IOException { + + ByteBuffer mBuffer = ByteBuffer.allocateDirect(24); + mBuffer.putInt((Integer)arg.get(0)); + mBuffer.putInt((Integer)arg.get(1)); + mBuffer.putInt((Integer)arg.get(2)); + mBuffer.putInt((Integer)arg.get(3)); + mBuffer.putLong((Long)arg.get(4)); + mBuffer.rewind(); + + for (int i=0; i<24; i++){ + mbuff.put(mBuffer.get()); + } + + trmWrittenLockEvents.increment(); + if (mbuff.remaining() < EVENT_LENGTH || ShutdownHookExecuted.isTrue()) { + writeBuffer(); + } + } + + public void trLockEventWrapper(int lockId, + int lockingContextId, + int lastTakenLockId, + int lastTakenLockingContextId, + long threadId) throws IOException{ + + + final Vector arg = new Vector(); + arg.add(Integer.valueOf(lockId)); + arg.add(Integer.valueOf(lockingContextId)); + arg.add(Integer.valueOf(lastTakenLockId)); + arg.add(Integer.valueOf(lastTakenLockingContextId)); + arg.add(Long.valueOf(threadId)); + Thread.doIt(new Callable() { + + public Boolean call() throws IOException { + tronLockEvent(arg); + return true; + } + + }); + + } + + private void writeBuffer() throws IOException { + mBuffer.flip(); + mFileChannel.write(mBuffer); + while (mBuffer.hasRemaining()) { + Thread.yield(); + mFileChannel.write(mBuffer); + } + mBuffer.clear(); + } + + + private void trwriteBuffer() throws IOException { + mbuff.flip(); + traf.write(mbuff.getBytes()); + while (mbuff.hasRemaining()) { + Thread.yield(); + traf.write(mbuff.getBytes()); + } + mbuff.clear(); + } + + + public synchronized void close() throws IOException { + + writeBuffer(); + mFileChannel.close(); + } + + public void trclose() throws IOException { + // System.out.println("clo"); + try{ + Thread.doIt(new Callable() { + + public Boolean call() { + try { + // System.out.println(Thread.currentThread() + " closeaborted in committing"); + writeBuffer(); + traf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return true; + }; + }); + }catch(GracefulException e){ + System.out.println(Thread.currentThread() + " close graceful exc"); + } + } + + private synchronized void shutdownHook() { + try { + if (mFileChannel.isOpen()) { + writeBuffer(); + } + } catch (IOException e) { + e.printStackTrace(); + } + mShutdownHookExecuted = true; + } + + + private void trshutdownHook() { + System.out.println(Thread.currentThread() + " ashut ddddddborted in committing"); + try{ + Thread.doIt(new Callable() { + + public Boolean call() { + + try { + if (traf.file.getChannel().isOpen()) { + writeBuffer(); + + } + } catch (IOException e) { + e.printStackTrace(); + } + + ShutdownHookExecuted.set(true); + return true; + } + }); + }catch(GracefulException e){ + System.out.println(Thread.currentThread() + " shut graceful exc"); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/LockEventListenerIfc.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/LockEventListenerIfc.java new file mode 100644 index 00000000..263054ab --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/LockEventListenerIfc.java @@ -0,0 +1,34 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.common.events; + +import java.io.IOException; +import java.util.Vector; + +public interface LockEventListenerIfc { + + void onLockEvent(int lockId, + int lockingContextId, + int lastTakenLockId, + int lastTakenLockingContextId, + long threadId)throws IOException; + + void tronLockEvent(Vector srgs)throws IOException; +} + + + diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Bool.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Bool.java new file mode 100644 index 00000000..1825b87d --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Bool.java @@ -0,0 +1,60 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.enea.jcarder.transactionalinterfaces; + +/** + * + * @author navid + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + + +import com.enea.jcarder.transactionalinterfaces.bytebuffer.byteholder; +import dstm2.AtomicArray; +import dstm2.atomic; +import dstm2.Thread; +import dstm2.factory.Factory; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * + * @author navid + */ +public class Bool { + + + static Factory factory = Thread.makeFactory(boolif.class); + boolif boolif; + + public void init(){ + boolif = factory.create(); + boolif.setValue(false); + } + + public void set(boolean v){ + boolif.setValue(v); + } + + public boolean isTrue(){ + return boolif.getValue(); + } + + + + @atomic public interface boolif{ + boolean getValue(); + void setValue(boolean value); + + // void setLong(long value); + } + + + +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Intif.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Intif.java new file mode 100644 index 00000000..8c333df4 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Intif.java @@ -0,0 +1,41 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.enea.jcarder.transactionalinterfaces; + +import dstm2.atomic; +import dstm2.Thread; +import dstm2.factory.Factory; + +/** + * + * @author navid + */ +public class Intif { + + public static Factory factory = Thread.makeFactory(positionif.class); + + positionif pos; + + public void init(){ + + pos = factory.create(); + //pos.setPosition(0); + } + + public void increment(int offset){ + pos.setPosition(pos.getPosition() + offset); + + } + + public int get(){ + return pos.getPosition(); + } + + @atomic public interface positionif{ + public int getPosition(); + public void setPosition(int pos); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/bytebuffer.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/bytebuffer.java new file mode 100644 index 00000000..610b3441 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/bytebuffer.java @@ -0,0 +1,127 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.enea.jcarder.transactionalinterfaces; + +import com.enea.jcarder.transactionalinterfaces.bytebuffer.byteholder; +import dstm2.AtomicArray; +import dstm2.atomic; +import dstm2.Thread; +import dstm2.factory.Factory; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * + * @author navid + */ +public class bytebuffer { + + + static Factory factory = Thread.makeFactory(bytebufferif.class); + public static Factory factory2 = Thread.makeFactory(byteholder.class); + + + public bytebufferif mbuffer; + + public int capacity(){ + return mbuffer.getCapacity(); + } + + public int position(){ + return mbuffer.getPosition(); + } + + public bytebufferif flip(){ + mbuffer.setLimit(mbuffer.getPosition()); + mbuffer.setPosition(0); + return mbuffer; + } + + public final bytebufferif clear(){ + mbuffer.setPosition(0); + mbuffer.setLimit(mbuffer.getCapacity()); + return mbuffer; + } + + public final boolean hasRemaining(){ + return mbuffer.getPosition() < mbuffer.getLimit(); + } + + public final int remaining(){ + return mbuffer.getLimit() - mbuffer.getPosition(); + } + + public void put(byte value){ + if (mbuffer.getByteHolder().get(mbuffer.getPosition()) == null) + mbuffer.getByteHolder().set(mbuffer.getPosition(), factory2.create()); + mbuffer.getByteHolder().get(mbuffer.getPosition()).setByte(value); + mbuffer.setPosition(mbuffer.getPosition()+1); + } + + public void put(ByteBuffer value){ + + if (remaining() < value.remaining()) + throw new BufferOverflowException(); + + for (int i=0; i(byteholder.class,capacity)); + mbuffer.setPosition(0); + mbuffer.setLimit(capacity); + mbuffer.setCapacity(capacity); + AtomicArray ar = mbuffer.getByteHolder(); + //for (int i=0; i getByteHolder(); + void setByteHolder(AtomicArray bytes); + } + + @atomic public interface byteholder{ + byte getByte(); + void setByte(byte value); + } + + + +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/BuildInformation.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/BuildInformation.java new file mode 100644 index 00000000..6a429a7c --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/BuildInformation.java @@ -0,0 +1,68 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import java.io.IOException; +import java.util.Properties; + +public final class BuildInformation { + + private BuildInformation() { } + + public static String getShortInfo() { + try { + Properties props = loadBuildProperties(); + return "JCarder (" + + props.getProperty("build.version") + + "/" + + props.getProperty("build.number") + + ")"; + } catch (IOException e) { + e.printStackTrace(); + return "JCarder"; + } + } + + public static void printLongBuildInformation() { + Properties props; + try { + props = loadBuildProperties(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + StringBuffer sb = new StringBuffer(); + sb.append("JCarder -- cards Java programs to keep threads" + + " disentangled\n"); + sb.append("\nCopyright (C) 2006-2007 Enea AB\n"); + sb.append("Copyright (C) 2007 Ulrik Svensson\n"); + sb.append("Copyright (C) 2007 Joel Rosdahl\n"); + sb.append("\nVersion: " + props.getProperty("build.version")); + sb.append("\nBuild : " + props.getProperty("build.number")); + sb.append("\nAt : " + props.getProperty("build.timestamp")); + sb.append("\nBy : " + props.getProperty("build.user.name")); + sb.append("\nOn : " + props.getProperty("build.os.name")); + System.out.println(sb.toString()); + } + + private static Properties loadBuildProperties() throws IOException { + Properties props = new Properties(); + ClassLoader classLoader = ClassLoader.getSystemClassLoader(); + props.load(classLoader.getResourceAsStream("build.properties")); + return props; + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/Counter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/Counter.java new file mode 100644 index 00000000..9d34ef75 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/Counter.java @@ -0,0 +1,44 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import com.enea.jcarder.util.logging.Logger; + +//import net.jcip.annotations.NotThreadSafe; + +//@NotThreadSafe +public final class Counter { + final int mLogIntervall; + final String mName; + final Logger mLogger; + int mValue = 0; + + public Counter(String name, Logger logger, int logInterval) { + mName = name; + mLogger = logger; + mLogIntervall = logInterval; + } + + public void increment() { + mValue++; + if ((mValue % mLogIntervall) == 0) { + mLogger.fine(mName + ": " + mValue); + } else if (mLogger.isLoggable(Logger.Level.FINEST)) { + mLogger.finest(mName + ": " + mValue); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/IdentityWeakHashMap.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/IdentityWeakHashMap.java new file mode 100644 index 00000000..37270544 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/IdentityWeakHashMap.java @@ -0,0 +1,144 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +//import net.jcip.annotations.NotThreadSafe; + +/** + * This class is similar to the java.util.WeakHashMap but compares objects with + * the == operator instead of with the Object.equals method. + * + * TODO Add basic tests for this class. + */ +//@NotThreadSafe +public final class IdentityWeakHashMap { + private final HashMap mHashMap; + private final ReferenceQueue mReferenceQueue; + private final StrongKey mStrongKey = new StrongKey(); + private Object mLastKey = null; // Cache to improve performance. + private V mLastValue = null; // Cache to improve performance. + private int mPutCounter = 0; + + public IdentityWeakHashMap() { + mHashMap = new HashMap(); + mReferenceQueue = new ReferenceQueue(); + } + + public V get(Object key) { + /* + * Avoid calls to removeGarbageCollectedKeys in this method in order to + * improve performance. + */ + if (key == mLastKey) { + return mLastValue; + } + mLastKey = key; + mStrongKey.setReferent(key); + mLastValue = mHashMap.get(mStrongKey); + return mLastValue; + } + + public void put(Object key, V value) { + assert value != null; + mLastKey = key; + mLastValue = value; + if (mPutCounter > 1000) { + // Don't call to often in order to improve performance. + removeGarbageCollectedKeys(); + mPutCounter = 0; + } else { + mPutCounter++; + } + mHashMap.put((new WeakKey(key, mReferenceQueue)), value); + } + + private void removeGarbageCollectedKeys() { + Reference e; + int noOfCollectedLocks = 0; + while ((e = mReferenceQueue.poll()) != null) { + noOfCollectedLocks++; + mHashMap.remove(e); + } + } + + private static interface IdentityComparableKey { + Object get(); + boolean equals(Object obj); + int hashCode(); + } + + private static class StrongKey implements IdentityComparableKey { + private Object mReferent; + + void setReferent(Object referent) { + assert referent != null; + mReferent = referent; + } + + public Object get() { + return mReferent; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + try { + IdentityComparableKey reference = (IdentityComparableKey) obj; + return (reference.get() == get()); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return System.identityHashCode(mReferent); + } + } + + private static class WeakKey extends WeakReference + implements IdentityComparableKey { + private final int mHash; + + WeakKey(Object referent, ReferenceQueue queue) { + super(referent, queue); + assert referent != null; + mHash = System.identityHashCode(referent); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + try { + IdentityComparableKey reference = (IdentityComparableKey) obj; + Object referent = reference.get(); + return (referent != null) && (referent == get()); + } catch (ClassCastException e) { + return false; + } + } + + public int hashCode() { + return mHash; + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/InvalidOptionException.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/InvalidOptionException.java new file mode 100644 index 00000000..e1b2d5bd --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/InvalidOptionException.java @@ -0,0 +1,23 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +public class InvalidOptionException extends Exception { + public InvalidOptionException(String message) { + super(message); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/MaxValueCounter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/MaxValueCounter.java new file mode 100644 index 00000000..0f17bdf6 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/MaxValueCounter.java @@ -0,0 +1,46 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import com.enea.jcarder.util.logging.Logger; + +//import net.jcip.annotations.NotThreadSafe; + +//@NotThreadSafe +public final class MaxValueCounter { + final String mName; + final Logger mLogger; + int mValue = 0; + int mMaxValue = 0; + + public MaxValueCounter(String name, Logger logger) { + mName = name; + mLogger = logger; + } + + public void set(int value) { + mValue = value; + if (mValue > mMaxValue) { + mMaxValue = mValue; + mLogger.fine("New " + mName + ": " + mMaxValue); + } + } + + public String toString() { + return String.valueOf(mMaxValue); + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionFormatter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionFormatter.java new file mode 100644 index 00000000..3085e6b6 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionFormatter.java @@ -0,0 +1,102 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is a helper class for an OptionParser. + */ +class OptionFormatter { + private final int mOptionIndent; + private final int mDescrIndent; + private final int mMaxWidth; + + public OptionFormatter(int optionIndent, int descrIndent, int maxWidth) { + mOptionIndent = optionIndent; + mDescrIndent = descrIndent; + mMaxWidth = maxWidth; + } + + public void format(StringBuilder sb, String option, String descr) { + addSpace(sb, mOptionIndent); + sb.append(option); + final int firstIndent; + if (mOptionIndent + option.length() >= mDescrIndent) { + sb.append("\n"); + firstIndent = mDescrIndent; + } else { + firstIndent = mDescrIndent - (mOptionIndent + option.length()); + } + if (descr == null) { + sb.append('\n'); + } else { + List descrLines = wrapText(descr, mMaxWidth - mDescrIndent); + indentText(sb, descrLines, mDescrIndent, firstIndent); + } + } + + static void addSpace(StringBuilder sb, int n) { + for (int i = 0; i < n; ++i) { + sb.append(' '); + } + } + + static List wrapText(String text, int maxWidth) { + ArrayList result = new ArrayList(); + int pos = 0; + StringBuilder sb = new StringBuilder(); + + for (String word : text.split("\\s+")) { + if (sb.length() > 0 && word.length() + 1 > maxWidth - sb.length()) { + result.add(sb.toString()); + sb.setLength(0); + sb.append(word); + pos = word.length(); + } else { + if (sb.length() > 0) { + sb.append(' '); + } + sb.append(word); + pos += word.length(); + } + } + if (sb.length() > 0) { + result.add(sb.toString()); + } + + return result; + } + + static void indentText(StringBuilder sb, + List lines, + int indent, + int firstIndent) { + boolean first = true; + addSpace(sb, firstIndent); + for (String line : lines) { + if (first) { + first = false; + } else { + addSpace(sb, indent); + } + sb.append(line); + sb.append('\n'); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionParser.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionParser.java new file mode 100644 index 00000000..a032f828 --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionParser.java @@ -0,0 +1,167 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class parses command-line options. + */ +public class OptionParser { + private class OptionData { + String valueName; + String description; + } + + private final List mArguments = new ArrayList(); + private final Map mParsedOptions = + new HashMap(); + private final Map mValidOptions = + new HashMap(); + + public OptionParser() { + } + + /** + * Tell the parser to recognize a new option. + * + * Options can be given on the following forms: + * + *
    + *
  • -option
  • + *
  • -option foo
  • + *
+ * + * The first form specifies an option without a value and the second + * specifies an option with a value. + * + * @param option + * The option. + * @param description + * Description of the option. + */ + public void addOption(String option, String description) { + final int spacePos = option.indexOf(' '); + final String flag; + final String valueName; + if (spacePos == -1) { + flag = option; + valueName = null; + } else { + flag = option.substring(0, spacePos); + valueName = option.substring(spacePos + 1); + } + OptionData data = new OptionData(); + data.valueName = valueName; + data.description = description; + mValidOptions.put(flag, data); + } + + /** + * Get arguments remaining after options (and their values) have been + * parsed. + * + * @return The arguments. + */ + public List getArguments() { + return mArguments; + } + + /** + * Get a string containing help text describing available options. + * + * @return The help text. + */ + public String getOptionHelp() { + StringBuilder sb = new StringBuilder(); + OptionFormatter formatter = new OptionFormatter(2, 23, 79); + ArrayList options = new ArrayList(); + options.addAll(mValidOptions.keySet()); + Collections.sort(options); + for (String option : options) { + final OptionData data = mValidOptions.get(option); + final String optAndVal; + if (data.valueName == null) { + optAndVal = option; + } else { + optAndVal = option + " " + data.valueName; + } + formatter.format(sb, optAndVal, data.description); + } + return sb.toString(); + } + + /** + * Get parsed options. + * + * The map is keyed on option and the values are the option values (null if + * no parameter was expected). + * + * @return The option map. + */ + public Map getOptions() { + return mParsedOptions; + } + + /** + * Parse arguments. + * + * @param arguments + * The arguments to parse. + * @throws InvalidOptionException + * If an invalid option is encountered. + */ + public void parse(String[] arguments) throws InvalidOptionException { + mArguments.clear(); + boolean reachedNonOption = false; + for (int i = 0; i < arguments.length; ++i) { + if (!reachedNonOption + && (arguments[i].length() == 0 + || arguments[i].charAt(0) != '-')) { + reachedNonOption = true; + } + if (reachedNonOption) { + mArguments.add(arguments[i]); + } else { + String option = arguments[i]; + if (mValidOptions.containsKey(option)) { + OptionData data = mValidOptions.get(option); + String optionArgument; + if (data.valueName != null) { + ++i; + if (i < arguments.length) { + optionArgument = arguments[i]; + } else { + String message = "value missing to flag " + option; + throw new InvalidOptionException(message); + } + } else { + optionArgument = null; + } + mParsedOptions.put(option, optionArgument); + } else { + throw new InvalidOptionException("invalid flag: " + + arguments[i]); + } + } + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/TransactionalCounter.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/TransactionalCounter.java new file mode 100644 index 00000000..b47fb73b --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/TransactionalCounter.java @@ -0,0 +1,39 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.enea.jcarder.util; + +import com.enea.jcarder.transactionalinterfaces.Intif; +import com.enea.jcarder.util.logging.Logger; + + +/** + * + * @author navid + */ +public class TransactionalCounter { + + final int mLogIntervall; + final String mName; + final Logger mLogger; + Intif.positionif mValue; + + public TransactionalCounter(String name, Logger logger, int logInterval) { + mValue = Intif.factory.create(); + mValue.setPosition(0); + mName = name; + mLogger = logger; + mLogIntervall = logInterval; + } + + public void increment() { + mValue.setPosition(mValue.getPosition()+1); + if (((mValue.getPosition()) % mLogIntervall) == 0) { + mLogger.fine(mName + ": " + mValue); + } else if (mLogger.isLoggable(Logger.Level.FINEST)) { + mLogger.finest(mName + ": " + mValue); + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/AppendableHandler.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/AppendableHandler.java new file mode 100644 index 00000000..499c28af --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/AppendableHandler.java @@ -0,0 +1,125 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util.logging; + +import TransactionalIO.core.TransactionalFile; +import java.io.IOException; + +import com.enea.jcarder.util.logging.Logger.Level; + +/** + * This class acts as a log handler for classes that that implement the + * Appendable interface. + * + * The Appendable does not need to be thread-safe; AppendableHandler + * synchronizes calls to Appendable's methods. + */ +public class AppendableHandler implements Handler { + private final Appendable mDestination; + private final Level mLevel; + private final String mMessageFormat; + TransactionalFile traf; + + public AppendableHandler(Appendable mDestination, TransactionalFile traf) { + this(mDestination, Logger.Level.FINEST, traf); + } + + + public AppendableHandler(Appendable mDestination, Level mLevel, TransactionalFile traf) { + this(mDestination, mLevel, "{level}: {message}\n", traf); + } + + + + /** + * Constructor. + * + * The default log level is FINEST and the default message format is + * "{level}: {message}\n" + * + * @param destination + * Destination of the log messages. + */ + + + + public AppendableHandler(Appendable destination) { + this(destination, Logger.Level.FINEST); + } + + /** + * Constructor. + * + * The default message format is "{level}: {message}\n" + * + * @param destination + * Destination of the log messages. + * @param logLevel + * Log level. + */ + public AppendableHandler(Appendable destination, Logger.Level logLevel) { + this(destination, logLevel, "{level}: {message}\n"); + } + + /** + * Constructor. + * + * Substrings like {keyword} are expanded in the message format string. + * + * Currently supported keywords: + * + * - {message} -- the message + * - {level} -- the log level + * + * @param destination Destination of the log messages. + * @param logLevel Log level. + * @param messageFormat Message format. + */ + public AppendableHandler(Appendable destination, + Logger.Level logLevel, + String messageFormat) { + mDestination = destination; + mLevel = logLevel; + mMessageFormat = messageFormat; + } + + public AppendableHandler(Appendable mDestination, Level mLevel, String mMessageFormat, TransactionalFile traf) { + this.mDestination = mDestination; + this.mLevel = mLevel; + this.mMessageFormat = mMessageFormat; + this.traf = traf; + } + + + + + public void publish(Level level, String message) { + if (level.compareTo(mLevel) <= 0) { + try { + String formattedMessage = mMessageFormat + .replace("{level}", level.toString()) + .replace("{message}", message); + synchronized (mDestination) { + mDestination.append(formattedMessage); + } + //traf.write(formattedMessage.getBytes()); + } catch (IOException e) { + // Ignore. + } + } + } +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Handler.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Handler.java new file mode 100644 index 00000000..74ac348f --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Handler.java @@ -0,0 +1,36 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util.logging; + +import com.enea.jcarder.util.logging.Logger.Level; + +/** + * This interface must be implemented by classes that handles log messages from + * the Logger class. + */ +public interface Handler { + /** + * Handle a published message. + * + * This method is called by a Logger class each time it receives a message + * to be logged. + * + * @param level Log level of the message. + * @param message The message. + */ + void publish(Level level, String message); +} diff --git a/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Logger.java b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Logger.java new file mode 100644 index 00000000..29042a9e --- /dev/null +++ b/Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Logger.java @@ -0,0 +1,191 @@ +/* + * JCarder -- cards Java programs to keep threads disentangled + * + * Copyright (C) 2006-2007 Enea AB + * Copyright (C) 2007 Ulrik Svensson + * Copyright (C) 2007 Joel Rosdahl + * + * This program is made available under the GNU GPL version 2, with a special + * exception for linking with JUnit. See the accompanying file LICENSE.txt for + * details. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package com.enea.jcarder.util.logging; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * A simple logging framework. + * + * We use our own simple Logging framework for several reasons: + * - To avoid interfering with logging from the user application. Even if we + * were using Log4J instead of java.util.Logging.*, JCarder might interfere with + * for example Log4J system properties. + * - The default java.util.logging.LogManager is reset by a shutdown hook and + * there is no fixed order in which the shutdown hooks are executed. + * - Minimizing the usage of the Java standard library improves performance and + * minimizes the risk of deadlock if the standard library is instrumented by + * JCarder. + */ +public final class Logger { + public static enum Level { + SEVERE, + WARNING, + INFO, + CONFIG, + FINE, + FINER, + FINEST; + + /** + * Parse a string and return the corresponding log level. + * + * @param string The string to parse. + * @return + */ + public static Level fromString(String string) { + for (Level level : values()) { + if (string.equalsIgnoreCase(level.toString())) { + return level; + } + } + return null; + } + + public static String getEnumeration() { + StringBuffer sb = new StringBuffer(); + boolean first = true; + for (Level level : values()) { + if (first) { + first = false; + } else { + sb.append(", "); + } + sb.append(level.toString()); + } + return sb.toString(); + } + } + + final Collection mHandlers; + final Level mLevel; + + /** + * Constructor setting default value of log level. + * + * The default is to log everything, i.e., log level FINEST. + * + * @param handlers Log handlers. null means no handlers. + */ + public Logger(Collection handlers) { + this(handlers, Level.FINEST); + } + + /** + * Constructor. + * + * @param handlers Log handlers. null means no handlers. + * @param logLevel Log level. + */ + public Logger(Collection handlers, Level logLevel) { + mLevel = logLevel; + mHandlers = new ArrayList(); + if (handlers != null) { + // Create a copy of the provided collection instead of sharing + // it, in order to make sure that mHandlers is immutable and + // thread safe. + mHandlers.addAll(handlers); + } + } + + /** + * Check whether a message with a certain level would be logged. + * + * @param level The level. + * @return True if the message would be logged, otherwise false. + */ + public boolean isLoggable(Level level) { + return level.compareTo(mLevel) <= 0; + } + + /** + * Log a message with level SEVERE. + * + * @param message The message. + */ + public void severe(String message) { + publishLog(Level.SEVERE, message); + } + + + /** + * Log a message with level WARNING. + * + * @param message The message. + */ + public void warning(String message) { + publishLog(Level.WARNING, message); + } + + + /** + * Log a message with level INFO. + * + * @param message The message. + */ + public void info(String message) { + publishLog(Level.INFO, message); + } + + + /** + * Log a message with level CONFIG. + * + * @param message The message. + */ + public void config(String message) { + publishLog(Level.CONFIG, message); + } + + + /** + * Log a message with level FINE. + * + * @param message The message. + */ + public void fine(String message) { + publishLog(Level.FINE, message); + } + + + /** + * Log a message with level FINER. + * + * @param message The message. + */ + public void finer(String message) { + publishLog(Level.FINER, message); + } + + /** + * Log a message with level FINEST. + * + * @param message The message. + */ + public void finest(String message) { + publishLog(Level.FINEST, message); + } + + private void publishLog(Level level, String message) { + if (isLoggable(level)) { + for (Handler handler : mHandlers) { + handler.publish(level, message); + } + } + } +}