2.17 Matrix源码解析
2.13.1 IO Canary
- 流程图
 
- 
    找到Hook函数, 通过PLT Hook方案 (ELF Hook方案), hook掉posix的 open、read、write、close函数
- 检查场景
    - 检测主线程IO, 检测条件
        - 操作线程为主线程
- 连续读写耗时超过一定阈值或单次write\read耗时超过一定阈值
 
- 读写Buffer过小, buffer过小导致read/write过多, 检测条件
        - buffer小于一定阈值
- read/write的次数超过一定的阈值
 
- 重复读
        - 同一线程读某个文件的次数超过阈值(5次)
 
 
- 检测主线程IO, 检测条件
        
- Closeable Leak监控(文件、Cursor等未及时close引起的泄露)
    - 
        例 FileInputStream在创建和close调用CloseGuard#open、CloseGuard#close、CloseGuard#warnIfOpen方法, 在CloseGuard#warnIfOpen中调用Reporter#report()public void warnIfOpen() { if (allocationSite == null || !ENABLED) { return; } ... REPORTER.report(message, allocationSite); }- 利用反射将CloseGuard#warnIfOpen中的ENABLED置为true
- 利用动态代理, 将REPORTER替换成指定proxy
 
- 利用反射将
 
- 
        
  // Matrix流程
  1. IOCanaryPlugin#start(), 调用#initDetectorsAndHookers(), 初始化hooker, 包括使用PLT hook, hook掉posix的`open`、`read`、`write`、`close`函数和CloseGuard#REPORTER
  2. IOCanaryJniBridge#install() {
      doHook();
    }
    // 直接找到jni接口
    private static native boolean doHook();
    Java_com_tencent_matrix_iocanary_core_IOCanaryJniBridge_doHook(JNIEnv *env, jclass type) {
        for (int i = 0; i < TARGET_MODULE_COUNT; ++i) {
                // hook掉libopenjdkjvm.so、libjavacore.so、libopenjdk.so
                const char* so_name = TARGET_MODULES[i];
                ...
                loaded_soinfo* soinfo = elfhook_open(so_name);
                ...
                // hook open接口
                elfhook_replace(soinfo, "open", (void*)ProxyOpen, (void**)&original_open);
                elfhook_replace(soinfo, "open64", (void*)ProxyOpen64, (void**)&original_open64);
                bool is_libjavacore = (strstr(so_name, "libjavacore.so") != nullptr);
                if (is_libjavacore) {
                    if (!elfhook_replace(soinfo, "read", (void*)ProxyRead, (void**)&original_read)) {
                        ...
                        if (!elfhook_replace(soinfo, "__read_chk", (void*)ProxyRead, (void**)&original_read)) {
                            ...
                            return false;
                        }
                    }
                    if (!elfhook_replace(soinfo, "write", (void*)ProxyWrite, (void**)&original_write)) {
                        ...
                        if (!elfhook_replace(soinfo, "__write_chk", (void*)ProxyWrite, (void**)&original_write)) {
                            ...
                            return false;
                        }
                    }
                }
                // hook close接口
                elfhook_replace(soinfo, "close", (void*)ProxyClose, (void**)&original_close);
                elfhook_close(soinfo);
            }
            return true;
    }
  3. CloseGuardHooker#hook() {
      ...
      boolean hookRet = tryHook();
      ...
    }
    private boolean tryHook() {
        ...
            // 反射CloseGuard.class
            Class<?> closeGuardCls = Class.forName("dalvik.system.CloseGuard");
            Class<?> closeGuardReporterCls = Class.forName("dalvik.system.CloseGuard$Reporter");
            Method methodGetReporter = closeGuardCls.getDeclaredMethod("getReporter");
            Method methodSetReporter = closeGuardCls.getDeclaredMethod("setReporter", closeGuardReporterCls);
            Method methodSetEnabled = closeGuardCls.getDeclaredMethod("setEnabled", boolean.class);
            // 获取CloseGuard#Repoeter实例
            sOriginalReporter = methodGetReporter.invoke(null);
            methodSetEnabled.invoke(null, true);
            // open matrix close guard also
            MatrixCloseGuard.setEnabled(true);
            ClassLoader classLoader = closeGuardReporterCls.getClassLoader();
            ...
            // 通过反射调用CloseGuard#setReporter, 更改Reporter
            methodSetReporter.invoke(null, Proxy.newProxyInstance(classLoader,
                new Class<?>[]{closeGuardReporterCls},
                new IOCloseLeakDetector(issueListener, sOriginalReporter)));
            return true;
        ...
    }
