Giter Club home page Giter Club logo

bytekit's People

Contributors

cc11001100 avatar hengyunabc avatar isadliliying avatar kylixs avatar raymondlam1 avatar songzhibin97 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

bytekit's Issues

有解决办法吗?当使用@Binding.InvokeArgs Object[] args时抛异常,急需使用,感谢求解~~

java.lang.IllegalArgumentException: Error at instruction 21: Expected an object reference, but found . test()V
00000 R . . . : : L0
00001 R . . . : : LINENUMBER 23 L0
00002 R . . . : : NEW com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample
00003 R . . . : R : DUP
00004 R . . . : R R : INVOKESPECIAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample. ()V
00005 R . . . : R : ASTORE 1
00006 R R . . : : L1
00007 R R . . : : LINENUMBER 24 L1
00008 R R . . : : ICONST_0
00009 R R . . : I : ISTORE 2
00010 R R I . : : L2
00011 R R I . : : FRAME APPEND [com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample I]
00012 R R I . : : ILOAD 2
00013 R R I . : I : ICONST_1
00014 R R I . : I I : IF_ICMPGE L3
00015 R R I . : : L4
00016 R R I . : : LINENUMBER 25 L4
00017 R R I . : : ALOAD 1
00018 R R I . : R : LDC "hello2"
00019 R R I . : R R : ILOAD 2
00020 R R I . : R R I : LDC "hello"
00021 R R I . : R R I R : ALOAD 3
00022 ? : INVOKESTATIC com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$TestAccessInterceptor.onInvoke (Ljava/lang/String;[Ljava/lang/Object;)V
00023 ? : INVOKEVIRTUAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample.hello (Ljava/lang/String;I)Ljava/lang/String;
00024 ? : POP
00025 ? : L5
00026 ? : LINENUMBER 24 L5
00027 ? : IINC 2 1
00028 ? : GOTO L2
00029 R R I . : : L3
00030 R R I . : : LINENUMBER 27 L3
00031 R R I . : : FRAME SAME
00032 R R I . : : RETURN
00033 ? : L6

org.springframework.web.context.ContextLoaderListener enhance failed

Problem

We fail to use one java agent to enhance org.springframework.web.context.ContextLoaderListener in Spring MVC and exception stack is here:

java.lang.ClassFormatError: Class file version does not support constant tag 16 in class file org/springframework/web/context/ContextLoaderListener
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:3296)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:1455)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1959)
	at com.taobao.tomcat.container.context.loader.AliWebappClassLoaderBase.loadClass(AliWebappClassLoaderBase.java:114)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1833)
	at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:536)
	at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:518)
	at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:148)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5138)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5745)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:1016)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:992)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:639)
	at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:2015)
	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.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:303)
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
	at org.apache.catalina.mbeans.MBeanFactory.createContext(MBeanFactory.java:805)
	at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:586)
	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.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:303)
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
	at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
	at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
	at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
	at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408)
	at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
	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 sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
	at sun.rmi.transport.Transport$1.run(Transport.java:200)
	at sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Analysis

Referenced from:

As you can see, tag 16 is CONSTANT_MethodType and according to:

* The operand of each ldc instruction and each ldc_w instruction must be a valid index into the constant_pool table. The constant pool entry referenced by that index must be of type:
  - CONSTANT_Integer, CONSTANT_Float, or CONSTANT_String if the class file version number is less than 49.0.
  - CONSTANT_Integer, CONSTANT_Float, CONSTANT_String, or CONSTANT_Class if the class file version number is 49.0 or 50.0.
  - CONSTANT_Integer, CONSTANT_Float, CONSTANT_String, CONSTANT_Class, CONSTANT_MethodType, or CONSTANT_MethodHandle if the class file version number is 51.0.

We know that CONSTANT_MethodType is only supported if the bytecode version is 51 which means jdk7

