br.jabuti.ui.cli.ClassSummary.java Source code

Java tutorial

Introduction

Here is the source code for br.jabuti.ui.cli.ClassSummary.java

Source

/*  Copyright 2003  Auri Marcelo Rizzo Vicenzi, Marcio Eduardo Delamaro,              Jose Carlos Maldonado
    
This file is part of Jabuti.
    
Jabuti is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as 
published by the Free Software Foundation, either version 3 of the      
License, or (at your option) any later version.
    
Jabuti 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.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with Jabuti.  If not, see <http://www.gnu.org/licenses/>.
 */

package br.jabuti.ui.cli;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.generic.ClassGen;
import org.aspectj.apache.bcel.generic.ConstantPoolGen;
import org.aspectj.apache.bcel.generic.MethodGen;

import br.jabuti.graph.datastructure.GraphCallNode;
import br.jabuti.graph.datastructure.GraphNode;
import br.jabuti.graph.datastructure.dug.CFG;
import br.jabuti.graph.datastructure.dug.CFGNode;
import br.jabuti.graph.datastructure.ig.InvalidInstructionException;
import br.jabuti.graph.datastructure.ig.InvalidStackArgument;
import br.jabuti.graph.datastructure.reducetree.DominatorTree;
import br.jabuti.graph.datastructure.reducetree.DominatorTreeNode;
import br.jabuti.graph.datastructure.reducetree.RRDominator;
import br.jabuti.graph.datastructure.reducetree.RRLiveDefs;
import br.jabuti.graph.datastructure.reducetree.ReduceNode;
import br.jabuti.util.Debug;
import br.jabuti.util.ToolConstants;

/**
 * This is the class that implements the functionality of a JVM code prober. Using such object it is
 * possible to insert JVM code in a given JVM method.
 */
public class ClassSummary {
    static boolean detailed = false;
    static boolean defuse = false;
    static boolean alive = false;
    static boolean dominators = false;
    static boolean childrens = false;
    static boolean cfg = false;
    static boolean callNodes = false;

    public static void usage() {
        System.out.println("Java Bytecode ClassSummary");
        System.out.println("\nUSAGE:");
        System.out.println("java graph.ClassSummary [options] -i <class files> | -jar <compressed file>\n");
        System.out.println("      -i <class files>        A list of classes to be instrumented these classes");
        System.out.println("                              should be reachable from the <main_class>.\n");
        System.out.println("      -jar <compressed file>  A compressed .jar or .zip file.\n");
        System.out.println("      [options] could be: [-h | -v | -du | -li | -do | -hi | -cfg | -all ]");
        System.out.println("      -h:                     This help.");
        System.out.println("      -v:                     ClassSummary version number.");
        System.out.println("      -call:                  Creates the CFG with call nodes.");
        System.out.println(
                "      -du:                    Shows Variables Definitions and Uses of each Node, if any.");
        System.out.println("      -li:                    Shows Alive Variables of each Node, if any.");
        System.out.println(
                "      -do:                    Shows Dominators and Inverse Dominators of each Node, if any.");
        System.out.println(
                "      -ch:                    Shows Primary and Secondary Children of each Node, if any.");
        System.out.println(
                "      -cfg:                   Generates a text file representation (dot format) with the CFG of each method.");
        System.out.println("      -all:                   Enables all options except -h and -v.");
        System.out.println(" If no option is specified a very simple summary w.r.t. ");
        System.out.println(" the given class files is printed out. ");
        System.out.println("\nCopyright (c) 2002\n");
    }

