美文网首页
Arthas技术内幕-命令

Arthas技术内幕-命令

作者: 老荀 | 来源:发表于2020-09-16 17:34 被阅读0次

Arthas是一个使用java编写的无代码侵入的线上诊断工具,通过attach一个jvm进程可以对该jvm进程的内存,线程等进行监控,并且还可以实时的对某些类某些方法进行织入,拦截打印方法参数,返回值和异常等,功能很强大,我对其内部的是如何做到上述功能很好奇,决定通过学习源码来看看这黑科技一样的功能是如何实现的。


本次先从简单的命令开始学习,所有的arthas内置的命令都放在了core子模块下的command包中。
先列出“所有”命令:

 pwd          
 jad          
 dashboard    
 stack        
 profiler     
 perfcounter  
 echo         
 heapdump     
 grep         
 tt           
 watch        
 monitor      
 stop         
 trace        
 sm           
 session      
 sysenv       
 vmoption     
 thread       
 mc           
 keymap       
 dump         
 reset        
 jvm          
 tee          
 classloader  
 ognl         
 logger       
 getstatic    
 mbean        
 options      
 help         
 redefine     
 sc           
 sysprop      
 history      
 version      

为什么要打引号呢?因为arthas还埋了两个彩蛋命令july,thanks,在源码中可以看到。

命令介绍

arthas的所有命令都是继承于AnnotatedCommand,并且重写最重要的方法process

public abstract class AnnotatedCommand {
    public abstract void process(CommandProcess process);
}

那命令的逻辑是怎么运作的呢?arthas对从接受终端的命令字符串开始,然后到发送服务端,服务端用对应的命令对象去处理,最后返回响应给客户端整个过程中有很多参与的对象和类,我只列出重要的调用方法片段。

一切开始从监听开始

@Override
public ShellServer listen(final Handler<Future<Void>> listenHandler) {
    ...
    for (TermServer termServer : toStart) {
        termServer.termHandler(new TermServerTermHandler(this));
        ...
    }
    ...
}

这里设置了TermServerTermHandler作为终端的处理类,它也只是委托给了ShellServerImpl

public class TermServerTermHandler implements Handler<Term> {
    private ShellServerImpl shellServer;

    public TermServerTermHandler(ShellServerImpl shellServer) {
        this.shellServer = shellServer;
    }
    @Override
    public void handle(Term term) {
        shellServer.handleTerm(term);
    }
}
public class ShellServerImpl extends ShellServer {
    public void handleTerm(Term term) {
        ...
        ShellImpl session = createShell(term);
        ...
        // readline就是读取终端的输入
        session.readline();
    }
}
public class ShellImpl implements Shell {
    private Term term;
    public void readline() {
        // 这里创建了ShellLineHandler
        term.readline(prompt, new ShellLineHandler(this),
                new CommandManagerCompletionHandler(commandManager));
    }
}
public class ShellLineHandler implements Handler<String> {
  
    @Override
    public void handle(String line) {
        ...
        // 对输入字符串进行分割
        List<CliToken> tokens = CliTokens.tokenize(line);
        CliToken first = TokenUtils.findFirstTextToken(tokens);
        ...
        Job job = createJob(tokens);
        if (job != null) {
            job.run();
        }
    }
    @Override
    public synchronized Job createJob(List<CliToken> args) {
        Job job = jobController.createJob(commandManager, args, session, new ShellJobHandler(this), term, null);
        return job;
    }
}
public class JobControllerImpl implements JobController {
    ...
    @Override
    public Job createJob(InternalCommandManager commandManager, List<CliToken> tokens, Session session, JobListener jobHandler, Term term, ResultDistributor resultDistributor) {
        ...
        Process process = createProcess(tokens, commandManager, jobId, term, resultDistributor);
        JobImpl job = new JobImpl(jobId, this, process, line.toString(), runInBackground, session, jobHandler);
        return job;
    }