Therefore, it may be that the original bytecode was compiled and generated by jdk 6, but our enhanced bytecode uses the features of jdk 7. We need change the version of the generated bytecode to a higher one referring to alibaba/arthas#1223

ReflectionUtils中的doWithMethods()方法处理逻辑不那么优雅..

背景:

  1. 我们是个代码洁癖爱好者
    2.该方法既然是个通用的工具方法,那么当我们的clazz是个接口时,此时实现逻辑会有那么一点尴尬(同一个方法会重复callback)~~

//步骤1.getDeclaredMethods(clazz)方法功能其实是已经去clazz继承的接口中拿了一下default以及static类型的方法。
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
XXX
}if (clazz.getSuperclass() != null) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}
//此时由于clazz是个接口,会走到这里
else if (clazz.isInterface()) {
//此时又去拿clazz接口继承的接口了,然后递归doWithMethods()时一定又会拿到上面"步骤1中继承的接口中已经拿到过的default和static方法",也就是说重复callback了
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}

AtSyncEnter,AtSyncExit命令捕捉MONITORENTRY阻塞耗时的问题

现在的AtSyncEnter注解是在MONITORENTRY之前进行带AtSyncEnter注解的Interceptor方法调用
AtSyncExit则是在MONITOREXIT之前调用对应Interceptor方法
但是遇到
synchronized(monitor) {
//io-bound or cpu-bound method spend long time
doSth();
}
在这种情况下,想单独知道进入临界区耗费的时间,通过AtSyncEnter,AtSyncExit的组合无法做到
是否可以加入一个AtSyncEntered注解的拦截点,放在MONITORENTRY命令之后,从而在不改变原先设计的基础上,配合
AtSyncEnter实现记录MONITORENTRY命令阻塞耗时的功能?

最后大致变成这样
INVOKESTATIC AtSyncEnterInterceptor
synchronized(monitor) {
INVOKESTATIC AtSyncEnteredInterceptor
//io-bound or cpu-bound method spend long time
doSth();
INVOKESTATIC AtSyncExitInterceptor
}

如果这个设计可行,我这边尝试提交pr是现在这个feature

com.alibaba.bytekit.asm.interceptor.AtInvokeTest2执行失败

