Matrix插桩适配 AGP8 解析

84 阅读9分钟

TraceCanary 适配 AGP8 解析

1. 项目背景

本文基于实际的 Matrix TraceCanary 适配 AGP8 项目,深入分析

2. MatrixTracePlugin 核心实现解析

2.1 插件架构设计

class MatrixTracePlugin : Plugin<Project> {
    override fun apply(project: Project) {
        // 1. 创建扩展配置
        val matrix = project.extensions.create("matrix", MatrixExtension::class.java)
        val traceExtension = (matrix as ExtensionAware).extensions
            .create("trace", MatrixTraceExtension::class.java)

        // 2. 验证 Android Application 插件
        if (!project.plugins.hasPlugin("com.android.application")) {
            throw GradleException("Matrix Plugin, Android Application plugin required.")
        }

        // 3. 配置日志级别
        project.afterEvaluate {
            Log.setLogLevel(matrix.logLevel)
        }

		// 4. 仅配置当前变体
        val startParameter = project.gradle.startParameter
        val taskNames = startParameter.taskNames.firstOrNull() ?: ""

        // 5. 注册 Instrumentation
        val androidComponents = project.extensions
            .getByType(AndroidComponentsExtension::class.java)
        androidComponents.onVariants { variant ->
            if (taskNames.contains(variant.name, true)) {
                configureVariant(project, variant, traceExtension)
            }
        }
    }
}

关键设计点:

  • 使用 AndroidComponentsExtension 替代旧的 Transform 注册方式
  • 通过 onVariants 回调为当前构建变体单独配置
  • 延迟配置确保所有依赖项已就绪

2.2 变体配置核心逻辑

private fun configureVariant(
    project: Project,
    variant: Variant,
    extension: MatrixTraceExtension
) {
	if (!extension.isEnable) {
            Log.i("MatrixTracePlugin", "not enable trace!")
            return
    }
    // 1. 设置 ASM 帧计算模式 COPY_FRAMES 是默认模式,可以不设置,大值会覆盖小值
    variant.instrumentation.setAsmFramesComputationMode(
        FramesComputationMode.COPY_FRAMES
    )

    // 2. 注册字节码转换器
    variant.instrumentation.transformClassesWith(
        MatrixTraceClassVisitorFactory::class.java,
        InstrumentationScope.ALL
    ) { params ->
        configureInstrumentationParams(params, project, extension)
    }

    // 3. 设置方法映射保存任务
    setupMappingSaveTask(project, variant)
}

2.3 黑名单处理机制

// 处理黑名单文件的核心逻辑
extension.blackListFile?.let { blockFile ->
    if (File(blockFile).exists()) {
        val buildDir = project.layout.buildDirectory.get().asFile
        val mappingFile = buildDir.resolve("outputs/matrix/mapping/${variant.name}").resolve("methodMapping.txt")
		//缓存 兼容: clean后会删除 mappingFile ,此时应该重新走所有插桩流程
		// 只改变参数,不参与到具体逻辑,不会打断 增量更新逻辑!!!
		// params.cacheFlag.set(mappingFile.exists())params.blockListFilePath.set(project.file(blockFile).absolutePath)
		val useCache = mappingFile.exists()
		if (!useCache) {
                val ct = System.currentTimeMillis()
                if (ct % 2 != 0L) {
                    params.cacheStmp.set(ct - 1)
                } else {
                    params.cacheStmp.set(ct)
                }
            } else {
                params.cacheStmp.set(1L)
            }

        // 解析黑名单内容
        val blockStr = (TraceBuildConstants.DEFAULT_BLOCK_TRACE
                + FileUtil.readFileAsString(project.file(blockFile).absolutePath))

        val blockArray = blockStr.trim().replace("/", ".").replace("\r", "")
            .split("\n").dropLastWhile { it.isEmpty() }.toTypedArray()

        val processor = MappingCollector()
        val blockSet = hashSetOf<String>()
        
        for (block in blockArray) {
            when {
                block.startsWith("-keepclass ") -> {
                    val className = block.replace("-keepclass ", "")
                    blockSet.add(processor.proguardClassName(className, className))
                }
                block.startsWith("-keeppackage ") -> {
                    val packageName = block.replace("-keeppackage ", "")
                    blockSet.add(processor.proguardPackageName(packageName, packageName))
                }
            }
        }
        params.blockSet.set(blockSet)
		//设置本地旧mapping
        loadMethodMapping(mappingFile)
    }
}

实现特点:

  • 支持 ProGuard 风格的配置语法
  • 区分类级别和包级别的过滤规则
  • 使用 MappingCollector 处理混淆映射

2.4 方法映射保存任务

