MatrixSource

MatrixPlugins设计

TracePlugin设计

main_title_sbuffer_array

main_title_structureddatatostack

其他main_title为UIThreadMonitor和LooperMonitor

StartupTracer

/**
 * <p>
 * firstMethod.i       LAUNCH_ACTIVITY   onWindowFocusChange   LAUNCH_ACTIVITY    onWindowFocusChange
 * ^                         ^                   ^                     ^                  ^
 * |                         |                   |                     |                  |
 * |---------app---------|---|---firstActivity---|---------...---------|---careActivity---|
 * |<--applicationCost-->|
 * |<--------------firstScreenCost-------------->|
 * |<---------------------------------------coldCost------------------------------------->|
 * .                         |<-----warmCost---->|
 *
 * </p>
 */

MatrixApplication.onCreate

@Override
 public void onCreate() {
   super.onCreate();

 Matrix.Builder builder = new Matrix.Builder(this);
 builder.patchListener(new TestPluginListener(this));

//trace
 TraceConfig traceConfig = new TraceConfig.Builder()
     .dynamicConfig(dynamicConfig)
     .enableFPS(fpsEnable)
     .enableEvilMethodTrace(traceEnable)
     .enableAnrTrace(traceEnable)
     .enableStartup(traceEnable)
     .splashActivities("sample.tencent.matrix.SplashActivity;")
     .isDebug(true)
     .isDevEnv(false)
     .build();

TracePlugin tracePlugin = (new TracePlugin(traceConfig));
 builder.plugin(tracePlugin);

//resource
 builder.plugin(new ResourcePlugin(new ResourceConfig.Builder()
     .dynamicConfig(dynamicConfig)
     .setDumpHprof(false)
     .setDetectDebuger(true)   //only set true when in sample, not in your app
     .build()));
 ResourcePlugin.activityLeakFixer(this);
 
 //io
 IOCanaryPlugin ioCanaryPlugin = new IOCanaryPlugin(new IOConfig.Builder()
     .dynamicConfig(dynamicConfig)
     .build());
 builder.plugin(ioCanaryPlugin);
 
 // prevent api 19 UnsatisfiedLinkError
 //sqlite
 SQLiteLintConfig config = initSQLiteLintConfig();
 SQLiteLintPlugin sqLiteLintPlugin = new SQLiteLintPlugin(config);
 builder.plugin(sqLiteLintPlugin);
 
 ThreadWatcher threadWatcher = new ThreadWatcher(new ThreadConfig.Builder().dynamicConfig(dynamicConfig).build());
 builder.plugin(threadWatcher);

 Matrix.init(builder.build());//main

 //start only startup tracer, close other tracer.
 tracePlugin.start();//main
 Matrix.with().getPluginByClass(ThreadWatcher.class).start();//main
 }

Plugin配置,构造Matrix单例对象

//Matrix
public static class Builder {
   private final Application application;
   private PluginListener pluginListener;
   private HashSet<Plugin> plugins = new HashSet<>();//main

public Builder(Application app) {
  this.application = app;
 }

public Builder patchListener(PluginListener pluginListener) {
   this.pluginListener = pluginListener;
   return this;
 }

public Builder plugin(Plugin plugin) {
   String tag = plugin.getTag();
   for (Plugin exist : plugins) {
     if (tag.equals(exist.getTag())) {
       throw new RuntimeException(String.format("plugin with tag %s is already exist", tag));
     }
   }
   plugins.add(plugin);
   return this;
 }

public Matrix build() {
   if (pluginListener == null) {
     pluginListener = new DefaultPluginListener(application);
   }
   return new Matrix(application, pluginListener, plugins);
 }

plugin.init

private Matrix(Application app, PluginListener listener, HashSet<Plugin> plugins) {
   this.application = app;
   this.pluginListener = listener;
   this.plugins = plugins;
   AppActiveMatrixDelegate.INSTANCE.init(application);
   for (Plugin plugin : plugins) {
     plugin.init(application, pluginListener);
     pluginListener.onInit(plugin);
   }
 }

public static Matrix init(Matrix matrix) {
   synchronized (Matrix.class) {
     if (sInstance == null) {
       sInstance = matrix;
     } else {
       MatrixLog.e(TAG, "Matrix instance is already set. this invoking will be ignored");
     }
   }
   return sInstance;
 }

public static Matrix with() {
   if (sInstance == null) {
     throw new RuntimeException("you must init Matrix sdk first");
   }
   return sInstance;
 }

tracePlugin.init

@Override
 public void init(Application app, PluginListener listener) {
   super.init(app, listener);
   MatrixLog.i(TAG, "trace plugin init, trace config: %s", traceConfig.toString());
   if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
     MatrixLog.e(TAG, "[FrameBeat] API is low Build.VERSION_CODES.JELLY_BEAN(16), TracePlugin is not supported");
     unSupportPlugin();
     return;
   }

