提交 92370c53 编写于 作者: oldratlee's avatar oldratlee 🔥

split TtlTransformer to JavassistTransformlet

上级 acae77ef
package com.alibaba.ttl.threadpool.agent;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.NotFoundException;
import java.io.IOException;
/**
* TTL {@code Transformlet} by {@code Javassist}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.5.1
*/
public interface JavassistTransformlet {
boolean needTransform(String className);
void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException;
}
package com.alibaba.ttl.threadpool.agent;
import com.alibaba.ttl.threadpool.agent.transformlet.TtlExecutorTransformlet;
import com.alibaba.ttl.threadpool.agent.transformlet.TtlForkJoinTransformlet;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -21,13 +25,22 @@ public final class TtlAgent {
}
public static void premain(String agentArgs, Instrumentation inst) {
logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
try {
ClassFileTransformer transformer = new TtlTransformer();
inst.addTransformer(transformer, true);
logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
logger.info("[TtlAgent.premain] end");
}
@SuppressWarnings("unchecked") ClassFileTransformer transformer = new TtlTransformer(TtlExecutorTransformlet.class, TtlForkJoinTransformlet.class);
inst.addTransformer(transformer, true);
logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
logger.info("[TtlAgent.premain] end");
} catch (Exception e) {
String msg = "Fail to load TtlAgent , cause: " + e.toString();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, msg, e);
}
throw new IllegalStateException(msg, e);
}
}
}
package com.alibaba.ttl.threadpool.agent;
import com.alibaba.ttl.TtlCallable;
import com.alibaba.ttl.TtlRunnable;
import javassist.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -18,40 +17,29 @@ import java.util.logging.Logger;
* TTL {@link ClassFileTransformer} of Java Agent
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @see java.util.concurrent.Executor
* @see java.util.concurrent.ExecutorService
* @see java.util.concurrent.ThreadPoolExecutor
* @see java.util.concurrent.ScheduledThreadPoolExecutor
* @see java.util.concurrent.Executors
* @since 0.9.0
*/
public class TtlTransformer implements ClassFileTransformer {
private static final Logger logger = Logger.getLogger(TtlTransformer.class.getName());
private static final String TTL_RUNNABLE_CLASS_NAME = TtlRunnable.class.getName();
private static final String TTL_CALLABLE_CLASS_NAME = TtlCallable.class.getName();
private static final byte[] EMPTY_BYTE_ARRAY = {};
private static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable";
private static final String CALLABLE_CLASS_NAME = "java.util.concurrent.Callable";
private static final String TIMER_TASK_CLASS_NAME = "java.util.TimerTask";
@SuppressWarnings("unchecked")
final List<JavassistTransformlet> transformletList = new ArrayList();
private static Set<String> EXECUTOR_CLASS_NAMES = new HashSet<String>();
@SuppressWarnings("unchecked")
public TtlTransformer(Class<? extends JavassistTransformlet>... transformletClasses) throws Exception {
for (Class<? extends JavassistTransformlet> transformletClass : transformletClasses) {
final JavassistTransformlet transformlet = transformletClass.getConstructor().newInstance();
transformletList.add(transformlet);
static {
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ThreadPoolExecutor");
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ScheduledThreadPoolExecutor");
logger.info("[TtlTransformer] add Transformlet " + transformletClass + " success");
}
}
private static final String FORK_JOIN_TASK_CLASS_NAME = "java.util.concurrent.ForkJoinTask";
private static final String TTL_RECURSIVE_ACTION_CLASS_NAME = "com.alibaba.ttl.TtlRecursiveAction";
private static final String TTL_RECURSIVE_TASK_CLASS_NAME = "com.alibaba.ttl.TtlRecursiveTask";
private static final byte[] EMPTY_BYTE_ARRAY = {};
@Override
public byte[] transform(final ClassLoader loader, final String classFile, final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain, final byte[] classFileBuffer) {
public final byte[] transform(final ClassLoader loader, final String classFile, final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain, final byte[] classFileBuffer) {
try {
// Lambda has no class file, no need to transform, just return.
if (classFile == null) {
......@@ -59,36 +47,12 @@ public class TtlTransformer implements ClassFileTransformer {
}
final String className = toClassName(classFile);
if (EXECUTOR_CLASS_NAMES.contains(className)) {
logger.info("Transforming class " + className);
final CtClass clazz = getCtClass(classFileBuffer, loader);
for (CtMethod method : clazz.getDeclaredMethods()) {
updateMethodOfExecutorClass(clazz, method);
}
return clazz.toBytecode();
} else if (FORK_JOIN_TASK_CLASS_NAME.equals(className)) {
logger.info("Transforming class " + className);
final CtClass clazz = getCtClass(classFileBuffer, loader);
updateForkJoinTaskClass(className, clazz);
return clazz.toBytecode();
} else if (TIMER_TASK_CLASS_NAME.equals(className)) {
final CtClass clazz = getCtClass(classFileBuffer, loader);
while (true) {
String name = clazz.getSuperclass().getName();
if (Object.class.getName().equals(name)) {
break;
}
if (TIMER_TASK_CLASS_NAME.equals(name)) {
logger.info("Transforming class " + className);
// FIXME add code here
return EMPTY_BYTE_ARRAY;
}
for (JavassistTransformlet transformlet : transformletList) {
if (transformlet.needTransform(className)) {
logger.info("Transforming class " + className);
final CtClass clazz = getCtClass(classFileBuffer, loader);
transformlet.doTransform(clazz);
return clazz.toBytecode();
}
}
} catch (Throwable t) {
......@@ -98,6 +62,7 @@ public class TtlTransformer implements ClassFileTransformer {
}
throw new IllegalStateException(msg, t);
}
return EMPTY_BYTE_ARRAY;
}
......@@ -106,76 +71,15 @@ public class TtlTransformer implements ClassFileTransformer {
}
private static CtClass getCtClass(final byte[] classFileBuffer, final ClassLoader classLoader) throws IOException {
ClassPool classPool = new ClassPool(true);
final ClassPool classPool = new ClassPool(true);
if (classLoader == null) {
classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
} else {
classPool.appendClassPath(new LoaderClassPath(classLoader));
}
CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classFileBuffer), false);
final CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classFileBuffer), false);
clazz.defrost();
return clazz;
}
private static void updateMethodOfExecutorClass(final CtClass clazz, final CtMethod method) throws NotFoundException, CannotCompileException {
if (method.getDeclaringClass() != clazz) {
return;
}
final int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) {
return;
}
CtClass[] parameterTypes = method.getParameterTypes();
StringBuilder insertCode = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
CtClass paraType = parameterTypes[i];
if (RUNNABLE_CLASS_NAME.equals(paraType.getName())) {
String code = String.format("$%d = %s.get($%d, false, true);", i + 1, TTL_RUNNABLE_CLASS_NAME, i + 1);
logger.info("insert code before method " + method + " of class " + method.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
} else if (CALLABLE_CLASS_NAME.equals(paraType.getName())) {
String code = String.format("$%d = %s.get($%d, false, true);", i + 1, TTL_CALLABLE_CLASS_NAME, i + 1);
logger.info("insert code before method " + method + " of class " + method.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
}
}
if (insertCode.length() > 0) {
method.insertBefore(insertCode.toString());
}
}
private static void updateForkJoinTaskClass(final String className, final CtClass clazz) throws CannotCompileException, NotFoundException {
// add new field
final String capturedFieldName = "captured$field$add$by$ttl";
final CtField capturedField = CtField.make("private final java.lang.Object " + capturedFieldName + ";", clazz);
clazz.addField(capturedField, "com.alibaba.ttl.TransmittableThreadLocal.Transmitter.capture();");
logger.info("add new field " + capturedFieldName + " to class " + className);
final String doExec_methodName = "doExec";
final CtMethod doExecMethod = clazz.getDeclaredMethod(doExec_methodName);
final CtMethod new_doExecMethod = CtNewMethod.copy(doExecMethod, doExec_methodName, clazz, null);
// rename original doExec method, and set to private method(avoid reflect out renamed method unexpectedly)
final String original_doExec_method_rename = "original$doExec$method$renamed$by$ttl";
doExecMethod.setName(original_doExec_method_rename);
doExecMethod.setModifiers(doExecMethod.getModifiers() & ~Modifier.PUBLIC /* remove public */ | Modifier.PRIVATE /* add private */);
// set new doExec method implementation
final String code = "{\n" +
// do nothing/directly return, if is TTL ForkJoinTask instance
"if (this instanceof " + TTL_RECURSIVE_ACTION_CLASS_NAME + " || this instanceof " + TTL_RECURSIVE_TASK_CLASS_NAME + ") {\n" +
" return " + original_doExec_method_rename + "($$);\n" +
"}\n" +
"java.lang.Object backup = com.alibaba.ttl.TransmittableThreadLocal.Transmitter.replay(" + capturedFieldName + ");\n" +
"try {\n" +
" return " + original_doExec_method_rename + "($$);\n" +
"} finally {\n" +
" com.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore(backup);\n" +
"}\n" + "}";
new_doExecMethod.setBody(code);
clazz.addMethod(new_doExecMethod);
logger.info("insert code around method " + doExecMethod + " of class " + className + ": " + code);
}
}
package com.alibaba.ttl.threadpool.agent.transformlet;
import com.alibaba.ttl.threadpool.agent.JavassistTransformlet;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
/**
* TTL {@link JavassistTransformlet} for {@link java.util.concurrent.Executor}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @see java.util.concurrent.Executor
* @see java.util.concurrent.ExecutorService
* @see java.util.concurrent.ThreadPoolExecutor
* @see java.util.concurrent.ScheduledThreadPoolExecutor
* @see java.util.concurrent.Executors
* @since 2.5.1
*/
public class TtlExecutorTransformlet implements JavassistTransformlet {
private static final Logger logger = Logger.getLogger(TtlExecutorTransformlet.class.getName());
private static final String TTL_RUNNABLE_CLASS_NAME = "com.alibaba.ttl.TtlRunnable";
private static final String TTL_CALLABLE_CLASS_NAME = "com.alibaba.ttl.TtlCallable";
private static final String RUNNABLE_CLASS_NAME = "java.lang.Runnable";
private static final String CALLABLE_CLASS_NAME = "java.util.concurrent.Callable";
private static Set<String> EXECUTOR_CLASS_NAMES = new HashSet<String>();
static {
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ThreadPoolExecutor");
EXECUTOR_CLASS_NAMES.add("java.util.concurrent.ScheduledThreadPoolExecutor");
}
@Override
public boolean needTransform(String className) {
return EXECUTOR_CLASS_NAMES.contains(className);
}
@Override
public void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException {
for (CtMethod method : clazz.getDeclaredMethods()) {
updateMethodOfExecutorClass(clazz, method);
}
}
private void updateMethodOfExecutorClass(final CtClass clazz, final CtMethod method) throws NotFoundException, CannotCompileException {
if (method.getDeclaringClass() != clazz) {
return;
}
final int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) {
return;
}
CtClass[] parameterTypes = method.getParameterTypes();
StringBuilder insertCode = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
CtClass paraType = parameterTypes[i];
if (RUNNABLE_CLASS_NAME.equals(paraType.getName())) {
String code = String.format("$%d = %s.get($%d, false, true);", i + 1, TTL_RUNNABLE_CLASS_NAME, i + 1);
logger.info("insert code before method " + method + " of class " + method.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
} else if (CALLABLE_CLASS_NAME.equals(paraType.getName())) {
String code = String.format("$%d = %s.get($%d, false, true);", i + 1, TTL_CALLABLE_CLASS_NAME, i + 1);
logger.info("insert code before method " + method + " of class " + method.getDeclaringClass().getName() + ": " + code);
insertCode.append(code);
}
}
if (insertCode.length() > 0) {
method.insertBefore(insertCode.toString());
}
}
}
package com.alibaba.ttl.threadpool.agent.transformlet;
import com.alibaba.ttl.threadpool.agent.JavassistTransformlet;
import javassist.*;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.logging.Logger;
/**
* TTL {@link JavassistTransformlet} for {@link java.util.concurrent.ForkJoinTask}.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author wuwen5 (wuwen.55 at aliyun dot com)
* @see java.util.concurrent.ForkJoinPool
* @see java.util.concurrent.ForkJoinTask
* @since 2.5.1
*/
public class TtlForkJoinTransformlet implements JavassistTransformlet {
private static final Logger logger = Logger.getLogger(TtlForkJoinTransformlet.class.getName());
private static final String FORK_JOIN_TASK_CLASS_NAME = "java.util.concurrent.ForkJoinTask";
private static final String TTL_RECURSIVE_ACTION_CLASS_NAME = "com.alibaba.ttl.TtlRecursiveAction";
private static final String TTL_RECURSIVE_TASK_CLASS_NAME = "com.alibaba.ttl.TtlRecursiveTask";
@Override
public boolean needTransform(String className) {
return FORK_JOIN_TASK_CLASS_NAME.equals(className);
}
@Override
public void doTransform(CtClass clazz) throws NotFoundException, CannotCompileException, IOException {
updateForkJoinTaskClass(clazz);
}
private void updateForkJoinTaskClass(final CtClass clazz) throws CannotCompileException, NotFoundException {
// add new field
final String className = clazz.getName();
final String capturedFieldName = "captured$field$add$by$ttl";
final CtField capturedField = CtField.make("private final java.lang.Object " + capturedFieldName + ";", clazz);
clazz.addField(capturedField, "com.alibaba.ttl.TransmittableThreadLocal.Transmitter.capture();");
logger.info("add new field " + capturedFieldName + " to class " + className);
final String doExec_methodName = "doExec";
final CtMethod doExecMethod = clazz.getDeclaredMethod(doExec_methodName);
final CtMethod new_doExecMethod = CtNewMethod.copy(doExecMethod, doExec_methodName, clazz, null);
// rename original doExec method, and set to private method(avoid reflect out renamed method unexpectedly)
final String original_doExec_method_rename = "original$doExec$method$renamed$by$ttl";
doExecMethod.setName(original_doExec_method_rename);
doExecMethod.setModifiers(doExecMethod.getModifiers() & ~Modifier.PUBLIC /* remove public */ | Modifier.PRIVATE /* add private */);
// set new doExec method implementation
final String code = "{\n" +
// do nothing/directly return, if is TTL ForkJoinTask instance
"if (this instanceof " + TTL_RECURSIVE_ACTION_CLASS_NAME + " || this instanceof " + TTL_RECURSIVE_TASK_CLASS_NAME + ") {\n" +
" return " + original_doExec_method_rename + "($$);\n" +
"}\n" +
"java.lang.Object backup = com.alibaba.ttl.TransmittableThreadLocal.Transmitter.replay(" + capturedFieldName + ");\n" +
"try {\n" +
" return " + original_doExec_method_rename + "($$);\n" +
"} finally {\n" +
" com.alibaba.ttl.TransmittableThreadLocal.Transmitter.restore(backup);\n" +
"}\n" + "}";
new_doExecMethod.setBody(code);
clazz.addMethod(new_doExecMethod);
logger.info("insert code around method " + doExecMethod + " of class " + className + ": " + code);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册