private fun setupMappingSaveTask(project: Project, variant: Variant) {
    val variantName = variant.name.capitalize()
    val saveMappingTask = project.tasks.register("saveMapping$variantName") { task ->
        task.group = "matrix"
        task.description = "Save method mapping for $variantName"
        task.doLast {
            // 检查当前是否正在构建该变体
            val isCurrentVariantBuilding = project.gradle.startParameter.taskNames
                .any { taskName ->
                    taskName.contains(variant.name, ignoreCase = true) ||
                    taskName.contains("assemble${variantName}", ignoreCase = true)
                }

            if (!isCurrentVariantBuilding) return@doLast

            // 保存方法映射文件
            val buildDir = project.layout.buildDirectory.get().asFile
            val mappingOutDir = buildDir.resolve("outputs/matrix/mapping/${variant.name}")
            mappingOutDir.mkdirs()
            
            val mappingFile = mappingOutDir.resolve("methodMapping.txt")
            if (mappingFile.exists()) {
                    mappingFile.delete() // 如果文件存在就删除
            }
            mappingFile.createNewFile()
            saveMethodMapping(mappingFile)
        }
    }

    // 配置任务依赖关系
    project.afterEvaluate {
        val mappingTask = saveMappingTask.get()
        
        // Transform 任务完成后执行
        project.tasks.matching { task ->
            task.name.contains("transform")
        }.configureEach { transformTask ->
            transformTask.finalizedBy(mappingTask)
        }

        // Assemble 任务依赖映射保存
        project.tasks.matching { task ->
            task.name.contains("assemble") &&
            task.name.contains(variant.name, ignoreCase = true)
        }.forEach { assembleTask ->
            assembleTask.dependsOn(saveMappingTask)
        }
    }
}

关键优化:

  • 智能检测当前构建变体,避免不必要的任务执行
  • 使用新的 Gradle API project.layout.buildDirectory
  • 合理的任务依赖关系确保映射文件及时生成

3. MatrixTraceClassVisitorFactory 深度解析

3.1 参数接口设计

interface Params : InstrumentationParameters {
	// 缓存占位: 有参数变更会触发全量插桩操作
    @get:Input
    val cacheStmp: Property<Long>
	
    @get:Input
    val packageName: Property<String>

    @get:Input
    val blockListFilePath: Property<String>

    @get:Input
    val skipCheckClass: Property<Boolean>

    @get:Input
    val blockSet: SetProperty<String>
}

注意:

  • 使用 Gradle Property API 确保类型安全和缓存支持
  • @Input 注解支持增量构建
  • SetProperty<String> 高效处理黑名单集合

3.2 类过滤机制

override fun isInstrumentable(classData: ClassData): Boolean {
    var isNeed = true

    // 检查类是否在黑名单中
    if (parameters.get().blockSet.get().contains(classData.className)) {
        isNeed = false
    } else {
        // 检查包名是否在黑名单中
        for (packageName in parameters.get().blockSet.get()) {
            if (classData.className.startsWith(packageName.replace("/", "."))) {
                isNeed = false
                break
            }
        }
    }
	// cacheStmp 参与到判断中,用于打断固有的增量 逻辑
	 if (isNeed && parameters.get().cacheStmp.get() % 2 == 0L) {
            insertClassSize.getAndIncrement()
        }
    return isNeed
}

过滤策略:

  • 精确类名匹配优先级最高
  • 包名前缀匹配作为补充
  • 早期过滤减少后续处理开销

3.3 方法 ID 生成与管理

companion object {
    private val methodIdGenerator = AtomicInteger(1)
    private val methodMap = ConcurrentHashMap<String, Int>()

    fun getOrCreateMethodId(className: String, methodName: String, desc: String): Long {
            val signature = "$className.$methodName$desc".replace("/", ".")
            return methodMap.computeIfAbsent(signature) {
                methodIdGenerator.getAndIncrement()
            }
        }

    fun saveMethodMapping(outputFile: File) {
            outputFile.bufferedWriter().use { writer ->
                val sortedByValue = methodMap.entries.sortedWith(compareBy { it.value })
                sortedByValue.forEach { (signature, id) ->
                    writer.write("$id,${signature}\n")
                }
                if (methodMap["message_dispatch_start"] == null) {
                    // 写入最后一行,标识消息分发开始
                    writer.write("${0xFFFFF - 1},message_dispatch_start\n")
                    Log.e(TAG, "saveMethodMapping: ${methodMap.entries.size + 1}")
                } else {
                    Log.e(TAG, "saveMethodMapping: ${methodMap.entries.size}")
                }
            }
        }
		//加载本地文件
        fun loadMethodMapping(inputFile: File) {
			insertClassSize.set(0L)
            if (!inputFile.exists()) {
                Log.i(TAG, "oldMethodMapping: file not exists!")
                return
            }
            inputFile.bufferedReader().use { reader ->
                reader.forEachLine { line ->
                    val list = line.split(",")
                    if (list.isNotEmpty()) {
                        val (id, signature) = list
                        methodMap[signature] = id.toLong()
                    }
                }
            }
            methodIdGenerator.set(methodMap.size.toLong() + 1)
            Log.i(TAG, "loadMethodMapping: ${methodIdGenerator.get() - 1}")
        }
}

