alibaba / bytekit Goto Github PK
View Code? Open in Web Editor NEWJava Bytecode Kit
License: Apache License 2.0
Java Bytecode Kit
License: Apache License 2.0
当用户需要时,自己放一个实现类进来。默认就是 Nop 实现。
目前是用asm tree api的,对于绝大部分 byte[] ,是否要找一种更高效的方式来读取解析?
ByteKit是否无法干预原有逻辑,比如在‘Before’位置或异常catch时直接返回一个预定值。
haven't tried yet. maybe some part just works. guess still worth mentioning
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
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)
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
RT
背景:
//步骤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注解是在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
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
环境: 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时就会从缓存中获取了,这里报错实在想不通。
比如:
[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有问题。不能稳定重现。
类似arthas的Enhancer.reset,场景是在某种情况下想临时在类上加些内容,完成之后再还原回去。
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)
在尝试为 arthas 添加 @AtLine
的支持,用于打印出方法运行时,局部变量的信息。
当前有了一个 MVP 的版本(commit: lotabout/arthas@5536242#diff-060c8feafaa130cf1bf5dad5a84703d46db28aa09e78fe2088b8d49288005949R48)
在自测时,发现 @AtLine
返回的 LocalVars
和 LocalVarName
都是空。
测试类如下:
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
的最后两个参数 varName
和 vars
都是空数组。
分别使用 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]
请问有invokeOrigin 技术的具体使用demo吗?感觉这块介绍的不是特别多
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);
}
目前的修改需要把整个 ClassReader/ ClassWriter 流程走一遍。
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 注解没有正常生效,这个绑定是否已经可以使用呢?
bytekit 后续可以拓展增强注解方面的 binding 操作,比如方便获取方法注解,类注解等等。
用户在编写 @Instrument
类的代码时,可能使用高版本的语法,编译生成的字节码也是高版本的。
那么就会导致增强应用的类时,在低版本的字节码里插入了高版本的字节码,jvm会校验失败。
所以,考虑下面的解决办法:
@Instrument
类的 major version这种情况下,有可能jvm本身版本比较低。比如 jvm版本是 jdk6,@Instrument
类的字节码版本是 jdk8的。这时@Instrument
类应该不生效。
参考: #16
比如对于 arthas里的 trace命令
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;
}
}
记录一下:
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)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.