Newer
Older
TracerOnJavassist / TracerOnJavassist / src / tracer / Tracer.java
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();");
		// }
		// });
	}
}