布景

关于APM项目,诸如像 Debug 日志,运行耗时监控等都会陆陆续续加入到源码中,跟着功用的增多,这些监控日志代码在某种程度上会影响甚至是搅扰事务代码的阅读,Matrix如何做到自动化在代码中自动刺进办法呢?

“插桩”就映入眼帘了,实质的思想都是 AOP,在编译或运行时动态注入代码。如下是app的包流程

腾讯性能监控框架Matrix源码分析(十二)插桩 之MatrixTrace

在Transform的过程中,从java到class,class到dex,dex到apk咱们都能够hook作插桩操作,Matrix这儿是从编译后的class 字节码下手,结合更高效的操作库ASM完结的。

很走运,Google 官方在 Android Gradle 的 1.5.0 版别今后供给了 Transfrom API, 答应第三方自定义插件在打包 dex 文件之前的编译过程中操作 .class 文件,所以这儿先要做的便是完结一个自定义的 Transform 进行.class文件遍历拿到一切办法,修正完结对原文件进行替换。这个功用叫做Android Gradle Plugin,简称AGP,具体相关常识咱们能够自行百度,下面开始源码剖析

Matrix-ASM插桩插件解析

matrix-plugin插件有两个功用模块:

  1. trace:给每个需求插桩的办法分配唯一的办法id,并在办法的进出口刺进一段代码,为TraceCanary模块剖析实际问题供给数据支撑。
  2. removeUnusedResources:在组成apk之前移除apkchecker检测出来的没有用到的资源清单,能够自动化的减少终究包体积巨细。

1. AGP的进口

程序都有main办法作为程序的进口办法,那么Android Gradle Plugin(AGP)的进口在哪里呢。

其实AGP的进口文件也比较固定,位于src/main/resources/META-INF/gradle-plugins目录下。在matrix-gradle-plugin的对应目录下咱们发现了一个文件:com.tencent.matrix-plugin.properties
.properties是文件的后缀名,因而这个文件的名称便是com.tencent.matrix-plugin。咱们在运用插件的时分,填上这个名字就行了。

咱们在sample中印证一下,看看在sample中是如何运用matrix-plugin的:apply plugin: 'com.tencent.matrix-plugin'

*.properties里边的写法也很固定:implementation-class=com.tencent.matrix.plugin.MatrixPlugin。这表明了这个Plugin真实的完结类是com.tencent.matrix.plugin.MatrixPlugin

因而,咱们能够直奔MatrixPlugin类看里边的完结了

有些库会供给多个插件,完结上只需求在src/main/resources/META-INF/gradle-plugins目录下放多个.properties文件,每个文件指定自己的完结类即可。

腾讯性能监控框架Matrix源码分析(十二)插桩 之MatrixTrace

2. MatrixPlugin

自定义的插件需求完结了Plugin接口,并在apply办法里边完结要做的事情。

MatrixPlugin中干了两件事。

  1. 首先是在项意图装备阶段经过project.extensions.create(name, type)办法将插件的自定义装备项以对应的type创立并保存起来,之后能够经过project.name获取到对应的装备项。
  2. 其次在项目装备结束的回调project.afterEvaluate(这个回调会在tasks履行之前进行履行)中,将要履行使命的刺进到task链中并设置依靠联系。这样跟着构建使命的一个个履行,会履行到咱们的代码。
    MatrixPlugin的两个子功用模块来说,这一步完结的方式有一点区别。trace模块由于是对一切有用的办法进行插桩,需求在proguard等使命完结之后在履行,而这个时序不太好经过依靠联系进行确认,因而选择了hook了class打包成dex的这一过程,终究达到了先插桩后打dex的意图。而removeUnusedResources只需求在将一切资源打包成apk之前履行即可。这两个子模块将会分开评论。
class MatrixPlugin : Plugin<Project> {
    companion object {
        const val TAG = "Matrix.Plugin"
    }
    override fun apply(project: Project) {
        // 创立并保存自定义装备项
        val matrix = project.extensions.create("matrix", MatrixExtension::class.java)
         //办法功用插桩
        val traceExtension = (matrix as ExtensionAware).extensions.create("trace", MatrixTraceExtension::class.java)
        //移除资源功用插桩
        val removeUnusedResourcesExtension = matrix.extensions.create("removeUnusedResources", MatrixRemoveUnusedResExtension::class.java)
        if (!project.plugins.hasPlugin("com.android.application")) {
            throw GradleException("Matrix Plugin, Android Application plugin required.")
        }
        project.afterEvaluate {
            Log.setLogLevel(matrix.logLevel)
        }
        MatrixTasksManager().createMatrixTasks(
                project.extensions.getByName("android") as AppExtension,
                project,
                traceExtension,
                removeUnusedResourcesExtension
        )
    }
}