3.4 类访问器核心实现

class MatrixTraceClassVisitor(
    nextClassVisitor: ClassVisitor,
    private val ps: Params
) : ClassVisitor(Opcodes.ASM9, nextClassVisitor) {
    
    private var className: String = ""
    private var isActivityOrSubClass = false
    private var hasWindowFocusMethod = false
    private var isNeedTrace = false

    override fun visit(version: Int, access: Int, name: String, 
                      signature: String?, superName: String, 
                      interfaces: Array<String?>?) {
        super.visit(version, access, name, signature, superName, interfaces)
        
        this.className = name
        this.isActivityOrSubClass = isActivityOrSubClass(className, superName)
        this.isNeedTrace = isNeedTrace(ps, className, null)
        
        // 过滤抽象类和接口
        if ((access and Opcodes.ACC_ABSTRACT) > 0 || 
            (access and Opcodes.ACC_INTERFACE) > 0) {
            this.isABSClass = true
        }
    }
}

3.5 方法插桩核心逻辑

private class TraceMethodVisitor(
    private val api: Int,
    private val mv: MethodVisitor,
    private val access: Int,
    private val name: String,
    private val desc: String,
    private val className: String,
    // ... 其他参数
) : MethodVisitor(api, mv) {
    
    private val traceMethod = TraceMethod.create(
        getOrCreateMethodId(className, name, desc),
        access, className, name, desc
    )

    override fun visitCode() {
        super.visitCode()
        insertMethodEnterTrace()
    }

    override fun visitInsn(opcode: Int) {
        // 在所有返回指令前插入退出追踪
        if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || 
            opcode == Opcodes.ATHROW) {
            insertMethodExitTrace()
        }
        super.visitInsn(opcode)
    }

    private fun insertMethodEnterTrace() {
        mv.visitLdcInsn(traceMethod.id)
        mv.visitMethodInsn(
            Opcodes.INVOKESTATIC,
            TraceBuildConstants.MATRIX_TRACE_CLASS,
            "i", "(I)V", false
        )
    }

    private fun insertMethodExitTrace() {
        mv.visitLdcInsn(traceMethod.id)
        mv.visitMethodInsn(
            Opcodes.INVOKESTATIC,
            TraceBuildConstants.MATRIX_TRACE_CLASS,
            "o", "(I)V", false
        )
    }
}

插桩策略:

  • 方法入口统一插入 i(methodId) 调用
  • 所有退出点(包括异常)插入 o(methodId) 调用
  • 使用方法 ID 而非字符串减少运行时开销

3.6 Activity 特殊处理

private fun isActivityOrSubClass(cN: String, sN: String): Boolean {
    return cN == TraceBuildConstants.MATRIX_TRACE_ACTIVITY_CLASS ||
           cN == TraceBuildConstants.MATRIX_TRACE_V4_ACTIVITY_CLASS ||
           cN == TraceBuildConstants.MATRIX_TRACE_V7_ACTIVITY_CLASS ||
           cN == TraceBuildConstants.MATRIX_TRACE_ANDROIDX_ACTIVITY_CLASS ||
		   cN == TraceBuildConstants.MATRIX_TRACE_ANDROIDX_F_ACTIVITY_CLASS ||
           sN == TraceBuildConstants.MATRIX_TRACE_ACTIVITY_CLASS ||
           sN == TraceBuildConstants.MATRIX_TRACE_V4_ACTIVITY_CLASS ||
           sN == TraceBuildConstants.MATRIX_TRACE_V7_ACTIVITY_CLASS ||
           sN == TraceBuildConstants.MATRIX_TRACE_ANDROIDX_ACTIVITY_CLASS ||
		   sN == TraceBuildConstants.MATRIX_TRACE_ANDROIDX_F_ACTIVITY_CLASS
}

// 插入 onWindowFocusChanged 方法
private fun insertWindowFocusChangeMethod(
    cv: ClassVisitor, classname: String, superClassName: String
) {
    val 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()
}

Activity 优化:

  • 兼容多个版本的 Activity 基类
  • 自动注入 onWindowFocusChanged 方法用于启动耗时统计
  • 保持原有方法调用链的完整性

4. AGP8 适配关键技术点

4.1 API 迁移对比

特性Transform API (旧)Instrumentation API (新)
注册方式registerTransform()transformClassesWith()
参数传递Transform 构造函数InstrumentationParameters
类过滤isIncremental()isInstrumentable()
处理粒度文件级别类级别
缓存支持基础支持强化增量构建

4.2 性能优化措施

override fun isInstrumentable(classData: ClassData): Boolean {
    // 在字节码解析前就过滤掉不需要的类
    return !isSystemClass(classData.className)
}

private val methodMap = ConcurrentHashMap<String, Int>()
private val methodIdGenerator = AtomicInteger(1)

