Arthas原理系列(四):字节码插装让一切变得有可能

本文深入解析Arthas的字节码插装机制,介绍如何通过InterceptorProcessor生成和工作,理解Binding系列注解的功能,以及异常处理器的插装。通过源码分析,展示了Arthas如何实现方法的入参、返回值等运行时信息的监控。
摘要由CSDN通过智能技术生成

历史文章:

  1. OGNL语法规范
  2. 消失的堆栈
  3. Arthas原理系列(一):利用JVM的attach机制实现一个极简的watch命令
  4. Arthas原理系列(二):总体架构和项目入口
  5. Arthas原理系列(三):服务端启动流程
  6. Arthas原理系列(四):字节码插装让一切变得有可能

前言

在前面的文章中我们可以看到watch命令对原来的字节码进行了插装,并且我们以此为思路实现了一个简易版的watch命令,但真实的watch提供的能力远不止统计方法的运行时间,我们最常用他的一个功能还是观察方法的入参返回值等运行时的变量。所有需要插装字节码的命令都继承了EnhancerCommand,总共有 5 个命令,分别是monitor,stack,tt,watch,trace,本片文章将会展示 arthas 为这么多的命令如何设计一个统一的插装流程的。

插装的主流程

在上篇文章中,我们看了watch命令的process方法:

@Override
public void process(final CommandProcess process) {
   
    // ctrl-C support
    process.interruptHandler(new CommandInterruptHandler(process));
    // q exit support
    process.stdinHandler(new QExitHandler(process));

    // start to enhance
    enhance(process);
}

可以看到整个插装的入口是enhance这个方法,这个方法在父类EnhancerCommand中实现:

protected void enhance(CommandProcess process) {
   
    Session session = process.session();
    if (!session.tryLock()) {
   
        String msg = "someone else is enhancing classes, pls. wait.";
        process.appendResult(new EnhancerModel(null, false, msg));
        process.end(-1, msg);
        return;
    }
    EnhancerAffect effect = null;
    int lock = session.getLock();
    try {
   
        Instrumentation inst = session.getInstrumentation();
        // 获取每个命令特有的AdviceListener,插装的时候会把这个方法注入到目标JVM中
        AdviceListener listener = getAdviceListenerWithId(process);
        if (listener == null) {
   
            logger.error("advice listener is null");
            String msg = "advice listener is null, check arthas log";
            process.appendResult(new EnhancerModel(effect, false, msg));
            process.end(-1, msg);
            return;
        }
        boolean skipJDKTrace = false;
        if(listener instanceof AbstractTraceAdviceListener) {
   
            // 如果是trace命令,判断是否需要跳过jdk提供的方法
            skipJDKTrace = ((AbstractTraceAdviceListener) listener).getCommand().isSkipJDKTrace();
        }

        Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getMethodNameMatcher());
        // 注册通知监听器
        process.register(listener, enhancer);
        // 开始插装
        effect = enhancer.enhance(inst);

        if (effect.getThrowable() != null) {
   
            String msg = "error happens when enhancing class: "+effect.getThrowable().getMessage();
            process.appendResult(new EnhancerModel(effect, false, msg));
            process.end(1, msg + ", check arthas log: " + LogUtil.loggingFile());
            return;
        }

        if (effect.cCnt() == 0 || effect.mCnt() == 0) {
   
            // no class effected
            // might be method code too large
            process.appendResult(new EnhancerModel(effect, false, "No class or method is affected"));
            String msg = "No class or method is affected, try:\n"
                    + "1. sm CLASS_NAME METHOD_NAME to make sure the method you are tracing actually exists (it might be in your parent class).\n"
                    + "2. reset CLASS_NAME and try again, your method body might be too large.\n"
                    + "3. check arthas log: " + LogUtil.loggingFile() + "\n"
                    + "4. visit https://github.com/alibaba/arthas/issues/47 for more details.";
            process.end(-1, msg);
            return;
        }

        // 这里做个补偿,如果在enhance期间,unLock被调用了,则补偿性放弃
        if (session.getLock() == lock) {
   
            if (process.isForeground()) {
   
                process.echoTips(Constants.Q_OR_CTRL_C_ABORT_MSG + "\n");
            }
        }

        process.appendResult(new EnhancerModel(effect, true));

        //异步执行,在AdviceListener中结束
    } catch (Throwable e) {
   
        String msg = "error happens when enhancing class: "+e.getMessage();
        logger.error(msg, e);
        process.appendResult(new EnhancerModel(effect, false, msg));
        process.end(-1, msg);
    } finally {
   
        if (session.getLock() == lock) {
   
            // enhance结束后解锁
            process.session().unLock();
        }
    }
}

这段代码稍微有点长,里面最重要的是两个地方,一个是调用getAdviceListenerWithId获得了一个AdviceListener,这个类实现了befor,arterReturning,afterThrowing等方法,会被插装函数注入到目标JVM中。另一个是创建了一个Enhancer对象,并开始对目标 JVM 的类和方法进行插装。

public synchronized EnhancerAffect enhance(final Instrumentation inst) throws UnmodifiableClassException {
   
    // 获取需要增强的类集合
    this.matchingClasses = GlobalOptions.isDisableSubClass
            ? SearchUtils.searchClass(inst, classNameMatcher)
            : SearchUtils.searchSubClass(inst, SearchUtils.searchClass(inst, classNameMatcher));

    // 过滤掉无法被增强的类
    filter(matchingClasses);

    logger.info("enhance matched classes: {}", matchingClasses);

    affect.setTransformer(this);

    try {
   
        ArthasBootstrap.getInstance().getTransformerManager().addTransformer(this, isTracing);

        // 批量增强
        if (GlobalOptions.isBatchReTransform) {
   
            final int size = matchingClasses.size();
            final Class<?>[] classArray = new Class<?>[size];
            arraycopy(matchingClasses.toArray(), 0, classArray, 0, size);
            if (classArray.length > 0) {
   
                inst.retransformClasses(classArray);
                logger.info("Success to batch transform classes: " + Arrays.toString(classArray));
            }
        } else {
   
            // for each 增强
            for (Class<?> clazz : matchingClasses) {
   
                try {
   
                    inst.retransformClasses(clazz);
                    logger.info("Success to transform class: " + clazz);
                } catch (Throwable t) {
   
                    logger.warn("retransform {} failed.", clazz, t);
                    if (t instanceof UnmodifiableClassException) {
   
                        throw (UnmodifiableClassException) t;
                    } else if (t instanceof RuntimeException) {
   
                        throw (RuntimeException) t;
                    } else {
   
                        throw new RuntimeException(t);
                    }
                }
            }
        }
    } catch (Throwable e) {
   
        logger.error("Enhancer error, matchingClasses: {}", matchingClasses, e);
        affect.setThrowable(e);
    }

    return affect;
}

enhance这个方法的实现如果看过之前实现watch命令那一篇应该不会陌生,在这个方法中最重要的事情就是注册了本类作为转换类,并且调用InstrumentationretransformClasses方法对类进行转换。在这个机制下,真正干活的就是transform方法了。

@Override
public byte[] transform(final ClassLoader inClassLoader, String className, Class<?> classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
   
    try {
   
        // 检查classloader能否加载到 SpyAPI,如果不能,则放弃增强
        try {
   
            if (inClassLoader != null) {
   
                inClassLoader.loadClass(SpyAPI.class.getName());
            }
        } catch (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值