java.lang.IllegalArgumentException: Error at instruction 28: Expected an object reference, but found . test()V
00000 R . . . : : L0
00001 R . . . : : LINENUMBER 22 L0
00002 R . . . : : NEW com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample
00003 R . . . : R : DUP
00004 R . . . : R R : INVOKESPECIAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample. ()V
00005 R . . . : R : ASTORE 1
00006 R R . . : : L1
00007 R R . . : : LINENUMBER 24 L1
00008 R R . . : : ICONST_0
00009 R R . . : I : ISTORE 2
00010 R R I . : : L2
00011 R R I . : : FRAME APPEND [com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample I]
00012 R R I . : : ILOAD 2
00013 R R I . : I : BIPUSH 10
00014 R R I . : I I : IF_ICMPGE L3
00015 R R I . : : L4
00016 R R I . : : LINENUMBER 25 L4
00017 R R I . : : GETSTATIC java/util/concurrent/TimeUnit.SECONDS : Ljava/util/concurrent/TimeUnit;
00018 R R I . : R : LCONST_1
00019 R R I . : R J : INVOKEVIRTUAL java/util/concurrent/TimeUnit.sleep (J)V
00020 R R I . : : L5
00021 R R I . : : LINENUMBER 27 L5
00022 R R I . : : ALOAD 1
00023 R R I . : R : LDC "hello"
00024 R R I . : R R : ILOAD 2
00025 R R I . : R R I : ALOAD 0
00026 R R I . : R R I R : LDC Lcom/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample;.class
00027 R R I . : R R I R R : BIPUSH 27
00028 R R I . : R R I R R I : ALOAD 3
00029 ? : INVOKESTATIC com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$TestAccessInterceptor.onInvoke (Ljava/lang/Object;Ljava/lang/Object;I[Ljava/lang/Object;)V
00030 ? : INVOKEVIRTUAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample.hello (Ljava/lang/String;I)Ljava/lang/String;
00031 ? : POP
00032 ? : L6
00033 ? : LINENUMBER 24 L6
00034 ? : IINC 2 1
00035 ? : GOTO L2
00036 R R I . : : L3
00037 R R I . : : LINENUMBER 29 L3
00038 R R I . : : FRAME SAME
00039 R R I . : : RETURN
00040 ? : L7

使用Bytekit 做Agent 对所有类做增强时,会出现`duplicate class definition`的错误

环境: Windows 10 64 bit/Oracle JDK8

问题复现的demo(必现问题) -> https://github.com/kongwu-/bytekit-agent-sample

Transform类很简单,什么增强内容都没做,只是调用了一下AsmUtils.toClassNode

public class SmpClassTransformer implements ClassFileTransformer {
	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
		System.out.println("enhance... "+className);
		try {
			ClassNode classNode = AsmUtils.toClassNode(classfileBuffer);

			// 获取增强后的字节码

                        // 打印ClassLoader 日志之后,发现这里会进行Class load,而且ClassLoader是Launcher$AppClassLoader
                        // 这个toBytes 会 load class 有点不太理解,只是生成字节码需要 define 一次嘛……
			byte[] bytes = AsmUtils.toBytes(classNode);
                        //transformer 返回后,会再次打印一遍Class Load日志,ClassLoader 仍然是 Launcher$AppClassLoader
			return bytes;
		}catch (Error e){
			e.printStackTrace();
			return classfileBuffer;
		}
	}

最后打包 Agent 运行时,就会出现重复定义类的错误:loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "org/springframework/core/NestedRuntimeException"(理论上很多类都会出现这个问题,只是碰巧报错的是Spring的)。

transform 方法中load 了两遍,也没报错,这里应该算是第三遍load了,此时发生错误:duplicate class definition

stack trace 也有一点奇怪:

Exception in thread "main" java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "org/springframework/core/NestedRuntimeException"

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:468)
	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 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

//native define Class 之后,又调用 loadClass
	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:468)
	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 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	at org.example.App.main(App.java:13)

按道理说,同一个ClassLoader,define一次之后就被缓存了,再次loadClass时就会从缓存中获取了,这里报错实在想不通。

gtihub ci不稳定,非常奇怪

比如:

[INFO] Running com.alibaba.bytekit.asm.TryCatchBlockTest
Error:  Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.001 s <<< FAILURE! - in com.alibaba.bytekit.asm.TryCatchBlockTest
Error:  com.alibaba.bytekit.asm.TryCatchBlockTest.test  Time elapsed: 0.001 s  <<< ERROR!
java.lang.IllegalAccessError: class java.io.FileOutputStream$1 tried to access private field java.io.FileOutputStream.fd (java.io.FileOutputStream$1 and java.io.FileOutputStream are in module java.base of loader 'bootstrap')
	at java.base/java.io.FileOutputStream$1.close(FileOutputStream.java:398)
	at java.base/java.io.FileDescriptor.closeAll(FileDescriptor.java:355)
	at java.base/java.io.FileOutputStream.close(FileOutputStream.java:396)
	at com.alibaba.bytekit.utils.IOUtils.close(IOUtils.java:77)
	at com.alibaba.bytekit.utils.IOUtils.close(IOUtils.java:63)
	at com.alibaba.bytekit.utils.FileUtils.writeByteArrayToFile(FileUtils.java:87)
	at com.alibaba.bytekit.utils.FileUtils.writeByteArrayToFile(FileUtils.java:48)
	at com.alibaba.bytekit.utils.FileUtils.writeByteArrayToFile(FileUtils.java:33)
	at com.alibaba.bytekit.utils.Decompiler.decompile(Decompiler.java:38)
	at com.alibaba.bytekit.asm.TryCatchBlockTest.test(TryCatchBlockTest.java:119)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:377)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:284)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:248)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:167)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:456)
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:169)
	at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:595)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:581)

