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();
}
}
}
}
网友评论