Git Product home page Git Product logo

one-java-agent's Introduction

one-java-agent

JavaCI maven license Average time to resolve an issue Percentage of issues still open

目标

  1. 提供插件化支持,统一管理众多的Java Agent
  2. 插件支持install/unstall,需要插件方实现接口
  3. 支持传统的java agent,即已经开发好的java agent

插件系统

插件如果希望感知生命周期,可以实现 PluginActivator接口:

public interface PluginActivator {
    // 让插件本身判断是否要启动
    boolean enabled(PluginContext context);

    public void init(PluginContext context) throws Exception;

    /**
     * Before calling this method, the {@link PluginState} is
     * {@link PluginState#STARTING}, after calling, the {@link PluginState} is
     * {@link PluginState#ACTIVE}
     *
     * @param context
     */
    public void start(PluginContext context) throws Exception;

    /**
     * Before calling this method, the {@link PluginState} is
     * {@link PluginState#STOPPING}, after calling, the {@link PluginState} is
     * {@link PluginState#RESOLVED}
     *
     * @param context
     */
    public void stop(PluginContext context) throws Exception;
}

传统的java agent

插件目录下面放一个plugin.properties,并且放上原生的agent jar文件。

例如:

type=traditional
name=demo-agent
version=1.0.0
agentJarPath=demo-agent.jar

则 one java agent会启动这个demo-agent

配置注入

One Java Agent很方便可以注入配置到插件里。

  1. 配置到插件的plugin.properties文件里
  2. 通过-D参数配置,比如插件aaa,则可以配置为-Doneagent.plugin.aaa.key1=value1

然后可以通过PluginContext#getProperty("key1")来获取值。

迁移

  • 从一个传统的Java Agent如何迁移到one java agent的形式?
  • 如何同时支持传统的 -javaagent 方式和 one java agent形式?

比如一个传统的Agent,它会有一个包含 premain 函数的类:

public class MyAgent {

    public static void premain(String args, Instrumentation inst) {
        // do something
    }
}

把上面的Agent做同时支持非常简单,先把原来的初始化逻辑抽取为init函数,把原来的初始化逻辑移到里面:

public class MyAgent {

    public static void premain(String args, Instrumentation inst) {
        init(args, inst);
    }

    public static void init(String args, Instrumentation inst) {
        // do something
    }
}

然后按上面的文档,编写一个MyActivator,在init函数里调用原来的MyAgent.init(args, instrumentation);函数

public class MyActivator implements PluginActivator {
...
    @Override
    public void init(PluginContext context) throws Exception {
        Instrumentation instrumentation = context.getInstrumentation();
        String args = context.getProperty("args");
        MyAgent.init(args, instrumentation);
    }
...
}

在MyActivator init函数里,args可以通过配置注入一节注入,或者通过自定义的方式来获取。

这样子,Agent就可以同时支持传统方式和One Java Agent方式启动。

插件之间类共享

参考fastjson-demo-plugin,它在plugin.properties里配置了exportPackages=com.alibaba.fastjson

当其它插件想引用共享的fastjson时,需要在plugin.properties里配置:

importPackages=com.alibaba.fastjson

插件注册自定义 ClassLoaderHandler

当插件增强应用ClassLoader里加载的类时,会出现一个问题,当调用插件自己的类时,会加载不到。因此提供一个ClassLoaderHandler机制,插件方可以自行注册处理自己package下的类加载。

参考dubbo-test-plugin里:

  • /dubbo-test-plugin/src/main/java/com/test/dubbo/DubboPluginClassLoaderHandler.java
  • /dubbo-test-instrument/src/main/java/org/apache/dubbo/monitor/support/MonitorFilter.java

MonitorFilter里调用了在 plugin里加载的com.test.dubbo.RpcUtils

配置define 工具类

在增强代码之后,如果把逻辑全部写到@Instrument里:

  • 增强代码会太复杂
  • 有重复的逻辑需要重用
  • @Instrument里插入的代码缺少行号

那么可以定义一些工具类,在运行时动态 define 到应用的 ClassLoader 里。

参考: dubbo-test-instrument/src/main/resources/instrument.propertiesdefine配置。

