main_title_structureddatatostack
其他main_title为UIThreadMonitor和LooperMonitor
/**
* <p>
* firstMethod.i LAUNCH_ACTIVITY onWindowFocusChange LAUNCH_ACTIVITY onWindowFocusChange
* ^ ^ ^ ^ ^
* | | | | |
* |---------app---------|---|---firstActivity---|---------...---------|---careActivity---|
* |<--applicationCost-->|
* |<--------------firstScreenCost-------------->|
* |<---------------------------------------coldCost------------------------------------->|
* . |<-----warmCost---->|
*
* </p>
*/
@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
}
//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);
}
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;
}
@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);
}
@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);
}
}
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;
}
@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;
}
}
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);
}
}
}
}
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);
}
}
}
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;
}
//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);
}
}
}
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);
}
}
}
});
@Override
public boolean queueIdle() {
resetPrinter();
return true;
}
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();
}
}
}
字节码插桩的类,每个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();
}
};
/**
\* 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;
}}
/**
\* 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;
}
}
/**
\* 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数组的item含义如下:
graph LR
sIndexRecordHead-->|next,index递增|IndexRecord1-->|next,index递增|IndexRecord2-->|next,index递增|IndexRecord3
//将当前调用的方法new IndexRecord(sIndex - 1)按照index升序排列插入到链表sIndexRecordHead当中
//返回new的record信息,相当于当前时刻的开始方法记录信息
public IndexRecord maskIndex(String source) {}
//从sBuffer中复制开始方法调用记录点到当前调用方法记录点中的堆栈信息,利用index信息
public long[] copyData(IndexRecord startRecord) {
return copyData(startRecord, new IndexRecord(sIndex - 1));}
public long[] copyData(IndexRecord startRecord, IndexRecord endRecord) {}
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}
/**
\* 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
}
TraceDataUtils
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;
}
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();
}
private Integer type;
private String tag;
private String key;
private JSONObject content;
private Plugin plugin;
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);
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;
}