    private Process createProcess(List<CliToken> line, InternalCommandManager commandManager, int jobId, Term term, ResultDistributor resultDistributor) {
            ...
            while (tokens.hasNext()) {
                CliToken token = tokens.next();
                if (token.isText()) {
                    // 这里就会根据命令的字符串找到对应的命令对象
                    Command command = commandManager.getCommand(token.value());
                    if (command != null) {
                        return createCommandProcess(command, tokens, jobId, term, resultDistributor);
                    ...
    }
}
public class JobImpl implements Job {
    ...
    final Process process;
    @Override
    public Job run(boolean foreground) {
        ...
        process.run(foreground);
        ...
    }
}
public class ProcessImpl implements Process {
    ...
    private CommandProcessImpl process;
    @Override
    public synchronized void run(boolean fg) {
        ...
        process = new CommandProcessImpl(this, tty);
        ...
        // 启动一个线程处理当前命令请求
        Runnable task = new CommandProcessTask(process);
        ArthasBootstrap.getInstance().execute(task);
    }
    private class CommandProcessTask implements Runnable {
        ...
        @Override
        public void run() {
            try {
                handler.handle(process);
            } catch (Throwable t) {
                ...
            }
        }
    }
}
public class AnnotatedCommandImpl extends Command {
    private class ProcessHandler implements Handler<CommandProcess> {
        @Override
        public void handle(CommandProcess process) {
            process(process);
        }
    }

    private void process(CommandProcess process) {
        AnnotatedCommand instance;
        try {
            // 每次接受命令都会用反射创建一个新的对象去处理
            instance = clazz.newInstance();
        } catch (Exception e) {
            ...
        }
        ...
        // 这里就是那个需要没一个命令实现的抽象命令
        instance.process(process);
    }
}

让我们开始吧,从简单的命令开始

july thanks

这里就把彩蛋命令一起说下吧
july就是把because of you的歌词输出

[arthas@37603]$ july
I will not make the same mistakes that you did
I will not let myself
Cause my heart so much misery
I will not break the way you did
You fell so hard

I ve learned the hard way
To never let it get that far

Because of you
I never stray too far from the sidewalk

thanks是输出一个在资源路径下的thanks.txt的内容。

PLATINUM DEVELOPERS

    vlinux
        email : oldmanpushcart@gmail.com
        weibo : http://weibo.com/vlinux

    chengtd
        email : chengtongda@163.com
        weibo : http://weibo.com/chengtd
    ...
ABOUT

    Thank you very much.

vmoption

获取或者设置虚拟机的参数,用到了HotSpotDiagnosticMXBean这个对象

public class VMOptionCommand extends AnnotatedCommand {
    @Override
    public void process(CommandProcess process) {
        run(process, name, value);
    }

    private static void run(CommandProcess process, String name, String value) {
        HotSpotDiagnosticMXBean hotSpotDiagnosticMXBean = ManagementFactory
                            .getPlatformMXBean(HotSpotDiagnosticMXBean.class);
        if (StringUtils.isBlank(name) && StringUtils.isBlank(value)) {
                // 获取所有的选项
                process.appendResult(new VMOptionModel(hotSpotDiagnosticMXBean.getDiagnosticOptions()));
            } else if (StringUtils.isBlank(value)) {
                // 查看指定的选项
                VMOption option = hotSpotDiagnosticMXBean.getVMOption(name);
                ...
            } else {
                VMOption vmOption = hotSpotDiagnosticMXBean.getVMOption(name);
                String originValue = vmOption.getValue();
                // 设置选项
                hotSpotDiagnosticMXBean.setVMOption(name, value);
                ...
            }
    }
}

jad

反编译class

public class JadCommand extends AnnotatedCommand {
    @Override
    public void process(CommandProcess process) {
        ...
        // 找到目标class,jad一次只能反编译一个class
        Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code);

        try {
            ExitStatus status = null;
            if (matchedClasses == null || matchedClasses.isEmpty()) {
                // 没找到,报错
                ...
            } else if (matchedClasses.size() > 1) {
                // 找到大于1个,报错
                ...
            } else { // matchedClasses size is 1
                ...
                status = processExactMatch(process, affect, inst, matchedClasses, withInnerClasses);
            }
           ...
    }
    
    private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set<Class<?>> matchedClasses, Set<Class<?>> withInnerClasses) {
            ...
            // source就是反编译出来的源码字符串
            String source = Decompiler.decompile(classFile.getAbsolutePath(), methodName, hideUnicode);
            ...
    }
    
    public static String decompile(String classFilePath, String methodName, boolean hideUnicode) {
        final StringBuilder result = new StringBuilder(8192);
        // arthas 用三方库 cfr 完成的 反编译功能
        OutputSinkFactory mySink = new OutputSinkFactory() {            
            ...
        };
        ...
        CfrDriver driver = new CfrDriver.Builder().withOptions(options).withOutputSink(mySink).build();
        List<String> toAnalyse = new ArrayList<String>();
        toAnalyse.add(classFilePath);
        driver.analyse(toAnalyse);
        return result.toString();
    }
}

cfr完整坐标如下:

<dependency>
    <groupId>org.benf</groupId>
    <artifactId>cfr</artifactId>
 </dependency>

mc

编译目标java文件(支持多个),生成对应的class文件

public class MemoryCompilerCommand extends AnnotatedCommand {

    @Override
    public void process(final CommandProcess process) {
            ...
            DynamicCompiler dynamicCompiler = new DynamicCompiler(classloader);
            ...
            for (String sourceFile : sourcefiles) {
                ...
                dynamicCompiler.addSource(name, sourceCode);
            }
            // 关键就是这行
            Map<String, byte[]> byteCodes = dynamicCompiler.buildByteCodes();
            ...
    }
}
public class DynamicCompiler {
    // javax.tools.JavaCompiler
    private final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
    private final DynamicClassLoader dynamicClassLoader;

    public Map<String, byte[]> buildByteCodes() {

        ...
        JavaFileManager fileManager = new DynamicJavaFileManager(standardFileManager, dynamicClassLoader);

        DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<JavaFileObject>();
        JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, collector, options, null, compilationUnits);
        boolean result = task.call();
        ...
        return dynamicClassLoader.getByteCodes();
        ...
    }
}

ognl

public class OgnlCommand extends AnnotatedCommand {
    @Override
    public void process(CommandProcess process) {
        ...
        Express unpooledExpress = ExpressFactory.unpooledExpress(classLoader);
        try {
            Object value = unpooledExpress.get(express);
         ...
    }
}

最终通过OgnlExpress会调用到Ognl.getValue

public class OgnlExpress implements Express {
    