编译开发

  • 本项目依赖 bytekit: https://github.com/alibaba/bytekit ,可能需要先mvn clean install bytekit
  • 执行测试: mvn clean package -DskipTests && mvn test
  • mvn clean package -P local -DskipTests会打包后安装最新到本地 ~/oneoneagent 目录下

one-java-agent's People

Contributors

hengyunabc avatar robberphex avatar wangji92 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

one-java-agent's Issues

记录一个 ClassLoaderHandler 类加载问题排查

在目前代码里,有一个 dubbo的示例,里面有一个 com.test.dubbo.DubboPluginClassLoaderHandler

public class DubboPluginClassLoaderHandler implements ClassLoaderHandler {

    @Override
    public Class<?> loadClass(String name) {
        if (name.startsWith("com.test.dubbo")) {
            try {
                Class<?> clazz = this.getClass().getClassLoader().loadClass(name);
                return clazz;
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}

显然它是为了保证 dubbo-test-plugin-common 下的测试类 com.test.dubbo.RpcUtils 能被其它ClassLoader正常加载。


当用户自己写了一个 dubbo 的测试,里面的代码是 com.test.dubbo.EchoService ,这里就出现问题了。

  • package 和 DubboPluginClassLoaderHandler 里的重名了,导致应用的 com.test.dubbo.EchoService 会被 dubbo-plugin PluginClassLoader 加载。

  • PluginClassLoader 目前的加载顺序,最终会向上调用 super 去查找,PluginClassLoader 的 parent 是启动 one-java-agent 的 ClassLoader 。

  • 所以最终可能 com.test.dubbo.EchoService 是被 one-java-agent 的 ClassLoader 加载到。而并不是应用的 ClassLoader。


一个异常栈:

Thread [main] (Suspended (breakpoint at line 26 in AgentSpyImpl))	
	owns: Object  (id=11559)	
	owns: OneAgentClassLoader  (id=11560)	
	owns: PluginClassLoader  (id=11561)	
	owns: Object  (id=11562)	
	AgentSpyImpl.loadClass(String) line: 26	
	Launcher$AppClassLoader(ClassLoader).loadClass(String) line: 49	
	ClassLoader.defineClass1(String, byte[], int, int, ProtectionDomain, String) line: not available [native method]	
	Launcher$AppClassLoader(ClassLoader).defineClass(String, byte[], int, int, ProtectionDomain) line: 763	
	Launcher$AppClassLoader(SecureClassLoader).defineClass(String, byte[], int, int, CodeSource) line: 142	
	Launcher$AppClassLoader(URLClassLoader).defineClass(String, Resource) line: 468	
	URLClassLoader.access$100(URLClassLoader, String, Resource) line: 74	
	URLClassLoader$1.run() line: 369	
	URLClassLoader$1.run() line: 363	
	AccessController.doPrivileged(PrivilegedExceptionAction<T>, AccessControlContext) line: not available [native method]	
	Launcher$AppClassLoader(URLClassLoader).findClass(String) line: 362	
	Launcher$AppClassLoader(ClassLoader).loadClass(String, boolean) line: 424	
	Launcher$AppClassLoader.loadClass(String, boolean) line: 349	
	OneAgentClassLoader(ClassLoader).loadClass(String, boolean) line: 411	
	PluginClassLoader(ClassLoader).loadClass(String, boolean) line: 411	
	PluginClassLoader.loadClass(String, boolean) line: 144	
	PluginClassLoader(ClassLoader).loadClass(String) line: 357	
	DubboPluginClassLoaderHandler.loadClass(String) line: 16	
	ClassLoaderHandlerManagerImpl.loadClass(String) line: 80	
	AgentSpyImpl.loadClass(String) line: 26	
	ReLaunchURLClassLoader(ClassLoader).loadClass(String) line: 49	
	Class<T>.forName0(String, boolean, ClassLoader, Class<?>) line: not available [native method]	
	Class<T>.forName(String, boolean, ClassLoader) line: 348	
	ClassUtils.forName(String, ClassLoader) line: 280	
	ClassUtils.resolveClassName(String, ClassLoader) line: 320	
	ServiceClassPostProcessor.resolveClass(BeanDefinition) line: 361	
	ServiceClassPostProcessor.resolveClass(BeanDefinitionHolder) line: 353	
	ServiceClassPostProcessor.registerServiceBean(BeanDefinitionHolder, BeanDefinitionRegistry, DubboClassPathBeanDefinitionScanner) line: 279	
	ServiceClassPostProcessor.registerServiceBeans(Set<String>, BeanDefinitionRegistry) line: 175	
	ServiceClassPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) line: 134	
	PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(Collection<BeanDefinitionRegistryPostProcessor>, BeanDefinitionRegistry) line: 275	
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List<BeanFactoryPostProcessor>) line: 125	
	AnnotationConfigApplicationContext(AbstractApplicationContext).invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) line: 706	
	AnnotationConfigApplicationContext(AbstractApplicationContext).refresh() line: 532	
	SpringApplication.refresh(ApplicationContext) line: 744	
	SpringApplication.refreshContext(ConfigurableApplicationContext) line: 391	
	SpringApplication.run(String...) line: 312	
	SpringApplication.run(Class<?>[], String[]) line: 1215	
	SpringApplication.run(Class<?>, String...) line: 1204	
	Application.main(String[]) line: 33	
	NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
	NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62	
	DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
	Method.invoke(Object, Object...) line: 498	
	LaunchRunner.run() line: 38	
	Thread.run() line: 748	