看 jdk 里的代码:

    public void close() throws IOException {
        if (closed) {
            return;
        }
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        FileChannel fc = channel;
        if (fc != null) {
            // possible race with getChannel(), benign since
            // FileChannel.close is final and idempotent
            fc.close();
        }

        fd.closeAll(new Closeable() {
            public void close() throws IOException {
               fd.close();
           }
        });
    }

这个 Closeable 使用 fd 肯定是没问题的。不知道是github 有问题,还是jvm有问题。不能稳定重现。

AsmUtils#toBytes 函数可能出错,需要调用 getCommonSuperClass ,抛出 TypeNotPresentException

2023-03-14 16:13:23 [main] ERROR c.a.o.s.OneAgentClassFileTransformer -transform error, loader: org.springframework.boot.loader.LaunchedURLClassLoader@2c1b194a, className: com/saleson/oneagent/springboot/app/demo/service/EmployeeServiceImpl, transformer: com.alibaba.bytekit.asm.instrument.InstrumentTransformer@488eb7f2
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 java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
        at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
        at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:555)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:458)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:452)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:451)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        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)

参考: alibaba/one-java-agent#47

`@AtLine` 应用在 arthas 中,`@Binding.LocalVars` 不生效

背景

在尝试为 arthas 添加 @AtLine 的支持,用于打印出方法运行时,局部变量的信息。
当前有了一个 MVP 的版本(commit: lotabout/arthas@5536242#diff-060c8feafaa130cf1bf5dad5a84703d46db28aa09e78fe2088b8d49288005949R48)

在自测时,发现 @AtLine 返回的 LocalVarsLocalVarName 都是空。

问题复现

测试类如下:

public class Hello {

  public static void main(String[] args) throws InterruptedException {
    Sample sample = new Sample();
    for (int i = 0; i < 10000; i++) {
      System.out.println(sample.hello());
      Thread.sleep(1000);
    }
  }


  private static class Sample {
    private int i = 0;

    public String hello() {
      int x = i * 2;
      String y = String.format("hello %d", x);
      return y;
    }
  }
}

启动测试类:

javac Hello.java
java Hello

随后有添加 AtLine 版本的 arthas 进行 attach

java -jar arthas-boot.jar
# 其中 `-l` 新 patch 后新加的参数
$ watch Hello$Sample hello '{varNames, vars}' -l

在 arthas 的 Enhancer 中已经加了 log 逻辑,打印出 transform 后的代码,如下:

/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  java.arthas.SpyAPI
 */
import java.arthas.SpyAPI;

private static class Hello.Sample {
    private int i = 0;

    public String hello() {
        Object[] objectArray = new Object[]{};
        String string = "hello|()Ljava/lang/String;";
        Class<Hello.Sample> clazz = Hello.Sample.class;
        Hello.Sample sample = this;
        SpyAPI.atEnter(clazz, (String)string, (Object)sample, (Object[])objectArray);
        try {
            String string2;
            String[] stringArray = new String[]{};
            Object[] objectArray2 = new Object[]{};
            int n = 16;
            Object[] objectArray3 = new Object[]{};
            String string3 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz2 = Hello.Sample.class;
            Hello.Sample sample2 = this;
            SpyAPI.atLine(clazz2, (String)string3, (Object)sample2, (Object[])objectArray3, (int)n, (String[])stringArray, (Object[])objectArray2);
            int n2 = this.i * 2;
            String[] stringArray2 = new String[]{};
            Object[] objectArray4 = new Object[]{};
            int n3 = 17;
            Object[] objectArray5 = new Object[]{};
            String string4 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz3 = Hello.Sample.class;
            Hello.Sample sample3 = this;
            SpyAPI.atLine(clazz3, (String)string4, (Object)sample3, (Object[])objectArray5, (int)n3, (String[])stringArray2, (Object[])objectArray4);
            String string5 = String.format("hello %d", n2);
            String[] stringArray3 = new String[]{};
            Object[] objectArray6 = new Object[]{};
            int n4 = 18;
            Object[] objectArray7 = new Object[]{};
            String string6 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz4 = Hello.Sample.class;
            Hello.Sample sample4 = this;
            SpyAPI.atLine(clazz4, (String)string6, (Object)sample4, (Object[])objectArray7, (int)n4, (String[])stringArray3, (Object[])objectArray6);
            String string7 = string2 = string5;
            Object[] objectArray8 = new Object[]{};
            String string8 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz5 = Hello.Sample.class;
            Hello.Sample sample5 = this;
            SpyAPI.atExit(clazz5, (String)string8, (Object)sample5, (Object[])objectArray8, (Object)string7);
            return string2;
        }
        catch (Throwable throwable) {
            Throwable throwable2 = throwable;
            Object[] objectArray9 = new Object[]{};
            String string9 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz6 = Hello.Sample.class;
            Hello.Sample sample6 = this;
            SpyAPI.atExceptionExit(clazz6, (String)string9, (Object)sample6, (Object[])objectArray9, (Throwable)throwable2);
            throw throwable;
        }
    }

    private Hello.Sample() {
    }
}

发现其中 atLine 的最后两个参数 varNamevars 都是空数组。

环境信息

分别使用 JDK 1.8 和 JDK 11 进行测试,都有这个问题。

另外,单独运行 bytekit 的 unit test,是能正确 transform 的,下面是 UT 的一些中间输出

vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 101, 100, 100100]
varNames: [this, i, s, abc]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 29
args: [10202]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10202, 100, 100100]
varNames: [this, i, s, abc]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 30
args: [10211]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10211, 100]
varNames: [this, i, s]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 41
args: [10211]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10211, 100]
varNames: [this, i, s]