    public static void main(String args[]) throws Throwable {
        JavaClass jc = null;

        HashSet toInstrumenter = null;
        String fileName; // A given class or compressed file name

        ZipFile zippedFile = null; // To handle compressed class files
        if (args.length > 0) {

            int i = 0;

            if (args.length == 1) {
                if (("-v".equals(args[i])) || ("-h".equals(args[i]))) {
                    if ("-v".equals(args[i])) {
                        System.out.println("Bytecode ClassSummaty v0.0001");
                    } else if ("-h".equals(args[i])) {
                        usage();
                    }
                    System.exit(0);
                }
            }

            while (i < args.length && args[i].startsWith("-")) {
                // -i: Classes to be instrumented
                if (("-i".equals(args[i])) && (i < args.length - 1)) {
                    if (zippedFile == null) {
                        if (toInstrumenter == null) {
                            toInstrumenter = new HashSet();
                        }
                        i++;
                        fileName = args[i];
                        toInstrumenter.add(getRealName(fileName, ".class"));
                        i++;
                    } else {
                        System.out.println("Options -i and -jar can not appear toghether. ");
                        System.out.println("try java instrumenter.ClassSummary -h for help.");
                        System.exit(0);
                    }
                } // Compressed file...
                else if (("-jar".equals(args[i])) && (i < args.length - 1)) {
                    if (toInstrumenter == null) {
                        i++;
                        fileName = args[i];

                        if (fileName.endsWith(".jar")) {
                            zippedFile = new JarFile(getRealName(fileName, ".jar"));
                        } else if (fileName.endsWith(".zip")) {
                            zippedFile = new ZipFile(getRealName(fileName, ".zip"));
                        } else {
                            System.out.println("ERROR: after a -jar should be specified a .jar or .zip file!!!");
                            System.exit(0);
                        }
                        i++;
                    } else {
                        System.out.println("Options -i and -jar can not appear toghether. ");
                        System.out.println("try java instrumenter.ClassSummary -h for help.");
                        System.exit(0);
                    }
                } else if (("-call".equals(args[i]))) {
                    i++;
                    callNodes = true;
                } else if (("-du".equals(args[i]))) {
                    i++;
                    detailed = true;
                    defuse = true;
                } else if (("-li".equals(args[i]))) {
                    i++;
                    detailed = true;
                    alive = true;
                } else if (("-do".equals(args[i]))) {
                    i++;
                    detailed = true;
                    dominators = true;
                } else if (("-ch".equals(args[i]))) {
                    i++;
                    detailed = true;
                    childrens = true;
                } else if (("-cfg".equals(args[i]))) {
                    i++;
                    cfg = true;
                } else if (("-all".equals(args[i]))) {
                    i++;
                    detailed = true;
                    defuse = true;
                    dominators = true;
                    childrens = true;
                    cfg = true;
                    alive = true;
                } else {
                    System.out.println("Unrecognized option: " + args[i]);
                    System.out.println("try java instrumenter.ClassSummary -h for help.");
                    System.exit(0);
                }
            }

            if (zippedFile != null) {
                Enumeration en = zippedFile.entries();
                ZipEntry zippedEntry = null;

                while (en.hasMoreElements()) {
                    zippedEntry = (ZipEntry) en.nextElement();
                    String className = zippedEntry.getName();

                    if (!className.endsWith(".class")) {
                        continue;
                    }

                    jc = new ClassParser(zippedFile.getInputStream(zippedEntry), className).parse(); // May
                    // throw
                    // IOException
                    getClassSummary(jc);
                }
            }

            if (toInstrumenter != null) {
                Iterator it = toInstrumenter.iterator();

                while (it.hasNext()) {
                    jc = new ClassParser((String) it.next()).parse();
                    getClassSummary(jc);
                }
            }
        } else {
            usage();
        }
        System.exit(0);
    }

    /**
     * Capturar e imprimir as informaes sobre definies e usuos de variveis. Gerar o CFG por
     * metodo de cada uma das classes.
     */