TODO

  • 仔细定义启动 one-java-agent的 ClassLoader 和 PluginClassLoader 的行为
  • 使用 ClassLoaderHandler 时要非常小心

增加 oneagent.verbose 配置

配置后 oneagent.verbose=true ,会打印trace级别的日志,打印日志到stdout

可以用 -D参数配置,也可以配置到agent args里。

在 addTransformer 时,不设置 canRetransform 为 true,会有一部分类不经过 ClassFileTransformer 处理

正常来说,一个普通的 java agent,如果是以 -javaagent: 方式启动,注册 ClassFileTransformer 之后,后续加载的类都会经过注册的 ClassFileTransformer 处理。

但实际发现,有部分类并不会被处理。只有注册时设置canRetransform为true时,才会被处理。

比如这个类:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

Instrumentation#addTransformer(ClassFileTransformer transformer, boolean canRetransform);

记录一下现象。

PluginActivator实现类强转PluginActivator遇到ClassCastException

image
如图,这行代码会抛出ClassCastException.
原因是:PluginActivator这个接口由AppClassLoader加载,activatorClass是由AppClassLoader的子类加载器PlguinClassLoader加载,强转抛出了ClassCastException.
是我的姿势不对吗,导致PluginActivator提前被AppClassLoader加载了吗?

如何识别transform 传过来的字节码,是否实现了某一接口,或者是否子类

对于ClassFileTransformer的回调函数,String className, Class<?> classBeingRedefined 都有可能是空的。因为类在第一次加载时,还没有被加载到JVM里。只能取到 byte[] classfileBuffer

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer)

但是只从byte[] classfileBuffer是没有办法判断它所有实现的接口和继承的父类的。 比如有多重继承时。

只能取到这个类本身显式 implementsextends的信息。


为了解决这个问题,有两种思路:

  1. 尝试用ClassLoader去 getResource,把每一个父类,父接口的.class读取出来,再一层层向上解析。但这个有可能不能工作。因为有些类并不一定会有 .class,并且ClassLoader也并不一定能读到对应的resource。

  2. 对于每一个传入的Class<?> classBeingRedefined 为 null 的类,都解析byte[] classfileBuffer,读取并记录它的信息。因为父类是要先于子类加载的,所以能保证在子类想要解析时,总能读出前面已保存过的父类的信息。 缺点是会占比较多内存,还有多ClassLoader情况,是否能正确工作?

catalina使用的类被增强后,无法引用到由oneagent ClassLoaderHandler加载的工具类

一开始测试注入spring的AsyncExecutionAspectSupport类时,plugin lib里的工具类AsyncTaskInfo能够在注入的代码执行时成功被加载;
然后测试注入java.util.concurrent.AbstractExecutorService类时,注入的代码执行时加载不到工具类AsyncTaskInfo了。

