package tracer; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CodeConverter; import javassist.CodeConverter.ArrayAccessReplacementMethodNames; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.BadBytecode; import javassist.bytecode.analysis.ControlFlow; import javassist.bytecode.analysis.ControlFlow.Block; import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; import javassist.expr.MethodCall; import javassist.expr.NewExpr; /** * トレース出力文を対象パッケージ内に織り込むクラス * * @author Nitta * */ public class Tracer { public static int lineNo = 1; private static final String STANDARD_CLASSES = "java.util.ListIterator|java.util.Iterator|java.util.List|java.util.Vector|java.util.ArrayList|java.util.Stack|java.util.Map|java.util.HashMap|java.util.Set|java.util.HashSet|java.util.Hashtable|java.util.LinkedList|java.lang.Thread"; private static final String CONCRETE_STANDARD_CLASSES = "java.util.Vector|java.util.ArrayList|java.util.Stack |java.util.HashMap|java.util.HashSet|java.util.Hashtable|java.util.LinkedList|java.lang.Thread"; private static OutputStatementsGenerator outputStatementsGenerator = null; public static void main(String[] args) { outputStatementsGenerator = new OutputStatementsGenerator(new JSONTraceGenerator()); // 引数で出力フォーマットを指定する String packageName = "arraySample"; // 指定したパッケージ直下の全クラスにインストゥルメンテーションを行う ClassLoader loader = Thread.currentThread().getContextClassLoader(); URL resource = loader.getResource(packageName); File dir; try { dir = new File(URLDecoder.decode(resource.getPath(), "UTF-8")); for (String file : dir.list()) { if (file.endsWith(".class")) { String className = packageName + "." + file.replace(".class", ""); classInstrumentation(className); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** * 指定したクラスにインストゥルメンテーションを行う(仮) * * @param className * クラス名 */ private static void classInstrumentation(String className) { ClassPool cp = ClassPool.getDefault(); CtClass cc; try { cc = cp.get(className); for (final CtConstructor c : cc.getDeclaredConstructors()) { methodInstrumentation(cc, c); } for (final CtMethod m : cc.getDeclaredMethods()) { methodInstrumentation(cc, m); } cc.writeFile("bin"); } catch (NotFoundException | BadBytecode | CannotCompileException | IOException e) { e.printStackTrace(); // } catch (CannotCompileException | IOException e) { // e.printStackTrace(); } } private static void methodInstrumentation(final CtClass cc, final CtBehavior m) throws BadBytecode, NotFoundException, CannotCompileException { // ControlFlow cf = new ControlFlow(m); // String methodSignature = m.getSignature(); // メソッド本体内のフィールドアクセスとメソッド呼び出しを置き換える m.instrument(new ExprEditor() { public void edit(FieldAccess f) throws CannotCompileException { if (f.isReader()) { if (!f.getFieldName().contains("$")) { // AspectJでは final local 変数からのゲットは無視されるので、それに合わせて除外する f.replace(outputStatementsGenerator.generateReplaceStatementsForFieldGet(cc, m, f)); } } else { if (!f.getFieldName().contains("$")) { // この条件がないとなぜか落ちる(無名フィールド?へのセットがあって、それを拾って落ちてる?) f.replace(outputStatementsGenerator.generateReplaceStatementsForFieldSet(cc, f)); } } } public void edit(MethodCall c) throws CannotCompileException { try { CtMethod m = c.getMethod(); if (m.getDeclaringClass().getName().matches(STANDARD_CLASSES)) { c.replace(outputStatementsGenerator.generateReplaceStatementsForCall(m.getDeclaringClass(), m)); } } catch (NotFoundException e) { e.printStackTrace(); } } public void edit(NewExpr n) throws CannotCompileException { try { CtConstructor m = n.getConstructor(); if (m.getDeclaringClass().getName().matches(CONCRETE_STANDARD_CLASSES)) { n.replace(outputStatementsGenerator.generateReplaceStatementsForCall(m.getDeclaringClass(), m)); } } catch (NotFoundException e) { e.printStackTrace(); } } // public void edit(ConstructorCall c) throws CannotCompileException { // try { // CtConstructor m = c.getConstructor(); // if (m.getDeclaringClass().getName().matches(CONCRETE_STANDARD_CLASSES)) { // c.replace(generateReplaceStatementsForCall(m.getDeclaringClass(), m)); // } // } catch (NotFoundException e) { // e.printStackTrace(); // } // } }); // メソッド用の出力文を生成する if (!m.isEmpty()) { // メソッドの実行前後に出力文を挿入する m.insertBefore(outputStatementsGenerator.generateInsertBeforeStatementsForMethodBody(cc, m)); m.insertAfter(outputStatementsGenerator.generateInsertAfterStatementsForMethodBody(cc, m)); } else { // メソッド本体が空のときはコンストラクタの場合のみ(=デフォルトコンストラクタ)本体に出力文を設定する if (m instanceof CtConstructor) { m.setBody(outputStatementsGenerator.generateInsertAfterStatementsForMethodBody(cc, m)); m.insertBefore(outputStatementsGenerator.generateInsertBeforeStatementsForMethodBody(cc, m)); } } // CodeConverter conv = new CodeConverter(); // conv.replaceArrayAccess(cc, new // ArrayAccessReplacementMethodNames() { // @Override // public String shortWrite() { // return null; // } // @Override // public String shortRead() { // return null; // } // @Override // public String objectWrite() { // return null; // } // @Override // public String objectRead() { // return null; // } // @Override // public String longWrite() { // return null; // } // @Override // public String longRead() { // return null; // } // @Override // public String intWrite() { // return null; // } // @Override // public String intRead() { // return null; // } // @Override // public String floatWrite() { // return null; // } // @Override // public String floatRead() { // return null; // } // @Override // public String doubleWrite() { // return null; // } // @Override // public String doubleRead() { // return null; // } // @Override // public String charWrite() { // return null; // } // @Override // public String charRead() { // return null; // } // @Override // public String byteOrBooleanWrite() { // return null; // } // @Override // public String byteOrBooleanRead() { // return null; // } // }); // Block[] blocks = cf.basicBlocks(); // int block0 = m.getMethodInfo().getLineNumber(blocks[0].position()); // int block1 = // m.getMethodInfo().getLineNumber(blocks[1].position()); // int block2 = // m.getMethodInfo().getLineNumber(blocks[2].position()); // int block3 = // m.getMethodInfo().getLineNumber(blocks[3].position()); // int block4 = // m.getMethodInfo().getLineNumber(blocks[4].position()); // m.insertAt(block0, "tracer.MyPrintStream.println(\"block0:\" + " + block0 + ");"); // m.insertAt(block1, "tracer.MyPrintStream.println(\"block1:\" + " + // block1 + ");"); // m.insertAt(block2, "tracer.MyPrintStream.println(\"block2:\" + " + // block2 + ");"); // m.insertAt(block3, "tracer.MyPrintStream.println(\"block3:\" + " + // block3 + ");"); // m.insertAt(block4, "tracer.MyPrintStream.println(\"block4:\" + " + // block4 + ");"); // int block0 = // m.getMethodInfo().getLineNumber(blocks[0].position()); // m.insertAt(block0, "tracer.MyPrintStream.println(\"block0\");"); // m = cc.getDeclaredMethod("getC"); // cf = new ControlFlow(m); // blocks = cf.basicBlocks(); // int block1 = // m.getMethodInfo().getLineNumber(blocks[1].position()); // m.insertAt(block1, "tracer.MyPrintStream.println(\"block1\");"); // m = cc.getDeclaredMethod("getC"); // cf = new ControlFlow(m); // blocks = cf.basicBlocks(); // int block2 = // m.getMethodInfo().getLineNumber(blocks[2].position()); // m.insertAt(block2, "tracer.MyPrintStream.println(\"block2\");"); // m.instrument(new ExprEditor() { // public void edit(MethodCall m) throws CannotCompileException // { // if (m.getClassName().equals("Hello") // && m.getMethodName().equals("say")) // m.replace("$0.hi();"); // } // }); } }