    private static void getClassSummary(JavaClass java_class) {
        ClassGen cg = new ClassGen(java_class);
        ConstantPoolGen cp = cg.getConstantPool();
        Method[] methods = cg.getMethods();

        System.out.println("Class File: " + cg.getClassName());

        for (int i = 0; i < methods.length; i++) {
            try {
                MethodGen mg = new MethodGen(methods[i], cg.getClassName(), cp);

                CFG g;
                g = new CFG(mg, cg);

                // For collecting data w.r.t Dominators, Inverse Dominator and Live Variables
                if (detailed) {
                    // g.computeDefUse();
                    RRDominator rrd = new RRDominator("Dominator");

                    g.roundRobinAlgorithm(rrd, true);

                    rrd = new RRDominator("IDominator");
                    g.roundIRobinAlgorithm(rrd, true);

                    RRLiveDefs rral = new RRLiveDefs("Alive definitions", RRLiveDefs.ALL);

                    g.roundRobinAlgorithm(rral, true);
                }

                // Gerarating the CFG file (dot format)
                if (cfg) {
                    createCFGDotFile(g, cg.getClassName(), methods[i].getName());
                    createAllTreeDotFiles(g, cg.getClassName(), methods[i].getName());
                }

                System.out.println("\t\tNumber of Blocks: " + g.size());
                if (detailed) {
                    System.out.println("\t\t\tBlock Details");
                }

                int decisions = 0;
                Vector decisionBlocks = new Vector(5, 5);
                CFGNode pred;

                for (int j = 0; j < g.size(); j++) {
                    pred = (CFGNode) g.elementAt(j);
                    if (detailed) {
                        System.out.println(
                                "\t\t\t\t" + pred.getLabel() + " PC: " + pred.getStart() + " to " + pred.getEnd());

                        // Variable Definitions and Usages
                        // Usages...
                        if (defuse) {
                            getUsages(pred);

                            // Definitions
                            getDefinitions(pred);
                        }

                        // Children
                        if (childrens) {
                            // Primary children
                            getPrimChildren(pred);

                            // Secondary children (Exceptions)
                            getSecChildren(pred);
                        }

                        if (dominators) {
                            // Dominators of a given node
                            getDominators(pred);

                            // Inverse Dominators
                            getInverseDominators(pred);
                        }

                        // Set of live definitions
                        if (alive) {
                            getAliveDefinitions(pred);
                        }
                    }
                    if (pred.getPrimNext().size() > 1) {
                        decisions++;
                        decisionBlocks.add(pred);
                    }
                }
                System.out.println("\t\tNumber of Decisions: " + decisions);
                if (detailed) {
                    System.out.println("\t\t\tDecision Details");
                    while (!(decisionBlocks.isEmpty())) {
                        pred = (CFGNode) decisionBlocks.firstElement();
                        System.out.println("\t\t\t\t" + pred.getLabel() + " Start: " + pred.getStart() + " End: "
                                + pred.getEnd());
                        decisionBlocks.remove(pred);
                    }
                }
            } catch (InvalidInstructionException ii) {
                ToolConstants.reportException(ii, ToolConstants.STDERR);
            } catch (InvalidStackArgument ia) {
                ToolConstants.reportException(ia, ToolConstants.STDERR);
            }

        }

    }

    public static String getRealName(String name, String ext) {
        String tmp = new String(name);

        if (tmp.indexOf(".class") >= 0) {
            tmp = tmp.substring(0, tmp.length() - 6);
        } else if ((tmp.indexOf(".jar") >= 0) || (tmp.indexOf(".zip") >= 0)) {
            tmp = tmp.substring(0, tmp.length() - 4);
        }

        return tmp.replace('.', '/') + ext;
    }

    public static String getRealNameNoExtention(String name) {
        String tmp = new String(name);

        if (tmp.indexOf(".class") >= 0) {
            tmp = tmp.substring(0, tmp.length() - 6);
        } else if ((tmp.indexOf(".jar") >= 0) || (tmp.indexOf(".zip") >= 0)) {
            tmp = tmp.substring(0, tmp.length() - 4);
        }

        return tmp.replace('.', '/');
    }