   anrTracer = new AnrTracer(traceConfig);
   frameTracer = new FrameTracer(traceConfig);
   evilMethodTracer = new EvilMethodTracer(traceConfig);
   startupTracer = new StartupTracer(traceConfig);
 }

tracePlugin.start

@Override
 public void start() {
   super.start();
   Runnable runnable = new Runnable() {
     @Override
     public void run() {
       if (!UIThreadMonitor.getMonitor().isInit()) {
         try {
           UIThreadMonitor.getMonitor().init(traceConfig);
         } catch (java.lang.RuntimeException e) {
           MatrixLog.e(TAG, "[start] RuntimeException:%s", e);
           return;
         }
       }
       AppMethodBeat.getInstance().onStart();
       UIThreadMonitor.getMonitor().onStart();
       
       anrTracer.onStartTrace();
       frameTracer.onStartTrace();
       evilMethodTracer.onStartTrace();
       startupTracer.onStartTrace();
     }
   };

   if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
     runnable.run();
   } else {
     MatrixLog.w(TAG, "start TracePlugin in Thread[%s] but not in mainThread!", Thread.currentThread().getId());
     MatrixHandlerThread.getDefaultMainHandler().post(runnable);
   }
 }

UIThreadMonitor(Choreographer中的vsync监听)

init

public void init(TraceConfig config) {
   if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
     throw new AssertionError("must be init in main thread!");
   }
   this.config = config;
   choreographer = Choreographer.getInstance();
   callbackQueueLock = reflectObject(choreographer, "mLock");
   callbackQueues = reflectObject(choreographer, "mCallbackQueues");

   addInputQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_INPUT], ADD_CALLBACK, long.class, Object.class, Object.class);
   addAnimationQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_ANIMATION], ADD_CALLBACK, long.class, Object.class, Object.class);
   addTraversalQueue = reflectChoreographerMethod(callbackQueues[CALLBACK_TRAVERSAL], ADD_CALLBACK, long.class, Object.class, Object.class);
   frameIntervalNanos = reflectObject(choreographer, "mFrameIntervalNanos");

   LooperMonitor.register(new LooperMonitor.LooperDispatchListener() {
     @Override
     public boolean isValid() {
       return isAlive;
     }

     @Override
     public void dispatchStart() {
       super.dispatchStart();
       UIThreadMonitor.this.dispatchBegin();
     }
     
     @Override
     public void dispatchEnd() {
       super.dispatchEnd();
       UIThreadMonitor.this.dispatchEnd();
     }

   });

   if (config.isDevEnv()) {
     addObserver(new LooperObserver() {
       @Override
       public void doFrame(String focusedActivityName, long start, long end, long frameCostMs, long inputCost, long animationCost, long traversalCost) {
         MatrixLog.i(TAG, "activityName[%s] frame cost:%sms [%s|%s|%s]ns", focusedActivityName, frameCostMs, inputCost, animationCost, traversalCost);
       }
     });
   }
   MatrixLog.i(TAG, "[UIThreadMonitor] %s %s %s %s %s frameIntervalNanos:%s", callbackQueueLock == null, callbackQueues == null, addInputQueue == null, addTraversalQueue == null, addAnimationQueue == null, frameIntervalNanos);
   this.isInit = true;
 }

