Giter Site home page Giter Site logo

bytekit's Introduction

ByteKit

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

bytecode kit for java.

目标

  1. 之前的Arthas里的字节码增强,是通过asm来处理的,代码逻辑不好修改,理解困难
  2. 基于ASM提供更高层的字节码处理能力,面向诊断/APM领域,不是通用的字节码库
  3. ByteKit期望能提供一套简洁的API,让开发人员可以比较轻松的完成字节码增强

对比

功能 函数Enter/Exit注入点 绑定数据 inline 防止重复增强 避免装箱/拆箱开销 origin调用替换 @ExceptionHandler
ByteKit @AtEnter
@AtExit
@AtExceptionExit
@AtFieldAccess
@AtInvoke
@AtInvokeException
@AtLine
@AtSyncEnter
@AtSyncExit
@AtThrow
this/args/return/throw
field
locals
子调用入参/返回值/子调用异常
line number
ByteBuddy @OnMethodEnter
@OnMethodExit
@OnMethodExit#onThrowable()
this/args/return/throw
field
locals
传统AOP Enter
Exit
Exception
this/args/return/throw

特性

1. 丰富的注入点支持

  • @AtEnter 函数入口
  • @AtExit 函数退出
  • @AtExceptionExit 函数抛出异常
  • @AtFieldAccess 访问field
  • @AtInvoke 在method里的子函数调用
  • @AtInvokeException 在method里的子函数调用抛出异常
  • @AtLine 在指定行号
  • @AtSyncEnter 进入同步块,比如synchronized
  • @AtSyncExit 退出同步块
  • @AtThrow 代码里显式throw异常点

2. 动态的Binding

  • @Binding.This this对象

  • @Binding.Class Class对象

  • @Binding.Method 函数调用的 Method 对象

  • @Binding.MethodName 函数的名字

  • @Binding.MethodDesc 函数的desc

  • @Binding.Return 函数调用的返回值

  • @Binding.Throwable 函数里抛出的异常

  • @Binding.Args 函数调用的入参

  • @Binding.ArgNames 函数调用的入参的名字

  • @Binding.LocalVars 局部变量

  • @Binding.LocalVarNames 局部变量的名字

  • @Binding.Field field对象属性字段

  • @Binding.InvokeArgs method里的子函数调用的入参

  • @Binding.InvokeReturn method里的子函数调用的返回值

  • @Binding.InvokeMethodName method里的子函数调用的名字

  • @Binding.InvokeMethodOwner method里的子函数调用的类名

  • @Binding.InvokeMethodDeclaration method里的子函数调用的desc

  • @Binding.Line 行号

  • @Binding.Monitor 同步块里监控的对象

3. 可编程的异常处理

  • @ExceptionHandler 在插入的增强代码,可以用try/catch块包围起来

4. inline支持

增强的代码 和 异常处理代码都可以通过 inline技术内联到原来的类里,达到最理想的增强效果。

5. invokeOrigin 技术

通常,我们要增强一个类,就想要办法在函数前后插入一个static的回调函数,但这样子局限太大。那么有没有更灵活的方式呢?

比如有一个 hello() 函数:

    public String hello(String str) {
        return "hello " + str;
    }

我们想对它做增强,那么可以编写下面的代码:

    public String hello(String str) {
        System.out.println("before");
        Object value = InstrumentApi.invokeOrigin();
        System.out.println("after, result: " + value);
        return object;
    }

增强后的结果是:

    public String hello(String str) {
        System.out.println("before");
        Object value = "hello " + str;
        System.out.println("after, result: " + value);
        return object;
    }

这种方式可以随意插入代码,非常灵活。

参考增强Dubbo Filter的示例: [查看源文件]

示例

ByteKitDemo.java为例说明,[查看源文件]。

1. 定义注入点和Binding数据

  • 在下面的 SampleInterceptor 时定义了要注入 @AtEnter/@AtExit/@AtExceptionExit三个地方,
  • @Binding绑定了不同的数据
  • @AtEnter里配置了 inline = true,则说明插入的SampleInterceptor#atEnter函数本身会被inline掉
  • 配置了 suppress = RuntimeException.classsuppressHandler = PrintExceptionSuppressHandler.class,说明插入的代码会被 try/catch 包围
    public static class SampleInterceptor {

        @AtEnter(inline = true, suppress = RuntimeException.class, suppressHandler = PrintExceptionSuppressHandler.class)
        public static void atEnter(@Binding.This Object object, 
                @Binding.Class Object clazz,
                @Binding.Args Object[] args, 
                @Binding.MethodName String methodName,
                @Binding.MethodDesc String methodDesc) {
            System.out.println("atEnter, args[0]: " + args[0]);
        }

        @AtExit(inline = true)
        public static void atExit(@Binding.Return Object returnObject) {
            System.out.println("atExit, returnObject: " + returnObject);
        }

        @AtExceptionExit(inline = true, onException = RuntimeException.class)
        public static void atExceptionExit(@Binding.Throwable RuntimeException ex,
                @Binding.Field(name = "exceptionCount") int exceptionCount) {
            System.out.println("atExceptionExit, ex: " + ex.getMessage() + ", field exceptionCount: " + exceptionCount);
        }
    }

2. @ExceptionHandler

在上面的 @AtEnter配置里,生成的代码会被 try/catch 包围,那么具体的内容是在PrintExceptionSuppressHandler

    public static class PrintExceptionSuppressHandler {

        @ExceptionHandler(inline = true)
        public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
            System.out.println("exception handler: " + clazz);
            e.printStackTrace();
        }
    }

3. 查看反编译结果

原始的Sample类是:

    public static class Sample {
        private int exceptionCount = 0;

        public String hello(String str, boolean exception) {
            if (exception) {
                exceptionCount++;
                throw new RuntimeException("test exception, str: " + str);
            }
            return "hello " + str;
        }
    }

增强后的字节码,再反编译:

package com.example;

public static class ByteKitDemo.Sample {
    private int exceptionCount = 0;

    public String hello(String string, boolean bl) {
        try {
            String string2 = "(Ljava/lang/String;Z)Ljava/lang/String;";
            String string3 = "hello";
            Object[] arrobject = new Object[]{string, new Boolean(bl)};
            Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
            ByteKitDemo.Sample sample = this;
            System.out.println("atEnter, args[0]: " + arrobject[0]);
        }
        catch (RuntimeException runtimeException) {
            Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
            RuntimeException runtimeException2 = runtimeException;
            System.out.println("exception handler: " + class_);
            runtimeException2.printStackTrace();
        }
        try {
            String string4;
            void str;
            void exception;
            if (exception != false) {
                ++this.exceptionCount;
                throw new RuntimeException("test exception, str: " + (String)str);
            }
            String string5 = string4 = "hello " + (String)str;
            System.out.println("atExit, returnObject: " + string5);
            return string4;
        }
        catch (RuntimeException runtimeException) {
            int n = this.exceptionCount;
            RuntimeException runtimeException3 = runtimeException;
            System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);
            throw runtimeException;
        }
    }
}

开发相关

deploy到远程仓库:

mvn clean deploy -DskipTests -P release

License

Apache License V2

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

store->callback-load作用

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

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

有解决办法吗?当使用@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

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

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

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

使用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时就会从缓存中获取了,这里报错实在想不通。

@Binding.Method 问题

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

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

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有问题。不能稳定重现。

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

对于bytekit个人建议

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

当使用@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

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

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

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

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

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

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

参考: #16

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

目标类的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)

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;
    }
}

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.