override fun visitMaxs(maxStack: Int, maxLocals: Int) {
    // 确保栈大小足够容纳插入的指令
    super.visitMaxs(maxStack + 4, maxLocals)
}

4.3 构建配置最佳实践

// build.gradle 推荐配置
matrix {
    logLevel "D"
    trace {
        enable = true
        blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"
    }
}

5. 实际应用效果

5.1 编译时输出

> Task :app:transformClassesWithMatrixTraceForDebug
Matrix.TraceClassVisitorFactory: 实例化.
MatrixTracePlugin: variant:debug
MatrixTraceClassVisitor: 处理类 com/example/MainActivity
MatrixTraceClassVisitor: 插桩方法 onCreate()V
MatrixTraceClassVisitor: 插桩方法 onResume()V
...
> Task :app:mergeAppTestReleaseGeneratedProguardFiles
> Task :app:saveMappingAppDevDebug
> Task :app:saveMappingAppDevRelease
> Task :app:saveMappingAppProDebug
> Task :app:saveMappingAppProRelease
> Task :app:saveMappingAppTestDebug

> Task :app:saveMappingAppTestRelease
[D][MatrixTracePlugin] 开始存储mapping

5.2 方法映射文件示例

...
271272,com.lagos.emobility.driver.ui.underway.google.GoogleNavigationController$initializeNavigationApi$2$1.onError(I)V
271273,com.lagos.emobility.driver.ui.underway.google.GoogleNavigationController.initializeNavigationApi$lambda$1(Lcom.lagos.emobility.driver.ui.underway.google.GoogleNavigationController;Lkotlin.jvm.functions.Function2;)V
271274,com.lagos.emobility.driver.ui.underway.google.GoogleNavigationController.withMapAsync$lambda$2(Lkotlin.jvm.functions.Function1;Lcom.google.android.gms.maps.GoogleMap;)V
1048574,message_dispatch_start
...

5.3 运行时监控数据

// 生成的字节码调用示例
public void onCreate(Bundle savedInstanceState) {
    MethodBeat.i(1);  // 方法进入
    try {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // ... 业务逻辑
    } finally {
        MethodBeat.o(1);  // 方法退出
    }
}

6 堆栈解析

6.1堆栈数据格式解析:

原始数据结构

{
  "nameValuePairs": {
    "machine": "HIGH",
    "cpu_app": 0,
    "mem": 8055660544,
    "mem_free": 3264324,
    "detail": "NORMAL",
    "cost": 905,
    "scene": "com.lagos.emobility.driver.ui.underway.OrderUnderwayActivity",
    "stack": "0,1048574,1,905\n1,258336,1,903\n2,20604,1,897\n3,22106,1,186\n4,35700,1,186\n5,21656,1,186\n6,26128,1,181\n7,81196,1,137\n8,23665,1,137\n3,24006,1,603\n4,58165,1,503\n5,22284,1,338\n6,64326,1,338\n7,60162,1,322\n8,60154,1,322\n9,35700,1,162\n10,35723,1,162\n11,35700,1,162\n12,64378,1,162\n13,63526,1,121\n14,65534,1,110\n15,81196,1,110\n16,63398,1,110\n17,35700,1,99\n18,35723,1,99\n19,35700,1,99\n20,64365,1,99\n21,17766,1,84\n9,63491,1,160\n10,63497,1,160\n",
    "stackKey": "60154|",
    "tag": "Trace_EvilMethod",
    "process": "com.lagos.emobility.driver",
    "time": 1750662360918
  }
}

堆栈格式说明

格式: depth,methodId,count,cost
- depth: 调用深度(0为根调用)
- methodId: 方法唯一标识符
- count: 调用次数
- cost: 耗时(毫秒)
特殊方法ID:
- 1048574 (0xFFFFF-1): message_dispatch_start 消息分发起始点
- 其他ID: 对应 methodMapping.txt 中的方法映射

Js解析脚本

完整解析工具

const fs = require('fs');