ClassMetaClassWriter 需要恢复支持指定 ClassLoader

java.lang.TypeNotPresentException: Type com/test/vbs/Facade not present
        at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1051)
        at com.alibaba.bytekit.asm.ClassMetaClassWriter.getCommonSuperClass(ClassMetaClassWriter.java:43)
        at com.alibaba.deps.org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1202)
        at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1300)
        at com.alibaba.deps.org.objectweb.asm.Frame.merge(Frame.java:1198)
        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:468)
        at com.alibaba.bytekit.utils.AsmUtils.toBytes(AsmUtils.java:80)
        at com.taobao.arthas.core.advisor.Enhancer.transform(Enhancer.java:256)
        at com.taobao.arthas.core.advisor.TransformerManager$1.transform(TransformerManager.java:59)
        at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
        at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
        at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
        at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
        at com.taobao.arthas.core.advisor.Enhancer.enhance(Enhancer.java:446)
        at com.taobao.arthas.core.command.monitor200.EnhancerCommand.enhance(EnhancerCommand.java:173)
        at com.taobao.arthas.core.command.monitor200.EnhancerCommand.process(EnhancerCommand.java:120)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
        at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
        at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:991)
Caused by: java.lang.ClassNotFoundException: com.test.vbs.Facade
        at java.base/java.net.URLClassLoader.findClassInternal(URLClassLoader.java:497)
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:456)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:626)
        at com.taobao.arthas.agent.ArthasClassloader.loadClass(ArthasClassloader.java:34)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:558)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at com.alibaba.deps.org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1049)
        ... 31 common frames omitted

