背景
evil字面意思是邪恶的、有害的意思。这个方法很邪恶就是该方法存在耗时多的情况,糟老头子坏的很呐!!因此,matrix的目的就是想去统计每个方法的执行耗时。
大体的流程是这样的:在calss字节码转dex文件阶段,通过自定义transform插件,利用ASM工具来操作修改.class文件。在每个方法执行前后插入AppMethodBeat.i
和AppMethodBeat.o
方法,在运行期计算两个的差值就得到方法的耗时。
优点:
- 兼容性好 无需通过hook手段
- 优化插桩性能 在方法收集阶段会主动过滤简单的set/get方法,以及一些匿名的构造函数
缺点:
- 无法统计到系统内执行的函数,但一般卡顿都是由我们自己的App代码导致,所以影响不大
我们先来看下编译期的基本实现(实现细节后续再单独分析):
private class TraceMethodAdapter extends AdviceAdapter {
private final String methodName;
private final String name;
private final String className;
private final boolean hasWindowFocusMethod;
private final boolean isNeedTrace;
private final boolean isActivityOrSubClass;
// ...省略
@Override
protected void onMethodEnter() {
//注意:并不是所有的方法都执行插桩,只有是我们需要收集的方法才会走插桩逻辑,优化插桩性能!
TraceMethod traceMethod = collectedMethodMap.get(methodName);
if (traceMethod != null) {
traceMethodCount.incrementAndGet();
mv.visitLdcInsn(traceMethod.id);
//1, 插入 AppMethodBeat.i 方法
mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "i", "(I)V", false);
if (checkNeedTraceWindowFocusChangeMethod(traceMethod)) {
// 2, 插入AppMethodBeat.at方法
traceWindowFocusChangeMethod(mv, className);
}
}
}
@Override
protected void onMethodExit(int opcode) {
TraceMethod traceMethod = collectedMethodMap.get(methodName);
if (traceMethod != null) {
traceMethodCount.incrementAndGet();
mv.visitLdcInsn(traceMethod.id);
//3, 插入 AppMethodBeat.o 方法
mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "o", "(I)V", false);
}
}
//...省略
}
traceWindowFocusChangeMethod():
private void traceWindowFocusChangeMethod(MethodVisitor mv, String classname) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "at", "(Landroid/app/Activity;Z)V", false);
}
- 总结
编译期插桩主要做了三件事(图片来自官方):
- 在
onMethodEnter()
中插入AppMethodBeat.i
方法 - 收集所有的Activity及其子类,在
onWindowFocusChange
方法中插入AppMethodBeat.at方法 - 在
onMethodExit()
插入AppMethodBeat.o
方法 - 注意:并不是所有的方法都执行插桩,只有属于我们需要收集的方法才会走插桩逻辑,优化插桩性能!
onMethodEnter() App所有方法的进入都会回调; onMethodExit App所有方法的退出都会回调
OK,编译插桩的基本原理已经清楚,现在我们回到EvilMethodTracer
类的分析。
一、构造方法
public EvilMethodTracer(TraceConfig config) {
this.config = config;
// evil阈值 默认是700ms,外部可以设置
this.evilThresholdMs = config.getEvilThresholdMs();
//evil 开关
this.isEvilMethodTraceEnable = config.isEvilMethodTraceEnable();
}
简单,配置了两个变量值: evil阈值
、evil开关
。
二、触发时机 onAlive
在第三篇文章分析中我们知道,TracePlugin
的start()
方法会调用trace
的onStartTrace
方法。
@Override
public void start() {
//省略...
if (traceConfig.isEvilMethodTraceEnable()) {
evilMethodTracer.o