PageRenderTime 51ms CodeModel.GetById 15ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/tools/FlowAnalyzer.java

http://github.com/kilim/kilim
Java | 202 lines | 177 code | 13 blank | 12 comment | 26 complexity | dad75f47c3b6add42f2e3f4782ce7fc6 MD5 | raw file
  1/* Copyright (c) 2006, Sriram Srinivasan
  2 *
  3 * You may distribute this software under the terms of the license 
  4 * specified in the file "License"
  5 */
  6
  7package kilim.tools;
  8import static kilim.analysis.Utils.dedent;
  9import static kilim.analysis.Utils.indent;
 10import static kilim.analysis.Utils.pn;
 11import static kilim.analysis.Utils.resetIndentation;
 12import static org.objectweb.asm.Opcodes.INVOKESTATIC;
 13
 14import java.io.FileInputStream;
 15import java.io.IOException;
 16import java.io.PrintStream;
 17import java.util.ArrayList;
 18import java.util.Arrays;
 19import java.util.Collections;
 20import java.util.Enumeration;
 21import java.util.jar.JarEntry;
 22import java.util.jar.JarFile;
 23import java.util.zip.ZipEntry;
 24
 25import kilim.analysis.BasicBlock;
 26import kilim.analysis.ClassFlow;
 27import kilim.analysis.Frame;
 28import kilim.analysis.KilimContext;
 29import kilim.analysis.MethodFlow;
 30import kilim.analysis.TypeDesc;
 31import kilim.analysis.Usage;
 32import kilim.analysis.Value;
 33
 34import org.objectweb.asm.tree.AbstractInsnNode;
 35import org.objectweb.asm.tree.MethodInsnNode;
 36
 37/**
 38 * Used to dump the stack and locals at the beginning of each basic block
 39 * @author ram
 40 */
 41public class FlowAnalyzer {
 42    
 43    public static void main(String[] args) throws Exception {
 44        if (args.length == 0) {
 45            System.err.println("Usage <class name | jar file name> [methodName]");
 46            System.exit(1);
 47        }
 48        String name = args[0];
 49        if (name.endsWith(".jar")) {
 50            analyzeJar(name);
 51        } else {
 52            analyzeClass(name);
 53        }
 54    }
 55    
 56    private static void analyzeClass(String className) {
 57        try {
 58            pn("-------------------------------------------------");
 59            pn("Class: " + className);
 60            System.out.flush();
 61            ClassFlow cf = null;
 62            if (className.endsWith(".class")) {
 63                FileInputStream fis = null;
 64                try {
 65                    fis = new FileInputStream(className);
 66                    cf = new ClassFlow(KilimContext.DEFAULT,fis);
 67                } finally {
 68                    if (fis != null) {fis.close();}
 69                }
 70            }
 71            if (cf == null) {
 72                cf = new ClassFlow(KilimContext.DEFAULT,className);
 73            }
 74            ArrayList<MethodFlow> flows = cf.analyze(true);
 75            for (MethodFlow flow: flows) {
 76                reportFlow(flow, className);
 77            }
 78        } catch (IOException e) {
 79            pn("##################################################");
 80            stackTrace(e);
 81        } catch (Throwable ie) {
 82            pn("##################################################");
 83            stackTrace(ie);
 84        }
 85    }
 86    
 87    private static void stackTrace(Throwable t) {
 88        PrintStream ps = new PrintStream(System.out);
 89        t.printStackTrace(ps);
 90    }
 91    
 92    private static void reportFlow(MethodFlow method, String className) {
 93        resetIndentation();
 94        pn("Method : "+  className + '.' + method.name);
 95        
 96        pn("MaxStack: " + method.maxStack);
 97        pn("MaxLocals: " + method.maxLocals);
 98        ArrayList<BasicBlock> bbs = method.getBasicBlocks();
 99        Collections.sort(bbs);
100        indent(2);
101        for (BasicBlock bb: bbs) {
102            AbstractInsnNode ainode = bb.getInstruction(bb.startPos);
103            if (ainode instanceof MethodInsnNode) {
104                MethodInsnNode m = (MethodInsnNode)ainode;
105                int n = getNumArgs(m); // This many will get consumed from stack
106                pn("Call(" + n + "): " + m.owner + "." + m.name + m.desc);
107                indent(2);
108                pn("Inframe: ");
109                indent(2);
110                Frame f = bb.startFrame;
111                pn(f.toString());
112                dedent(2);
113                pn("Live locals:");
114                indent(2);
115                Usage u = bb.getVarUsage();
116                pn(u.toString());
117                dedent(2);
118                pn("Actual usage: " + uniqueItems(bb, f, u, n));
119                dedent(2);
120            }
121        }
122        dedent(2);
123    }
124    
125    private static String uniqueItems(BasicBlock bb, Frame f, Usage u, int nStack) {
126        StringBuffer sb = new StringBuffer(80);
127        int numNonConstants = 0;
128        int numLive = 0;
129        ArrayList<Value> set = new ArrayList<Value>(10);
130        for (int i = 0; i < f.getMaxLocals(); i++) {
131            if (u.isLiveIn(i)) {
132                numLive++;
133                Value v = f.getLocal(i);
134                if (!set.contains(v)) set.add(v);
135            }
136        }
137        nStack = f.getStackLen() - nStack;
138        for (int i = 0; i < nStack; i++) {
139            Value v = f.getStack(i);
140            if (!set.contains(v)) set.add(v);
141        }
142        char[] sig = new char[set.size()];
143        // create canonical sig. Convert types to one of 'O', 'I', 'F', 'L', 'D' and
144        // put in sorted order
145        // Also count non constants while we are iterating anyway.
146        for (int i = 0; i < set.size(); i++) {
147            Value v = set.get(i);
148            char c = v.getTypeDesc().charAt(0);
149            switch (c) {
150                case 'L': case '[': case 'N': 
151                    c = 'O'; break;
152                case 'I': case 'B': case 'S': case 'Z': case 'C': 
153                    c = 'I'; break;
154                case 'J':
155                    c = 'J'; break;
156                case 'F': 
157                    c = 'F'; break;
158                case 'U': 
159                    default: {
160                    c = 'U';
161                    System.err.println("***************************************");
162                    System.err.println("Undefined/unrecognized value " + v);
163                    System.err.println("BasicBlock:\n" + bb);
164                    break;
165                }
166            }
167            sig[i] = c;
168            if (v.getConstVal() == Value.NO_VAL) {
169                numNonConstants++;
170            }
171        }
172        Arrays.sort(sig);
173        numLive += nStack;
174        sb.append("avail: ").append(nStack + f.getMaxLocals());
175        sb.append(", live: " + numLive);
176        sb.append(", unique: ").append(set.size());
177        sb.append(", unique non-const: ").append(numNonConstants);
178        sb.append("\nState signature: ").append(set.size() == 0 ? "None" : new String(sig));
179        return sb.toString();
180    }
181    
182    private static int getNumArgs(MethodInsnNode m) {
183        int ret = TypeDesc.getNumArgumentTypes(m.desc);
184        if (m.getOpcode() != INVOKESTATIC) ret++; 
185        return ret;
186    }
187    
188    public static void analyzeJar(String jarFile) {
189        try {
190            Enumeration<JarEntry> e = new JarFile(jarFile).entries();
191            while (e.hasMoreElements()) {
192                ZipEntry en = (ZipEntry) e.nextElement();
193                String n = en.getName();
194                if (!n.endsWith(".class")) continue;
195                n = n.substring(0, n.length() - 6).replace('/','.');
196                analyzeClass(n);
197            }
198        } catch (Exception e) {
199            e.printStackTrace();
200        }
201    }
202}