support arthas.enhanceLoaders config/add jboss ModuleClassLoader testcase. #1596

pull/1604/head
hengyunabc 4 years ago
parent 9c847d2dd5
commit bc70853b0c

@ -235,6 +235,14 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.modules</groupId>
<artifactId>jboss-modules</artifactId>
<version>1.11.0.Final</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.benf</groupId>
<artifactId>cfr</artifactId>

@ -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

@ -28,6 +28,11 @@ public class Configure {
private String tunnelServer;
private String agentId;
/**
* ClassLoader ,
*/
private String enhanceLoaders;
/**
* <pre>
* 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;
}
/**
*
*

@ -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<String> loaders = new HashSet<String>();
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<String, String> argsMap) throws IOException {
if (arthasEnvironment == null) {
arthasEnvironment = new ArthasEnvironment();

@ -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<String> 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);
}
}
}
}
}

@ -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);
}
}
Loading…
Cancel
Save