mirror of
https://github.com/wahyd4/pinpoint.git
synced 2026-08-20 10:16:13 +10:00
- version을 0.0.2에서 0.0.3-SNAPSHOT으로 변경. git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-tomcat-profiler/trunk@1730 84d0f5b1-2673-498c-a247-62c4ff18d310
644 lines
32 KiB
Java
644 lines
32 KiB
Java
package com.profiler.interceptor.bci;
|
|
|
|
import java.io.IOException;
|
|
|
|
import com.nhn.pinpoint.common.mapping.ApiMappingTable;
|
|
import com.nhn.pinpoint.common.mapping.ApiUtils;
|
|
import com.profiler.interceptor.*;
|
|
import com.profiler.logging.LoggerFactory;
|
|
import com.profiler.util.JavaAssistUtils;
|
|
import javassist.*;
|
|
|
|
public class JavaAssistClass implements InstrumentClass {
|
|
|
|
private final com.profiler.logging.Logger logger = LoggerFactory.getLogger(this.getClass().getName());
|
|
|
|
private JavaAssistByteCodeInstrumentor instrumentor;
|
|
private CtClass ctClass;
|
|
private static final int STATIC_INTERCEPTOR = 0;
|
|
private static final int SIMPLE_INTERCEPTOR = 1;
|
|
|
|
|
|
public JavaAssistClass(JavaAssistByteCodeInstrumentor instrumentor, CtClass ctClass) {
|
|
this.instrumentor = instrumentor;
|
|
this.ctClass = ctClass;
|
|
}
|
|
|
|
|
|
@Override
|
|
public boolean insertCodeBeforeConstructor(String[] args, String code) {
|
|
try {
|
|
CtConstructor constructor = getConstructor(args);
|
|
constructor.insertBefore(code);
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean insertCodeAfterConstructor(String[] args, String code) {
|
|
try {
|
|
CtConstructor constructor = getConstructor(args);
|
|
constructor.insertAfter(code);
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean insertCodeBeforeMethod(String methodName, String[] args, String code) {
|
|
try {
|
|
CtMethod method = getMethod(methodName, args);
|
|
method.insertBefore(code);
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean insertCodeAfterMethod(String methodName, String[] args, String code) {
|
|
try {
|
|
CtMethod method = getMethod(methodName, args);
|
|
method.insertAfter(code);
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void addTraceVariable(String variableName, String setterName, String getterName, String variableType, String initValue) throws InstrumentException {
|
|
addTraceVariable0(variableName, setterName, getterName, variableType, initValue);
|
|
}
|
|
|
|
public void addTraceVariable(String variableName, String setterName, String getterName, String variableType) throws InstrumentException {
|
|
addTraceVariable0(variableName, setterName, getterName, variableType, null);
|
|
}
|
|
|
|
public void addTraceVariable0(String variableName, String setterName, String getterName, String variableType, String initValue) throws InstrumentException {
|
|
try {
|
|
CtClass type = instrumentor.getClassPool().get(variableType);
|
|
CtField traceVariable = new CtField(type, variableName, ctClass);
|
|
if (initValue == null) {
|
|
ctClass.addField(traceVariable);
|
|
} else {
|
|
ctClass.addField(traceVariable, initValue);
|
|
}
|
|
if (setterName != null) {
|
|
CtMethod setterMethod = CtNewMethod.setter(setterName, traceVariable);
|
|
ctClass.addMethod(setterMethod);
|
|
}
|
|
if (getterName != null) {
|
|
CtMethod getterMethod = CtNewMethod.getter(getterName, traceVariable);
|
|
ctClass.addMethod(getterMethod);
|
|
}
|
|
} catch (NotFoundException e) {
|
|
throw new InstrumentException(variableName + " addTraceVariable fail. Cause:" + e.getMessage(), e);
|
|
} catch (CannotCompileException e) {
|
|
throw new InstrumentException(variableName + " addTraceVariable fail. Cause:" + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
public int addConstructorInterceptor(String[] args, Interceptor interceptor) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(null, args, interceptor, -1, Type.auto, false);
|
|
}
|
|
|
|
public int addConstructorInterceptor(String[] args, Interceptor interceptor, Type type) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(null, args, interceptor, -1, type, false);
|
|
}
|
|
|
|
|
|
@Override
|
|
public int addInterceptorCallByContextClassLoader(String methodName, String[] args, Interceptor interceptor) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(methodName, args, interceptor, -1, Type.auto, true);
|
|
}
|
|
|
|
@Override
|
|
public int addInterceptorCallByContextClassLoader(String methodName, String[] args, Interceptor interceptor, Type type) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(methodName, args, interceptor, -1, type, true);
|
|
}
|
|
|
|
@Override
|
|
public int addInterceptor(String methodName, String[] args, Interceptor interceptor) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(methodName, args, interceptor, -1, Type.auto, false);
|
|
}
|
|
|
|
@Override
|
|
public int reuseInterceptor(String methodName, String[] args, int interceptorId) throws InstrumentException, NotFoundInstrumentException {
|
|
return addInterceptor0(methodName, args, null, interceptorId, Type.auto, false);
|
|
}
|
|
|
|
@Override
|
|
public int reuseInterceptor(String methodName, String[] args, int interceptorId, Type type) throws InstrumentException, NotFoundInstrumentException {
|
|
return addInterceptor0(methodName, args, null, interceptorId, type, false);
|
|
}
|
|
|
|
@Override
|
|
public int addInterceptor(String methodName, String[] args, Interceptor interceptor, Type type) throws InstrumentException, NotFoundInstrumentException {
|
|
if (interceptor == null) {
|
|
throw new IllegalArgumentException("interceptor is null");
|
|
}
|
|
return addInterceptor0(methodName, args, interceptor, -1, type, false);
|
|
}
|
|
|
|
private CtBehavior getBehavior(String methodName, String[] args) throws NotFoundException {
|
|
if (methodName == null) {
|
|
return getConstructor(args);
|
|
}
|
|
return getMethod(methodName, args);
|
|
}
|
|
|
|
private int addInterceptor0(String methodName, String[] args, Interceptor interceptor, int interceptorId, Type type, boolean useContextClassLoader) throws InstrumentException, NotFoundInstrumentException {
|
|
CtBehavior behavior = null;
|
|
try {
|
|
behavior = getBehavior(methodName, args);
|
|
} catch (NotFoundException e) {
|
|
// target method나 constructor를 차지 못했을 경우는 NotFoundInstrumentException을 던진다.
|
|
if (interceptor == null) {
|
|
throw new NotFoundInstrumentException(interceptorId + " add fail. Cause:" + e.getMessage(), e);
|
|
} else {
|
|
throw new NotFoundInstrumentException(interceptor.getClass().getSimpleName() + " add fail. Cause:" + e.getMessage(), e);
|
|
}
|
|
}
|
|
try {
|
|
if (interceptor != null) {
|
|
if(interceptor instanceof StaticAroundInterceptor) {
|
|
StaticAroundInterceptor staticAroundInterceptor = (StaticAroundInterceptor) interceptor;
|
|
interceptorId = InterceptorRegistry.addInterceptor(staticAroundInterceptor);
|
|
} else if(interceptor instanceof SimpleAroundInterceptor) {
|
|
SimpleAroundInterceptor simpleAroundInterceptor = (SimpleAroundInterceptor) interceptor;
|
|
interceptorId = InterceptorRegistry.addSimpleInterceptor(simpleAroundInterceptor);
|
|
} else {
|
|
throw new InstrumentException("unsupported Interceptor Type:" + interceptor);
|
|
}
|
|
// traceContext는 가장먼제 inject되어야 한다.
|
|
if (interceptor instanceof TraceContextSupport) {
|
|
((TraceContextSupport)interceptor).setTraceContext(instrumentor.getAgent().getTraceContext());
|
|
}
|
|
if (interceptor instanceof ByteCodeMethodDescriptorSupport) {
|
|
setMethodDescriptor(behavior, (ByteCodeMethodDescriptorSupport) interceptor);
|
|
}
|
|
if (interceptor instanceof ApiIdSupport) {
|
|
setApiId(behavior, (ApiIdSupport) interceptor);
|
|
}
|
|
} else {
|
|
interceptor = InterceptorRegistry.findInterceptor(interceptorId);
|
|
}
|
|
// 이제는 aroundType 인터셉터만 받고 코드 인젝션을 별도 type으로 받아야 함.
|
|
if (interceptor instanceof StaticAroundInterceptor) {
|
|
switch (type) {
|
|
case around:
|
|
addStaticAroundInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case before:
|
|
addStaticBeforeInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case after:
|
|
addStaticAfterInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case auto:
|
|
addStaticAroundInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
default:
|
|
throw new UnsupportedOperationException("unsupport type");
|
|
}
|
|
} else if(interceptor instanceof SimpleAroundInterceptor) {
|
|
switch (type) {
|
|
case around:
|
|
addSimpleAroundInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case before:
|
|
addSimpleBeforeInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case after:
|
|
addSimpleAfterInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
case auto:
|
|
addSimpleAroundInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
break;
|
|
default:
|
|
throw new UnsupportedOperationException("unsupport type");
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("unsupported");
|
|
}
|
|
return interceptorId;
|
|
} catch (NotFoundException e) {
|
|
throw new InstrumentException(interceptor.getClass().getSimpleName() + " add fail. Cause:" + e.getMessage(), e);
|
|
} catch (CannotCompileException e) {
|
|
throw new InstrumentException(interceptor.getClass().getSimpleName() + "add fail. Cause:" + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
private void setApiId(CtBehavior behavior, ApiIdSupport interceptor) throws NotFoundException {
|
|
CtClass[] parameterTypes = behavior.getParameterTypes();
|
|
String[] parameterType = JavaAssistUtils.getParameterType(parameterTypes);
|
|
int apiId = ApiMappingTable.findApiId(ctClass.getName(), behavior.getName(), parameterType);
|
|
interceptor.setApiId(apiId);
|
|
}
|
|
|
|
private void setMethodDescriptor(CtBehavior behavior, ByteCodeMethodDescriptorSupport interceptor) throws NotFoundException {
|
|
DefaultMethodDescriptor methodDescriptor = new DefaultMethodDescriptor();
|
|
|
|
String methodName = behavior.getName();
|
|
methodDescriptor.setMethodName(methodName);
|
|
|
|
methodDescriptor.setClassName(ctClass.getName());
|
|
|
|
CtClass[] ctParameterTypes = behavior.getParameterTypes();
|
|
String[] parameterTypes = JavaAssistUtils.getParameterType(ctParameterTypes);
|
|
methodDescriptor.setParameterTypes(parameterTypes);
|
|
|
|
String[] parameterVariableName = JavaAssistUtils.getParameterVariableName(behavior);
|
|
methodDescriptor.setParameterVariableName(parameterVariableName);
|
|
|
|
int lineNumber = JavaAssistUtils.getLineNumber(behavior);
|
|
methodDescriptor.setLineNumber(lineNumber);
|
|
|
|
String parameterDescriptor = ApiUtils.mergeParameterVariableNameDescription(parameterTypes, parameterVariableName);
|
|
methodDescriptor.setParameterDescriptor(parameterDescriptor);
|
|
|
|
String apiDescriptor = ApiUtils.mergeApiDescriptor(ctClass.getName(), methodName, parameterDescriptor);
|
|
methodDescriptor.setApiDescriptor(apiDescriptor);
|
|
|
|
interceptor.setMethodDescriptor(methodDescriptor);
|
|
}
|
|
|
|
private void addStaticAroundInterceptor(String methodName, int id, CtBehavior method, boolean useContextClassLoader) throws NotFoundException, CannotCompileException {
|
|
addStaticBeforeInterceptor(methodName, id, method, useContextClassLoader);
|
|
addStaticAfterInterceptor(methodName, id, method, useContextClassLoader);
|
|
}
|
|
|
|
private void addStaticBeforeInterceptor(String methodName, int id, CtBehavior behavior, boolean useContextClassLoader) throws CannotCompileException, NotFoundException {
|
|
addBeforeInterceptor(methodName, id, behavior, useContextClassLoader, STATIC_INTERCEPTOR);
|
|
}
|
|
|
|
private void addStaticAfterInterceptor(String methodName, int interceptorId, CtBehavior behavior, boolean useContextClassLoader) throws NotFoundException, CannotCompileException {
|
|
addAfterInterceptor(methodName, interceptorId, behavior, useContextClassLoader, STATIC_INTERCEPTOR);
|
|
}
|
|
|
|
private void addSimpleAroundInterceptor(String methodName, int interceptorId, CtBehavior behavior, boolean useContextClassLoader) throws NotFoundException, CannotCompileException {
|
|
addSimpleBeforeInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
addSimpleAfterInterceptor(methodName, interceptorId, behavior, useContextClassLoader);
|
|
}
|
|
|
|
private void addSimpleBeforeInterceptor(String methodName, int interceptorId, CtBehavior behavior, boolean useContextClassLoader) throws NotFoundException, CannotCompileException {
|
|
addBeforeInterceptor(methodName, interceptorId, behavior, useContextClassLoader, SIMPLE_INTERCEPTOR);
|
|
}
|
|
|
|
private void addSimpleAfterInterceptor(String methodName, int interceptorId, CtBehavior behavior, boolean useContextClassLoader) throws NotFoundException, CannotCompileException {
|
|
addAfterInterceptor(methodName, interceptorId, behavior, useContextClassLoader, SIMPLE_INTERCEPTOR);
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addAfterInterceptor(String methodName, int id, CtBehavior behavior, boolean useContextClassLoader, int interceptorType) throws NotFoundException, CannotCompileException {
|
|
String returnType = getReturnType(behavior);
|
|
String target = getTarget(behavior);
|
|
|
|
String parameterTypeString = null;
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
parameterTypeString = JavaAssistUtils.getParameterDescription(behavior.getParameterTypes());
|
|
}
|
|
final String parameter = getParameter(behavior);
|
|
|
|
final CodeBuilder after = new CodeBuilder();
|
|
if (useContextClassLoader) {
|
|
after.begin();
|
|
beginAddFindInterceptorCode(id, after, interceptorType);
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
after.format(" java.lang.Class[] methodArgsClassParams = new Class[]{java.lang.Object.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.Object[].class, java.lang.Object.class};");
|
|
} else {
|
|
after.format(" java.lang.Class[] methodArgsClassParams = new Class[]{java.lang.Object.class, java.lang.Object[].class, java.lang.Object.class};");
|
|
}
|
|
after.format(" java.lang.reflect.Method method = interceptor.getClass().getMethod(\"%1$s\", methodArgsClassParams);", "after");
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
after.format(" java.lang.Object[] methodParams = new java.lang.Object[] { %1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s, %6$s };", target, ctClass.getName(), methodName, parameterTypeString, parameter, returnType);
|
|
} else {
|
|
after.format(" java.lang.Object[] methodParams = new java.lang.Object[] { %1$s, %2$s, %3$s };", target, parameter, returnType);
|
|
}
|
|
after.format(" method.invoke(interceptor, methodParams);");
|
|
endAddFindInterceptorCode(after);
|
|
after.end();
|
|
} else {
|
|
after.begin();
|
|
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
after.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticAroundInterceptor.class.getName(), id);
|
|
after.format(" interceptor.after(%1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s, %6$s);", target, ctClass.getName(), methodName, parameterTypeString, parameter, returnType);
|
|
} else {
|
|
after.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getSimpleInterceptor(%2$d);", SimpleAroundInterceptor.class.getName(), id);
|
|
after.format(" interceptor.after(%1$s, %2$s, %3$s);", target, parameter, returnType);
|
|
}
|
|
after.end();
|
|
}
|
|
final String buildAfter = after.toString();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("addAfterInterceptor after behavior:{} code:{}", behavior.getLongName(), buildAfter);
|
|
}
|
|
behavior.insertAfter(buildAfter);
|
|
|
|
|
|
CodeBuilder catchCode = new CodeBuilder();
|
|
if (useContextClassLoader) {
|
|
catchCode.begin();
|
|
beginAddFindInterceptorCode(id, catchCode, interceptorType);
|
|
if(interceptorType == STATIC_INTERCEPTOR) {
|
|
catchCode.format(" java.lang.Class[] methodArgsClassParams = new Class[]{java.lang.Object.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.Object[].class, java.lang.Object.class};");
|
|
} else {
|
|
catchCode.format(" java.lang.Class[] methodArgsClassParams = new Class[]{java.lang.Object.class, java.lang.Object[].class, java.lang.Object.class};");
|
|
}
|
|
catchCode.format(" java.lang.reflect.Method method = interceptor.getClass().getMethod(\"%1$s\", methodArgsClassParams);", "after");
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
catchCode.format(" java.lang.Object[] methodParams = new java.lang.Object[] { %1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s, (java.lang.Object) $e };", target, ctClass.getName(), methodName, parameterTypeString, parameter);
|
|
} else {
|
|
catchCode.format(" java.lang.Object[] methodParams = new java.lang.Object[] { %1$s, %2$s, (java.lang.Object) $e };", target, parameter);
|
|
}
|
|
catchCode.format(" method.invoke(interceptor, methodParams);");
|
|
endAddFindInterceptorCode(catchCode);
|
|
catchCode.format(" throw $e;");
|
|
catchCode.end();
|
|
} else {
|
|
catchCode.begin();
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
catchCode.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticAroundInterceptor.class.getName(), id);
|
|
catchCode.format(" interceptor.after(%1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s, $e);", target, ctClass.getName(), methodName, parameterTypeString, parameter);
|
|
} else {
|
|
catchCode.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getSimpleInterceptor(%2$d);", SimpleAroundInterceptor.class.getName(), id);
|
|
catchCode.format(" interceptor.after(%1$s, %2$s, $e);", target, parameter);
|
|
}
|
|
catchCode.append(" throw $e;");
|
|
catchCode.end();
|
|
}
|
|
String buildCatch = catchCode.toString();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("addAfterInterceptor catch behavior:{} code:{}", behavior.getLongName(), buildCatch);
|
|
}
|
|
CtClass th = instrumentor.getClassPool().get("java.lang.Throwable");
|
|
behavior.addCatch(buildCatch, th);
|
|
|
|
}
|
|
|
|
private void endAddFindInterceptorCode(CodeBuilder catchCode) {
|
|
catchCode.format("}");
|
|
}
|
|
|
|
private void beginAddFindInterceptorCode(int id, CodeBuilder after, int interceptorType) {
|
|
after.format("java.lang.ClassLoader contextClassLoader = java.lang.Thread.currentThread().getContextClassLoader();");
|
|
after.format("if (contextClassLoader != null) {");
|
|
after.format(" java.lang.Class interceptorRegistryClass = contextClassLoader.loadClass(\"com.profiler.interceptor.InterceptorRegistry\");");
|
|
if(interceptorType == STATIC_INTERCEPTOR) {
|
|
after.format(" java.lang.reflect.Method getInterceptorMethod = interceptorRegistryClass.getMethod(\"getInterceptor\", new java.lang.Class[]{ int.class });");
|
|
} else {
|
|
after.format(" java.lang.reflect.Method getInterceptorMethod = interceptorRegistryClass.getMethod(\"getSimpleInterceptor\", new java.lang.Class[]{ int.class });");
|
|
}
|
|
after.format(" java.lang.Object[] interceptorParams = new java.lang.Object[] { java.lang.Integer.valueOf(%1$d) };", id);
|
|
after.format(" java.lang.Object interceptor = getInterceptorMethod.invoke(interceptorRegistryClass, interceptorParams);");
|
|
}
|
|
|
|
private String getTarget(CtBehavior behavior) {
|
|
boolean staticMethod = JavaAssistUtils.isStaticBehavior(behavior);
|
|
if (staticMethod) {
|
|
return "null";
|
|
} else {
|
|
return "this";
|
|
}
|
|
}
|
|
|
|
public String getReturnType(CtBehavior behavior) throws NotFoundException {
|
|
if (behavior instanceof CtMethod) {
|
|
CtClass returnType = ((CtMethod) behavior).getReturnType();
|
|
if (CtClass.voidType == returnType) {
|
|
return "null";
|
|
}
|
|
}
|
|
return "($w)$_";
|
|
}
|
|
|
|
|
|
private void addBeforeInterceptor(String methodName, int id, CtBehavior behavior, boolean useContextClassLoader, int interceptorType) throws CannotCompileException, NotFoundException {
|
|
String target = getTarget(behavior);
|
|
// 인터셉터 호출시 최대한 연산량을 줄이기 위해서 정보는 가능한 정적 데이터로 생성한다.
|
|
String parameterDescription = null;
|
|
if(interceptorType == STATIC_INTERCEPTOR) {
|
|
parameterDescription = JavaAssistUtils.getParameterDescription(behavior.getParameterTypes());
|
|
}
|
|
String parameter = getParameter(behavior);
|
|
|
|
CodeBuilder code = new CodeBuilder();
|
|
if (useContextClassLoader) {
|
|
code.begin();
|
|
// java.lang.ClassLoader contextClassLoader = java.lang.Thread.currentThread().getContextClassLoader();
|
|
// java.lang.Class<?> interceptorRegistryClass = contextClassLoader.loadClass("com.profiler.interceptor.InterceptorRegistry");
|
|
// java.lang.reflect.Method getInterceptorMethod = interceptorRegistryClass.getMethod("getInterceptor", int.class);
|
|
// java.lang.Object interceptor = getInterceptorMethod.invoke(interceptorRegistryClass, 1);
|
|
// java.lang.reflect.Method beforeMethod = interceptor.getClass().getMethod("before", java.lang.Object.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.Object[].class);
|
|
// beforeMethod.invoke(interceptor, null, null, null, null, null);
|
|
//
|
|
beginAddFindInterceptorCode(id, code, interceptorType);
|
|
if(interceptorType == STATIC_INTERCEPTOR) {
|
|
code.format(" java.lang.Class[] beforeMethodParams = new Class[]{java.lang.Object.class, java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.Object[].class};");
|
|
} else {
|
|
code.format(" java.lang.Class[] beforeMethodParams = new Class[]{java.lang.Object.class, java.lang.Object[].class};");
|
|
}
|
|
code.format(" java.lang.reflect.Method beforeMethod = interceptor.getClass().getMethod(\"%1$s\", beforeMethodParams);", "before");
|
|
if(interceptorType == STATIC_INTERCEPTOR) {
|
|
code.format(" java.lang.Object[] beforeParams = new java.lang.Object[] { %1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s };", target, ctClass.getName(), methodName, parameterDescription, parameter);
|
|
} else {
|
|
code.format(" java.lang.Object[] beforeParams = new java.lang.Object[] { %1$s, %2$s };", target, parameter);
|
|
}
|
|
code.format(" beforeMethod.invoke(interceptor, beforeParams);");
|
|
code.format("}");
|
|
code.end();
|
|
} else {
|
|
code.begin();
|
|
if (interceptorType == STATIC_INTERCEPTOR) {
|
|
code.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getInterceptor(%2$d);", StaticAroundInterceptor.class.getName(), id);
|
|
code.format(" interceptor.before(%1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s);", target, ctClass.getName(), methodName, parameterDescription, parameter);
|
|
} else {
|
|
// simpleInterceptor인덱스에서 검색하여 typecasting을 제거한다.
|
|
code.format(" %1$s interceptor = com.profiler.interceptor.InterceptorRegistry.getSimpleInterceptor(%2$d);", SimpleAroundInterceptor.class.getName(), id);
|
|
code.format(" interceptor.before(%1$s, %2$s);", target, parameter);
|
|
}
|
|
code.end();
|
|
}
|
|
String buildBefore = code.toString();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("addStaticBeforeInterceptor catch behavior:{} code:{}", behavior.getLongName(), buildBefore);
|
|
}
|
|
|
|
if (behavior instanceof CtConstructor) {
|
|
((CtConstructor) behavior).insertBeforeBody(buildBefore);
|
|
} else {
|
|
behavior.insertBefore(buildBefore);
|
|
}
|
|
}
|
|
|
|
private String getParameter(CtBehavior behavior) throws NotFoundException {
|
|
CtClass[] parameterTypes = behavior.getParameterTypes();
|
|
if (parameterTypes.length == 0) {
|
|
return "null";
|
|
}
|
|
return "$args";
|
|
}
|
|
|
|
public boolean addDebugLogBeforeAfterMethod() {
|
|
String className = this.ctClass.getName();
|
|
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(className);
|
|
int id = InterceptorRegistry.addInterceptor(loggingInterceptor);
|
|
try {
|
|
CtClass cc = this.instrumentor.getClassPool().get(className);
|
|
CtMethod[] methods = cc.getDeclaredMethods();
|
|
|
|
for (CtMethod method : methods) {
|
|
if (method.isEmpty()) {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("{} is empty.", method.getLongName());
|
|
}
|
|
continue;
|
|
}
|
|
String methodName = method.getName();
|
|
|
|
// TODO method의 prameter type을 interceptor에 별도 추가해야 될것으로 보임.
|
|
String params = getParamsToString(method.getParameterTypes());
|
|
addStaticAroundInterceptor(methodName, id, method, false);
|
|
}
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 제대로 동작안함 다시 봐야 될것 같음. 생성자일경우의 bytecode 수정시 에러가 남.
|
|
*
|
|
* @return
|
|
*/
|
|
@Deprecated
|
|
public boolean addDebugLogBeforeAfterConstructor() {
|
|
String className = this.ctClass.getName();
|
|
LoggingInterceptor loggingInterceptor = new LoggingInterceptor(className);
|
|
int id = InterceptorRegistry.addInterceptor(loggingInterceptor);
|
|
try {
|
|
CtClass cc = this.instrumentor.getClassPool().get(className);
|
|
CtConstructor[] constructors = cc.getConstructors();
|
|
|
|
for (CtConstructor constructor : constructors) {
|
|
if (constructor.isEmpty()) {
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug(constructor.getLongName() + " is empty.");
|
|
}
|
|
continue;
|
|
}
|
|
String constructorName = constructor.getName();
|
|
String params = getParamsToString(constructor.getParameterTypes());
|
|
|
|
// constructor.insertAfter("{System.out.println(\"*****" +
|
|
// constructorName + " Constructor:Param=(" + params +
|
|
// ") is finished. \" + $args);}");
|
|
// constructor.addCatch("{System.out.println(\"*****" +
|
|
// constructorName + " Constructor:Param=(" + params +
|
|
// ") is finished.\"); throw $e; }"
|
|
// , instrumentor.getClassPool().get("java.lang.Throwable"));
|
|
addStaticAroundInterceptor(constructorName, id, constructor, false);
|
|
}
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (logger.isWarnEnabled()) {
|
|
logger.warn(e.getMessage(), e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private String getParamsToString(CtClass[] params) throws NotFoundException {
|
|
StringBuilder sb = new StringBuilder(512);
|
|
if (params.length != 0) {
|
|
int paramsLength = params.length;
|
|
for (int loop = paramsLength - 1; loop > 0; loop--) {
|
|
sb.append(params[loop].getName()).append(",");
|
|
}
|
|
}
|
|
String paramsStr = sb.toString();
|
|
if (logger.isDebugEnabled()) {
|
|
logger.debug("params type:{}");
|
|
}
|
|
return paramsStr;
|
|
}
|
|
|
|
private CtMethod getMethod(String methodName, String[] args) throws NotFoundException {
|
|
CtClass[] params = JavaAssistUtils.getCtParameter(args, instrumentor.getClassPool());
|
|
// cttime에는 직접 구현클래스를 조작해야 되므로 상속관계의 method를 찾으면 안됨.
|
|
return ctClass.getDeclaredMethod(methodName, params);
|
|
}
|
|
|
|
private CtConstructor getConstructor(String[] args) throws NotFoundException {
|
|
CtClass[] params = JavaAssistUtils.getCtParameter(args, instrumentor.getClassPool());
|
|
return ctClass.getDeclaredConstructor(params);
|
|
}
|
|
|
|
|
|
@Override
|
|
public byte[] toBytecode() {
|
|
try {
|
|
byte[] bytes = ctClass.toBytecode();
|
|
ctClass.detach();
|
|
return bytes;
|
|
} catch (IOException e) {
|
|
logger.info("IoException class:" + ctClass.getName() + " " + e.getMessage(), e);
|
|
} catch (CannotCompileException e) {
|
|
logger.info("CannotCompileException class:" + ctClass.getName() + " " + e.getMessage(), e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public Class<?> toClass() throws InstrumentException {
|
|
try {
|
|
return ctClass.toClass();
|
|
} catch (CannotCompileException e) {
|
|
throw new InstrumentException( "CannotCompileException class:" + ctClass.getName() + " " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
public CtMethod[] getDeclaredMethods() {
|
|
return ctClass.getDeclaredMethods();
|
|
}
|
|
|
|
public boolean isInterceptable() {
|
|
return !ctClass.isInterface() && !ctClass.isAnnotation() && !ctClass.isModified();
|
|
}
|
|
}
|