2.13.2 Trace Canary
- 编译期
    - 编译期的任务transformClassesWithDexTask, 将全局class文件作为输入, 例如ASM工具, 在指定方法前后增加MethodBeat#i()和方法结束增加MethodBeat#o()
- 流程图(参考Matrix官网)
 
 
- 编译期的任务
- 运行期
    - 函数执行前后都会调用MethodBeat#i/o(), 再将method id, 及时间offset放到long类型变量中
- 通过Choreographer#postFrameCallback注册监听, 在doFrame()帧与帧时间差是否超过阈值, 超过则获取数组index前的所有数据上报, ANR则在doFrame()每一帧到来时重置定时器, 如果5s没cancel, 则dump ANR和buffer数据
 
- 函数执行前后都会调用
- Matrix源码流程 (还是从编译器和运行期分析)
    - 
        编译器 MatrixTraceTransform#transform(TransformInvocation transformInvocation) { ... doTransform(transformInvocation); // hack ... } private void doTransform(TransformInvocation transformInvocation) throws ExecutionException, InterruptedException { ... /** * step 1, 收集jar */ ... /** * step 2, 收集方法 */ ... /** * step 3, 在指定方法插入MethodBeat#i/o() */ MethodTracer methodTracer = new MethodTracer(executor, mappingCollector, config, methodCollector.getCollectedMethodMap(), methodCollector.getCollectedClassExtendMap()); methodTracer.trace(dirInputOutMap, jarInputOutMap); } public void trace(Map<File, File> srcFolderList, Map<File, File> dependencyJarList) throws ExecutionException, InterruptedException { ... // 跟踪src的方法, 逻辑在innerTraceMethodFromSrc(), 直接看innerTraceMethodFromSrc() traceMethodFromSrc(srcFolderList, futures); traceMethodFromJar(dependencyJarList, futures); ... } private void innerTraceMethodFromSrc(File input, File output) { // 通过ASM, 插入方法, 逻辑在TraceClassAdapter类中 ClassReader classReader = new ClassReader(is); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor classVisitor = new TraceClassAdapter(Opcodes.ASM5, classWriter); classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES); ... os.write(classWriter.toByteArray()); } // TraceMethodAdapter.java protected void onMethodEnter() { ... // 进入指定方法插入AppMethodBeat#i() mv.visitLdcInsn(traceMethod.id); mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "i", "(I)V", false); } protected void onMethodExit(int opcode) { ... // 退出指定方法插入AppMethodBeat#o() mv.visitLdcInsn(traceMethod.id); mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "o", "(I)V", false); }
- 
        运行期 
 TracePlugin#start() { // 启动跟踪ANR anrTracer.onStartTrace(); // 启动跟踪卡顿 frameTracer.onStartTrace(); } // AnrTracer public void dispatchBegin(long beginMs, long cpuBeginMs, long token) { ... // 每帧刷新时, remove anr dump 任务 if (null != anrTask) { anrHandler.removeCallbacks(anrTask); } // anr dump 任务 anrTask = new AnrHandleTask(AppMethodBeat.getInstance().maskIndex("AnrTracer#dispatchBegin"), token); ... // 每秒发送任务, 如果5秒后没有被remove anrHandler.postDelayed(anrTask, Constants.DEFAULT_ANR); } // FrameTracer public void doFrame(String focusedActivityName, long start, long end, long frameCostMs, long inputCostNs, long animationCostNs, long traversalCostNs) { // 调用IDoFrameListener#doFrameAsync, 默认IDoFrameListener即FPSCollector notifyListener(focusedActivityName, frameCostMs); } // FPSCollector public void doFrameAsync(String focusedActivityName, long frameCost, int droppedFrames) { ... // 判断执行耗时是否大于阈值, 如果是则上报 if (item.sumFrameCost >= timeSliceMs) { // report map.remove(focusedActivityName); item.report(); } }
- 
        
2.13.3 Resource Canary
- 实现原理类似LeakCanary, 在ActivityLifecycleCallbacks#onActivityDestroyed把Activity放到WeakReference, 在ActivityLifecycleCallbacks#onActivityStarted调用Runtime#gc()GC, 如果Activity未被回收则说明存在泄露
