matrix-trace-processor

背景

在使用微信開源的 Matrix 項目中的 matrix-trace-canary 組件時,感受有些不方便。matrix-trace-canary 使用 Gradle Plugin 在遍歷類文件時,提早在特定方法中插入耗時統計代碼:java

// com.tencent.matrix.trace.MethodTracer.java
@Override                                                                                                       
protected void onMethodEnter() {                                                                                
    TraceMethod traceMethod = collectedMethodMap.get(methodName);                                               
    if (traceMethod != null) {                                                                                  
        traceMethodCount.incrementAndGet();                                                                     
        mv.visitLdcInsn(traceMethod.id);                                                                        
        mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "i", "(I)V", false);           
    }                                                                                                           
}

@Override                                                                                                                                   
protected void onMethodExit(int opcode) {                                                                                                   
    TraceMethod traceMethod = collectedMethodMap.get(methodName);                                                                           
    if (traceMethod != null) {                                                                                                              
        if (hasWindowFocusMethod && isActivityOrSubClass && isNeedTrace) {                                                                  
            TraceMethod windowFocusChangeMethod = TraceMethod.create(-1, Opcodes.ACC_PUBLIC, className,                                     
                    TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD, TraceBuildConstants.MATRIX_TRACE_ON_WINDOW_FOCUS_METHOD_ARGS); 
            if (windowFocusChangeMethod.equals(traceMethod)) {                                                                              
                traceWindowFocusChangeMethod(mv, className);                                                                                
            }                                                                                                                               
        }                                                                                                                                   
                                                                                                                                            
        traceMethodCount.incrementAndGet();                                                                                                 
        mv.visitLdcInsn(traceMethod.id);                                                                                                    
        mv.visitMethodInsn(INVOKESTATIC, TraceBuildConstants.MATRIX_TRACE_CLASS, "o", "(I)V", false);                                       
    }                                                                                                                                       
}                                                                                                                                           
複製代碼

TraceBuildConstants.MATRIX_TRACE_CLASScom/tencent/matrix/trace/core/AppMethodBeatpython

也就是說,在原先的方法體的前和後各插入一段代碼:android

AppMethodBeat.i(methodId);
// 原先的代碼
AppMethodBeat.o(methodId);
複製代碼

這裏 matrix-trace-canary 用的是 int 類型的 methodId 來代替方法名,這樣的好處應該是爲了節約內存,由於會緩存全部的 methodId,因此會提早聲明一個數組來存儲:git

public static final int BUFFER_SIZE = 100 * 10000; // 7.6M
private static long[] sBuffer = new long[Constants.BUFFER_SIZE];
複製代碼

這裏若是使用 String 來存儲的話,所須要大小遠遠大於 7.6M。github

但這帶來一個問題,matrix-trace-canary 在慢函數檢測打印堆棧時,會用這個 methodId 來標記實際的方法名。shell

0,1048574,1,11239
1,47,1,11237
2,53,1,11237
3,56,1,381
4,60,1,160
5,64,1,16
5,66,1,15
5,68,1,21
4,69,1,20
3,71,1,57
4,75,1,21
4,76,1,5
4,80,1,11
3,82,1,10001
複製代碼

因此 matrix-trace-canary 在插入統計代碼時,會將這個映射表也輸出來,默認爲 build/outputs/mapping/*/methodMapping.txt:數組

1,0,sample.tencent.matrix.trace.TestFpsActivity$3 <init> (Lsample.tencent.matrix.trace.TestFpsActivity;Landroid.content.Context;I[Ljava.lang.Object;)V
2,1,sample.tencent.matrix.trace.TestTraceFragmentActivity <init> ()V
3,1,sample.tencent.matrix.listener.TestPluginListener <init> (Landroid.content.Context;)V
4,1,sample.tencent.matrix.trace.TestFpsActivity <init> ()V
5,0,sample.tencent.matrix.trace.TestFpsActivity$1 <init> (Lsample.tencent.matrix.trace.TestFpsActivity;)V
6,1,sample.tencent.matrix.trace.TestFpsActivity$2 doFrameAsync (Ljava.lang.String;JJIZ)V
7,1,sample.tencent.matrix.listener.TestPluginListener onReportIssue (Lcom.tencent.matrix.report.Issue;)V
8,1,sample.tencent.matrix.trace.TestFpsActivity$1 execute (Ljava.lang.Runnable;)V
9,4,sample.tencent.matrix.SplashActivity onCreate (Landroid.os.Bundle;)V
10,1,sample.tencent.matrix.trace.StartUpService onStartCommand (Landroid.content.Intent;II)I
11,1,sample.tencent.matrix.trace.StartUpBroadcastReceiver onReceive (Landroid.content.Context;Landroid.content.Intent;)V
12,1,sample.tencent.matrix.trace.FirstFragment onCreateView (Landroid.view.LayoutInflater;Landroid.view.ViewGroup;Landroid.os.Bundle;)Landroid.view.View;
...
複製代碼

matrix-trace-processor

matrix-trace-processor 就是爲了知足上面這個需求開發的,只要提供 matrix-trace-canary 生成的堆棧和 methodMapping.txt 文件,就能轉化爲實際的方法名,同時生成的是 DTrace 格式的堆棧文件,支持 stackcollapse 分析,也就是說能夠轉化成其餘可視化格式,例如火焰圖。緩存

執行 python3 main.py -hbash

Matrix commandline utility!

positional arguments:
  {pull_traces,workflow_traces}
                        Supported features
    pull_traces         Pull traces from the device
    workflow_traces     Processing analysis traces

optional arguments:
  -h, --help            show this help message and exit
複製代碼

pull_traces

支持從設備中拉取指定包名的堆棧文件,須要存放在內部存儲 cache 目錄,文件格式爲 log。微信

即 Context.getCacheDir()

usage: main.py pull_traces [-h] (--last | --all | --count COUNT) package

positional arguments:
  package        Specify the package name using Matrix, e.g. com.foo.bar

optional arguments:
  -h, --help     show this help message and exit
  --last         Pull only the last trace
  --all          Pull all existing traces
  --count COUNT  Pull the last COUNT traces
複製代碼

例如,python3 main.py pull_traces --last sample.tencent.matrix 表示拉取包名爲 sample.tencent.matrix 的最新更新的堆棧文件。

workflow_traces

分析輸出堆棧。

usage: main.py workflow_traces [-h] trace methodMapping

positional arguments:
  trace          Path to downloaded trace
  methodMapping  methodMapping

optional arguments:
  -h, --help     show this help message and exit
複製代碼

例如,python3 main.py workflow_traces demo/1581129760409.log demo/methodMapping.txt > demo/1581129760409.txt

1581129760409.txt 就是分析後生成的堆棧文件,咱們能夠使用 FlameGraph 將它轉化爲火焰圖。

  1. 先使用 stackcollapse 生成 Fold stacks

    ./stackcollapse.pl demo/1581129760409.txt > demo/1581129760409.folded
    複製代碼
  2. 使用 flamegraph 生成 SVG

    ./flamegraph.pl demo/1581129760409.folded > demo/1581129760409.svg
    複製代碼

最終效果以下:

png

源碼

github.com/LinXiaoTao/…

感謝

profilo

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息