my version
authornavid <navid>
Mon, 5 Jan 2009 18:19:39 +0000 (18:19 +0000)
committernavid <navid>
Mon, 5 Jan 2009 18:19:39 +0000 (18:19 +0000)
47 files changed:
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EnteredMonitor.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListener.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/EventListenerIfc.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/JavaAgent.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockIdGenerator.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/LockingContextIdCache.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/StaticEventListener.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/ThreadLocalEnteredMonitors.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/ClassTransformer.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentConfig.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentationUtilities.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/InstrumentedAttribute.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/MonitorEnterMethodAdapter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/SimulateMethodSyncMethodAdapter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/agent/instrument/StackAnalyzeMethodVisitor.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Analyzer.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/Cycle.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/CycleDetector.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/DuplicatedEdgesHandler.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/GraphvizGenerator.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockEdge.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockGraphBuilder.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/analyzer/LockNode.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/Lock.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/LockingContext.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileReader.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextFileWriter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextMemory.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextReaderIfc.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/contexts/ContextWriterIfc.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileReader.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/EventFileWriter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/common/events/LockEventListenerIfc.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Bool.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/Intif.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/transactionalinterfaces/bytebuffer.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/BuildInformation.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/Counter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/IdentityWeakHashMap.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/InvalidOptionException.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/MaxValueCounter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionFormatter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/OptionParser.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/TransactionalCounter.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/AppendableHandler.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Handler.java [new file with mode: 0644]
Robust/Transactions/jcarderbenchmarks/mysrc/com/enea/jcarder/util/logging/Logger.java [new file with mode: 0644]

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 (file)
index 0000000..35b0772
--- /dev/null
@@ -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<Object>(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 (file)
index 0000000..7d4acdb
--- /dev/null
@@ -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<EnteredMonitor> 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<Boolean>() {
+          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 (file)
index 0000000..f62580b
--- /dev/null
@@ -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 (file)
index 0000000..62dc534
--- /dev/null
@@ -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<Handler> handlers = new ArrayList<Handler>();
+        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 (file)
index 0000000..3afe3a4
--- /dev/null
@@ -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<Integer> 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<Integer>();
+        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 (file)
index 0000000..cb33b33
--- /dev/null
@@ -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<EqualsComparableKey, Integer> mCache;
+    private final ReferenceQueue<Object> 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<EqualsComparableKey, Integer>();
+        mReferenceQueue = new ReferenceQueue<Object>();
+        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<LockingContext>
+    implements EqualsComparableKey {
+        private final int mHash;
+
+        SoftKey(LockingContext referent, ReferenceQueue<Object> 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 (file)
index 0000000..74010e9
--- /dev/null
@@ -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 (file)
index 0000000..e54e5d2
--- /dev/null
@@ -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<ArrayList<EnteredMonitor>> {
+
+    public ArrayList<EnteredMonitor> initialValue() {
+        return new ArrayList<EnteredMonitor>();
+    }
+
+    Iterator<EnteredMonitor> getIterator() {
+        return get().iterator();
+    }
+
+    EnteredMonitor getFirst() {
+        ArrayList<EnteredMonitor> 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 (file)
index 0000000..a278e90
--- /dev/null
@@ -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 (file)
index 0000000..8b2f420
--- /dev/null
@@ -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 (file)
index 0000000..0751f59
--- /dev/null
@@ -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 (file)
index 0000000..a135ebc
--- /dev/null
@@ -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 (file)
index 0000000..595ce5b
--- /dev/null
@@ -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 (file)
index 0000000..99392c1
--- /dev/null
@@ -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.<init>() : 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 (file)
index 0000000..761733a
--- /dev/null
@@ -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<Object> mStack = new Stack<Object>();
+    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("<init>");
+    }
+
+    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("<localVariable" + index + ">");
+            }
+            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 (file)
index 0000000..8b502db
--- /dev/null
@@ -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<Handler> handlers = new ArrayList<Handler>();
+      //  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<HashSet<LockEdge>> cycles =
+            cycleDetector.mergeCyclesWithIdenticalLocks();
+        for (HashSet<LockEdge> 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<LockEdge> allEdges = new LinkedList<LockEdge>();
+        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 <directory>",
+                     "Read results to analyze from <directory> (default:"
+                     + " current directory)");
+        op.addOption("-includepackages",
+                     "Include packages (not only class names) in graph");
+        op.addOption("-outputmode <mode>",
+                     "Set output mode to <mode> (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<String, String> 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<Cycle> cycles,
+                                       ContextReaderIfc reader) {
+        if (!mPrintDetails) {
+            return;
+        }
+        SortedSet<String> threads = new TreeSet<String>();
+        SortedSet<String> methods = new TreeSet<String>();
+        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<LockNode> 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 (file)
index 0000000..81fc0e0
--- /dev/null
@@ -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<LockEdge> mEdgesInCycle = new HashSet<LockEdge>();
+
+    Cycle(Collection<LockEdge> edgesInTheCycle) {
+        mEdgesInCycle.addAll(edgesInTheCycle);
+        assert mEdgesInCycle.size() >= 2;
+    }
+
+    HashSet<LockEdge> getEdges() {
+        return mEdgesInCycle;
+    }
+
+    HashSet<LockNode> getNodes() {
+        HashSet<LockNode> nodes = new HashSet<LockNode>();
+        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<LockEdge> 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<LockEdge> otherEdges =
+            new LinkedList<LockEdge>(other.mEdgesInCycle);
+        // TODO Refactor the following code?
+        outerLoop:
+        for (LockEdge edge : mEdgesInCycle) {
+            Iterator<LockEdge> 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 (file)
index 0000000..84edea9
--- /dev/null
@@ -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<Cycle> 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<Cycle>();
+        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<LockNode> nodes) {
+        ArrayList<LockNode> nodesOnStack = new ArrayList<LockNode>(10);
+        ArrayList<LockEdge> edgesOnStack = new ArrayList<LockEdge>(10);
+        HashSet<LockNode> visitedNodes = new HashSet<LockNode>();
+        HashSet<LockEdge> visitedEdges = new HashSet<LockEdge>();
+        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<LockNode> nodesOnStack,
+                             final ArrayList<LockEdge> edgesOnStack,
+                             final HashSet<LockNode> visitedNodes,
+                             final HashSet<LockEdge> 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<LockNode> nodesOnStack,
+                             final ArrayList<LockEdge> edgesOnStack,
+                             final HashSet<LockNode> visitedNodes,
+                             final HashSet<LockEdge> visitedEdges) {
+        if (!visitedEdges.contains(edge)) {
+            mMaxDepth.set(nodesOnStack.size());
+            final int index = nodesOnStack.indexOf(edge.getTarget());
+            if (index >= 0) {
+                final List<LockEdge> 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<LockEdge> edgesToRemove =
+                    edgesInCycle.subList(1, edgesInCycle.size());
+                visitedEdges.removeAll(edgesToRemove);
+            } else {
+                visitedEdges.add(edge);
+                analyzeNode(edge.getTarget(),
+                            nodesOnStack,
+                            edgesOnStack,
+                            visitedNodes,
+                            visitedEdges);
+            }
+        }
+    }
+
+
+    HashSet<Cycle> getCycles() {
+        return mCycles;
+    }
+
+    private static boolean containsAlike(Cycle cycle, Iterable<Cycle> 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<Cycle> uniqueCycles = new ArrayList<Cycle>();
+        Iterator<Cycle> 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<Cycle>(mCycles));
+    }
+
+    /**
+     * Remove cycles that are formed by a only one thread.
+     */
+    void removeSingleThreadedCycles() {
+        int removedCycles = 0;
+        Iterator<Cycle> 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<LockEdge> edges = new HashSet<LockEdge>();
+        for (Cycle cycle : mCycles) {
+            edges.addAll(cycle.getEdges());
+        }
+        return edges.size();
+    }
+
+    /**
+     * Get the total number of nodes in all known cycles.
+     */
+    int getNumberOfNodes() {
+        HashSet<LockNode> nodes = new HashSet<LockNode>();
+        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<HashSet<LockEdge>> mergeCyclesWithIdenticalLocks() {
+        /*
+         * TODO Refactor this method? The temporary data structure is too
+         * complex?
+         */
+        HashMap<HashSet<LockNode>, HashSet<LockEdge>> setOfNodesToEdgesMap =
+            new HashMap<HashSet<LockNode>, HashSet<LockEdge>>();
+        for (Cycle cycle : mCycles) {
+            final HashSet<LockNode> nodes = cycle.getNodes();
+            HashSet<LockEdge> edges = setOfNodesToEdgesMap.get(nodes);
+            if (edges == null) {
+                edges = new HashSet<LockEdge>();
+                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 (file)
index 0000000..0cba0aa
--- /dev/null
@@ -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<LockNode> mLockNodes;
+    private final Map<Integer, Integer> mContextIdTranslation;
+    private final Map<LockingContext, TreeSet<Integer>> 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<LockNode> lockNodes,
+                                   ContextReaderIfc reader) {
+        mLockNodes = lockNodes;
+        mContextIdTranslation = populateTranslationMap();
+        mContextToIdMap = createContextToIdMap(reader);
+    }
+
+    public static void mergeDuplicatedEdges(Iterable<LockNode> 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<Integer, Integer> populateTranslationMap() {
+        final HashMap<Integer, Integer> contextIds =
+            new HashMap<Integer, Integer>();
+        for (LockNode node : mLockNodes) {
+            node.populateContextIdTranslationMap(contextIds);
+        }
+        return contextIds;
+    }
+
+    private Map<LockingContext, TreeSet<Integer>>
+    createContextToIdMap(ContextReaderIfc reader) {
+        final Map<LockingContext, TreeSet<Integer>> contextToId =
+            new HashMap<LockingContext, TreeSet<Integer>>();
+        for (Integer id : mContextIdTranslation.values()) {
+            LockingContext context = reader.readContext(id);
+            TreeSet<Integer> ids = contextToId.get(context);
+            if (ids == null) {
+                ids = new TreeSet<Integer>();
+                contextToId.put(context, ids);
+            }
+            ids.add(id);
+        }
+        return contextToId;
+    }
+
+    private void updateContextIdTranslationMap() {
+        for (TreeSet<Integer> ids : mContextToIdMap.values()) {
+            if (ids.size() > 1) {
+                Iterator<Integer> 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 (file)
index 0000000..4d49009
--- /dev/null
@@ -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 <http://www.graphviz.org> 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" +
+        "     <table align=\"left\" border=\"0\" cellborder=\"0\"\n" +
+        "            cellspacing=\"0\" cellpadding=\"0\">\n" +
+        "       <tr>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\" colspan=\"2\">" +
+        "Thread: %1$s<br align=\"left\"/>" +
+        "</td>\n" +
+        "       </tr>\n" +
+        "       <tr>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\" colspan=\"2\">" +
+        "holding: %2$s<br align=\"left\"/>" +
+        "</td>\n" +
+        "       </tr>\n" +
+        "       <tr>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\">" +
+        "in: %3$s<br align=\"left\"/>" +
+        "</td>\n" +
+        "       </tr>\n" +
+        "       <tr>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\" colspan=\"2\">" +
+        "taking: %4$s<br align=\"left\"/>" +
+        "</td>\n" +
+        "       </tr> \n" +
+        "       <tr>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\">  </td>\n" +
+        "         <td align=\"left\">" +
+        "in: %5$s<br align=\"left\"/>" +
+        "</td>\n" +
+        "       </tr>\n" +
+        "     </table>\n" +
+        "    >]";
+
+    public String generate(Iterable<LockEdge> 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<LockNode> alreadyAppendedNodes = new HashSet<LockNode>();
+        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("&", "&amp;")
+                .replace("<", "&lt;")
+                .replace(">", "&gt;")
+                .replace("\"", "&quot;")
+                .replace("\'", "&#039;");
+    }
+
+    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<LockNode> 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 (file)
index 0000000..c317573
--- /dev/null
@@ -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<Integer, Integer> 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 (file)
index 0000000..1da9af7
--- /dev/null
@@ -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<Integer, LockNode> mLocks =
+        new HashMap<Integer, LockNode>();
+
+    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<LockNode> 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 (file)
index 0000000..06d3e72
--- /dev/null
@@ -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<LockEdge, LockEdge> mOutgoingEdges;
+    private CycleType mCycleType = CycleType.NO_CYCLE;
+
+    LockNode(final int lockId) {
+        mLockId = lockId;
+        mOutgoingEdges = new HashMap<LockEdge, LockEdge>();
+    }
+
+    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<Integer, Integer> translationMap) {
+        for (LockEdge edge : mOutgoingEdges.values()) {
+            translationMap.put(edge.getSourceLockingContextId(),
+                               edge.getSourceLockingContextId());
+            translationMap.put(edge.getTargetLockingContextId(),
+                               edge.getTargetLockingContextId());
+        }
+    }
+
+    void translateContextIds(Map<Integer, Integer> translation) {
+        Map<LockEdge, LockEdge> oldEdges = mOutgoingEdges;
+        mOutgoingEdges = new HashMap<LockEdge, LockEdge>(oldEdges.size());
+        for (LockEdge edge : oldEdges.values()) {
+            edge.translateContextIds(translation);
+            addOutgoingEdge(edge);
+        }
+    }
+
+    Set<LockEdge> 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 (file)
index 0000000..114997b
--- /dev/null
@@ -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 (file)
index 0000000..7d5ed8a
--- /dev/null
@@ -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 (file)
index 0000000..8afad30
--- /dev/null
@@ -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 (file)
index 0000000..737c4fc
--- /dev/null
@@ -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<Boolean>() {
+         
+            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<Boolean>() {
+                
+            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<byteholder> 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<Integer>() {
+       
+               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<byteholder> 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<Integer>() {
+                
+               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 (file)
index 0000000..80fb988
--- /dev/null
@@ -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<Lock> mLocks = new LinkedList<Lock>();
+    private final LinkedList<LockingContext> mLockingContexts =
+        new LinkedList<LockingContext>();
+
+    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 (file)
index 0000000..1dfb1e4
--- /dev/null
@@ -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 (file)
index 0000000..2999990
--- /dev/null
@@ -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 (file)
index 0000000..c4a9851
--- /dev/null
@@ -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 (file)
index 0000000..976155e
--- /dev/null
@@ -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<Boolean>() {
+                
+            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<Boolean>() {
+                
+            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<Boolean>() {
+         
+            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 (file)
index 0000000..263054a
--- /dev/null
@@ -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 (file)
index 0000000..1825b87
--- /dev/null
@@ -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<boolif> 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 (file)
index 0000000..8c333df
--- /dev/null
@@ -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<positionif> 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 (file)
index 0000000..610b344
--- /dev/null
@@ -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<bytebufferif> factory = Thread.makeFactory(bytebufferif.class);
+    public static Factory<byteholder> 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<value.remaining(); i++){
+            if (mbuffer.getByteHolder().get(mbuffer.getPosition()) ==  null)
+                mbuffer.getByteHolder().set(mbuffer.getPosition(), factory2.create());
+            mbuffer.getByteHolder().get(mbuffer.getPosition()).setByte(value.get());
+            mbuffer.setPosition(mbuffer.getPosition()+1);
+            //System.out.println("sss");
+        }
+    }
+    
+
+    
+    public bytebuffer allocateDirect(int capacity){
+        mbuffer = factory.create();
+        mbuffer.setByteHolder(new AtomicArray<byteholder>(byteholder.class,capacity));
+        mbuffer.setPosition(0);
+        mbuffer.setLimit(capacity);
+        mbuffer.setCapacity(capacity);
+        AtomicArray<byteholder> ar = mbuffer.getByteHolder();
+        //for (int i=0; i<capacity; i++){
+          //  ar.set(i, factory2.create());
+        //}
+      //  for (int i=0; i<capacity; i++)
+      //      mbuffer.getByteHolder().set(i, factory2.create());
+       // mbuffer.getByteHolder().set(0, factory2.create());
+       // mbuffer.getByteHolder().get(0).setByte((byte)2);
+        return this;
+    }
+    
+    public byte[] getBytes(){
+        int length = remaining();
+        byte[] result = new byte[length];
+        int i = 0;
+        while (hasRemaining()) {
+            result[i] = mbuffer.getByteHolder().get(mbuffer.getPosition()).getByte();
+            mbuffer.setPosition(mbuffer.getPosition()+1);
+            i++;
+        }
+        return result;
+    }
+     
+     @atomic public interface bytebufferif{
+        int getLimit();
+        void setLimit(int value);
+        int getPosition();
+        void setPosition(int value);
+        int getCapacity();
+        void setCapacity(int value);
+        AtomicArray<byteholder> getByteHolder();
+        void setByteHolder(AtomicArray<byteholder> 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 (file)
index 0000000..6a429a7
--- /dev/null
@@ -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 (file)
index 0000000..9d34ef7
--- /dev/null
@@ -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 (file)
index 0000000..3727054
--- /dev/null
@@ -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<V> {
+    private final HashMap<IdentityComparableKey, V> mHashMap;
+    private final ReferenceQueue<Object> 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<IdentityComparableKey, V>();
+        mReferenceQueue = new ReferenceQueue<Object>();
+    }
+
+    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<Object>
+    implements IdentityComparableKey {
+        private final int mHash;
+
+        WeakKey(Object referent, ReferenceQueue<Object> 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 (file)
index 0000000..e1b2d5b
--- /dev/null
@@ -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 (file)
index 0000000..0f17bdf
--- /dev/null
@@ -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 (file)
index 0000000..3085e6b
--- /dev/null
@@ -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<String> 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<String> wrapText(String text, int maxWidth) {
+        ArrayList<String> result = new ArrayList<String>();
+        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<String> 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 (file)
index 0000000..a032f82
--- /dev/null
@@ -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<String> mArguments = new ArrayList<String>();
+    private final Map<String, String> mParsedOptions =
+        new HashMap<String, String>();
+    private final Map<String, OptionData> mValidOptions =
+        new HashMap<String, OptionData>();
+
+    public OptionParser() {
+    }
+
+    /**
+     * Tell the parser to recognize a new option.
+     *
+     * Options can be given on the following forms:
+     *
+     * <ul>
+     * <li>-option</li>
+     * <li>-option foo</li>
+     * </ul>
+     *
+     * 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<String> 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<String> options = new ArrayList<String>();
+        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<String, String> 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 (file)
index 0000000..b47fb73
--- /dev/null
@@ -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 (file)
index 0000000..499c28a
--- /dev/null
@@ -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 (file)
index 0000000..74ac348
--- /dev/null
@@ -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 (file)
index 0000000..29042a9
--- /dev/null
@@ -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<Handler> 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<Handler> handlers) {
+        this(handlers, Level.FINEST);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param handlers Log handlers. null means no handlers.
+     * @param logLevel Log level.
+     */
+    public Logger(Collection<Handler> handlers, Level logLevel) {
+        mLevel = logLevel;
+        mHandlers = new ArrayList<Handler>();
+        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);
+            }
+        }
+    }
+}