日志:
在ClassLoader_Instrument打印了loadClass的记录,可以看到工具类AsyncTaskInfo是被PluginClassLoader加载成功了的

 :: Spring Boot ::                (v2.6.6)

05-12 10:51:39,865  INFO [main] (StartupInfoLogger.java:55) : Starting HttpApplication using Java 1.8.0_232 on wangzp13960 with PID 33152 (E:\workspace\FinTech\MONI\dubbo.demo\dubbo.demo.alibaba.consumer\target\classes started by wangzp13 in E:\workspace\FinTech\MONI\one-java-agent-plugins)
05-12 10:51:39,891 DEBUG [main] (StartupInfoLogger.java:56) : Running with Spring Boot v2.6.6, Spring v5.3.18
05-12 10:51:39,893  INFO [main] (SpringApplication.java:640) : No active profile set, falling back to 1 default profile: "default"
ClassLoader_Instrument: AsyncExecutionAspectSupport开始被加载 -sun.misc.Launcher$AppClassLoader
ClassLoader_Instrument: AsyncExecutionAspectSupport准备被one agent classloader spy加载
ClassLoader_Instrument: AsyncExecutionAspectSupport没有被one agent classloader spy加载到
ClassLoader_Instrument: AsyncExecutionAspectSupport被ClassLoader加载到 -sun.misc.Launcher$AppClassLoader
ClassLoader_Instrument: AsyncTaskInfo开始被加载 -sun.misc.Launcher$AppClassLoader
ClassLoader_Instrument: AsyncTaskInfo准备被one agent classloader spy加载
ClassLoader_Instrument: AsyncTaskInfo开始被加载 -com.alibaba.oneagent.plugin.classloader.PluginClassLoader
ClassLoader_Instrument: AsyncTaskInfo被ClassLoader加载到 -com.alibaba.oneagent.plugin.classloader.PluginClassLoader
ClassLoader_Instrument: AsyncTaskInfo被one agent classloader spy加载到
ClassLoader_Instrument: AbstractExecutorService开始被加载 - sun.misc.Launcher$AppClassLoader
ClassLoader_Instrument: AbstractExecutorService准备被one agent classloader spy加载
ClassLoader_Instrument: AbstractExecutorService没有被one agent classloader spy加载到
ClassLoader_Instrument: AbstractExecutorService被ClassLoader加载到 - sun.misc.Launcher$AppClassLoader
ThreadPoolExecutorInstrument:开始检查classloader能否加载到AsyncTaskInfo - sun.misc.Launcher$AppClassLoader
ThreadPoolExecutorInstrument:classloader加载不到AsyncTaskInfo - sun.misc.Launcher$AppClassLoader
ThreadPoolExecutorInstrument:开始检查classloader能否加载到AsyncTaskInfo - sun.misc.Launcher$AppClassLoader
ThreadPoolExecutorInstrument:classloader加载不到AsyncTaskInfo - sun.misc.Launcher$AppClassLoader
java.lang.ClassNotFoundException: com/xxx/monitor/oneagent/common/async/AsyncTaskInfo
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:19)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919)
	at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:927)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486)
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123)
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104)
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:478)
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
	at com.xxx.jr.dubbo.demo.alibaba.consumer.HttpApplication.main(HttpApplication.java:43)
java.lang.ClassNotFoundException: com/xxx/monitor/oneagent/common/async/AsyncTaskInfo
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:19)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919)
	at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919)
	at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:927)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486)
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123)
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104)
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:478)
	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
	at com.xxx.dubbo.demo.alibaba.consumer.HttpApplication.main(HttpApplication.java:43)

插件之间service共享机制

有两种思路:

假定 one java agent本身启动好后,叫 container。

简单的Service机制

  1. 提供最简单的方案,每个插件自己启动后,向 container 注册某些类,或者注册一个handler,用来判断某个类是否应该从我这里加载。 然后插件方在使用时,要先 getService(serviceClassName),得到一个对象,然后再强转,得到真正的 serviceImpl,然后再调用。

这种方式很可能工作不了。另外,插件本身编程时也可能会有问题。

完整的类隔离方案,类似 pandora/osgi

pandora的方式

