*** empty log message ***
[IRC.git] / Robust / Transactions / jcarderdstm2version / src / com / enea / jcarder / analyzer / Analyzer.java
diff --git a/Robust/Transactions/jcarderdstm2version/src/com/enea/jcarder/analyzer/Analyzer.java b/Robust/Transactions/jcarderdstm2version/src/com/enea/jcarder/analyzer/Analyzer.java
new file mode 100644 (file)
index 0000000..ada5483
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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 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.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>();
+        handlers.add(new AppendableHandler(System.out,
+                                           Logger.Level.CONFIG,
+                                           "{message}\n"));
+        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();
+    }
+}