    public static void createCFGDotFile(CFG gfc, String className, String methodName) {
        String dotFileName;

        if (methodName.equals("<init>")) {
            methodName = "init";
        }
        dotFileName = getRealNameNoExtention(className) + "_" + methodName + ".dot";

        System.out.println("DOT FILE: " + dotFileName);

        try {
            PrintWriter dotFile = new PrintWriter(new FileOutputStream(dotFileName));

            dotFile.println("digraph " + methodName);
            dotFile.println("{");
            dotFile.println("\tsize=\"7.5,10\"; ");
            dotFile.println("\tratio=auto;");
            // dotFile.println("\tnode [shape=circle, fixedsize=true]; ");
            dotFile.println("\tnodesep=0.1;");

            if (gfc != null) {
                GraphNode[] fdt = gfc.findDFTNodes(true);

                for (int i = 0; i < fdt.length; i++) {
                    CFGNode current = (CFGNode) fdt[i];

                    // if (i == 0) {
                    // dotFile.println("\t" + current.getLabel() + " [style=bold];");
                    // } else

                    if (current instanceof GraphCallNode) {
                        dotFile.println("\t" + current.getLabel() + " [shape=doublecircle];");
                    } else if ((current.getPrimNext().size() == 0) && (current.getSecNext().size() == 0)) {
                        // Termination node
                        dotFile.println("\t" + current.getLabel() + " [style=bold];");
                    }

                    // Normal edges
                    Set<GraphNode> childrenPrimaryEdge = current.getPrimNext();

                    if (childrenPrimaryEdge.size() > 0) {
                        String str = " -> { ";
                        Iterator<GraphNode> j = childrenPrimaryEdge.iterator();
                        while (j.hasNext()) {
                            GraphNode node = j.next();
                            str += ((CFGNode) node).getLabel() + "; ";
                        }
                        str += "};";
                        dotFile.println("\t" + current.getLabel() + str);
                    }

                    // Exception edges
                    Set<GraphNode> childrenSecondaryEdge = current.getSecNext();
                    if (childrenSecondaryEdge.size() > 0) {
                        String str = " -> { ";
                        Iterator<GraphNode> j = childrenSecondaryEdge.iterator();
                        while (j.hasNext()) {
                            GraphNode node = j.next();
                            str += ((CFGNode) node).getLabel() + "; ";
                        }
                        str += "}";
                        dotFile.println("\t" + current.getLabel() + str + "[style=dashed];");
                    }
                }
            }
            dotFile.println("}");
            dotFile.close();
        } catch (FileNotFoundException e) {
            ToolConstants.reportException(e, ToolConstants.STDERR);
        }
    }

    public static void createAllTreeDotFiles(CFG gfc, String className, String methodName) {

        if (methodName.equals("<init>")) {
            methodName = "init";
        }

        try {
            RRDominator rd = new RRDominator(ToolConstants.LABEL_DOMINATOR);

            gfc.roundRobinAlgorithm(rd, true);

            rd = new RRDominator(ToolConstants.LABEL_IDOMINATOR);
            gfc.roundIRobinAlgorithm(rd, true);

            // Calculating the dominator tree...
            DominatorTree dtDom = new DominatorTree(gfc, ToolConstants.LABEL_DOMINATOR);

            dtDom.setDefaultNumbering();

            // Printing the current tree
            String dotFileName = getRealNameNoExtention(className) + "_" + methodName;

            createTreeDotFile(dtDom, dotFileName + "_Dominator.dot");

            // Calculating the inverse dominator tree...
            DominatorTree dtIDom = new DominatorTree(gfc, ToolConstants.LABEL_IDOMINATOR);

            dtIDom.setDefaultNumbering();

            // Printing the current tree
            createTreeDotFile(dtIDom, dotFileName + "_IDominator.dot");

            // Merging both trees
            dtDom.merge(dtIDom);

            // Printing the current tree
            createTreeDotFile(dtDom, dotFileName + "_MergedTree.dot");

            // Calculating the Basic Block Dominator TREE
            DominatorTree bbDom = (DominatorTree) DominatorTree.reduceSCC(dtDom, false);

            if (dtDom.getFirstEntryNode() != null) {
                bbDom.setEntryNode(bbDom.getReduceNodeOf(dtDom.getFirstEntryNode()));
                bbDom.setDefaultNumbering();

                // Calculating the Final Basic Block Dominator TREE
                bbDom.removeComposite(false);
            }

            if (methodName.equals("<init>")) {
                methodName = "init";
            }

            createSuperBlockFile(bbDom, dotFileName + "_SuperBlock.dot");
        } catch (Exception e) {
            ToolConstants.reportException(e, ToolConstants.STDERR);
            return;
        }
    }

