org.benf
cfr
diff --git a/core/src/main/java/arthas.properties b/core/src/main/java/arthas.properties
index aab9953fe..99d9f256a 100644
--- a/core/src/main/java/arthas.properties
+++ b/core/src/main/java/arthas.properties
@@ -6,6 +6,8 @@ arthas.ip=127.0.0.1
# seconds
arthas.sessionTimeout=1800
+arthas.enhanceLoaders=java.lang.ClassLoader
+
#arthas.appName=demoapp
#arthas.tunnelServer=ws://127.0.0.1:7777/ws
#arthas.agentId=mmmmmmyiddddd
diff --git a/core/src/main/java/com/taobao/arthas/core/config/Configure.java b/core/src/main/java/com/taobao/arthas/core/config/Configure.java
index c0982b01b..1ebe0db8b 100644
--- a/core/src/main/java/com/taobao/arthas/core/config/Configure.java
+++ b/core/src/main/java/com/taobao/arthas/core/config/Configure.java
@@ -28,6 +28,11 @@ public class Configure {
private String tunnelServer;
private String agentId;
+ /**
+ * 需要被增强的ClassLoader的全类名,多个用英文 , 分隔
+ */
+ private String enhanceLoaders;
+
/**
*
* 1. 如果显式传入 arthas.agentId ,则直接使用
@@ -135,6 +140,14 @@ public class Configure {
this.appName = appName;
}
+ public String getEnhanceLoaders() {
+ return enhanceLoaders;
+ }
+
+ public void setEnhanceLoaders(String enhanceLoaders) {
+ this.enhanceLoaders = enhanceLoaders;
+ }
+
/**
* 序列化成字符串
*
diff --git a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
index ef6e77de1..ab758a121 100644
--- a/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
+++ b/core/src/main/java/com/taobao/arthas/core/server/ArthasBootstrap.java
@@ -9,10 +9,12 @@ import java.lang.reflect.Method;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
+import java.util.Set;
import java.util.Timer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -25,9 +27,12 @@ import com.alibaba.arthas.deps.ch.qos.logback.classic.LoggerContext;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.arthas.tunnel.client.TunnelClient;
+import com.alibaba.bytekit.asm.instrument.InstrumentConfig;
import com.alibaba.bytekit.asm.instrument.InstrumentParseResult;
import com.alibaba.bytekit.asm.instrument.InstrumentTemplate;
import com.alibaba.bytekit.asm.instrument.InstrumentTransformer;
+import com.alibaba.bytekit.asm.matcher.SimpleClassMatcher;
+import com.alibaba.bytekit.utils.AsmUtils;
import com.alibaba.bytekit.utils.IOUtils;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.ArthasConstants;
@@ -59,7 +64,9 @@ import com.taobao.arthas.core.shell.term.impl.http.api.HttpApiHandler;
import com.taobao.arthas.core.shell.term.impl.httptelnet.HttpTelnetTermServer;
import com.taobao.arthas.core.util.ArthasBanner;
import com.taobao.arthas.core.util.FileUtils;
+import com.taobao.arthas.core.util.InstrumentationUtils;
import com.taobao.arthas.core.util.LogUtil;
+import com.taobao.arthas.core.util.StringUtils;
import com.taobao.arthas.core.util.UserStatUtil;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
import com.taobao.arthas.core.util.matcher.WildcardMatcher;
@@ -126,10 +133,12 @@ public class ArthasBootstrap {
// 3. init logger
loggerContext = LogUtil.initLooger(arthasEnvironment);
- // 4. init beans
+ // 4. 增强ClassLoader
+ enhanceClassLoader();
+ // 5. init beans
initBeans();
- // 5. start agent server
+ // 6. start agent server
bind(configure);
executorService = Executors.newScheduledThreadPool(1, new ThreadFactory() {
@@ -182,19 +191,38 @@ public class ArthasBootstrap {
throw new IllegalStateException("can not find " + ARTHAS_SPY_JAR);
}
}
+ }
+
+ void enhanceClassLoader() throws IOException, UnmodifiableClassException {
+ if (configure.getEnhanceLoaders() == null) {
+ return;
+ }
+ Set loaders = new HashSet();
+ for (String s : configure.getEnhanceLoaders().split(",")) {
+ loaders.add(s.trim());
+ }
// 增强 ClassLoader#loadClsss ,解决一些ClassLoader加载不到 SpyAPI的问题
// https://github.com/alibaba/arthas/issues/1596
- InstrumentTemplate template = new InstrumentTemplate();
byte[] classBytes = IOUtils.getBytes(ArthasBootstrap.class.getClassLoader()
.getResourceAsStream(ClassLoader_Instrument.class.getName().replace('.', '/') + ".class"));
- template.addInstrumentClass(classBytes);
- InstrumentParseResult instrumentParseResult = template.build();
+ SimpleClassMatcher matcher = new SimpleClassMatcher(loaders);
+ InstrumentConfig instrumentConfig = new InstrumentConfig(AsmUtils.toClassNode(classBytes), matcher);
+
+ InstrumentParseResult instrumentParseResult = new InstrumentParseResult();
+ instrumentParseResult.addInstrumentConfig(instrumentConfig);
classLoaderInstrumentTransformer = new InstrumentTransformer(instrumentParseResult);
- instrumentation.addTransformer(classLoaderInstrumentTransformer);
- }
+ instrumentation.addTransformer(classLoaderInstrumentTransformer, true);
+ if (loaders.size() == 1 && loaders.contains(ClassLoader.class.getName())) {
+ // 如果只增强 java.lang.ClassLoader,可以减少查找过程
+ instrumentation.retransformClasses(ClassLoader.class);
+ } else {
+ InstrumentationUtils.trigerRetransformClasses(instrumentation, loaders);
+ }
+ }
+
private void initArthasEnvironment(Map argsMap) throws IOException {
if (arthasEnvironment == null) {
arthasEnvironment = new ArthasEnvironment();
diff --git a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
index 51b781a1f..3bcaa1c09 100644
--- a/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
+++ b/core/src/main/java/com/taobao/arthas/core/util/InstrumentationUtils.java
@@ -2,6 +2,7 @@ package com.taobao.arthas.core.util;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
+import java.util.Collection;
import java.util.Set;
import com.alibaba.arthas.deps.org.slf4j.Logger;
@@ -38,4 +39,17 @@ public class InstrumentationUtils {
inst.removeTransformer(transformer);
}
}
+
+ public static void trigerRetransformClasses(Instrumentation inst, Collection classes) {
+ for (Class> clazz : inst.getAllLoadedClasses()) {
+ if (classes.contains(clazz.getName())) {
+ try {
+ inst.retransformClasses(clazz);
+ } catch (Throwable e) {
+ String errorMsg = "retransformClasses class error, name: " + clazz.getName();
+ logger.error(errorMsg, e);
+ }
+ }
+ }
+ }
}
diff --git a/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
new file mode 100644
index 000000000..00a1177e5
--- /dev/null
+++ b/core/src/test/java/com/taobao/arthas/core/server/ArthasBootstrapTest.java
@@ -0,0 +1,55 @@
+package com.taobao.arthas.core.server;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.lang.instrument.Instrumentation;
+
+import org.jboss.modules.ModuleClassLoader;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+import com.taobao.arthas.core.bytecode.TestHelper;
+import com.taobao.arthas.core.config.Configure;
+
+import net.bytebuddy.agent.ByteBuddyAgent;
+
+/**
+ *
+ * @author hengyunabc 2020-12-02
+ *
+ */
+public class ArthasBootstrapTest {
+ @Test
+ public void test() throws Exception {
+ Instrumentation instrumentation = ByteBuddyAgent.install();
+ TestHelper.appendSpyJar(instrumentation);
+
+ ArthasBootstrap arthasBootstrap = Mockito.mock(ArthasBootstrap.class);
+ Mockito.doCallRealMethod().when(arthasBootstrap).enhanceClassLoader();
+
+ Configure configure = Mockito.mock(Configure.class);
+ Mockito.when(configure.getEnhanceLoaders())
+ .thenReturn("java.lang.ClassLoader,org.jboss.modules.ConcurrentClassLoader");
+ FieldSetter.setField(arthasBootstrap, ArthasBootstrap.class.getDeclaredField("configure"), configure);
+ FieldSetter.setField(arthasBootstrap, ArthasBootstrap.class.getDeclaredField("instrumentation"),
+ instrumentation);
+
+ org.jboss.modules.ModuleClassLoader moduleClassLoader = Mockito.mock(ModuleClassLoader.class);
+
+ boolean flag = false;
+ try {
+ moduleClassLoader.loadClass("java.arthas.SpyAPI");
+ } catch (Exception e) {
+ flag = true;
+ }
+ assertThat(flag).isTrue();
+
+ arthasBootstrap.enhanceClassLoader();
+
+ Class> loadClass = moduleClassLoader.loadClass("java.arthas.SpyAPI");
+
+ System.err.println(loadClass);
+
+ }
+}