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