pandora的方式有一些缺点。

pandora是把所有的插件的导出类放到一个 cache map里。 这个实现导致类加载可能会提前,也会加载不必要的类。 可以改用lazy init方式加载类。但这个方案本身终究不是很合理。

pandora不支持导出 resources 。支持这个会导致加载效率不可避免的降低。 或者可以做一些限定。比如只允许导出固定名称的resource,或者只允许导出固定 prefix 的resource。 这样子在 getResources 先做一个前缀判断??

轻量级osgi的方式

完整的osgi太复杂了,要做一个剪裁。但剪裁的度在哪里,不好把握。


提供类共享机制,会有一个比较大的问题: 插件不能随意unistall了。因为 class 对象会被其它的插件,或者container本身持有。

是否对于类共享的插件,不再支持 uninstall ?

tomcat下使用agent无法加载类

premain、main默认是appclassloader,但是tomcat加载catalina是urlclassloader,由于是双亲委托机制,父类appclassloader不能访问子类urlclassloader,所以会报错not found
怎么解决

增强com/mysql/jdbc/StatementImpl,报错:JSR/RET are not supported with computeFrames option

jdbc driver版本:

     <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
            <scope>provided</scope>
        </dependency>

代码:

import com.alibaba.bytekit.agent.inst.Instrument;
import com.alibaba.bytekit.agent.inst.InstrumentApi;


import java.sql.ResultSet;
import java.sql.SQLException;

@Instrument(Class = "com.mysql.jdbc.StatementImpl")
public abstract class ConnectionImplInstrument {

    public ResultSet executeQuery(String sql) throws SQLException {
        try {
            System.out.println(" sql is : " + sql);
            ResultSet result = InstrumentApi.invokeOrigin();
            return result;
        } catch (Throwable e) {
            throw e;
        }

    }

}

报错内容如下

ERROR i.o.s.OneAgentClassFileTransformer -transform error, loader: sun.misc.Launcher$AppClassLoader@18b4aac2, className: com/mysql/jdbc/StatementImpl, transformer: com.alibaba.bytekit.asm.instrument.InstrumentTransformer@49de1505
java.lang.IllegalArgumentException: JSR/RET are not supported with computeFrames option
	at com.alibaba.deps.org.objectweb.asm.Frame.execute(Frame.java:1018)
	at com.alibaba.deps.org.objectweb.asm.MethodWriter.visitJumpInsn(MethodWriter.java:1146)
	at com.alibaba.deps.org.objectweb.asm.tree.JumpInsnNode.accept(JumpInsnNode.java:79)
	at com.alibaba.deps.org.objectweb.asm.tree.InsnList.accept(InsnList.java:144)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:749)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:647)
	at com.alibaba.deps.org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:468)
	at com.alibaba.bytekit.utils.AsmUtils.toBytes(AsmUtils.java:80)
	at com.alibaba.bytekit.asm.instrument.InstrumentTransformer.transform(InstrumentTransformer.java:121)
	at io.oneagent.service.OneAgentClassFileTransformer.transform(OneAgentClassFileTransformer.java:44)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker.<init>(MySqlValidConnectionChecker.java:56)
	at com.alibaba.druid.pool.DruidDataSource.initValidConnectionChecker(DruidDataSource.java:1158)
	at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:826)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1758)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:467)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1342)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1249)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
	at com.zhuanzhuan.zzcloud.ApplicationMain.main(ApplicationMain.java:33)

arthas有相同问题:alibaba/arthas#1304

期待解决

@robberphex @alibaba-oss @WangJi92 @hengyunabc

当多线程并发执行appendToClassLoaderSearch0时,会抛出异常

当前one-java-agent会并发加载plugin,其中有可能并发执行InstrumentationImpl.appendToSystemClassLoaderSearch,有概率抛出IllegalArgumentExceptionjava.lang.InternalError

附上报错信息:

