Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] 使用byte-buddy重构agent增强功能,支持重新增强已经加载过的类 #324

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README-EN.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 📌 TransmittableThreadLocal(TTL) 📌

[![Build Status](https://img.shields.io/travis/alibaba/transmittable-thread-local/master?logo=travis-ci&logoColor=white)](https://travis-ci.org/alibaba/transmittable-thread-local)
[![Windows Build Status](https://img.shields.io/appveyor/ci/oldratlee/transmittable-thread-local/master?label=windows%20build&logo=appveyor&logoColor=white)](https://ci.appveyor.com/project/oldratlee/transmittable-thread-local)
[![Coverage Status](https://img.shields.io/codecov/c/github/alibaba/transmittable-thread-local/master?logo=codecov&logoColor=white)](https://codecov.io/gh/alibaba/transmittable-thread-local/branch/master)
[![Build Status](https://img.shields.io/travis/alibaba/transmittable-thread-local/v2.13.0-Beta1?logo=travis-ci&logoColor=white)](https://travis-ci.org/alibaba/transmittable-thread-local)
[![Windows Build Status](https://img.shields.io/appveyor/ci/oldratlee/transmittable-thread-local/v2.13.0-Beta1?label=windows%20build&logo=appveyor&logoColor=white)](https://ci.appveyor.com/project/oldratlee/transmittable-thread-local)
[![Coverage Status](https://img.shields.io/codecov/c/github/alibaba/transmittable-thread-local/v2.13.0-Beta1?logo=codecov&logoColor=white)](https://codecov.io/gh/alibaba/transmittable-thread-local/branch/v2.13.0-Beta1)
[![Maintainability](https://badgen.net/codeclimate/maintainability/codeclimate/codeclimate?icon=codeclimate)](https://codeclimate.com/github/alibaba/transmittable-thread-local)
[![License](https://img.shields.io/github/license/alibaba/transmittable-thread-local?color=4EB1BA)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![Javadocs](https://img.shields.io/github/release/alibaba/transmittable-thread-local?label=javadoc&color=3d7c47&logo=microsoft-academic&logoColor=white)](https://alibaba.github.io/transmittable-thread-local/apidocs/)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 📌 TransmittableThreadLocal(TTL) 📌

[![Build Status](https://img.shields.io/travis/alibaba/transmittable-thread-local/master?logo=travis-ci&logoColor=white)](https://travis-ci.org/alibaba/transmittable-thread-local)
[![Windows Build Status](https://img.shields.io/appveyor/ci/oldratlee/transmittable-thread-local/master?label=windows%20build&logo=appveyor&logoColor=white)](https://ci.appveyor.com/project/oldratlee/transmittable-thread-local)
[![Coverage Status](https://img.shields.io/codecov/c/github/alibaba/transmittable-thread-local/master?logo=codecov&logoColor=white)](https://codecov.io/gh/alibaba/transmittable-thread-local/branch/master)
[![Build Status](https://img.shields.io/travis/alibaba/transmittable-thread-local/v2.13.0-Beta1?logo=travis-ci&logoColor=white)](https://travis-ci.org/alibaba/transmittable-thread-local)
[![Windows Build Status](https://img.shields.io/appveyor/ci/oldratlee/transmittable-thread-local/v2.13.0-Beta1?label=windows%20build&logo=appveyor&logoColor=white)](https://ci.appveyor.com/project/oldratlee/transmittable-thread-local)
[![Coverage Status](https://img.shields.io/codecov/c/github/alibaba/transmittable-thread-local/v2.13.0-Beta1?logo=codecov&logoColor=white)](https://codecov.io/gh/alibaba/transmittable-thread-local/branch/v2.13.0-Beta1)
[![Maintainability](https://badgen.net/codeclimate/maintainability/codeclimate/codeclimate?icon=codeclimate)](https://codeclimate.com/github/alibaba/transmittable-thread-local)
[![License](https://img.shields.io/github/license/alibaba/transmittable-thread-local?color=4EB1BA)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![Javadocs](https://img.shields.io/github/release/alibaba/transmittable-thread-local?label=javadoc&color=3d7c47&logo=microsoft-academic&logoColor=white)](https://alibaba.github.io/transmittable-thread-local/apidocs/)
Expand Down
20 changes: 8 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.13.0-SNAPSHOT</version>
<version>2.13.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>TransmittableThreadLocal(TTL)</name>
<description>
Expand Down Expand Up @@ -99,14 +99,10 @@
</properties>

<dependencies>
<!--
javassist v3.23 is the last version support Java 6
DO NOT upgrade javassist version!
-->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.2-GA</version>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.11.12</version>
<optional>true</optional>
</dependency>
<!--
Expand Down Expand Up @@ -298,18 +294,18 @@
<configuration>
<relocations>
<relocation>
<pattern>javassist</pattern>
<shadedPattern>com.alibaba.ttl.threadpool.agent.transformlet.javassist</shadedPattern>
<pattern>net.bytebuddy</pattern>
<shadedPattern>com.alibaba.ttl.threadpool.agent.transformlet.net.bytebuddy</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<includes>
<include>org.javassist:javassist</include>
<include>net.bytebuddy:byte-buddy</include>
</includes>
</artifactSet>
<filters>
<filter>
<artifact>org.javassist:javassist</artifact>
<artifact>net.bytebuddy:byte-buddy</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
</excludes>
Expand Down
2 changes: 1 addition & 1 deletion pom4ide.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.13.0-SNAPSHOT</version>
<version>2.13.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>TransmittableThreadLocal(TTL)</name>
<description>
Expand Down
13 changes: 4 additions & 9 deletions src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

import com.alibaba.ttl.threadpool.agent.logging.Logger;
import com.alibaba.ttl.threadpool.agent.transformlet.TtlTransformlet;
import com.alibaba.ttl.threadpool.agent.transformlet.internal.ForkJoinTtlTransformlet;
import com.alibaba.ttl.threadpool.agent.transformlet.internal.JdkExecutorTtlTransformlet;
import com.alibaba.ttl.threadpool.agent.transformlet.internal.TimerTaskTtlTransformlet;
import com.alibaba.ttl.threadpool.agent.transformlet.internal.*;
import edu.umd.cs.findbugs.annotations.NonNull;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -203,18 +200,16 @@ public static void premain(final String agentArgs, @NonNull final Instrumentatio

try {
logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);

logger.info(logTtlAgentConfig());

final List<TtlTransformlet> transformletList = new ArrayList<TtlTransformlet>();
transformletList.add(new JdkExecutorTtlTransformlet());
transformletList.add(new ForkJoinTtlTransformlet());
if (isEnableTimerTask()) transformletList.add(new TimerTaskTtlTransformlet());

final ClassFileTransformer transformer = new TtlTransformer(transformletList, isLogClassTransform());
inst.addTransformer(transformer, true);
logger.info("[TtlAgent.premain] add Transformer " + transformer.getClass().getName() + " success");

TtlTransformer transformer = new TtlTransformer(transformletList, isLogClassTransform());
transformer.transform(inst);
logger.info("[TtlAgent.premain] add Transformer success");
logger.info("[TtlAgent.premain] end");

ttlAgentLoaded = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.alibaba.ttl.threadpool.agent;

import com.alibaba.ttl.threadpool.agent.logging.Logger;
import com.alibaba.ttl.threadpool.agent.transformlet.ClassInfo;
import com.alibaba.ttl.threadpool.agent.transformlet.TtlTransformlet;
import edu.umd.cs.findbugs.annotations.NonNull;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import net.bytebuddy.agent.builder.AgentBuilder;

import java.io.BufferedReader;
import java.io.IOException;
Expand All @@ -15,8 +13,6 @@
import java.net.URL;
import java.util.*;

import static com.alibaba.ttl.threadpool.agent.transformlet.helper.TtlTransformletHelper.getLocationUrlOfClass;

/**
* @author Jerry Lee (oldratlee at gmail dot com)
* @since 2.13.0
Expand All @@ -29,20 +25,18 @@ final class TtlExtensionTransformletManager {
public TtlExtensionTransformletManager() {
}

public String extensionTransformletDoTransform(@NonNull final ClassInfo classInfo) throws NotFoundException, CannotCompileException, IOException {
final Map<String, TtlTransformlet> transformlets = classLoader2ExtensionTransformletsIncludeParentCL.get(classInfo.getClassLoader());

public String extensionTransformletDoTransform(AgentBuilder agentBuilder) throws IOException {
final Map<String, TtlTransformlet> transformlets = new HashMap<String, TtlTransformlet>();
// classLoader2ExtensionTransformletsIncludeParentCL.get(classInfo.getClassLoader());
if (transformlets == null) return null;

for (Map.Entry<String, TtlTransformlet> entry : transformlets.entrySet()) {
final String className = entry.getKey();
final TtlTransformlet transformlet = entry.getValue();

transformlet.doTransform(classInfo);
if (classInfo.isModified()) {
return className;
}
transformlet.doTransform(agentBuilder);
}

// TODO: 增加扩展的包信息
return null;
}

Expand All @@ -57,8 +51,9 @@ public String extensionTransformletDoTransform(@NonNull final ClassInfo classInf
private final WeakHashMap<ClassLoader, Map<String, TtlTransformlet>> classLoader2ExtensionTransformletsIncludeParentCL =
new WeakHashMap<ClassLoader, Map<String, TtlTransformlet>>(512);

public void collectExtensionTransformlet(@NonNull final ClassInfo classInfo) throws IOException {
final ClassLoader classLoader = classInfo.getClassLoader();
public void collectExtensionTransformlet() throws IOException {
final ClassLoader classLoader = null;
// classInfo.getClassLoader();
// classloader may null be if the bootstrap loader,
// which classloader must contains NO Ttl Agent Extension Transformlet, so just safe skip
if (classLoader == null) return;
Expand Down Expand Up @@ -169,7 +164,7 @@ static <T> Map<ClassLoader, Set<T>> loadExtensionInstances(
if (!superType.isAssignableFrom(clazz)) {
final String msg = foundMsgHead + className
+ " from classloader " + classLoader
+ " at location " + getLocationUrlOfClass(clazz)
//+ " at location " + getLocationUrlOfClass(clazz)
+ ", but NOT subtype of " + superType.getName() + ", ignored!";
logger.error(msg);
continue;
Expand All @@ -186,8 +181,7 @@ static <T> Map<ClassLoader, Set<T>> loadExtensionInstances(
set.add(superType.cast(instance));

final String msg = foundMsgHead + className
+ ", and loaded from classloader " + classLoader
+ " at location " + getLocationUrlOfClass(clazz);
+ ", and loaded from classloader " + classLoader;
logger.info(msg);
} catch (ClassNotFoundException e) {
final String msg = failLoadMsgHead + className + " from classloader " + classLoader + ", cause: " + e.toString();
Expand Down
126 changes: 78 additions & 48 deletions src/main/java/com/alibaba/ttl/threadpool/agent/TtlTransformer.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.alibaba.ttl.threadpool.agent;

import com.alibaba.ttl.threadpool.agent.logging.Logger;
import com.alibaba.ttl.threadpool.agent.transformlet.ClassInfo;
import com.alibaba.ttl.threadpool.agent.transformlet.TtlTransformlet;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;

import static com.alibaba.ttl.threadpool.agent.transformlet.helper.TtlTransformletHelper.getLocationUrlOfClass;
import static net.bytebuddy.matcher.ElementMatchers.*;
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;


/**
* TTL {@link ClassFileTransformer} of Java Agent
Expand All @@ -22,76 +28,100 @@
* @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
* @since 0.9.0
*/
public class TtlTransformer implements ClassFileTransformer {
public class TtlTransformer {
private static final Logger logger = Logger.getLogger(TtlTransformer.class);

/**
* "<code>null</code> if no transform is performed",
* see {@code @return} of {@link ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte[])}
*/
@SuppressFBWarnings({"EI_EXPOSE_REP"})
// [ERROR] com.alibaba.ttl.threadpool.agent.TtlTransformer.transform(ClassLoader, String, Class, ProtectionDomain, byte[])
// may expose internal representation by returning TtlTransformer.NO_TRANSFORM
// the value is null, so there is NO "EI_EXPOSE_REP" problem actually.
private static final byte[] NO_TRANSFORM = null;

private final TtlExtensionTransformletManager extensionTransformletManager;
private final List<TtlTransformlet> transformletList = new ArrayList<TtlTransformlet>();
private final boolean logClassTransform;
private final AgentBuilder agentBuilder;

TtlTransformer(List<? extends TtlTransformlet> transformletList, boolean logClassTransform) {
extensionTransformletManager = new TtlExtensionTransformletManager();

this.logClassTransform = logClassTransform;
for (TtlTransformlet ttlTransformlet : transformletList) {
this.transformletList.add(ttlTransformlet);
logger.info("[TtlTransformer] add Transformlet " + ttlTransformlet.getClass().getName());
}

this.agentBuilder = new AgentBuilder.Default()
.ignore(agentIgnore())
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new TransformLoggingListener());
}

/**
* info about class loader: may be <code>null</code> if the bootstrap loader.
* <p>
* more info see {@link ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])}
*/
@Override
public final byte[] transform(@Nullable final ClassLoader loader, @Nullable final String classFile, final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain, @NonNull final byte[] classFileBuffer) {
try {
// Lambda has no class file, no need to transform, just return.
if (classFile == null) return NO_TRANSFORM;

final ClassInfo classInfo = new ClassInfo(classFile, classFileBuffer, loader);
if (logClassTransform)
logger.info("[TtlTransformer] transforming " + classInfo.getClassName()
+ " from classloader " + classInfo.getClassLoader()
+ " at location " + classInfo.getLocationUrl());
private AgentBuilder.RawMatcher agentIgnore() {
return new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.")
.and(not(nameStartsWith(NamingStrategy.SuffixingRandom.BYTE_BUDDY_RENAME_PACKAGE + ".")))
.or(nameStartsWith("sun.reflect.")
.or(nameStartsWith("jdk.reflect."))
.or(nameStartsWith("org.slf4j."))
.or(nameStartsWith("org.groovy."))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
.or(nameContains(".reflectasm."))
.or(nameContains("$$FastClassByGuice$$"))
.or(nameStartsWith("sun.reflect")))
.<TypeDescription>or(isSynthetic()));
}

extensionTransformletManager.collectExtensionTransformlet(classInfo);

for (TtlTransformlet transformlet : transformletList) {
transformlet.doTransform(classInfo);
if (classInfo.isModified()) {
logger.info("[TtlTransformer] " + transformlet.getClass().getName() + " transformed " + classInfo.getClassName()
+ " from classloader " + classInfo.getClassLoader()
+ " at location " + classInfo.getLocationUrl());
return classInfo.getCtClass().toBytecode();
}
public void transform(Instrumentation instrumentation) {
try {
// Lambda has no class file, no need to transform, just return.
if (logClassTransform) {

}

final String transformlet = extensionTransformletManager.extensionTransformletDoTransform(classInfo);
if (classInfo.isModified()) {
logger.info("[TtlTransformer] " + transformlet + " transformed " + classInfo.getClassName()
+ " from classloader " + classInfo.getClassLoader()
+ " at location " + classInfo.getLocationUrl());
return classInfo.getCtClass().toBytecode();
extensionTransformletManager.collectExtensionTransformlet();
AgentBuilder agentBuilder = this.agentBuilder;
for (TtlTransformlet transformlet : transformletList) {
agentBuilder = transformlet.doTransform(agentBuilder);
}
extensionTransformletManager.extensionTransformletDoTransform(agentBuilder);
agentBuilder.installOn(instrumentation);
} catch (Throwable t) {
String msg = "[TtlTransformer] fail to transform class " + classFile + ", cause: " + t.toString();
String msg = "[TtlTransformer] fail to transform class " + ", cause: " + t.toString();
logger.error(msg, t);
throw new IllegalStateException(msg, t);
}
}

/**
* 类增强日志监听器
*/
class TransformLoggingListener implements AgentBuilder.Listener {

@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
logger.error("[TtlTransformer] fail to transform class " + typeName
+ " from classloader " + classLoader, throwable);
}

@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded,
DynamicType dynamicType) {
logger.info("[TtlTransformer] transformed " + typeDescription.getName()
+ " from classloader " + classLoader);
}

@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
}

return NO_TRANSFORM;
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
// 对每个加载的类都会匹配无论是否匹配成功,直接忽略
}

@Override
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
if (logClassTransform) {
logger.info("[TtlTransformer] transforming " + typeName
+ " from classloader " + classLoader);
}
}
}
}
Loading