也就是下面的代码里,都有可能获取到空的 type2Bytes, 这样子会调用 super.getCommonSuperClass,但默认情况下, classLoader是 ClassWriter 的 classLoader。

    @Override
    protected String getCommonSuperClass(String type1, String type2) {
        byte[] type1Bytes = ClassLoaderUtils.readBytecodeByName(classLoader, type1);
        if (type1Bytes == null) {
            return super.getCommonSuperClass(type1, type2);
        }
        byte[] type2Bytes = ClassLoaderUtils.readBytecodeByName(classLoader, type2);
        if (type2Bytes == null) {
            return super.getCommonSuperClass(type1, type2);
        }

当使用@Binding.InvokeArgs Object[] args时抛异常

java.lang.IllegalArgumentException: Error at instruction 21: Expected an object reference, but found . test()V
00000 R . . . : : L0
00001 R . . . : : LINENUMBER 23 L0
00002 R . . . : : NEW com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample
00003 R . . . : R : DUP
00004 R . . . : R R : INVOKESPECIAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample. ()V
00005 R . . . : R : ASTORE 1
00006 R R . . : : L1
00007 R R . . : : LINENUMBER 24 L1
00008 R R . . : : ICONST_0
00009 R R . . : I : ISTORE 2
00010 R R I . : : L2
00011 R R I . : : FRAME APPEND [com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample I]
00012 R R I . : : ILOAD 2
00013 R R I . : I : ICONST_1
00014 R R I . : I I : IF_ICMPGE L3
00015 R R I . : : L4
00016 R R I . : : LINENUMBER 25 L4
00017 R R I . : : ALOAD 1
00018 R R I . : R : LDC "hello2"
00019 R R I . : R R : ILOAD 2
00020 R R I . : R R I : LDC "hello"
00021 R R I . : R R I R : ALOAD 3
00022 ? : INVOKESTATIC com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$TestAccessInterceptor.onInvoke (Ljava/lang/String;[Ljava/lang/Object;)V
00023 ? : INVOKEVIRTUAL com/alibaba/bytekit/asm/interceptor/AtInvokeTest2$Sample.hello (Ljava/lang/String;I)Ljava/lang/String;
00024 ? : POP
00025 ? : L5
00026 ? : LINENUMBER 24 L5
00027 ? : IINC 2 1
00028 ? : GOTO L2
00029 R R I . : : L3
00030 R R I . : : LINENUMBER 27 L3
00031 R R I . : : FRAME SAME
00032 R R I . : : RETURN
00033 ? : L6

@Binding.Method 问题

发现 @Binding.Method 注解没有正常生效,这个绑定是否已经可以使用呢?

对于bytekit个人建议

bytekit 后续可以拓展增强注解方面的 binding 操作,比如方便获取方法注解,类注解等等。

自动更新目标类字节码的 major version

用户在编写 @Instrument 类的代码时,可能使用高版本的语法,编译生成的字节码也是高版本的。

那么就会导致增强应用的类时,在低版本的字节码里插入了高版本的字节码,jvm会校验失败。

所以,考虑下面的解决办法:

  • 自动把应用类的 字节码的 major version 更新为 @Instrument 类的 major version

这种情况下,有可能jvm本身版本比较低。比如 jvm版本是 jdk6,@Instrument 类的字节码版本是 jdk8的。这时@Instrument 类应该不生效。

参考: #16

MethodProcessor 里对 inline method 是否要 remove line number需要区分

比如对于 arthas里的 trace命令

  • 要在invoke指令前插入 SpyTraceInterceptor2#onInvoke 调用
  • 然后把这个 SpyTraceInterceptor2#onInvoke 函数在原来的 函数里 inline 掉

那么就要在 MethodProcessor 里把MethodNode toInlineMethodNode,这里面的 line number删掉。

    public static class SpyTraceInterceptor2 {
        @AtInvoke(name = "", inline = true, whenComplete = false, excludes = { "**SpyAPI**" })
        public static void onInvoke(@Binding.This Object target, @Binding.Class Class<?> clazz,
                @Binding.MethodName String method,
                @Binding.InvokeInfo String invokeInfo) {
            
            SpyAPI.atBeforeInvoke(clazz, method, invokeInfo, target);
        }
    }

但是对于下面的 InstrumentApi.invokeOrigin(),要被inline的函数反而是要保留line number。要把外部的 DubboFilter_APM#invoke 函数的 line number删掉。

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

    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.err.println("invoker class: " + this.getClass().getName());
        Result result = InstrumentApi.invokeOrigin();

        return result;
    }
}