onStart

@Override
 public synchronized void onStart() {
   if (!isInit) {
     throw new RuntimeException("never init!");
   }
   if (!isAlive) {
     this.isAlive = true;
     synchronized (this) {
       MatrixLog.i(TAG, "[onStart] callbackExist:%s %s", Arrays.toString(callbackExist), Utils.getStack());
       callbackExist = new boolean[CALLBACK_LAST + 1];
     }
     queueStatus = new int[CALLBACK_LAST + 1];
     queueCost = new long[CALLBACK_LAST + 1];
     addFrameCallback(CALLBACK_INPUT, this, true);//main,反射添加callbackQueues的指定类型监听
   }
 }
@Override
 public void run() {
 //记录input, animation, traversal的耗时情况
  final long start = System.nanoTime();
   try {
     doFrameBegin(token);//main
     doQueueBegin(CALLBACK_INPUT);
 
     addFrameCallback(CALLBACK_ANIMATION, new Runnable() {
       @Override
       public void run() {
         doQueueEnd(CALLBACK_INPUT);
         doQueueBegin(CALLBACK_ANIMATION);
       }
     }, true);
 
     addFrameCallback(CALLBACK_TRAVERSAL, new Runnable() {
       @Override
       public void run() {
         doQueueEnd(CALLBACK_ANIMATION);
         doQueueBegin(CALLBACK_TRAVERSAL);
       }
     }, true);
   }
private void doQueueBegin(int type) {
   queueStatus[type] = DO_QUEUE_BEGIN;
   queueCost[type] = System.nanoTime();
 }

 private void doQueueEnd(int type) {
   queueStatus[type] = DO_QUEUE_END;
   queueCost[type] = System.nanoTime() - queueCost[type];
   synchronized (this) {
     callbackExist[type] = false;
   }
 }

dispatchBegin

private void dispatchBegin() {
   token = dispatchTimeMs[0] = SystemClock.uptimeMillis();
   dispatchTimeMs[2] = SystemClock.currentThreadTimeMillis();
   AppMethodBeat.i(AppMethodBeat.METHOD_ID_DISPATCH);

   synchronized (observers) {
     for (LooperObserver observer : observers) {
       if (!observer.isDispatchBegin()) {
         observer.dispatchBegin(dispatchTimeMs[0], dispatchTimeMs[2], token);
       }
     }
   }
 }

dispatchEnd

private void dispatchEnd() {
  if (isBelongFrame) {
    //doFrame作为一次dispatchMessage的内容,如果在dispatchEnd时isBelongFrame为true,表示这次dispatchMessage是一次doFrame绘制任务
    doFrameEnd(token);
   }
   dispatchTimeMs[3] = SystemClock.currentThreadTimeMillis();//cpuEnd
   dispatchTimeMs[1] = SystemClock.uptimeMillis();//end
   AppMethodBeat.o(AppMethodBeat.METHOD_ID_DISPATCH);
   synchronized (observers) {
     for (LooperObserver observer : observers) {
       if (observer.isDispatchBegin()) {
         observer.dispatchEnd(dispatchTimeMs[0], dispatchTimeMs[2], dispatchTimeMs[1], dispatchTimeMs[3], token, isBelongFrame);
       }
     }
   }

doFrameEnd

private void doFrameEnd(long token) {
   doQueueEnd(CALLBACK_TRAVERSAL);
   for (int i : queueStatus) {
     if (i != DO_QUEUE_END) {
       queueCost[i] = DO_QUEUE_END_ERROR;
       if (config.isDevEnv) {
         throw new RuntimeException(String.format("UIThreadMonitor happens type[%s] != DO_QUEUE_END", i));
       }
     }
   }
   queueStatus = new int[CALLBACK_LAST + 1];
   long start = token;
   long end = SystemClock.uptimeMillis();
   synchronized (observers) {
     for (LooperObserver observer : observers) {
       if (observer.isDispatchBegin()) {
         observer.doFrame(AppMethodBeat.getVisibleScene(), start, end, end - start, queueCost[CALLBACK_INPUT], queueCost[CALLBACK_ANIMATION], queueCost[CALLBACK_TRAVERSAL]);//main
       }
     }
   }

  addFrameCallback(CALLBACK_INPUT, this, true);//监听下一帧,main
  this.isBelongFrame = false;
 }

private void doFrameBegin(long token) {
   this.isBelongFrame = true;
 }

LooperMonitor(main looper中的dispatchMessage监听)

//main looper中的dispatchMessage监听
LooperMonitor implements MessageQueue.IdleHandler {
  private static final HashSet<LooperDispatchListener> listeners = new HashSet<>();
  public static void register(LooperDispatchListener listener) {
   synchronized (listeners) {
     listeners.add(listener);
   }
 }
  
 private static final LooperMonitor monitor = new LooperMonitor();
 private LooperMonitor() {
   resetPrinter();//main

   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
     Looper.getMainLooper().getQueue().addIdleHandler(this);//main
   } else {
     MessageQueue queue = reflectObject(Looper.getMainLooper(), "mQueue");
     queue.addIdleHandler(this);
   }
 }
}

resetPrinter

private static void resetPrinter() {
   final Printer originPrinter = reflectObject(Looper.getMainLooper(), "mLogging");
   if (originPrinter == printer && null != printer) {
     return;
   }
   if (null != printer) {
     MatrixLog.w(TAG, "[resetPrinter] maybe looper printer was replace other!");
   }
   Looper.getMainLooper().setMessageLogging(printer = new Printer() {
     boolean isHasChecked = false;
     boolean isValid = false;
 
     @Override
     public void println(String x) {
       if (null != originPrinter) {
         originPrinter.println(x);
       }
 
       if (!isHasChecked) {//Looper中queue.next获取到消息之后在//msg.target.dispatchMessage(msg);前后会调用logging打印>>>>>和<<<<<的log
        isValid = x.charAt(0) == '>' || x.charAt(0) == '<';
         isHasChecked = true;
         if (!isValid) {
           MatrixLog.e(TAG, "[println] Printer is inValid! x:%s", x);
         }
       }
 
       if (isValid) {
         dispatch(x.charAt(0) == '>');
         if (null != testPrinter) {
           testPrinter.println(x);
         }
       }
 
     }
   });

queueIdle

@Override
 public boolean queueIdle() {
   resetPrinter();
   return true;
 }

dispatchToListeners

private static void dispatch(boolean isBegin) {
   for (LooperDispatchListener listener : listeners) {
     if (listener.isValid()) {
       if (isBegin) {
         if (!listener.isHasDispatchStart) {
           listener.dispatchStart();
         }
       } else {
         if (listener.isHasDispatchStart) {
           listener.dispatchEnd();
         }
       }
     } else if (!isBegin && listener.isHasDispatchStart) {
       listener.dispatchEnd();
     }
   }
 }

AppMethodBeat

字节码插桩的类,每个app方法执行开始处调用AppMethodBeat.i,结束之前调用AppMethodBeat.o

private static AppMethodBeat sInstance = new AppMethodBeat();
private static HandlerThread sTimerUpdateThread = MatrixHandlerThread.getNewHandlerThread("matrix_time_update_thread");
private static Handler sHandler = new Handler(sTimerUpdateThread.getLooper());
private static LooperMonitor.LooperDispatchListener looperMonitorListener = new LooperMonitor.LooperDispatchListener() {
   @Override
   public boolean isValid() {
     return status >= STATUS_READY;
   }

   @Override
   public void dispatchStart() {
     super.dispatchStart();
     AppMethodBeat.dispatchBegin();
   }

   @Override
   public void dispatchEnd() {
     super.dispatchEnd();
     AppMethodBeat.dispatchEnd();
   }
 };

i(methodId)

/**
 \* hook method when it's called in.
 */
 public static void i(int methodId) {
   if (status <= STATUS_STOPPED) {
     return;
   }
   if (methodId >= METHOD_ID_MAX) {
     return;
   }
   if (status == STATUS_DEFAULT) {
     synchronized (statusLock) {
       if (status == STATUS_DEFAULT) {
         realExecute();
         status = STATUS_READY;
       } }}
   if (Thread.currentThread().getId() == sMainThread.getId()) {
     if (assertIn) {
       android.util.Log.e(TAG, "ERROR!!! AppMethodBeat.i Recursive calls!!!");
       return;
     }
     assertIn = true;
     if (sIndex < Constants.BUFFER_SIZE) {
       mergeData(methodId, sIndex, true);//main
     } else {
       sIndex = -1;
     }
     ++sIndex;
     assertIn = false;
   }}

o(methodId)

/**
 \* hook method when it's called out.
 */
 public static void o(int methodId) {
   if (status <= STATUS_STOPPED) {
     return;
   }
   if (methodId >= METHOD_ID_MAX) {
     return;
   }
   if (Thread.currentThread().getId() == sMainThread.getId()) {
     if (sIndex < Constants.BUFFER_SIZE) {
       mergeData(methodId, sIndex, false);//main
     } else {
       sIndex = -1;
     }
     ++sIndex;
   }
 }

mergeData

/**
 \* merge trace info as a long data
 *
 \* @param methodId
 \* @param index
 \* @param isIn
 */
 private static void mergeData(int methodId, int index, boolean isIn) {
   if (methodId == AppMethodBeat.METHOD_ID_DISPATCH) {
     sCurrentDiffTime = SystemClock.uptimeMillis() - sDiffTime;
   }
   long trueId = 0L;
   if (isIn) {
     trueId |= 1L << 63;
   }
   trueId |= (long) methodId << 43;
   trueId |= sCurrentDiffTime & 0x7FFFFFFFFFFL;//43位1

   sBuffer[index] = trueId;//main
   checkPileup(index);
   sLastIndex = index;
 }
private static IndexRecord sIndexRecordHead = null;//链表,每个item的index由小到大排列
private static long[] sBuffer = new long[Constants.BUFFER_SIZE];
private static int sIndex = 0;//未到阈值时只增
private static int sLastIndex = -1;

sBuffer_array

sBuffer数组的item含义如下:

image-20210302165430564

sIndexRecordHead链表

graph LR
sIndexRecordHead-->|next,index递增|IndexRecord1-->|next,index递增|IndexRecord2-->|next,index递增|IndexRecord3

maskIndex

//将当前调用的方法new IndexRecord(sIndex - 1)按照index升序排列插入到链表sIndexRecordHead当中
//返回new的record信息,相当于当前时刻的开始方法记录信息
public IndexRecord maskIndex(String source) {}

copyData

//从sBuffer中复制开始方法调用记录点到当前调用方法记录点中的堆栈信息,利用index信息
public long[] copyData(IndexRecord startRecord) {
  return copyData(startRecord, new IndexRecord(sIndex - 1));}

public long[] copyData(IndexRecord startRecord, IndexRecord endRecord) {}

IndexRecord.release

public static final class IndexRecord {
   public IndexRecord(int index) {
     this.index = index;
   }
   public int index;
   private IndexRecord next;
   public boolean isValid = true;
   public String source;

public void release() {//在sIndexRecordHead 整个链表中,删除this对应的节点record}

at(插桩后 在Activity的onWindowFocusChanged中调用)

/**
 \* when the special method calls,it's will be called.
 *
 \* @param activity now at which activity
 \* @param isFocus this window if has focus

 */
 public static void at(Activity activity, boolean isFocus) {
   String activityName = activity.getClass().getName();
   if (isFocus) {
     if (sFocusActivitySet.add(activityName)) {
       synchronized (listeners) {
         for (IAppMethodBeatListener listener : listeners) {
           listener.onActivityFocused(activityName);//main
         }

structuredDataToStack

TraceDataUtils

示意图

image-20210302170300409

public static void structuredDataToStack(long[] buffer, LinkedList<MethodItem> result, boolean isStrict, long endTime) {
 ......
//addMethodItem构造linkedlist stack结构,在方法out时向前找到对应的in,记录一条methodItem在linkedlist头部[...id4,id7,id6,id5]
addMethodItem(result, methodItem);

TreeNode root = new TreeNode(null, null);
stackToTree(result, root);//根据depth转成tree结构,执行完之后可以根据depth为0的节点查看其children也就是depth为1的节点的耗时情况,如果出现depth为0耗时远大于所有children节点耗时之和,说明有系统方法作为depth为1的节点且耗时比较久,统计进depth为0的时间却没有统计进app内部方法depth为1的节点

result.clear();
treeToStack(root, result);//深度优先tree转换为list
}
//逆序删除list中靠后的且耗时小于5ms的元素,保留最后30个
public static void trimStack(List<MethodItem> stack, int targetCount, IStructuredDataFilter filter) {}
//key综合了大于总耗时0.3的methodItem得到
public static String getTreeKey(List<MethodItem> stack, long stackCost) {
public static final class TreeNode {
   MethodItem item;
   TreeNode father;

   LinkedList<TreeNode> children = new LinkedList<>();

   TreeNode(MethodItem item, TreeNode father) {
     this.item = item;
     this.father = father;
   }

其他

PluginListener

classDiagram
class PluginListener {
 +onInit(Plugin plugin) void
 +onStart(Plugin plugin) void
 +onStop(Plugin plugin) void
 +onDestroy(Plugin plugin) void
 +onReportIssue(Issue issue) void
}
PluginListener<|--DefaultPluginListener
DefaultPluginListener<|--TestPluginListener
//TestPluginListener
@Override
 public void onReportIssue(Issue issue) {
   super.onReportIssue(issue);
   MatrixLog.e(TAG, issue.toString());

   IssuesMap.put(IssueFilter.getCurrentFilter(), issue);
   jumpToIssueActivity();
 }

Issue

private Integer  type;
 private String   tag;
 private String   key;
 private JSONObject content;
 private Plugin   plugin;

IDynamicConfig

enum ExptEnum {
   // trace
   clicfg_matrix_trace_fps_enable,
   clicfg_matrix_trace_care_scene_set,
   clicfg_matrix_trace_fps_time_slice,
   clicfg_matrix_trace_evil_method_threshold,
}
 String get(String key, String defStr);
 int get(String key, int defInt);
 long get(String key, long defLong);
 boolean get(String key, boolean defBool);
 float get(String key, float defFloat);

TraceConfig

public IDynamicConfig dynamicConfig;
 public boolean defaultFpsEnable;
 public boolean defaultMethodTraceEnable;
 public boolean defaultStartupEnable;
 public boolean defaultAnrEnable;
 public boolean isDebug;
 public boolean isDevEnv;
 public String splashActivities;
 public Set<String> splashActivitiesSet;

public static class Builder {

private TraceConfig config = new TraceConfig();

public Builder dynamicConfig(IDynamicConfig dynamicConfig) {
   config.dynamicConfig = dynamicConfig;
   return this;
 }

public TraceConfig build() {
   return config;
 }