    public static void createTreeDotFile(DominatorTree bbDom, String dotFileName) {
        try {
            PrintWriter dotFile = new PrintWriter(new FileOutputStream(dotFileName));

            dotFile.println("digraph tree");
            dotFile.println("{");
            dotFile.println("\tsize=\"7.5,10\"; ");
            dotFile.println("\tratio=auto;");
            dotFile.println("\tnode [fixedsize=false]; ");
            dotFile.println("\tnodesep=0.1;");

            if (bbDom != null) {
                for (int z1 = 0; z1 < bbDom.size(); z1++) {
                    DominatorTreeNode dtn = (DominatorTreeNode) bbDom.elementAt(z1);

                    dotFile.println(
                            "\t" + dtn.getLabel() + " [label=\"" + dtn.getOriginalNode().getLabel() + "\"];");

                    // Normal edges
                    Set<GraphNode> childrenPrimaryEdge = dtn.getPrimNext();

                    if (childrenPrimaryEdge.size() > 0) {
                        String str = " -> { ";
                        Iterator<GraphNode> i = childrenPrimaryEdge.iterator();
                        while (i.hasNext()) {
                            GraphNode node = i.next();
                            DominatorTreeNode dtnChild = (DominatorTreeNode) node;
                            dotFile.println("\t" + dtnChild.getLabel() + " [label=\""
                                    + dtnChild.getOriginalNode().getLabel() + "\"];");
                            str += dtnChild.getLabel() + "; ";
                        }
                        str += "};";
                        dotFile.println("\t" + dtn.getLabel() + str);
                    }
                }
            }
            dotFile.println("}");
            dotFile.close();
        } catch (FileNotFoundException e) {
            ToolConstants.reportException(e, ToolConstants.STDERR);
            System.err.println("File " + dotFileName + " not created!!!");
        }
    }

    public static void createSuperBlockFile(DominatorTree bbDom, String dotFileName) {
        try {
            PrintWriter dotFile = new PrintWriter(new FileOutputStream(dotFileName));

            dotFile.println("digraph tree");
            dotFile.println("{");
            dotFile.println("\tsize=\"7.5,10\"; ");
            dotFile.println("\tratio=auto;");
            dotFile.println("\tnode [fixedsize=false]; ");
            dotFile.println("\tnodesep=0.1;");

            if (bbDom != null) {
                for (int z1 = 0; z1 < bbDom.size(); z1++) {
                    DominatorTreeNode dtn = (DominatorTreeNode) bbDom.elementAt(z1);

                    dotFile.println("\t" + dtn.getLabel() + " [label=\"" + getOriginalNodeNames(dtn) + "\"];");

                    // Normal edges
                    Set<GraphNode> childrenPrimaryEdge = dtn.getPrimNext();

                    if (childrenPrimaryEdge.size() > 0) {
                        String str = " -> { ";
                        Iterator<GraphNode> i = childrenPrimaryEdge.iterator();
                        while (i.hasNext()) {
                            GraphNode node = i.next();
                            DominatorTreeNode dtnChild = (DominatorTreeNode) node;
                            dotFile.println("\t" + dtnChild.getLabel() + " [label=\""
                                    + getOriginalNodeNames(dtnChild) + "\"];");
                            str += dtnChild.getLabel() + "; ";
                        }
                        str += "};";
                        dotFile.println("\t" + dtn.getLabel() + str);
                    }
                }
            }
            dotFile.println("}");
            dotFile.close();
        } catch (FileNotFoundException e) {
            ToolConstants.reportException(e, ToolConstants.STDERR);
            System.err.println("File " + dotFileName + " not created!!!");
        }
    }