目标类的target是 jdk 1.6,然后使用jdk 1.8来编译instrument类,可能会出现下面的错误

记录一下:

Exception in thread "main" java.lang.VerifyError: Illegal type at constant pool entry 328 in class org.apache.http.impl.client.InternalHttpClient
Exception Details:
  Location:
    org/apache/http/impl/client/InternalHttpClient.doExecute(Lorg/apache/http/HttpHost;Lorg/apache/http/HttpRequest;Lorg/apache/http/protocol/HttpContext;)Lorg/apache/http/client/methods/CloseableHttpResponse; @29: invokestatic
  Reason:
    Constant pool index 328 is invalid
  Bytecode:
    0000000: b201 2dbb 012f 59b7 0130 1301 32b6 0136
    0000010: 2bb6 0139 b601 36b6 013c b601 42b8 0148
    0000020: 572a 2b2c 2d3a 073a 063a 053a 0419 0612
    0000030: 22b8 0007 5701 3a08 1906 c100 8299 000a
    0000040: 1906 c000 823a 0819 0619 05b8 0024 3a09
    0000050: 1907 c600 0819 07a7 000a bb00 2559 b700
    0000060: 26b8 0027 3a0a 013a 0b19 06c1 0028 9900
    0000070: 0f19 06c0 0028 b900 2901 003a 0b19 0bc7
    0000080: 0041 1906 b900 1201 003a 0c19 0cc1 002a
    0000090: 9900 2419 0cc0 002a b900 2b01 00b9 002c
    00000a0: 0100 9a00 1e19 0c19 04b4 0010 b800 2d3a
    00000b0: 0ba7 000f 190c 1904 b400 10b8 002d 3a0b
    00000c0: 190b c600 0a19 0a19 0bb6 002e 1904 190a
    00000d0: b700 2f19 0419 0519 0919 0ab7 0030 3a0c
    00000e0: 1904 b400 0a19 0c19 0919 0a19 08b9 0031
    00000f0: 0500 a700 0f3a 09bb 0033 5919 09b7 0034
    0000100: bfb0                                   
  Exception Handler Table:
    bci [71, 242] => handler: 245
  Stackmap Table:
    full_frame(@71,{Object[#136],Object[#111],Object[#186],Object[#132],Object[#136],Object[#111],Object[#186],Object[#132],Object[#130]},{})
    append_frame(@90,Object[#207])
    same_locals_1_stack_item_frame(@97,Object[#132])
    append_frame(@125,Object[#195],Object[#134])
    append_frame(@180,Object[#189])
    chop_frame(@192,1)
    same_frame(@204)
    full_frame(@245,{Object[#136],Object[#111],Object[#186],Object[#132],Object[#136],Object[#111],Object[#186],Object[#132],Object[#130]},{Object[#138]})
    full_frame(@257,{Object[#136],Object[#111],Object[#186],Object[#132],Object[#136],Object[#111],Object[#186],Object[#132],Object[#130],Object[#207],Object[#195],Object[#134],Object[#330]},{Object[#332]})

	at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:1250)
	at org.apache.http.impl.client.HttpClients.createDefault(HttpClients.java:56)
	at com.trace.demo.httpclient.TTT.test(TTT.java:22)
	at com.trace.demo.httpclient.HttpClientDemo.main(HttpClientDemo.java:21)

store->callback-load作用

image
如上逻辑,store -> callback ->load,理论上,调用完callback,栈顶数据应该是原来的数据啊,比如return指令之前插入的callback,那callback执行完后栈顶应该是return的值。感觉store->load看上去是多余的。所以这对操作的作用是什么,防止一些异常情况污染操作数栈?

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.