跟进MatrixTasksManager().createMatrixTasks()

fun createMatrixTasks(android: AppExtension,
                      project: Project,
                      traceExtension: MatrixTraceExtension,
                      removeUnusedResourcesExtension: MatrixRemoveUnusedResExtension) {
    createMatrixTraceTask(android, project, traceExtension)
    createRemoveUnusedResourcesTask(android, project, removeUnusedResourcesExtension)
}

createRemoveUnusedResourcesTask

private fun createRemoveUnusedResourcesTask(
        android: AppExtension,
        project: Project,
        removeUnusedResourcesExtension: MatrixRemoveUnusedResExtension) {
    project.afterEvaluate {
        if (!removeUnusedResourcesExtension.enable) {
            return@afterEvaluate
        }
        android.applicationVariants.all { variant ->
            if (Util.isNullOrNil(removeUnusedResourcesExtension.variant) ||
                    variant.name.equals(removeUnusedResourcesExtension.variant, true)) {
                Log.i(TAG, "RemoveUnusedResourcesExtension: %s", removeUnusedResourcesExtension)
                //兼容v1 v2
                val removeUnusedResourcesTaskProvider = if (removeUnusedResourcesExtension.v2) {
                    val action = RemoveUnusedResourcesTaskV2.CreationAction(
                            CreationConfig(variant, project), removeUnusedResourcesExtension
                    )
                    project.tasks.register(action.name, action.type, action)
                } else {
                    val action = RemoveUnusedResourcesTask.CreationAction(
                            CreationConfig(variant, project), removeUnusedResourcesExtension
                    )
                    project.tasks.register(action.name, action.type, action)
                }
                // assembleProvider依靠于removeUnusedResourcesTaskProvider,即assembleProvider依靠于removeUnusedResourcesTaskProvider先履行
                variant.assembleProvider?.configure {
                    it.dependsOn(removeUnusedResourcesTaskProvider)
                }
                // removeUnusedResourcesTaskProvider依靠于packageApplicationProvider,即removeUnusedResourcesTaskProvider先履行
                removeUnusedResourcesTaskProvider.configure {
                    it.dependsOn(variant.packageApplicationProvider)
                }
                // 也便是说,履行顺序为packageApplicationProvider -> removeUnusedResourcesTaskProvider -> assemble
            }
        }
    }
}

履行顺序为packageApplicationProvider -> removeUnusedResourcesTaskProvider -> assemble

createMatrixTraceTask()

private fun createMatrixTraceTask(
        android: AppExtension,
        project: Project,
        traceExtension: MatrixTraceExtension) {
    MatrixTraceCompat().inject(android, project, traceExtension)
}

依据不同版别的AGP和功用进行了相应的适配,终究都会走到一致的处理MatrixTrace

fun inject(appExtension: AppExtension, project: Project, extension: MatrixTraceExtension) {
    when {
        VersionsCompat.lessThan(AGPVersion.AGP_3_6_0) ->
            legacyInject(appExtension, project, extension)
        VersionsCompat.greatThanOrEqual(AGPVersion.AGP_4_0_0) -> {
            if (project.extensions.extraProperties.has(LEGACY_FLAG) &&
                (project.extensions.extraProperties.get(LEGACY_FLAG) as? String?) == "true") {
                legacyInject(appExtension, project, extension)
            } else {
                traceInjection!!.inject(appExtension, project, extension)
            }
        }
        else -> Log.e(TAG, "Matrix does not support Android Gradle Plugin " +
                "${VersionsCompat.androidGradlePluginVersion}!.")
    }
}

然后走入

private fun legacyInject(appExtension: AppExtension,
                         project: Project,
                         extension: MatrixTraceExtension) {
    project.afterEvaluate {
        if (!extension.isEnable) {
            return@afterEvaluate
        }
        appExtension.applicationVariants.all {
            MatrixTraceLegacyTransform.inject(extension, project, it)
        }
    }
}

3. MatrixTraceTransform

上面MatrixPlugin的代码中能够看到,关于trace模块调用了MatrixTraceTransform#inject办法。

在该办法中会遍历task,找到指定名称的task,替换里边的transform目标为MatrixTraceTransform目标。