    @Override
    public Object get(String express) throws ExpressException {
        try {
            return Ognl.getValue(express, context, bindObject);
        } catch (Exception e) {
            logger.error("Error during evaluating the expression:", e);
            throw new ExpressException(express, e);
        }
    }
}

最终的实现交给ognl这个三方库

<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
</dependency>

dump

输出一个类的class文件

public class DumpClassCommand extends AnnotatedCommand {

    @Override
    public void process(CommandProcess process) {
        ...
        ExitStatus status = null;
        // 这个就是之前说过的 字节码打桩 的对象
        Instrumentation inst = process.session().getInstrumentation();
        if (matchedClasses == null || matchedClasses.isEmpty()) {
            ...
        } else if (matchedClasses.size() > limit) {
            ...
        } else {
            status = processMatch(process, effect, inst, matchedClasses);
        }
        ...
    }

    private ExitStatus processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set<Class<?>> matchedClasses) {
        try {
            Map<Class<?>, File> classFiles = dump(inst, matchedClasses);
            ...
        }
    }

    private Map<Class<?>, File> dump(Instrumentation inst, Set<Class<?>> classes) throws UnmodifiableClassException {
        // 这个Transformer对象很重要
        ClassDumpTransformer transformer = null;
        if (directory != null) {
            transformer = new ClassDumpTransformer(classes, new File(directory));
        } else {
            transformer = new ClassDumpTransformer(classes);
        }
        // 这行是 dump 的关键
        InstrumentationUtils.retransformClasses(inst, transformer, classes);
        return transformer.getDumpResult();
    }
}
// ClassFileTransformer 是 这个 java.lang.instrument 包下的
class ClassDumpTransformer implements ClassFileTransformer {
    ...
    // 实现了这个方法
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,  ProtectionDomain protectionDomain, byte[] classfileBuffer)
            throws IllegalClassFormatException {
        if (classesToEnhance.contains(classBeingRedefined)) {
            dumpClassIfNecessary(classBeingRedefined, classfileBuffer);
        }
        return null;
    }