function parseStackTrace(mappingFilePath, stackInput) {
try {
  // 读取mapping文件
  let mapping = {};
  if (fs.existsSync(mappingFilePath)) {
    const mappingContent = fs.readFileSync(mappingFilePath, 'utf8');
    
    // 解析mapping文件格式: methodId,methodSignature
    const lines = mappingContent.trim().split('\n');
    lines.forEach(line => {
      if (line.trim()) {
        const commaIndex = line.indexOf(',');
        if (commaIndex > 0) {
          const id = parseInt(line.substring(0, commaIndex).trim());
          const signature = line.substring(commaIndex + 1).trim();
          
          // 简化方法签名显示
          const simplifiedName = simplifyMethodSignature(signature);
          mapping[id] = {
            full: signature,
            simple: simplifiedName
          };
        }
      }
    });
    
    console.log(`成功加载 ${Object.keys(mapping).length} 个方法映射`);
  } else {
    console.log(`Warning: Mapping file ${mappingFilePath} not found`);
  }

  // 读取堆栈数据(支持文件或字符串)
  let stackString;
  if (fs.existsSync(stackInput)) {
    stackString = fs.readFileSync(stackInput, 'utf8');
    console.log(`从文件读取堆栈数据: ${stackInput}`);
  } else {
    stackString = stackInput;
    console.log(`使用传入的堆栈字符串`);
  }

  // 解析堆栈数据
  const lines = stackString.trim().split('\\n');
  console.log("lines----->",lines);
  const parsedStack = [];

  console.log('\n堆栈调用信息:');
  console.log('='.repeat(100));

  lines.forEach((line, index) => {
if (line.trim()) {
  const parts = line.split(',');
  if (parts.length >= 4) {
    const level = parseInt(parts[0]);
    const methodId = parseInt(parts[1]);
    const callCount = parseInt(parts[2]);
    const duration = parseInt(parts[3]);
    
    const methodInfo = mapping[methodId];
    const methodName = methodInfo ? methodInfo.full : `Unknown_${methodId}`;
    
    // 简单的层级缩进
    const indent = '│ '.repeat(level) + (level > 0 ? '├─ ' : '');
    
    // 简洁的单行显示
    console.log(`${indent}${methodName} (${duration}ms)`);
    
    parsedStack.push({
      level,
      methodId,
      methodName,
      fullSignature: methodInfo ? methodInfo.full : `Method_${methodId}`,
      callCount,
      duration
    });
  }
}
});

  // 统计信息
  const totalDuration = parsedStack.reduce((sum, item) => sum + item.duration, 0);
  const maxLevel = Math.max(...parsedStack.map(item => item.level));
  const totalCalls = parsedStack.reduce((sum, item) => sum + item.callCount, 0);
  
  console.log('='.repeat(100));

  // 按层级统计
  const levelStats = {};
  parsedStack.forEach(item => {
    if (!levelStats[item.level]) {
      levelStats[item.level] = { count: 0, totalDuration: 0, totalCalls: 0 };
    }
    levelStats[item.level].count++;
    levelStats[item.level].totalDuration += item.duration;
    levelStats[item.level].totalCalls += item.callCount;
  });

  // 耗时最长的方法
  const sortedByDuration = [...parsedStack].sort((a, b) => b.duration - a.duration);
  console.log('\n🔥 耗时最长的前10个方法:');
  sortedByDuration.slice(0, 10).forEach((item, index) => {
    console.log(`   ${index + 1}. ${item.methodName} - ${item.duration}ms (${item.callCount}次调用)`);
  });

  return parsedStack;

} catch (error) {
  console.error('❌ 解析错误:', error.message);
  return null;
}
}

// 简化方法签名显示
function simplifyMethodSignature(signature) {
try {
  // 提取类名和方法名
  const lastSlash = signature.lastIndexOf('/');
  const firstDot = signature.indexOf('.', lastSlash);
  const firstParen = signature.indexOf('(');
  
  if (lastSlash >= 0 && firstDot > lastSlash && firstParen > firstDot) {
    const className = signature.substring(lastSlash + 1, firstDot);
    const methodName = signature.substring(firstDot + 1, firstParen);
    
    // 提取参数类型(简化显示)
    const paramsPart = signature.substring(firstParen + 1, signature.indexOf(')'));
    const simplifiedParams = simplifyParams(paramsPart);
    
    return `${className}.${methodName}(${simplifiedParams})`;
  }
  
  return signature;
} catch (e) {
  return signature;
}
}

// 简化参数类型显示
function simplifyParams(params) {
if (!params) return '';

const typeMap = {
  'Z': 'boolean',
  'B': 'byte',
  'C': 'char',
  'S': 'short',
  'I': 'int',
  'J': 'long',
  'F': 'float',
  'D': 'double',
  'V': 'void'
};

let result = '';
let i = 0;

while (i < params.length) {
  if (params[i] === 'L') {
    // 对象类型
    const semicolon = params.indexOf(';', i);
    if (semicolon > i) {
      const className = params.substring(i + 1, semicolon);
      const simpleName = className.substring(className.lastIndexOf('/') + 1);
      result += simpleName;
      i = semicolon + 1;
    } else {
      i++;
    }
  } else if (params[i] === '[') {
    // 数组类型
    result += '[]';
    i++;
  } else if (typeMap[params[i]]) {
    result += typeMap[params[i]];
    i++;
  } else {
    i++;
  }
  
  if (i < params.length && result && !result.endsWith('[]')) {
    result += ', ';
  }
}

return result;
}

// 主函数
function main() {
const args = process.argv.slice(2);

if (args.length < 2) {
  console.log('🚀 使用方法: node stack-parser.js <mapping文件路径> <堆栈文件路径或字符串>');
  console.log('📝 示例1 (文件): node stack-parser.js mapping.txt stack.txt');
  console.log('📝 示例2 (字符串): node stack-parser.js mapping.txt "0,1048574,1,710"');
  console.log('📋 Mapping文件格式: methodId,methodSignature');
  return;
}

const mappingFile = args[0];
const stackInput = args[1];

console.log(`📁 Mapping文件: ${mappingFile}`);
console.log(`🔍 解析堆栈数据...\n`);

parseStackTrace(mappingFile, stackInput);
}