class MatrixTraceLegacyTransform(
        private val project: Project,
        private val config: Configuration,
        private val origTransform: Transform
) : Transform() {
    companion object {
        const val TAG = "Matrix.TraceLegacyTransform"
        fun inject(extension: MatrixTraceExtension, project: Project, variant: BaseVariant) {
            //初始化装备文件
            val mappingOut = Joiner.on(File.separatorChar).join(
                    project.buildDir.absolutePath,
                    FD_OUTPUTS,
                    "mapping",
                    variant.dirName)
            val traceClassOut = Joiner.on(File.separatorChar).join(
                    project.buildDir.absolutePath,
                    FD_OUTPUTS,
                    "traceClassOut",
                    variant.dirName)
            val config = Configuration.Builder()
                    .setPackageName(variant.applicationId)
                    .setBaseMethodMap(extension.baseMethodMapFile)
                    .setBlockListFile(extension.blackListFile)
                    .setMethodMapFilePath("$mappingOut/methodMapping.txt")
                    .setIgnoreMethodMapFilePath("$mappingOut/ignoreMethodMapping.txt")
                    .setMappingPath(mappingOut)
                    .setTraceClassOut(traceClassOut)
                    .setSkipCheckClass(extension.isSkipCheckClass)
                    .build()
            val hardTask = getTransformTaskName(extension.customDexTransformName, variant.name)
            //在该办法中会遍历task,找到指定名称的task,替换里边的transform目标为`MatrixTraceLegacyTransform`目标。
            for (task in project.tasks) {
                for (str in hardTask) {
                    if (task.name.equals(str, ignoreCase = true) && task is TransformTask) {
                        Log.i(TAG, "successfully inject task:" + task.name)
                        //反射搞起来
                        val field = TransformTask::class.java.getDeclaredField("transform")
                        field.isAccessible = true
                        field.set(task, MatrixTraceLegacyTransform(project, config, task.transform))
                        break
                    }
                }
            }
        }
private fun getTransformTaskName(customDexTransformName: String?, buildTypeSuffix: String): Array<String> {
    return if (!Util.isNullOrNil(customDexTransformName)) {
        arrayOf(
                "${customDexTransformName}For$buildTypeSuffix"
        )
    } else {
        arrayOf(
                "transformClassesWithDexBuilderFor$buildTypeSuffix",
                "transformClassesWithDexFor$buildTypeSuffix"
        )
    }
}

extension.getCustomDexTransformName()一般没有装备,以release版别为例,所以终究要hook的task为transformClassesWithDexBuilderForRelease以及transformClassesWithDexForRelease,对应的transform为DexTransform

此外,MatrixTraceLegacyTransform还有几个要素,即完结其getInputTypesgetOutputTypesgetScopesgetNameisIncremental以及最重要的transform办法。换句话说,自定义transform需求指定什么范围的什么输入,经过怎么样的transform,最终输出什么。

override fun getName(): String {
    return TAG
}
override fun getInputTypes(): Set<QualifiedContent.ContentType> {
    return TransformManager.CONTENT_CLASS
}
override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
    return TransformManager.SCOPE_FULL_PROJECT
}
override fun isIncremental(): Boolean {
    return true
}

之后走到transform

override fun transform(transformInvocation: TransformInvocation) {
    super.transform(transformInvocation);
    val start = System.currentTimeMillis();
    try {
        //自定义插桩逻辑
        doTransform(transformInvocation); // hack
    } catch (e: Throwable) {
        e.printStackTrace()
    }
    val cost = System.currentTimeMillis() - start;
    val begin = System.currentTimeMillis();
    //体系原始的操作
    origTransform.transform(transformInvocation);
    val origTransformCost = System.currentTimeMillis() - begin;
    Log.i("Matrix.$name", "[transform] cost time: %dms %s:%sms MatrixTraceTransform:%sms",
            System.currentTimeMillis() - start, origTransform.javaClass.simpleName, origTransformCost, cost);
}

进入doTransform

private fun doTransform(invocation: TransformInvocation) {
    val start = System.currentTimeMillis()
    val isIncremental = invocation.isIncremental && this.isIncremental
    //初始化容器,兼并源码和Jar依靠文件
    val changedFiles = ConcurrentHashMap<File, Status>()
    val inputFiles = ArrayList<File>()
    val fileToInput = ConcurrentHashMap<File, QualifiedContent>()
    for (input in invocation.inputs) {
        //源码依靠文件
        for (directoryInput in input.directoryInputs) {
            changedFiles.putAll(directoryInput.changedFiles)
            inputFiles.add(directoryInput.file)
            fileToInput[directoryInput.file] = directoryInput
        }
        //jar引证文件
        for (jarInput in input.jarInputs) {
            changedFiles[jarInput.file] = jarInput.status
            inputFiles.add(jarInput.file)
            fileToInput[jarInput.file] = jarInput
        }
    }
    if (inputFiles.size == 0) {
        Log.i(TAG, "Matrix trace do not find any input files")
        return
    }
    val legacyReplaceChangedFile = { inputDir: File, map: Map<File, Status> ->
        replaceChangedFile(fileToInput[inputDir] as DirectoryInput, map)
        inputDir as Object
    }
    var legacyReplaceFile = { input: File, output: File ->
        replaceFile(fileToInput[input] as QualifiedContent, output)
        input as Object
    }
    MatrixTrace(
            ignoreMethodMapFilePath = config.ignoreMethodMapFilePath,
            methodMapFilePath = config.methodMapFilePath,
            baseMethodMapPath = config.baseMethodMapPath,
            blockListFilePath = config.blockListFilePath,
            mappingDir = config.mappingDir,
            project = project
    ).doTransform(
            classInputs = inputFiles,
            changedFiles = changedFiles,
            isIncremental = isIncremental,
            skipCheckClass = config.skipCheckClass,
            traceClassDirectoryOutput = File(config.traceClassOut),
            inputToOutput = ConcurrentHashMap(),
            legacyReplaceChangedFile = legacyReplaceChangedFile,
            legacyReplaceFile = legacyReplaceFile,
            uniqueOutputName = true
    )
    val cost = System.currentTimeMillis() - start
    Log.i(TAG, " Insert matrix trace instrumentations cost time: %sms.", cost)
}

终究到了最中心的类MatrixTrace

doTransform 分为3步,咱们一步一步介绍。

榜首步
var start = System.currentTimeMillis()
val futures = LinkedList<Future<*>>()
val mappingCollector = MappingCollector()
val methodId = AtomicInteger(0)
val collectedMethodMap = ConcurrentHashMap<String, TraceMethod>()
//处理混杂和黑名单
futures.add(executor.submit(ParseMappingTask(
        mappingCollector, collectedMethodMap, methodId, config)))
// 贮存 class 输入输出联系的
val dirInputOutMap = ConcurrentHashMap<File, File>()
// 贮存 jar 输入输出联系的
val jarInputOutMap = ConcurrentHashMap<File, File>()
// 处理输入
// 首要是将输入类的字段替换掉,替换到指定的输出方位
// 里边做了增量的处理
for (file in classInputs) {
    if (file.isDirectory) {
        futures.add(executor.submit(CollectDirectoryInputTask(
                directoryInput = file,
                mapOfChangedFiles = changedFiles,
                mapOfInputToOutput = inputToOutput,
                isIncremental = isIncremental,
                traceClassDirectoryOutput = traceClassDirectoryOutput,
                legacyReplaceChangedFile = legacyReplaceChangedFile,
                legacyReplaceFile = legacyReplaceFile,
                // result
                resultOfDirInputToOut = dirInputOutMap
        )))
    } else {
        val status = Status.CHANGED
        futures.add(executor.submit(CollectJarInputTask(
                inputJar = file,
                inputJarStatus = status,
                inputToOutput = inputToOutput,
                isIncremental = isIncremental,
                traceClassFileOutput = traceClassDirectoryOutput,
                legacyReplaceFile = legacyReplaceFile,
                uniqueOutputName = uniqueOutputName,
                // result
                resultOfDirInputToOut = dirInputOutMap,
                resultOfJarInputToOut = jarInputOutMap
        )))
    }
}
for (future in futures) {
    future.get()
}
futures.clear()

解析 Proguard mapping file 解析黑名单 加载已分配 method id 的函数列表

class ParseMappingTask
constructor(
        private val mappingCollector: MappingCollector,
        private val collectedMethodMap: ConcurrentHashMap<String, TraceMethod>,
        private val methodId: AtomicInteger,
        private val config: Configuration
) : Runnable {
    override fun run() {
        val start = System.currentTimeMillis()
        // 解析 Proguard mapping file
        val mappingFile = File(config.mappingDir, "mapping.txt")
        if (mappingFile.isFile) {
            val mappingReader = MappingReader(mappingFile)
            mappingReader.read(mappingCollector)
        }
        // 解析黑名单
        val size = config.parseBlockFile(mappingCollector)
        val baseMethodMapFile = File(config.baseMethodMapPath)
        // 加载已分配 method id 的函数列表
        getMethodFromBaseMethod(baseMethodMapFile, collectedMethodMap)
        retraceMethodMap(mappingCollector, collectedMethodMap)
        Log.i(TAG, "[ParseMappingTask#run] cost:%sms, black size:%s, collect %s method from %s",
                System.currentTimeMillis() - start, size, collectedMethodMap.size, config.baseMethodMapPath)
    }

创立 ParseMappingTask,读取 mapping 文件,由于这个时分 class 已经被混杂了,之所以选在混杂后,是由于防止插桩导致某些编译器优化失效,等编译器优化完了再插桩。读取 mapping 文件有几个用处,榜首,需求输出某些信息,肯定不能输出混杂后的class信息。第二,装备文件的类是没有混杂过的,读取进来需求能够转换为混杂后的,才能处理。

创立了两个 map,贮存 class jar 文件的输入输出方位。 创立 CollectDirectoryInputTask,收集 class 文件到 map。 创立 CollectJarInputTask,收集 jar 文件到 map。

CollectDirectoryInputTask 与 CollectJarInputTask 里边还用到了反射,更改了其输出目录到 build/output/traceClassout。所以咱们能够在这儿看到插桩后的类。这两个类就做了这些事,就不贴代码了。

后边便是调用 future 的 get 办法,等候这儿 task 履行完结,再进行下一步。

第二步
/**
 * step 2
 */
start = System.currentTimeMillis()
val methodCollector = MethodCollector(executor, mappingCollector, methodId, config, collectedMethodMap)
methodCollector.collect(dirInputOutMap.keys, jarInputOutMap.keys)
public void collect(Set<File> srcFolderList, Set<File> dependencyJarList) throws ExecutionException, InterruptedException {
    List<Future> futures = new LinkedList<>();
    // 将 文件/目录下 class ,全部放到 list 中
    for (File srcFile : srcFolderList) {
        ArrayList<File> classFileList = new ArrayList<>();
        if (srcFile.isDirectory()) {
            listClassFiles(classFileList, srcFile);
        } else {
            classFileList.add(srcFile);
        }
    // 每个 class 都分配一个 CollectSrcTask
        for (File classFile : classFileList) {
            futures.add(executor.submit(new CollectSrcTask(classFile)));
        }
    }
    // 每个 jar 都分配一个 CollectSrcTask
    for (File jarFile : dependencyJarList) {
        futures.add(executor.submit(new CollectJarTask(jarFile)));
    }
    //等候使命完结
    for (Future future : futures) {
        future.get();
    }
    futures.clear();
    futures.add(executor.submit(new Runnable() {
        @Override
        public void run() {
         // 将被插桩的办法名存入ignoreMethodMapping.txt 中
            saveIgnoreCollectedMethod(mappingCollector);
        }
    }));
    futures.add(executor.submit(new Runnable() {
        @Override
        public void run() {
         // 将被插桩的 办法名 存入 methodMapping.txt 中
            saveCollectedMethod(mappingCollector);
        }
    }));
    for (Future future : futures) {
        future.get();
    }
    futures.clear();
}

CollectSrcTask和CollectJarTask差不多,咱们看其中一个

@Override
public void run() {
    ...
        is = new FileInputStream(classFile);
        // ASM 的运用
        // 拜访者模式,便是将对数据结构拜访的操作分离出去
        // 代价便是需求将数据结构本身传递进来
        ClassReader classReader = new ClassReader(is);
        // 修正字节码,有时分需求改动本地变量数与stack巨细,自己核算费事,能够直接运用这个自动核算
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        // ASM5 api版别
        ClassVisitor visitor = new TraceClassAdapter(Opcodes.ASM5, classWriter);
        classReader.accept(visitor, 0);
     ...
}

最终走到,TraceClassAdapter:

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    super.visit(version, access, name, signature, superName, interfaces);
    this.className = name;
    // 是否抽象类
    if ((access & Opcodes.ACC_ABSTRACT) > 0 || (access & Opcodes.ACC_INTERFACE) > 0) {
        this.isABSClass = true;
    }
    // 保存父类,便于剖析继承联系
    collectedClassExtendMap.put(className, superName);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
                                 String signature, String[] exceptions) {
    // 越过抽象类,接口
    if (isABSClass) {
        return super.visitMethod(access, name, desc, signature, exceptions);
    } else {
        if (!hasWindowFocusMethod) {
            // 是否有 onWindowFocusChanged 办法,针对 activity 的
            hasWindowFocusMethod = isWindowFocusChangeMethod(name, desc);
        }
        return new CollectMethodNode(className, access, name, desc, signature, exceptions);
    }
}

这儿边没有首要逻辑,首要逻辑在 CollectMethodNode:

@Override
public void visitEnd() {
    super.visitEnd();
    TraceMethod traceMethod = TraceMethod.create(0, access, className, name, desc);
    // 是否结构办法
    if ("<init>".equals(name)) {
        isConstructor = true;
    }
    // 判别类是否 被装备在了 黑名单中
    boolean isNeedTrace = isNeedTrace(configuration, traceMethod.className, mappingCollector);
    //为了减少插桩量及功能损耗,经过遍历`class`办法指令集,判别扫描的函数是否只含有`PUT/READ FIELD`等简略的指令,来过滤一些默许或匿名结构函数,以及`get/set`等简略不耗时函数
    if ((isEmptyMethod() || isGetSetMethod() || isSingleMethod())
            && isNeedTrace) {
        ignoreCount.incrementAndGet();
        // 存入 ignore map
        collectedIgnoreMethodMap.put(traceMethod.getMethodName(), traceMethod);
        return;
    }
    // 不在黑名单中
    if (isNeedTrace && !collectedMethodMap.containsKey(traceMethod.getMethodName())) {
        traceMethod.id = methodId.incrementAndGet();
        // 存入 map
        collectedMethodMap.put(traceMethod.getMethodName(), traceMethod);
        incrementCount.incrementAndGet();
    } else if (!isNeedTrace && !collectedIgnoreMethodMap.containsKey(traceMethod.className)) {
        ignoreCount.incrementAndGet();
        // 存入 ignore map
        collectedIgnoreMethodMap.put(traceMethod.getMethodName(), traceMethod);
    }
}

能够看到,终究是将 class 中满意条件的办法,都存入到了 collectedMethodMap,忽略的办法存入了 collectedIgnoreMethodMap。

第三步
/**
 * step 3
 */
start = System.currentTimeMillis()
val methodTracer = MethodTracer(executor, mappingCollector, config, methodCollector.collectedMethodMap, methodCollector.collectedClassExtendMap)
//兼并src和jar
val allInputs = ArrayList<File>().also {
    it.addAll(dirInputOutMap.keys)
    it.addAll(jarInputOutMap.keys)
}
val traceClassLoader = TraceClassLoader.getClassLoader(project, allInputs)
//进行插桩
methodTracer.trace(dirInputOutMap, jarInputOutMap, traceClassLoader, skipCheckClass)

子线程履行

public void trace(Map<File, File> srcFolderList, Map<File, File> dependencyJarList, ClassLoader classLoader, boolean ignoreCheckClass) throws ExecutionException, InterruptedException {
    List<Future> futures = new LinkedList<>();
    traceMethodFromSrc(srcFolderList, futures, classLoader, ignoreCheckClass);
    traceMethodFromJar(dependencyJarList, futures, classLoader, ignoreCheckClass);
    for (Future future : futures) {
        future.get();
    }
    if (traceError) {
        throw new IllegalArgumentException("something wrong with trace, see detail log before");
    }
    futures.clear();
}

逻辑差不多,这块看src

private void traceMethodFromSrc(Map<File, File> srcMap, List<Future> futures, final ClassLoader classLoader, final boolean skipCheckClass) {
    if (null != srcMap) {
        for (Map.Entry<File, File> entry : srcMap.entrySet()) {
            futures.add(executor.submit(new Runnable() {
                @Override
                public void run() {
                    innerTraceMethodFromSrc(entry.getKey(), entry.getValue(), classLoader, skipCheckClass);
                }
            }));
        }
    }
}

进行文件的真实插桩操作 中心的改动在TraceClassAdapter

private void innerTraceMethodFromSrc(File input, File output, ClassLoader classLoader, boolean ignoreCheckClass) {
    ArrayList<File> classFileList = new ArrayList<>();
    if (input.isDirectory()) {
        listClassFiles(classFileList, input);
    } else {
        classFileList.add(input);
    }
    for (File classFile : classFileList) {
        InputStream is = null;
        FileOutputStream os = null;
        try {
            final String changedFileInputFullPath = classFile.getAbsolutePath();
            final File changedFileOutput = new File(changedFileInputFullPath.replace(input.getAbsolutePath(), output.getAbsolutePath()));
            if (changedFileOutput.getCanonicalPath().equals(classFile.getCanonicalPath())) {
                throw new RuntimeException("Input file(" + classFile.getCanonicalPath() + ") should not be same with output!");
            }
            if (!changedFileOutput.exists()) {
                changedFileOutput.getParentFile().mkdirs();
            }
            changedFileOutput.createNewFile();
            if (MethodCollector.isNeedTraceFile(classFile.getName())) {
                is = new FileInputStream(classFile);
                ClassReader classReader = new ClassReader(is);
                ClassWriter classWriter = new TraceClassWriter(ClassWriter.COMPUTE_FRAMES, classLoader);
                ClassVisitor classVisitor = new TraceClassAdapter(AgpCompat.getAsmApi(), classWriter);
                classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
                is.close();
                byte[] data = classWriter.toByteArray();
                if (!ignoreCheckClass) {
                    try {
                        ClassReader cr = new ClassReader(data);
                        ClassWriter cw = new ClassWriter(0);
                        ClassVisitor check = new CheckClassAdapter(cw);
                        cr.accept(check, ClassReader.EXPAND_FRAMES);
                    } catch (Throwable e) {
                        System.err.println("trace output ERROR : " + e.getMessage() + ", " + classFile);
                        traceError = true;
                    }
                }
                if (output.isDirectory()) {
                    os = new FileOutputStream(changedFileOutput);
                } else {
                    os = new FileOutputStream(output);
                }
                os.write(data);
                os.close();
            } else {
                FileUtil.copyFileUsingStream(classFile, changedFileOutput);
            }
        } catch (Exception e) {
            Log.e(TAG, "[innerTraceMethodFromSrc] input:%s e:%s", input.getName(), e.getMessage());
            try {
                Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                is.close();
                os.close();
            } catch (Exception e) {
                // ignore
            }
        }
    }
}

进入TraceClassAdapter

private class TraceClassAdapter extends ClassVisitor {
    private String className;
    private String superName;
    private boolean isABSClass = false;
    private boolean hasWindowFocusMethod = false;
    private boolean isActivityOrSubClass;
    private boolean isNeedTrace;
    TraceClassAdapter(int i, ClassVisitor classVisitor) {
        super(i, classVisitor);
    }
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this.className = name;
        this.superName = superName;
        this.isActivityOrSubClass = isActivityOrSubClass(className, collectedClassExtendMap);
        this.isNeedTrace = MethodCollector.isNeedTrace(configuration, className, mappingCollector);
        if ((access & Opcodes.ACC_ABSTRACT) > 0 || (access & Opcodes.ACC_INTERFACE) > 0) {
            this.isABSClass = true;
        }
    }
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
                                     String signature, String[] exceptions) {
        //是否有获取焦点办法论
        if (!hasWindowFocusMethod) {
            hasWindowFocusMethod = MethodCollector.isWindowFocusChangeMethod(name, desc);
        }
        //是否是抽象类
        if (isABSClass) {
            return super.visitMethod(access, name, desc, signature, exceptions);
        } else {
            //办法插桩
            MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions);
            return new TraceMethodAdapter(api, methodVisitor, access, name, desc, this.className,
                    hasWindowFocusMethod, isActivityOrSubClass, isNeedTrace);
        }
    }
    @Override
    public void visitEnd() {
       //针对界面发动耗时,由于要计算从`Activity.onCreate`到
       //Ativity.onWindowFocusChange`间的耗时,
       //在插桩过程中需求收集运用内一切`Activity`的完结类,
       //覆盖`onWindowFocusChange`函数进行打点
        if (!hasWindowFocusMethod && isActivityOrSubClass && isNeedTrace) {
            //假如 Activity 没有覆盖 onWindowFocusChanged 则覆盖之
            insertWindowFocusChangeMethod(cv, className, superName);
        }
        super.visitEnd();
    }
}