    private void dumpClassIfNecessary(Class<?> clazz, byte[] data) {
        ...
        // 将类字节码写入文件
        try {
            FileUtils.writeByteArrayToFile(dumpClassFile, data);
            dumpResult.put(clazz, dumpClassFile);
        } catch (IOException e) {
            logger.warn("dump class:{} to file {} failed.", className, dumpClassFile, e);
        }
        ...
    }
}
public class InstrumentationUtils {
    private static final Logger logger = LoggerFactory.getLogger(InstrumentationUtils.class);

    public static void retransformClasses(Instrumentation inst, ClassFileTransformer transformer,
            Set<Class<?>> classes) {
        try {
            inst.addTransformer(transformer, true);
            for (Class<?> clazz : classes) {
                try {
                    // 这里是真正的dump
                    inst.retransformClasses(clazz);
                ...
    }
}

redefine

热更新class,可以通过jad->mc->redefine组合拳来热更新代码

public class RedefineCommand extends AnnotatedCommand {
    @Override
    public void process(CommandProcess process) {
        Instrumentation inst = process.session().getInstrumentation();
        ...
        Map<String, byte[]> bytesMap = new HashMap<String, byte[]>();
        for (String path : paths) {
            RandomAccessFile f = null;
            try {
                f = new RandomAccessFile(path, "r");
                final byte[] bytes = new byte[(int) f.length()];
                // 读取class文件
                f.readFully(bytes);
                bytesMap.put(clazzName, bytes);
        ...
        List<ClassDefinition> definitions = new ArrayList<ClassDefinition>();
        // 创建并收集 ClassDefinition
        for (Class<?> clazz : inst.getAllLoadedClasses()) {
            ...
            definitions.add(new ClassDefinition(clazz, bytesMap.get(clazz.getName())));

        ...
        // 调用字节码插桩去 热更新class
        inst.redefineClasses(definitions.toArray(new ClassDefinition[0]));
        ...
    }
}

增强

接下来的命令都是涉及字节码增强了,先看看统一的父类

public abstract class EnhancerCommand extends AnnotatedCommand {
    ...
}

其他相关命令都是继承了这个父类

public class MonitorCommand extends EnhancerCommand {
public class StackCommand extends EnhancerCommand {
public class TimeTunnelCommand extends EnhancerCommand {
public class TraceCommand extends EnhancerCommand {
public class WatchCommand extends EnhancerCommand {

而父类的process方法如下:

    @Override
    public void process(final CommandProcess process) {
        ...
        // start to enhance
        enhance(process);
    }

    protected void enhance(CommandProcess process) {
            ...
            // 获取字节码插桩
            Instrumentation inst = session.getInstrumentation();
            // 获取监听器(重要)
            AdviceListener listener = getAdviceListenerWithId(process);
            ...
            // 创建增强类
            Enhancer enhancer = new Enhancer(listener, listener instanceof InvokeTraceable, skipJDKTrace, getClassNameMatcher(), getMethodNameMatcher());
            // 注册通知监听器
            process.register(listener, enhancer);
            // 进行增强
            effect = enhancer.enhance(inst);
            ...
    }

先来看看监听器,最终会调用到这个方法,需要由子类重写

protected abstract AdviceListener getAdviceListener(CommandProcess process);

需要返回一个监听器的实现,每一个命令返回的不一样

// 这个接口定义了各种生命周期的函数
public interface AdviceListener {
    void create();
    void destroy();
    void before(
            Class<?> clazz, String methodName, String methodDesc,
            Object target, Object[] args) throws Throwable;

    void afterReturning(
            Class<?> clazz, String methodName, String methodDesc,
            Object target, Object[] args,
            Object returnObject) throws Throwable;

    void afterThrowing(
            Class<?> clazz, String methodName, String methodDesc,
            Object target, Object[] args,
            Throwable throwable) throws Throwable;
}
public abstract class AdviceListenerAdapter implements AdviceListener, ProcessAware {
class MonitorAdviceListener extends AdviceListenerAdapter {
// 其他实现省略
...

让我们回到Enhancer,和之前介绍的dump命令一样,也实现了java.lang.instrument的接口

public class Enhancer implements ClassFileTransformer {
    ...
    public synchronized EnhancerAffect enhance(final Instrumentation inst) throws UnmodifiableClassException {
        ...
        try {
            // 和dump命令一样,this(就是Enhancer)需要添加到字节码插桩中
            ArthasBootstrap.getInstance().getTransformerManager().addTransformer(this, isTracing);

            // 默认是批量增强
            if (GlobalOptions.isBatchReTransform) {
                    ...
                    // 调用这个 retransformClasses 就会执行到 Enhancer 重写到方法中
                    inst.retransformClasses(classArray);
                }
            } else {
                ...
    }
    @Override
    public byte[] transform(final ClassLoader inClassLoader, String className, Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            ...
            // 这里的 ClassNode ClassReader 都来自于 arthas中的repackage的模块中
            // 我个人认为就是将其他项目的代码复制过来并重新打包的
            // 所以可以看作使用的仍然是三方库
            ClassNode classNode = new ClassNode(Opcodes.ASM8);
            ClassReader classReader = AsmUtils.toClassNode(classfileBuffer, classNode);
            ...
            DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser();

            final List<InterceptorProcessor> interceptorProcessors = new ArrayList<InterceptorProcessor>();
            // 这里的 SpyInterceptor1 SpyInterceptor2 SpyInterceptor3 可以看成拦截器
            interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor1.class));
            interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor2.class));
            interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor3.class));

            ...
            for (MethodNode methodNode : matchedMethods) {
                ...
                for (InterceptorProcessor interceptor : interceptorProcessors) {
                        try {
                            List<Location> locations = interceptor.process(methodProcessor);
                            for (Location location : locations) {
                                if (location instanceof MethodInsnNodeWare) {
                                    MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
                                    MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();                                    
                                    // 把这些拦截器就是放到一个Map中
                                    AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,  methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
                ...              

                // enter/exist
                AdviceListenerManager.registerAdviceListener(inClassLoader, className, methodNode.name, methodNode.desc,
                        listener);
            }
            ...
    }

repackage


刚刚还有一个没提到的Enhancer中,在这个类加载的时候会进行设置Spy的实现

    private static SpyImpl spyImpl = new SpyImpl();
    static {
        SpyAPI.setSpy(spyImpl);
    }

这是我之前Spy没有继续深入的地方,在SpyAPI中有

    public static abstract class AbstractSpy {
        public abstract void atEnter(Class<?> clazz, String methodInfo, Object target,
                Object[] args);

        public abstract void atExit(Class<?> clazz, String methodInfo, Object target, Object[] args,
                Object returnObject);

        public abstract void atExceptionExit(Class<?> clazz, String methodInfo, Object target,
                Object[] args, Throwable throwable);

        public abstract void atBeforeInvoke(Class<?> clazz, String invokeInfo, Object target);

        public abstract void atAfterInvoke(Class<?> clazz, String invokeInfo, Object target);

        public abstract void atInvokeException(Class<?> clazz, String invokeInfo, Object target, Throwable throwable);
    }

SpyAPI

public class SpyAPI {
    public static final AbstractSpy NOPSPY = new NopSpy();
    private static volatile AbstractSpy spyInstance = NOPSPY;

    public static void setSpy(AbstractSpy spy) {
        spyInstance = spy;
    }
    ... 

    public static void atEnter(Class<?> clazz, String methodInfo, Object target, Object[] args) {
        spyInstance.atEnter(clazz, methodInfo, target, args);
    }

    public static void atExit(Class<?> clazz, String methodInfo, Object target, Object[] args,
            Object returnObject) {
        spyInstance.atExit(clazz, methodInfo, target, args, returnObject);
    }

    public static void atExceptionExit(Class<?> clazz, String methodInfo, Object target,
            Object[] args, Throwable throwable) {
        spyInstance.atExceptionExit(clazz, methodInfo, target, args, throwable);
    }

    public static void atBeforeInvoke(Class<?> clazz, String invokeInfo, Object target) {
        spyInstance.atBeforeInvoke(clazz, invokeInfo, target);
    }

    public static void atAfterInvoke(Class<?> clazz, String invokeInfo, Object target) {
        spyInstance.atAfterInvoke(clazz, invokeInfo, target);
    }

    public static void atInvokeException(Class<?> clazz, String invokeInfo, Object target, Throwable throwable) {
        spyInstance.atInvokeException(clazz, invokeInfo, target, throwable);
    }

就拿一个生命周期atEnter的方法举个例子

public class SpyImpl extends AbstractSpy {
    @Override
    public void atEnter(Class<?> clazz, String methodInfo, Object target, Object[] args) {
        // 从之前保存的Map中去除监听器
        List<AdviceListener> listeners = AdviceListenerManager.queryAdviceListeners(classLoader, clazz.getName(),
                methodName, methodDesc);
        if (listeners != null) {
            for (AdviceListener adviceListener : listeners) {
                    ...
                    // 执行监听器的对应方法
                    adviceListener.before(clazz, methodName, methodDesc, target, args);
                ...
            }
        }
        ...
    }

最后是之前提到的拦截器,这里的@AtEnter,@AtExit,@ExceptionExit.使用的是arthas的另一个模块bytekit


public class SpyInterceptors {

    public static class SpyInterceptor1 {

        @AtEnter(inline = true)
        public static void atEnter(@Binding.This Object target, @Binding.Class Class<?> clazz,
                @Binding.MethodInfo String methodInfo, @Binding.Args Object[] args) {
            SpyAPI.atEnter(clazz, methodInfo, target, args);
        }
    }
    
    public static class SpyInterceptor2 {
        @AtExit(inline = true)
        public static void atExit(@Binding.This Object target, @Binding.Class Class<?> clazz,
                @Binding.MethodInfo String methodInfo, @Binding.Args Object[] args, @Binding.Return Object returnObj) {
            SpyAPI.atExit(clazz, methodInfo, target, args, returnObj);
        }
    }
    
    public static class SpyInterceptor3 {
        @AtExceptionExit(inline = true)
        public static void atExceptionExit(@Binding.This Object target, @Binding.Class Class<?> clazz,
                @Binding.MethodInfo String methodInfo, @Binding.Args Object[] args,
                @Binding.Throwable Throwable throwable) {
            SpyAPI.atExceptionExit(clazz, methodInfo, target, args, throwable);
        }
    }
    ...

而每一个注解都会指定一个parser

@InterceptorParserHander(parserHander = EnterInterceptorProcessorParser.class)
public @interface AtEnter {
    ...
    class EnterInterceptorProcessorParser implements InterceptorProcessorParser {
        @Override
        public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
            ...
            // 而parser会返回一个拦截器的对象
            return InterceptorParserUtils.createInterceptorProcessor(method,
                    locationMatcher,
                    atEnter.inline(),
                    atEnter.suppress(),
                    atEnter.suppressHandler());
        }
    }
}

InterceptorProcessor就是字节码增强的精髓所在了

public class InterceptorProcessor {
    ...
    public List<Location> process(MethodProcessor methodProcessor) throws Exception {
        ...
        for (Location location : locations) {

            // 有三小段代码,1: 保存当前栈上的值的 , 2: 插入的回调的 , 3:恢复当前栈的

            InsnList toInsert = new InsnList();            
            ...

           // 组装好要调用的 static 函数的参数
            for(int i = 0 ; i < argumentTypes.length; ++i) {
                Binding binding = interceptorBindings.get(i);
                binding.pushOntoStack(toInsert, bindingContext);
            }

            toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, interceptorMethodConfig.getOwner(), interceptorMethodConfig.getMethodName(),
                    interceptorMethodConfig.getMethodDesc(), false));

            ...
            // 字节码增强插入try catch
            TryCatchBlock errorHandlerTryCatchBlock = null;
            if( exceptionHandlerConfig != null) {
                LabelNode gotoDest = new LabelNode();
                errorHandlerTryCatchBlock = new TryCatchBlock(methodProcessor.getMethodNode(), exceptionHandlerConfig.getSuppress());
                toInsert.insertBefore(toInsert.getFirst(), errorHandlerTryCatchBlock.getStartLabelNode());
                toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
                toInsert.add(errorHandlerTryCatchBlock.getEndLabelNode());
                errorHandler(methodProcessor, toInsert);
                toInsert.add(gotoDest);
            }
            stackSaveInsnList.add(toInsert);
            if (location.isWhenComplete()) {
                methodProcessor.getMethodNode().instructions.insert(location.getInsnNode(), stackSaveInsnList);
            } else {                
                methodProcessor.getMethodNode().instructions.insertBefore(location.getInsnNode(), stackSaveInsnList);
            }
            ...

            // inline callback
            if(interceptorMethodConfig.isInline()) {

                Class<?> forName = classLoader.loadClass(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
                MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, interceptorMethodConfig.getMethodName(), interceptorMethodConfig.getMethodDesc());

                methodProcessor.inline(interceptorMethodConfig.getOwner(), toInlineMethodNode);
            }
            if(exceptionHandlerConfig != null && exceptionHandlerConfig.isInline()) {

                Class<?> forName = classLoader.loadClass(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
                MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, exceptionHandlerConfig.getMethodName(), exceptionHandlerConfig.getMethodDesc());

                methodProcessor.inline(exceptionHandlerConfig.getOwner(), toInlineMethodNode);
            }
        }
        
        return locations;
    }
}

增强的过程总结下:
EnhancerCommand#process->Enhancer#enhance->Instrumentation#retransformClasses
->Enhancer#transform->收集 SpyInterceptor1 拦截器->InterceptorProcessor#process
->AdviceListenerManager#registerTraceAdviceListener->@AtEnter等切面
->SpyAPI#atEnter->SpyImpl#atEnter->AdviceListener#before

AdviceListener的各个实现用来处理不同的业务。

trace

public class AbstractTraceAdviceListener extends AdviceListenerAdapter {
    @Override
    public void before(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args)
            throws Throwable {
        TraceEntity traceEntity = threadLocalTraceEntity(loader);
        traceEntity.tree.begin(clazz.getName(), method.getName(), -1, false);
        traceEntity.deep++;
        // 开始计算本次方法调用耗时
        threadLocalWatch.start();
    }

    @Override
    public void afterReturning(ClassLoader loader, Class<?> clazz, ArthasMethod method, Object target, Object[] args,
                               Object returnObject) throws Throwable {
        threadLocalTraceEntity(loader).tree.end();
        final Advice advice = Advice.newForAfterRetuning(loader, clazz, method, target, args, returnObject);
        finishing(loader, advice);
    }

    private void finishing(ClassLoader loader, Advice advice) {
        // 本次调用的耗时
        TraceEntity traceEntity = threadLocalTraceEntity(loader);
        double cost = threadLocalWatch.costInMillis();
        if (--traceEntity.deep == 0) {
            try {
                boolean conditionResult = isConditionMet(command.getConditionExpress(), advice, cost);
                ...
                if (conditionResult) {
                    // 满足输出条件
                    process.times().incrementAndGet();
                    process.appendResult(traceEntity.getModel());
                }
            ...
            } finally {
                threadBoundEntity.remove();
            }
        }
    }
}

相关文章

  • Arthas技术内幕-命令

    Arthas是一个使用java编写的无代码侵入的线上诊断工具,通过attach一个jvm进程可以对该jvm进程的内...

  • JVM调优工具Arthas

    arthas官方文档:命令列表 | arthas (aliyun.com)[https://arthas.aliy...

  • Arthas实战

    已经熟悉Arthas操作命令来排查线上问题的同学可以直接跳过。观看下边的文章: arthas源码分析Arthas源...

  • Arthas - Alibaba开源的Java诊断工具

    官方在线教程,可以直接命令行交互模式操作:https://arthas.aliyun.com/doc/arthas...

  • arthas使用

    安装 第一步:下载arthas 安装jar包 日常使用命令 详细参考:https://arthas.aliyun....

  • Arthas实践

    Arthas实践 简介 Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,进一步方便进行问...

  • 排查线上CPU飙高

    1、本案例的排查过程使用的阿里开源的Arthas工具进行的,不使用arthas,使用JDK自带的命令也是可以。 2...

  • arthas常用命令

    执行成功后, arthas提供了一种命令行方式的交互方式, arthas会检测当前服务器上的Javai程,并将进程...

  • 阿尔萨斯-jvm

    Jvm jvm 检测工具 阿尔萨斯(Arthas) 安装 监控命令常用命令 dashboard(仪表盘) Thre...

  • Arthas入门篇

    系列 Arthas入门篇Arthas功能介绍Arthas 启动过程分析 Arthas介绍 Arthas(阿尔萨斯)...

网友评论

      本文标题:Arthas技术内幕-命令

      本文链接:https://www.haomeiwen.com/subject/akrfyktx.html