diff --git a/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java b/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java index 4af6d7544..7ed354b55 100644 --- a/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java +++ b/src/main/java/com/profiler/interceptor/bci/JavaAssistClass.java @@ -1,6 +1,7 @@ package com.profiler.interceptor.bci; import java.io.IOException; +import java.util.Formatter; import java.util.logging.Level; import java.util.logging.Logger; @@ -97,80 +98,99 @@ public class JavaAssistClass implements InstrumentClass { } } - // TODO return type을 별도 exception으로 할지 추가 검토가 필요함. - @Override - public boolean addTraceVariable(String variableName, String setterName, String getterName, String variableType) { - try { - CtClass type = instrumentor.getClassPool().get(variableType); - CtField traceVariable = new CtField(type, variableName, ctClass); - ctClass.addField(traceVariable); - CtMethod setterMethod = CtNewMethod.setter(setterName, traceVariable); - ctClass.addMethod(setterMethod); - CtMethod getterMethod = CtNewMethod.getter(getterName, traceVariable); - ctClass.addMethod(getterMethod); - return true; - } catch (NotFoundException e) { - if (logger.isLoggable(Level.WARNING)) { + // TODO return type을 별도 exception으로 할지 추가 검토가 필요함. + public boolean addTraceVariable(String variableName, String setterName, String getterName, String variableType) { + try { + CtClass type = instrumentor.getClassPool().get(variableType); + CtField traceVariable = new CtField(type, variableName, ctClass); + ctClass.addField(traceVariable); + if (setterName != null) { + CtMethod setterMethod = CtNewMethod.setter(setterName, traceVariable); + ctClass.addMethod(setterMethod); + } + if (getterName != null) { + CtMethod getterMethod = CtNewMethod.getter(getterName, traceVariable); + ctClass.addMethod(getterMethod); + } + return true; + } catch (NotFoundException e) { + if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, e.getMessage(), e); } - } catch (CannotCompileException e) { - if (logger.isLoggable(Level.WARNING)) { + } catch (CannotCompileException e) { + if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, e.getMessage(), e); } - } - return false; - } + } + return false; + } - @Override + public boolean addConstructorInterceptor(String[] args, Interceptor interceptor) { + return addInterceptor(null, args, interceptor); + } + + + @Override public boolean addInterceptor(String methodName, String[] args, Interceptor interceptor) { return addInterceptor(methodName, args, interceptor, Type.auto); } @Override public boolean addInterceptor(String methodName, String[] args, Interceptor interceptor, Type type) { - if (interceptor == null) + if (interceptor == null) { return false; - - CtMethod method = getMethod(methodName, args); - if (method == null) { + } + CtBehavior behavior = getBehavior(methodName, args); + if (behavior == null) { return false; } - int id = InterceptorRegistry.addInterceptor(interceptor); - try { - if (type == Type.auto) { - if (interceptor instanceof StaticAroundInterceptor) { - addStaticAroundInterceptor(methodName, id, method); - } else if (interceptor instanceof StaticBeforeInterceptor) { - addStaticBeforeInterceptor(methodName, id, method); - } else if (interceptor instanceof StaticAfterInterceptor) { - addStaticAfterInterceptor(methodName, id, method); - } else { - return false; - } - } else if (type == Type.around && interceptor instanceof StaticAroundInterceptor) { - addStaticAroundInterceptor(methodName, id, method); - } else if (type == Type.before && interceptor instanceof StaticBeforeInterceptor) { - addStaticBeforeInterceptor(methodName, id, method); - } else if (type == Type.after && interceptor instanceof StaticAfterInterceptor) { - addStaticAfterInterceptor(methodName, id, method); - } else { - return false; - } - return true; - } catch (NotFoundException e) { - if (logger.isLoggable(Level.WARNING)) { - logger.log(Level.WARNING, e.getMessage(), e); - } - } catch (CannotCompileException e) { - if (logger.isLoggable(Level.WARNING)) { - logger.log(Level.WARNING, e.getMessage(), e); - } - } - return false; + return addInterceptor0(methodName, interceptor, type, behavior); } - private void addStaticAroundInterceptor(String methodName, int id, CtBehavior method) throws NotFoundException, CannotCompileException { + private CtBehavior getBehavior(String methodName, String[] args) { + if (methodName == null) { + return getConstructor(args); + } + return getMethod(methodName, args); + } + + private boolean addInterceptor0(String methodName, Interceptor interceptor, Type type, CtBehavior behavior) { + int id = InterceptorRegistry.addInterceptor(interceptor); + try { + if (type == Type.auto) { + if (interceptor instanceof StaticAroundInterceptor) { + addStaticAroundInterceptor(methodName, id, behavior); + } else if (interceptor instanceof StaticBeforeInterceptor) { + addStaticBeforeInterceptor(methodName, id, behavior); + } else if (interceptor instanceof StaticAfterInterceptor) { + addStaticAfterInterceptor(methodName, id, behavior); + } else { + return false; + } + } else if (type == Type.around && interceptor instanceof StaticAroundInterceptor) { + addStaticAroundInterceptor(methodName, id, behavior); + } else if (type == Type.before && interceptor instanceof StaticBeforeInterceptor) { + addStaticBeforeInterceptor(methodName, id, behavior); + } else if (type == Type.after && interceptor instanceof StaticAfterInterceptor) { + addStaticAfterInterceptor(methodName, id, behavior); + } else { + return false; + } + return true; + } catch (NotFoundException e) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, e.getMessage(), e); + } + } catch (CannotCompileException e) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, e.getMessage(), e); + } + } + return false; + } + + private void addStaticAroundInterceptor(String methodName, int id, CtBehavior method) throws NotFoundException, CannotCompileException { addStaticBeforeInterceptor(methodName, id, method); addStaticAfterInterceptor(methodName, id, method); } @@ -178,10 +198,10 @@ public class JavaAssistClass implements InstrumentClass { private void addStaticAfterInterceptor(String methodName, int id, CtBehavior behavior) throws NotFoundException, CannotCompileException { StringBuilder after = new StringBuilder(1024); after.append("{"); - addGetStaticAfterInterceptor(after, id); + format(after, " %1$s interceptor = (%1$s) com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticAfterInterceptor.class.getName(), id); String target = getTarget(behavior); String returnType = getReturnType(behavior); - after.append(" interceptor.after(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, " + returnType + ");"); + format(after, " interceptor.after(%1$s, \"%2$s\", \"%3$s\", $args, %4$s);", target, ctClass.getName(), methodName, returnType); after.append("}"); String buildAfter = after.toString(); if (logger.isLoggable(Level.INFO)) { @@ -189,10 +209,11 @@ public class JavaAssistClass implements InstrumentClass { } behavior.insertAfter(buildAfter); + StringBuilder catchCode = new StringBuilder(1024); catchCode.append("{"); - addGetStaticAfterInterceptor(catchCode, id); - catchCode.append(" interceptor.after(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args, $e);"); + format(catchCode, " %1$s interceptor = (%1$s) com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticAfterInterceptor.class.getName(), id); + format(catchCode, " interceptor.after(%1$s, \"%2$s\", \"%3$s\", $args, $e);", target, ctClass.getName(), methodName); catchCode.append(" throw $e;"); catchCode.append("}"); String buildCatch = catchCode.toString(); @@ -228,18 +249,12 @@ public class JavaAssistClass implements InstrumentClass { return java.lang.reflect.Modifier.isStatic(modifiers); } - private void addGetStaticAfterInterceptor(StringBuilder after, int id) { - after.append(" com.profiler.interceptor.StaticAfterInterceptor interceptor = " + "(com.profiler.interceptor.StaticAfterInterceptor) com.profiler.interceptor.InterceptorRegistry.getInterceptor("); - after.append(id); - after.append(");"); - } - private void addStaticBeforeInterceptor(String methodName, int id, CtBehavior behavior) throws CannotCompileException { StringBuilder code = new StringBuilder(1024); code.append("{"); - addGetBeforeInterceptor(id, code); + format(code, " %1$s interceptor = (%1$s) com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticBeforeInterceptor.class.getName(), id); String target = getTarget(behavior); - code.append(" interceptor.before(" + target + ", \"" + ctClass.getName() + "\", \"" + methodName + "\", $args);"); + format(code, " interceptor.before(%1$s, \"%2$s\", \"%3$s\", $args);", target, ctClass.getName(), methodName); code.append("}"); String buildBefore = code.toString(); if (logger.isLoggable(Level.INFO)) { @@ -253,11 +268,11 @@ public class JavaAssistClass implements InstrumentClass { } } - private void addGetBeforeInterceptor(int id, StringBuilder code) { - code.append(" com.profiler.interceptor.StaticBeforeInterceptor interceptor = " + "(com.profiler.interceptor.StaticBeforeInterceptor)com.profiler.interceptor.InterceptorRegistry.getInterceptor("); - code.append(id); - code.append(");"); - } + private void format(StringBuilder codeBlock, String format, Object... args) { + Formatter formatter = new Formatter(codeBlock); + formatter.format(format, args); + } + public boolean addDebugLogBeforeAfterMethod() { String className = this.ctClass.getName(); diff --git a/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java index ef4a458a6..706ff1a31 100644 --- a/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java +++ b/src/main/java/com/profiler/modifier/db/mysql/interceptors/CreateStatementInterceptor.java @@ -2,16 +2,10 @@ package com.profiler.modifier.db.mysql.interceptors; import com.profiler.context.Trace; import com.profiler.interceptor.StaticAfterInterceptor; -import com.profiler.interceptor.StaticBeforeInterceptor; import com.profiler.modifier.db.ConnectionTrace; -import com.profiler.util.ReflectionUtils; -import java.beans.Statement; -import java.io.FileDescriptor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.sql.Connection; import java.util.Arrays; import java.util.logging.Level; diff --git a/src/test/java/com/profiler/interceptor/bci/FormatTest.java b/src/test/java/com/profiler/interceptor/bci/FormatTest.java new file mode 100644 index 000000000..3280b76c5 --- /dev/null +++ b/src/test/java/com/profiler/interceptor/bci/FormatTest.java @@ -0,0 +1,27 @@ +package com.profiler.interceptor.bci; + +import org.junit.Test; + +import java.text.Format; +import java.util.Formatter; + +public class FormatTest { + @Test + public void format() { + StringBuilder sb = new StringBuilder(); + Formatter formatter = new Formatter(sb); + formatter.format("interceptor.after(%1s)", "tsest"); + + formatter.format("interceptor.afteddddr(%1s)", "tsest", "dd"); + System.out.println(); + } + @Test + public void format2() { + StringBuilder sb = new StringBuilder(); + sb.append("dddd"); + Formatter formatter = new Formatter(sb); + + formatter.format("interceptor.afteddddr(%s, %s, %s)", 16, 34234, 333); + System.out.println(sb.toString()); + } +} diff --git a/src/test/java/com/profiler/interceptor/bci/MethodRenameInterceptorTest.java b/src/test/java/com/profiler/interceptor/bci/MethodRenameInterceptorTest.java new file mode 100644 index 000000000..ebe502a34 --- /dev/null +++ b/src/test/java/com/profiler/interceptor/bci/MethodRenameInterceptorTest.java @@ -0,0 +1,95 @@ +package com.profiler.interceptor.bci; + +import javassist.*; +import org.junit.Test; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class MethodRenameInterceptorTest { + @Test + public void methodRename() { + // Method rename을 사용해서 local 변수를 공유하는 방법의 경우 call stack이 변경되는 문제점이있다. + try { + String methodName = "callA"; + String objectName = "com.profiler.interceptor.bci.TestObject"; + // start by getting the class file and method + CtClass clas = ClassPool.getDefault().get(objectName); + if (clas == null) { + System.err.println("Class " + objectName + " not found"); + } else { + // add timing interceptor to the class + addTiming(clas, methodName); + clas.writeFile(); + System.out.println("Added timing to method " + objectName + "." + methodName); + + } + Class aClass = clas.toClass(); + Object o = aClass.newInstance(); + Method method = o.getClass().getMethod(methodName, null); + Object invoke = method.invoke(o, null); + } catch (CannotCompileException ex) { + ex.printStackTrace(); + } catch (NotFoundException ex) { + ex.printStackTrace(); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (NoSuchMethodException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (InstantiationException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (IllegalAccessException e) { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + private static void addTiming(CtClass clas, String mname) + throws NotFoundException, CannotCompileException { + + // get the method information (throws exception if method with + // given name is not declared directly by this class, returns + // arbitrary choice if more than one with the given name) + CtMethod mold = clas.getDeclaredMethod(mname); + + // rename old method to synthetic name, then duplicate the + // method with original name for use as interceptor + String nname = mname + "$impl"; + mold.setName(nname); + CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null); + + // start the body text generation by saving the start time + // to a local variable, then call the timed method; the + // actual code generated needs to depend on whether the + // timed method returns a value + String type = mold.getReturnType().getName(); + StringBuffer body = new StringBuffer(); + body.append("{\nlong start = System.currentTimeMillis();\n"); + if (!"void".equals(type)) { + body.append(type + " result = "); + } + body.append(nname + "($$);\n"); + + // finish body text generation with call to print the timing + // information, and return saved value (if not void) + body.append("System.out.println(\"Call to method " + mname + + " took \" +\n (System.currentTimeMillis()-start) + " + + "\" ms.\");\n"); + if (!"void".equals(type)) { + body.append("return result;\n"); + } + body.append("}"); + + // replace the body of the interceptor method with generated + // code block and add it to class + mnew.setBody(body.toString()); + clas.addMethod(mnew); + // print the generated code block just to show what was done + System.out.println("Interceptor method body:"); + System.out.println(body.toString()); + + } + +} diff --git a/src/test/java/com/profiler/interceptor/bci/ReflectionTest.java b/src/test/java/com/profiler/interceptor/bci/ReflectionTest.java new file mode 100644 index 000000000..8eceb384c --- /dev/null +++ b/src/test/java/com/profiler/interceptor/bci/ReflectionTest.java @@ -0,0 +1,29 @@ +package com.profiler.interceptor.bci; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.NotFoundException; +import org.junit.Test; + +import java.lang.reflect.Constructor; + +public class ReflectionTest { + @Test + public void test() throws NotFoundException { + Constructor[] constructors = String.class.getConstructors(); + for(Constructor c: constructors) { + System.out.println(c.getName()); + } + + ClassPool pool = new ClassPool(); + pool.appendSystemPath(); + CtClass ctClass = pool.get("java.lang.String"); + CtConstructor[] constructors1 = ctClass.getConstructors(); + for(CtConstructor cc : constructors1) { + System.out.println(cc.getName()); + System.out.println(cc.getLongName()); + System.out.println(cc.getSignature()); + } + } +}