假如类没有遇到onWindowFocusChanged办法且是Activity或子类且需求插桩,则运用ASM API刺进这么一段代码:

private void insertWindowFocusChangeMethod(ClassVisitor cv, String classname) {
    // public void onWindowFocusChanged (boolean)
    MethodVisitor methodVisitor = cv.visitMethod(Opcodes.ACC_PUBLIC, TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD,
            TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD_ARGS, null, null);
    // {
    methodVisitor.visitCode();
    // this
    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
    // boolean
    methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
    // super.onWindowFocusChanged(boolean)
    methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, TraceBuildConstants.MATRIX_TRACE_ACTIVITY_CLASS, TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD,
            TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD_ARGS, false);
    // com/tencent/matrix/trace/core/AppMethodBeat.at(this, boolean)
    traceWindowFocusChangeMethod(methodVisitor, classname);
    // 回来句子
    methodVisitor.visitInsn(Opcodes.RETURN);
    methodVisitor.visitMaxs(2, 2);
    methodVisitor.visitEnd();
}

上面这段代码可能看着头疼,由于这触及到了字节码的层面。不过也不必太忧虑,咱们能够在AS上下载ASM Bytecode Viewer插件,先写好要插桩的代码,然后运用此插件检查ASM的对应写法,能够添加功率。 办法插桩终究走到TraceMethodAdapter