2022-06-16 09:51:09 [oneagent plugin ahas-java-agent start] ERROR c.a.o.plugin.PluginManagerImpl -start plugin error, name: ahas-java-agent
com.alibaba.oneagent.plugin.PluginException: start error, agent jar::/home/admin/.opt/ArmsAgent/plugins/ahas-java-agent/ahas-java-agent.jar
  at com.alibaba.oneagent.plugin.TraditionalPlugin.start(TraditionalPlugin.java:113)
  at com.alibaba.oneagent.plugin.PluginManagerImpl.startOnePlugin(PluginManagerImpl.java:294)
  at com.alibaba.oneagent.plugin.PluginManagerImpl.access$200(PluginManagerImpl.java:22)
  at com.alibaba.oneagent.plugin.PluginManagerImpl$2.run(PluginManagerImpl.java:325)
  at java.lang.Thread.run(Thread.java:855)
Caused by: java.lang.IllegalArgumentException: null
  at sun.instrument.InstrumentationImpl.appendToClassLoaderSearch0(Native Method)
  at sun.instrument.InstrumentationImpl.appendToSystemClassLoaderSearch(InstrumentationImpl.java:200)
  at com.alibaba.oneagent.plugin.TraditionalPlugin.start(TraditionalPlugin.java:100)
  ... 4 common frames omitted
2022-06-16 09:51:09 [oneagent plugin arms-agent start] ERROR c.a.o.plugin.PluginManagerImpl -start plugin error, name: arms-agent
com.alibaba.oneagent.plugin.PluginException: start error, agent jar::/home/admin/.opt/ArmsAgent/plugins/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar
  at com.alibaba.oneagent.plugin.TraditionalPlugin.start(TraditionalPlugin.java:113)
  at com.alibaba.oneagent.plugin.PluginManagerImpl.startOnePlugin(PluginManagerImpl.java:294)
  at com.alibaba.oneagent.plugin.PluginManagerImpl.access$200(PluginManagerImpl.java:22)
  at com.alibaba.oneagent.plugin.PluginManagerImpl$2.run(PluginManagerImpl.java:325)
  at java.lang.Thread.run(Thread.java:855)
Caused by: java.lang.IllegalArgumentException: null
  at sun.instrument.InstrumentationImpl.appendToClassLoaderSearch0(Native Method)
  at sun.instrument.InstrumentationImpl.appendToSystemClassLoaderSearch(InstrumentationImpl.java:200)
  at com.alibaba.oneagent.plugin.TraditionalPlugin.start(TraditionalPlugin.java:100)
  ... 4 common frames omitted

SpringBoot fatjar模式下,Instrument方法中出现强转逻辑导致asm报 java.lang.TypeNotPresentException: Type com/one/agent/app/api/model/EmployeeAndy not present

异常栈:

2023-03-14 10:32:39 [main] ERROR c.a.o.s.OneAgentClassFileTransformer -transform error, loader: org.springframework.boot.loader.LaunchedURLClassLoader@6a03bcb1, className: com/saleson/oneagent/springboot/app/demo/service/EmployeeServiceImpl, transformer: com.alibaba.bytekit.asm.instrument.InstrumentTransformer@293d0107
java.lang.TypeNotPresentException: Type com/one/agent/app/api/model/EmployeeAndy not present
	at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1041)
	at com.alibaba.deps.org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1202)
	at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1299)
	at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1197)
	at com.alibaba.deps.org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1611)
	at com.alibaba.deps.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1547)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:767)
	at com.alibaba.deps.org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:647)
	at com.alibaba.deps.org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:451)
	at com.alibaba.bytekit.utils.AsmUtils.toBytes(AsmUtils.java:86)
	at com.alibaba.bytekit.asm.instrument.InstrumentTransformer.transform(InstrumentTransformer.java:60)
	at com.alibaba.oneagent.service.OneAgentClassFileTransformer.transform(OneAgentClassFileTransformer.java:35)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at org.springframework.util.ClassUtils.forName(ClassUtils.java:284)
	at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:469)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1621)
	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1548)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:704)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:674)
	at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1684)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:570)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542)
	at org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition.checkServletRegistration(DispatcherServletAutoConfiguration.java:187)
	at org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition.getMatchOutcome(DispatcherServletAutoConfiguration.java:167)
	at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
	at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:225)
	at org.springframework.context.annotation.ConfigurationClassParser.processMemberClasses(ConfigurationClassParser.java:371)
	at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:271)
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:249)
	at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:599)
	at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:110)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809)
	at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780)
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:192)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:331)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:423)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
	at com.saleson.oneagent.springboot.app.demo.AppDemoApplication.main(AppDemoApplication.java:21)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.ClassNotFoundException: com.one.agent.app.api.model.EmployeeAndy
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:348)
	at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1039)
	... 72 common frames omitted