    private static String getOriginalNodeNames(DominatorTreeNode dtn) {
        GraphNode[] nodes = dtn.getOriginalNodes();

        String label = new String();

        String[] labels = new String[nodes.length];

        for (int z = 0; z < nodes.length; z++) {
            GraphNode curNode = ((ReduceNode) nodes[z]).getOriginalNode();

            labels[z] = new String(curNode.getLabel());
        }

        Arrays.sort(labels);
        for (int z = 0; z < labels.length; z++) {
            if (z == 0)
                label += labels[z];
            else
                label += ", " + labels[z];
        }
        return label;
    }

    static void getUsages(CFGNode pred) {
        if (pred.uses.size() > 0) {
            System.out.print("\t\t\t\t\tVariable uses:");
            Enumeration it = pred.uses.keys();

            while (it.hasMoreElements()) {
                String s = (String) it.nextElement();

                System.out.print("  " + s + " " + "PC: " + pred.uses.get(s));
            }
            System.out.println();
        }
    }

    static void getDefinitions(CFGNode pred) {
        if (pred.definitions.size() > 0) {
            System.out.print("\t\t\t\t\tVariable definitions:");
            Enumeration it = pred.definitions.keys();

            while (it.hasMoreElements()) {
                String s = (String) it.nextElement();

                System.out.print("  " + s + " " + "PC: " + pred.definitions.get(s));
            }
            System.out.println();
        }
    }

    static void getPrimChildren(CFGNode pred) {
        Set<GraphNode> next = pred.getPrimNext();

        if (next.size() > 0) {
            System.out.print("\t\t\t\t\tChildren:");
            Iterator<GraphNode> i = next.iterator();
            while (i.hasNext()) {
                GraphNode node = i.next();
                CFGNode n = (CFGNode) node;
                System.out.print(" " + n.getLabel());
            }
            System.out.println();
        }
    }

    static void getSecChildren(CFGNode pred) {
        Set<GraphNode> secNext = pred.getSecNext();

        if (secNext.size() > 0) {
            System.out.print("\t\t\t\t\tException Children:");
            Iterator<GraphNode> i = secNext.iterator();
            while (i.hasNext()) {
                GraphNode node = i.next();
                CFGNode n = (CFGNode) node;
                System.out.print(" " + n.getLabel());
            }
            System.out.println();
        }
    }

    static void getDominators(CFGNode pred) {
        HashSet h1 = (HashSet) pred.getUserData("Dominator");
        Iterator it = h1.iterator();

        System.out.print("\t\t\t\t\tDominators: ");
        while (it.hasNext()) {
            CFGNode gdom = (CFGNode) it.next();

            System.out.print(gdom.getLabel() + " ");
        }
        System.out.println();
    }

    static void getInverseDominators(CFGNode pred) {
        HashSet h1 = (HashSet) pred.getUserData("IDominator");
        Iterator it = h1.iterator();

        System.out.print("\t\t\t\t\tInverse Dominators: ");
        while (it.hasNext()) {
            CFGNode gdom = (CFGNode) it.next();

            System.out.print(gdom.getLabel() + " ");
        }
        System.out.println();
    }

    static void getAliveDefinitions(CFGNode pred) {
        HashSet h1 = (HashSet) pred.getUserData("Alive definitions");
        Iterator it = h1.iterator();

        System.out.print("\t\t\t\t\tAlive definitions: ");
        while (it.hasNext()) {
            Object p = it.next();

            Debug.D("TYPE: " + p.getClass());
            Vector pair = (Vector) p;
            String def = (String) pair.elementAt(0);
            CFGNode gfcn = (CFGNode) pair.elementAt(1);

            System.out.print("  " + def + " BK: " + gfcn.getLabel());
        }
        System.out.println();
    }
}