protected TraceMethodAdapter(int api, MethodVisitor mv, int access, String name, String desc, String className,
                             boolean hasWindowFocusMethod, boolean isActivityOrSubClass, boolean isNeedTrace) {
    super(api, mv, access, name, desc);
    TraceMethod traceMethod = TraceMethod.create(0, access, className, name, desc);
    this.methodName = traceMethod.getMethodName();
    this.hasWindowFocusMethod = hasWindowFocusMethod;
    this.className = className;
    this.name = name;
    this.isActivityOrSubClass = isActivityOrSubClass;
    this.isNeedTrace = isNeedTrace;
}
@Override
protected void onMethodEnter() {
    TraceMethod traceMethod = collectedMethodMap.get(methodName);
    if (traceMethod != null) {
        traceMethodCount.incrementAndGet();
        mv.visitLdcInsn(traceMethod.id);
        // 刺进 void com/tencent/matrix/trace/core/AppMethodBeat.i(int)
        mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "i", "(I)V", false);
        if (checkNeedTraceWindowFocusChangeMethod(traceMethod)) {
            traceWindowFocusChangeMethod(mv, className);
        }
    }
}
@Override
protected void onMethodExit(int opcode) {
    TraceMethod traceMethod = collectedMethodMap.get(methodName);
    if (traceMethod != null) {
        traceMethodCount.incrementAndGet();
        mv.visitLdcInsn(traceMethod.id);
        // 刺进 void com/tencent/matrix/trace/core/AppMethodBeat.o(int)
        mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "o", "(I)V", false);
    }
}
private void traceWindowFocusChangeMethod(MethodVisitor mv, String classname) {
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitVarInsn(Opcodes.ILOAD, 1);
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, 
    // com/tencent/matrix/trace/core/AppMethodBeat.at(this, boolean)
    TraceBuildConstants.MATRIX_TRACE_CLASS, "at", "(Landroid/app/Activity;Z)V", false);
}
private void insertWindowFocusChangeMethod(ClassVisitor cv, String classname, String superClassName) {
    MethodVisitor methodVisitor = cv.visitMethod(Opcodes.ACC_PUBLIC, TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD,
            TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD_ARGS, null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
    methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
    methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassName, TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD,
            TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD_ARGS, false);
    traceWindowFocusChangeMethod(methodVisitor, classname);
    methodVisitor.visitInsn(Opcodes.RETURN);
    methodVisitor.visitMaxs(2, 2);
    methodVisitor.visitEnd();
}