复现逻辑:

  1. 在common module中新建2个类,Employee, 以及其子类EmployeeAndy。
  2. 新建springboot module, 并添加spring-boot-maven-plugin 将其编译为springboot fatjar。
  3. 在instrument-lib.jar中添加Instrument类,并在增强方法中加入强转代码:
@Instrument(Interface = {"com.one.agent.app.api.EmployeeService"})
public class EmployeeServiceInst {

    public void check(Employee employee) {
        if(employee instanceof EmployeeAndy){
            employee = (EmployeeAndy) employee;
            System.out.println("EmployeeServiceInst.check employee type is " + EmployeeAndy.class);
        }
        
        InstrumentApi.invokeOrigin();

    }
  1. 添加one-java-agent参数运行springboot fatjar, 在oneagent.log中出现上述异常日志。

原因:

反复排查后,发现asm在计算方法的最大栈空间和最大本地变量数时,遇到强转逻辑会执行到ClassWriter.getCommonSuperClass()方法,该方法会去尝试加载被转换对象的class和转换的目标class,进行assignable from比对判断。问题在于此方法中的ClassLoader是Launcher$AppClassLoader, debug截图如下:
image

在springboot fatjar 运行模式下, Launcher$AppClassLoader是无法加载到springboot fatjar中的class和资源,所以导致此处抛出ClassNotFoundException, 被捕获后包装成TypeNotPresentException抛出。

demo-all.zip

关于插件打包

看了源码,我是这样打包的:
image
感觉有点粗暴,这样虽然能跑起来,但总感觉怪怪的,有什么更好的姿势吗

springboot fat jar启动时,PluginClassLoader的parent不是LaunchedURLClassLoader

用插件自定义ClassLoaderHandler来指定了插件自己的工具类包名后,发现工具类是被PluginClassLoader加载的,而PluginClassLoader的parent是sun.misc.Launcher$AppClassLoader;
springboot fat jar是用org.springframework.boot.loader.LaunchedURLClassLoader来加载应用类,LaunchedURLClassLoader的parent也是sun.misc.Launcher$AppClassLoader。

PluginClassLoader的parent不是LaunchedURLClassLoader,于是工具类的代码只能调用jdk的类,slf4j、spring的类都不能调用,一调用就报ClassNotFound。

One Java Agent本身是否支持重新加载

One Java Agent本身是一个Java Agent,它要支持两种Java Agent的工作方式:

  • premain
  • agentmain

但如果按普通的方式实现,java agent jar会被加到 SystemClassLoader里,那么这个Agent本身是没办法实现更新的。

如果要支持动态更新,那么就要像arthas那样,再包装一层,引用一个自定义的ClassLoader,再来启动。

但这样子会带来复杂性:

  1. Agent wrapper这一层代码,它本身是要有日志输出到哪/怎么加载配置项等问题的
  2. Agent core这一层,它能否加载到SystemClassLoader的类?
  3. plugin 的ClassLoader,它的parent是否应该是 Agent core的ClassLoader?
  4. plugin 里的代码,能否加载到 SystemClassLoader里的类?

考虑到复杂性,是否简单起见,One Java Agent本身即一个传统的Java Agent,它自身不考虑可以更新?

解决agent的API类在自定义的ClassLoader里加载的问题

  • 默认情况下,加载java agent的是SystemClassLoader,那么应用自己定义的ClassLoader,如果parent不是 SystemClassLoader,就加载不到java agent里的类。

比如,有一个类Foo,它有一个函数bar:

public class Foo {
	public void bar() {

	}
}

有一个Java Agent想做一些类增强,会插入一些代码:

public class Foo {
	public void bar() {
		SampleAgent.log("here");
	}
}
  • 这就要求加载Foo的ClassLoader必须要能加载到SystemClassLoader里的SampleAgent类。

明确 one-java-agent 的加载方式