// 如果直接运行此脚本
if (require.main === module) {
main();
}

// 导出函数供其他模块使用
module.exports = { parseStackTrace };

使用方式 node trace_parser.js ~/code/methodMapping.txt "0,1048574,1,905\n1,258336,1...."

 Mapping文件: /Users/heyufei/code/methodMapping.txt
🔍 解析堆栈数据...

成功加载 270940 个方法映射
使用传入的堆栈字符串
lines-----> [
  '0,1048574,1,905', '1,258336,1,903',
  '2,20604,1,897',   '3,22106,1,186',
  '4,35700,1,186',   '5,21656,1,186',
  '6,26128,1,181',   '7,81196,1,137',
  '8,23665,1,137',   '3,24006,1,603',
  '4,58165,1,503',   '5,22284,1,338',
  '6,64326,1,338',   '7,60162,1,322',
  '8,60154,1,322',   '9,35700,1,162',
  '10,35723,1,162',  '11,35700,1,162',
  '12,64378,1,162',  '13,63526,1,121',
  '14,65534,1,110',  '15,81196,1,110',
  '16,63398,1,110',  '17,35700,1,99',
  '18,35723,1,99',   '19,35700,1,99',
  '20,64365,1,99',   '21,17766,1,84',
  '9,63491,1,160',   '10,63497,1,160',
  ''
]