总结

MatrixGradle插件的完结类为MatrixPlugin,首要做了三件事:

  1. 添加Extension,用于供给给用户自定义装备选项
  2. 读取extension装备,假如启用trace功用,则履行MatrixTraceTransform,计算办法并插桩
  3. 读取extension装备,假如启用removeUnusedResources功用,则履行RemoveUnusedResourcesTask,删去不需求的资源

需求留意的是,插桩使命是在编译期履行的,这是为了防止对混杂操作产生影响。由于proguard操作是在该使命之前就完结的,意味着插桩时的 class 文件已经被混杂过的。而选择proguard之后去插桩,是由于假如提早插桩会造成部分办法不符合内联规则,无法在proguard时进行优化,终究导致程序办法数无法减少,从而引发办法数过大问题transform首要分三步履行:

  1. 依据装备文件(mapping.txtblackMethodList.txtbaseMethodMapFile)剖析办法计算规则,比方混杂后的类名和原始类名之间的映射联系、不需求插桩的办法黑名单等
  2. 借助 ASM 拜访一切Class文件的办法,记载其 ID,并写入到文件中(methodMapping.txt
  3. 插桩

插桩处理流程首要包含四步

  1. 进入办法时履行AppMethodBeat.i,传入办法 ID,记载时刻戳
  2. 退出办法时履行AppMethodBeat.o,传入办法 ID,记载时刻戳
  3. 假如是Activity,并且没有onWindowFocusChanged办法,则刺进该办法
  4. 跟踪onWindowFocusChanged办法,退出时履行AppMethodBeat.at,核算发动耗时

值得留意的细节有

  1. 计算的办法包含运用自身的、JAR 依靠包中的,以及额外添加的 ID 固定的dispatchMessage办法
  2. 抽象类或接口类不需求计算
  3. 空办法、get & set办法等简略办法不需求计算
  4. blackMethodList.txt中指定的办法不需求计算。

关于资源的插桩逻辑后续清楚包的资源常识后再共享