  • 最普通常见的,是 -javaagent:/xxx/one-java-agent.jar ,这时 one-java-agent 是在SystemClassLoader 里

  • 动态加载

动态加载可能有两种方式:

  • 把 one-java-agent.jar append 到 SystemClassLoader 里,这样子和 -javaagent的方式差不多

  • 新建一个 ClassLoader 来加载 one-java-agent.jar ,这样子可以动态升级。

    • 缺点比较多,并且编程时会遇到 one-java-agent.jar 的 ClassLoader 不一样的问题。要同时支持SystemClassLoader 和 非 SystemClassLoader
    • 需求到底有多强?

暂时先保留动态加载,并且 新建ClassLoader 来加载 one-java-agent.jar 的支持。后续遇到解决不了的问题,再考虑去掉。

匹配 interface的机制会提前加载类,导致 ClassFileTransformer#transform 丢失部分类

重现方式

写一个匹配interface的@Instrument,比如

@Instrument(Interface = "org.apache.dubbo.rpc.Invoker")
public abstract class Invoker {

    /**
     * invoke.
     *
     * @param invocation
     * @return result
     * @throws RpcException
     */
    public Result invoke(Invocation invocation) throws RpcException {
        DubboUtils.test(invocation);
        System.err.println("invoker class: " + this.getClass().getName());
        Result result = InstrumentApi.invokeOrigin();
        System.err.println("result:" + result + ", invoker class: " + this.getClass().getName());
        return result;
    }

}

再写一个匹配abstract类的匹配,比如:

@Instrument(Class = "org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory")
public class AbstractAutowireCapableBeanFactory {

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        return InstrumentApi.invokeOrigin();
    }

那么在加载类 org.springframework.beans.factory.support.DefaultListableBeanFactory 时,会经过Invoker的判断

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory

@Instrument(Interface = "org.apache.dubbo.rpc.Invoker") 的匹配判断比较复杂,因为 interface 可能有多重继承

  • 目前的实现比较简单,直接调用loader.loadClass来加载 interface了。
  • 导致结果是在 ClassFileTransformer#transform 处理 DefaultListableBeanFactory 时,又加载了 AbstractAutowireCapableBeanFactory ,所以后面 AbstractAutowireCapableBeanFactory 不会被 ClassFileTransformer#transform 处理了!
  • 这里 jdk处理的逻辑不符合用户的想像
public class SimpleInterfaceMatcher implements ClassMatcher {

    @Override
    public boolean match(ClassLoader loader, String className, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        loader = ClassLoaderUtils.wrap(loader);

        if (classBeingRedefined != null) { // 在retransform 时,可以直接判断
            return match(classBeingRedefined);
        } else {
            // 读取出具体的 类名,还有父类名, 如果有匹配,则返回 true,没有,就返回 false
            ClassReader reader = new ClassReader(classfileBuffer);
            String clazzName = reader.getClassName();
            String superName = reader.getSuperName();
            String[] interfacesArray = reader.getInterfaces();

            // 如果是接口,则没有需要处理的地方
            if ((reader.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
                return false;
            }

            if (interfaces != null && interfaces.contains(clazzName.replace('/', '.'))) {
                return true;
            }

            for (String i : interfacesArray) {
                try {
                    Class<?> interfaceClass = loader.loadClass(i.replace('/', '.'));
                    if (matchInterface(interfaceClass)) {
                        return true;
                    }
                } catch (ClassNotFoundException e) {
                    // ignore
                }
            }

后续

  • 需要重新实现 interface匹配的实现,避免加载类

增强 java.lang.ClassLoader ,允许加载插件注册的类

在插件增强目标类的字节码之后,通常会插入 插件自身的代码,那么要解决这些插入的代码的加载问题。

常见的办法有几种:

  • 把插件的jar,强行加到ClassLoader的 urls里,像pinpoint就这样子的
  • 把插件的jar,加到 system classloader的 urls,或者 bootstrap classloader的 urls

还有一种办法是增强 java.lang.ClassLoader ,在 loadClass 函数里,查找到插件的类,直接返回。

同时可能还要修改 checkPackageAccess 函数。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.