堆栈调用信息:
====================================================================================================
message_dispatch_start (905ms)
│ ├─ com.lagos.emobility.driver.ui.underway.OrderUnderwayActivity$4.run()V (903ms)
│ │ ├─ com.google.android.libraries.navigation.NavigationView.onCreate(Landroid.os.Bundle;)V (897ms)
│ │ │ ├─ com.google.android.libraries.navigation.environment.l.bl()Lcom.google.android.libraries.navigation.internal.aal.n; (186ms)
│ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.e.a()Ljava.lang.Object; (186ms)
│ │ │ │ │ ├─ com.google.android.libraries.navigation.environment.ay.a()Ljava.lang.Object; (186ms)
│ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aal.n.c(Landroid.content.Context;Lcom.google.android.libraries.navigation.internal.aal.ih;Lcom.google.android.libraries.navigation.internal.aal.gf;Lcom.google.android.libraries.navigation.internal.aal.j;Lcom.google.android.libraries.navigation.internal.vi.e;)Lcom.google.android.libraries.navigation.internal.aal.n; (181ms)
│ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.yg.bv.a()Ljava.lang.Object; (137ms)
│ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aal.ay.a()Ljava.lang.Object; (137ms)
│ │ │ ├─ com.google.android.libraries.navigation.internal.aal.co.aB(Lcom.google.android.gms.maps.GoogleMapOptions;ZLcom.google.android.libraries.navigation.internal.aal.bf;Lcom.google.android.libraries.navigation.internal.aal.n;Lcom.google.android.libraries.navigation.internal.aal.cp;Lcom.google.android.libraries.navigation.internal.aal.gf;)Lcom.google.android.libraries.navigation.internal.aal.co; (603ms)
│ │ │ │ ├─ com.google.android.libraries.navigation.internal.lv.ct.a(Ljava.lang.String;Lcom.google.android.libraries.navigation.internal.aal.bf;Lcom.google.android.libraries.navigation.internal.aal.n;Ljava.lang.String;Ljava.lang.Integer;ILandroid.view.View;Lcom.google.android.libraries.navigation.internal.aal.ab;ZLandroid.widget.TextView;Lcom.google.android.libraries.navigation.internal.aal.ec;Lcom.google.android.libraries.navigation.internal.aal.cb;Lcom.google.android.libraries.navigation.internal.aal.hy;ZLcom.google.android.libraries.navigation.internal.aal.d;Lcom.google.android.libraries.navigation.internal.aal.ag;ZLcom.google.android.libraries.navigation.internal.aal.da;Lcom.google.android.libraries.navigation.internal.aal.bo;)Lcom.google.android.libraries.navigation.internal.aal.ed; (503ms)
│ │ │ │ │ ├─ com.google.android.libraries.navigation.environment.u.run()V (338ms)
│ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.ph.c.h()V (338ms)
│ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.nt.h.h()V (322ms)
│ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.nt.h.b()Lcom.google.android.libraries.navigation.internal.nt.d; (322ms)
│ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.e.a()Ljava.lang.Object; (162ms)
│ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.m.a()Ljava.lang.Object; (162ms)
│ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.e.a()Ljava.lang.Object; (162ms)
│ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.ph.q.a()Ljava.lang.Object; (162ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.pb.gi.<init>(Ljava.lang.String;Ljava.lang.Integer;Landroid.content.res.Resources;Lcom.google.android.libraries.navigation.internal.px.x;Lcom.google.android.libraries.navigation.internal.nu.m;Landroid.content.Context;Lcom.google.android.libraries.navigation.internal.pf.y;Lcom.google.android.libraries.navigation.internal.acm.an;Lcom.google.android.libraries.navigation.internal.aep.a;Lcom.google.android.libraries.navigation.internal.pb.gn;Lcom.google.android.libraries.navigation.internal.aep.a;Lcom.google.android.libraries.navigation.internal.aep.a;Lcom.google.android.libraries.navigation.internal.pq.k;Landroid.content.Context;Lcom.google.android.libraries.navigation.internal.gb.f;Lcom.google.android.libraries.navigation.internal.kh.b;Lcom.google.android.libraries.navigation.internal.oa.p;Lcom.google.android.libraries.navigation.internal.oz.c;Lcom.google.android.libraries.navigation.internal.mg.b;Lcom.google.android.libraries.navigation.internal.gn.p;Lcom.google.android.libraries.navigation.internal.nd.a;Lcom.google.android.libraries.navigation.internal.ov.s;Lcom.google.android.libraries.navigation.internal.ja.e;Lcom.google.android.libraries.navigation.internal.kc.c;Lcom.google.android.libraries.navigation.internal.ps.d;Lcom.google.android.libraries.navigation.internal.hr.f;Lcom.google.android.libraries.navigation.internal.do.c;Lcom.google.android.libraries.navigation.internal.qm.h;Lcom.google.android.libraries.navigation.internal.oz.a;Lcom.google.android.libraries.navigation.internal.qo.a;Lcom.google.android.libraries.navigation.internal.hu.v;Lcom.google.android.libraries.navigation.internal.zk.bl;Lcom.google.android.libraries.navigation.internal.zk.bl;Ljava.util.concurrent.Executor;Ljava.util.concurrent.Executor;Lcom.google.android.libraries.navigation.internal.zk.bl;Lcom.google.android.libraries.navigation.internal.qi.ce;Lcom.google.android.libraries.navigation.internal.ot.e;Lcom.google.android.libraries.navigation.internal.px.cm;Lcom.google.android.libraries.navigation.internal.pa.g;Lcom.google.android.libraries.navigation.internal.yg.bs;Lcom.google.android.libraries.navigation.internal.nu.r;Lcom.google.android.libraries.navigation.internal.qg.a;Lcom.google.android.libraries.navigation.internal.qq.c;Lcom.google.android.libraries.geo.mapcore.api.model.ba;Z)V (121ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.pk.cv.<init>(Lcom.google.android.libraries.navigation.internal.zk.bl;Lcom.google.android.libraries.navigation.internal.yg.bs;)V (110ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.yg.bv.a()Ljava.lang.Object; (110ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.pb.fw.a()Ljava.lang.Object; (110ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.e.a()Ljava.lang.Object; (99ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.m.a()Ljava.lang.Object; (99ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.aes.e.a()Ljava.lang.Object; (99ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.ph.m.a()Ljava.lang.Object; (99ms)
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.geo.mapcore.renderer.ax.<init>(Landroid.content.Context;Lcom.google.android.libraries.navigation.internal.kh.b;Lcom.google.android.libraries.navigation.internal.nd.a;Lcom.google.android.libraries.navigation.internal.afp.a;Landroid.content.res.Resources;)V (84ms)
│ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.pb.gi.a()Lcom.google.android.libraries.navigation.internal.nt.d; (160ms)
│ │ │ │ │ │ │ │ │ │ ├─ com.google.android.libraries.navigation.internal.pb.gi.i()Lcom.google.android.libraries.navigation.internal.nt.d; (160ms)
====================================================================================================

🔥 耗时最长的前10个方法:
   1. message_dispatch_start - 905ms (1次调用)
   2. com.lagos.emobility.driver.ui.underway.OrderUnderwayActivity$4.run()V - 903ms (1次调用)
   3. com.google.android.libraries.navigation.NavigationView.onCreate(Landroid.os.Bundle;)V - 897ms (1次调用)
   4. com.google.android.libraries.navigation.internal.aal.co.aB(Lcom.google.android.gms.maps.GoogleMapOptions;ZLcom.google.android.libraries.navigation.internal.aal.bf;Lcom.google.android.libraries.navigation.internal.aal.n;Lcom.google.android.libraries.navigation.internal.aal.cp;Lcom.google.android.libraries.navigation.internal.aal.gf;)Lcom.google.android.libraries.navigation.internal.aal.co; - 603ms (1次调用)
   5. com.google.android.libraries.navigation.internal.lv.ct.a(Ljava.lang.String;Lcom.google.android.libraries.navigation.internal.aal.bf;Lcom.google.android.libraries.navigation.internal.aal.n;Ljava.lang.String;Ljava.lang.Integer;ILandroid.view.View;Lcom.google.android.libraries.navigation.internal.aal.ab;ZLandroid.widget.TextView;Lcom.google.android.libraries.navigation.internal.aal.ec;Lcom.google.android.libraries.navigation.internal.aal.cb;Lcom.google.android.libraries.navigation.internal.aal.hy;ZLcom.google.android.libraries.navigation.internal.aal.d;Lcom.google.android.libraries.navigation.internal.aal.ag;ZLcom.google.android.libraries.navigation.internal.aal.da;Lcom.google.android.libraries.navigation.internal.aal.bo;)Lcom.google.android.libraries.navigation.internal.aal.ed; - 503ms (1次调用)
   6. com.google.android.libraries.navigation.environment.u.run()V - 338ms (1次调用)
   7. com.google.android.libraries.navigation.internal.ph.c.h()V - 338ms (1次调用)
   8. com.google.android.libraries.navigation.internal.nt.h.h()V - 322ms (1次调用)
   9. com.google.android.libraries.navigation.internal.nt.h.b()Lcom.google.android.libraries.navigation.internal.nt.d; - 322ms (1次调用)
   10. com.google.android.libraries.navigation.environment.l.bl()Lcom.google.android.libraries.navigation.internal.aal.n; - 186ms (1次调用)

7. 问题排查指南

7.1 常见编译错误

错误 1:ASM 版本冲突

java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.<init>

解决方案:

configurations.all {
    resolutionStrategy {
        force 'org.ow2.asm:asm:9.6'
        force 'org.ow2.asm:asm-commons:9.6'
    }
}

错误 2:方法映射文件未生成

检查任务依赖关系是否正确配置

解决方案:

// 确保 saveMapping 任务在正确的时机执行
project.tasks.matching { task ->
    task.name.contains("transform")
}.configureEach { transformTask ->
    transformTask.finalizedBy(mappingTask)
}

7.2 运行时问题

问题1:方法找不到

java.lang.NoSuchMethodError: MethodBeat.i(I)V

解决方案:

# proguard-rules.pro
-keep class com.tencent.matrix.trace.core.MethodBeat {
    public static *** i(int);
    public static *** o(int);
}

问题2:插桩无效,未启动trace ,trace task 空

tag[Trace_EvilMethod]type[0];key[null];content[{"machine":"HIGH","cpu_app":0,"mem":8055660544,"mem_free":3377132,"detail":"NORMAL","cost":1740,"scene":"com.lagos.emobility.driver.ui.underway.OrderUnderwayActivity","stack":"","stackKey":"","tag":"Trace_EvilMethod","process":"com.lagos.emobility.driver","time":1750319930441}]

解决方案

private TracePlugin configTracePlugin(DynamicConfigImplDemo dynamicConfig) {
		//1. 配置设置TracePlugin
        TraceConfig config = new TraceConfig.Builder()
                .dynamicConfig(dynamicConfig)
                .enableAnrTrace(true)
                .enableAppMethodBeat(dynamicConfig.isTraceEnable())
                .enableEvilMethodTrace(dynamicConfig.isTraceEnable())
                .enableTouchEventTrace(true)
                .build();
        //fix matrix sdk 版本限制
		//2. 手动调用 UIThreadMonitor.getMonitor().init(config);
        if (!UIThreadMonitor.getMonitor().isInit()) {
            try {
                UIThreadMonitor.getMonitor().init(config);
            } catch (java.lang.RuntimeException e) {
                MatrixLog.e(TAG, "[start] RuntimeException:%s", e);
            }
        }

        return new TracePlugin(config);
    }

分析

image.png UIThreadMonitor.getMonitor().init(traceConfig); ->> LooperMonitor.registe(xxxx) ->dispatchStart() -->插桩AppMethodBeat.i(AppMethodBeat.METHOD_ID_DISPATCH); ... EvilMethodTracer.AnalyseTask.analyse() -->check AppMethodBeat.METHOD_ID_DISPATCH

image.png

慢方法检测 入口必须是这个接入点(消息分发起始点)

8. 总结与展望

8.1 适配成果

  1. 完全兼容 AGP8:成功迁移到新的 Instrumentation API
  2. 功能保持完整:所有原有监控功能正常工作
  3. 解析堆栈:可以正常解析采集到的堆栈数据,用于后续分析

8.2 技术收获

  • 深入理解 AGP8 新架构和 API 设计
  • 掌握 ASM 字节码操作的最佳实践
  • 学会 Gradle 插件开发的现代化模式

8.3 后续优化方向

  1. 智能采样策略:根据设备性能动态调整采样率
  2. 增量插桩优化:进一步利用 AGP8 的增量构建能力
  3. 多模块支持:扩展到 Android Library 项目
  4. 云端分析集成:与监控后台深度集成

通过这次 TraceCanary 适配 AGP8 的实践,我们不仅解决了兼容性问题,还获得了显著的性能提升。这为后续的 Matrix 其他模块适配提供了宝贵的经验和技术基础。