简介

TraceCanary分为帧率监控、慢方法监控、ANR监控、启动耗时、主线程优先级检测、IdleHandler耗时检测这6个功能。

基本使用

文档

TraceCanary 文档
Matrix GitHub

用法

1. 项目依赖

  • 在项目根目录下的 gradle.properties中配置要依赖的Matrix版本号
MATRIX_VERSION=2.0.5
  • 在项目根目录下的build.gradle文件添加Matrix依赖
dependencies {classpath ("com.tencent.matrix:matrix-gradle-plugin:${MATRIX_VERSION}") { changing = true }
}
  • 在 app/build.gradle 文件中添加 Matrix 各模块的依赖
apply plugin: 'com.tencent.matrix-plugin'matrix {trace {enable = true //if you don't want to use trace canary, set falsebaseMethodMapFile = "${project.buildDir}/matrix_output/Debug.methodmap"blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"}
}dependencies {implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: MATRIX_VERSION, changing: trueimplementation group: "com.tencent.matrix", name: "matrix-android-commons", version: MATRIX_VERSION, changing: trueimplementation group: "com.tencent.matrix", name: "matrix-trace-canary", version: MATRIX_VERSION, changing: true
}

2. 必要代码 (参考 Matrix中sample-android示例代码)

  • 实现PluginListener,接收Matrix处理后的数据(参考TestPluginListener)
public class PluginListener extends DefaultPluginListener {public static final String TAG = "Matrix.PluginListener";public SoftReference<Context> softReference;private final Handler mHandler = new Handler(Looper.getMainLooper());public PluginListener(Context context) {super(context);softReference = new SoftReference<>(context);}@Overridepublic void onReportIssue(Issue issue) {super.onReportIssue(issue);MatrixLog.e(TAG, issue.toString());IssuesMap.put(IssueFilter.getCurrentFilter(), issue);jumpToIssueActivity();}public void jumpToIssueActivity() {Context context = softReference.get();Intent intent = new Intent(context, IssuesListActivity.class);if (context instanceof Application) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}context.startActivity(intent);}
}

TraceCanary项目本身是没有可视化页面,onReportIssue()方法会输出解析后数据。如果要想可视化效果,可以直接用sample-android中的IssuesListActivity类来集成到自己项目中。同时,还需要sample-android中issue包下IssueFilter、IssuesMap、ParseIssueUtil这些收集和解析数据功能的工具类。

  • 实现动态配置接口,可修改Matrix内部参数(参考DynamicConfigImplDemo类)
public class DynamicConfigImpl implements IDynamicConfig {private static final String TAG = "Matrix.DynamicConfigImplDemo";public DynamicConfigImpl() {}public boolean isFPSEnable() {return true;}public boolean isTraceEnable() {return true;}public boolean isSignalAnrTraceEnable() {return true;}public boolean isMatrixEnable() {return true;}@Overridepublic String get(String key, String defStr) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.// for Activity leak detectif ((ExptEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key) || ExptEnum.clicfg_matrix_resource_detect_interval_millis_bg.name().equals(key))) {Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_detect_interval_millis 10s");return String.valueOf(TimeUnit.SECONDS.toMillis(5));}if (ExptEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {Log.d("DynamicConfig", "Matrix.ActivityRefWatcher: clicfg_matrix_resource_max_detect_times 5");return String.valueOf(3);}return defStr;}@Overridepublic int get(String key, int defInt) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.if (MatrixEnum.clicfg_matrix_resource_max_detect_times.name().equals(key)) {MatrixLog.i(TAG, "key:" + key + ", before change:" + defInt + ", after change, value:" + 2);return 2;//new value}if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {return 10000;}if (MatrixEnum.clicfg_matrix_trace_fps_time_slice.name().equals(key)) {return 12000;}return defInt;}@Overridepublic long get(String key, long defLong) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.if (MatrixEnum.clicfg_matrix_trace_fps_report_threshold.name().equals(key)) {return 10000L;}if (MatrixEnum.clicfg_matrix_resource_detect_interval_millis.name().equals(key)) {MatrixLog.i(TAG, key + ", before change:" + defLong + ", after change, value:" + 2000);return 2000;}return defLong;}@Overridepublic boolean get(String key, boolean defBool) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.return defBool;}@Overridepublic float get(String key, float defFloat) {//TODO here return default value which is inside sdk, you can change it as you wish. matrix-sdk-key in class MatrixEnum.return defFloat;}}
  • 在Application的继承类中,对Matrix进行初始化(参考MatrixApplication)
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// Switch.DynamicConfigImpl dynamicConfig = new DynamicConfigImpl();Matrix.Builder builder = new Matrix.Builder(this);TracePlugin tracePlugin = configureTracePlugin(dynamicConfig);builder.plugin(tracePlugin);Matrix.init(builder.build());tracePlugin.start();}private TracePlugin configureTracePlugin(DynamicConfigImpl dynamicConfig) {boolean fpsEnable = dynamicConfig.isFPSEnable();boolean traceEnable = dynamicConfig.isTraceEnable();boolean signalAnrTraceEnable = dynamicConfig.isSignalAnrTraceEnable();File traceFileDir = new File(getApplicationContext().getFilesDir(), "matrix_trace");if (!traceFileDir.exists()) {if (traceFileDir.mkdirs()) {MatrixLog.e(TAG, "failed to create traceFileDir");}}File anrTraceFile = new File(traceFileDir, "anr_trace"); File printTraceFile = new File(traceFileDir, "print_trace");TraceConfig traceConfig = new TraceConfig.Builder().dynamicConfig(dynamicConfig).enableFPS(fpsEnable).enableEvilMethodTrace(traceEnable).enableAnrTrace(traceEnable).enableStartup(traceEnable).enableIdleHandlerTrace(traceEnable)                   .enableMainThreadPriorityTrace(true)                    .enableSignalAnrTrace(signalAnrTraceEnable)             .anrTracePath(anrTraceFile.getAbsolutePath()).printTracePath(printTraceFile.getAbsolutePath())// 换成自己项目启动页.splashActivities("sample.tencent.matrix.SplashActivity;").isDebug(true).isDevEnv(false).build();return new TracePlugin(traceConfig);        }
}

至此,Matrix就已成功集成到你的项目中,并且开始收集和分析性能相关异常数据。

sample-android效果:

原理

TraceCanary 采用ASM插桩方案。插桩打点操作的类就是AppMethodBeat,该类会在编译期对函数的出入进行插桩i(methodId)/o(methodId),并在Activity#onWindowFocusChanged方法中插入at方法,供后面各种tracer使用。插桩的核心代码在插件中的MethodTracer。

插桩之外,我们还需要监控主线程MessageQueue(LooperMonitor)以及Choreographer(UIThreadMonitor),从里面抽象出一个可以通知Message开始执行、执行完成、Choreographer开始渲染的接口(LooperObserver)。基于这个接口,我们可以开始监控的实现。

FrameTracer(帧率监控)

在UIThreadMonitor中会通过LooperMonitor监听所有主线程的Message的执行,同时会向Choreographer#callbackQueues中插入一个回调来监听CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL这三种事务的执行耗时。

这样当Choreographer#FrameHandler开始执行的vsync时,UIThreadMonitor就可以捕获到vsync执行的起止时间,以及doFrame时各个部分的耗时。然后可以通过一系列计算来计算出帧率。实际上,只需要知道渲染每一帧的起止时间就可以算出帧率了。

// 具体代码见:sample-android 的TestFpsActivity类,下面给出关键代码public class TestFpsActivity extends Activity {// 帧率变化监听private IDoFrameListener mDoFrameListener = new IDoFrameListener(....){public void doFrameAsync(String focusedActivity, long startNs, long endNs, int dropFrame, boolean isVsyncFrame, long intendedFrameTimeNs, long inputCostNs, long animationCostNs, long traversalCostNs) {super.doFrameAsync(focusedActivity, startNs, endNs, dropFrame, isVsyncFrame, intendedFrameTimeNs, inputCostNs, animationCostNs, traversalCostNs);...}};protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);...IssueFilter.setCurrentFilter(IssueFilter.ISSUE_TRACE);// FrameTracer开始跟踪Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().onStartTrace();// 添加监听Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().addListener(mDoFrameListener);...} protected void onDestroy() {super.onDestroy();// 移除监听Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().removeListener(mDoFrameListener);// FrameTracer结束跟踪Matrix.with().getPluginByClass(TracePlugin.class).getFrameTracer().onCloseTrace();}
}

结果:

字段含义:

  • tag

Trace_FPS

  • scene

帧率对应的场景

  • dropLevel

衡量帧率掉帧的水平(次数)

  • dropSum

总共掉帧的总时长

  • fps

帧率

慢方法监控(EvilMethodTracer)

通过监控主线程中每个Message执行的起止时间,如果时间差超过一定的阈值,就认为发生了慢函数调用。此时可以通过AppMethodBeat中的数据,分析出这段时间内函数执行的堆栈信息,以及每个函数执行的耗时。这样,慢函数无所遁形了。


结果:

字段含义:

  • tag

Trace_EvilMethod

  • detail(具体的耗时场景)

NORMAL:代表普通慢函数场景。 ENTER:代表Activity进入场景。 ANR:anr超时场景。
FULL:代表满buffer场景。 STARTUP:启动耗时场景。

如果detail == ENTER,会增加viewInfo字段。包含3个属性:viewDeep:view的深度;viewCount :view的数量;activity :activity的name。

如果detail == STARTUP,会增加subType字段(默认值-1)。如果subType=1,代表application初始化过程的堆栈;如果subType=2,代表启动第一个界面初始化过程的堆栈。

  • cost

耗时

  • stack

堆栈

  • stackKey

客户端提取的 key,用来标识issue的唯一性(方法ID)

打开app/build/outputs/mapping/debug/methodMapping.txt文件。从上图可知stackKey是118也就是方法id。

由上图可知,stackKey=118,对应就是TestEnterActivity类的onCreate 方法

ANR监控(AnrTracer)

在主线程中一般认为超过5s就会发生ANR。通过分析AppMethodBeat中的数据得到函数执行的堆栈以及耗时。

具体代码见:sample-android 的TestTraceMainActivity类public void testSignalANR(final View view) {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}

结果:

字段含义:

  • tag

Trace_EvilMethod

  • detail(具体的耗时场景)

ANR:anr超时场景

其他字段和慢方法监控字段一样。

在methodMapping.txt 文件中对应结果如下:

StartupTracer(启动耗时)

启动过程分析流程图:

firstMethod.i       LAUNCH_ACTIVITY   onWindowFocusChange   LAUNCH_ACTIVITY    onWindowFocusChange^                         ^                   ^                     ^                  ^|                         |                   |                     |                  |t0                        t1                  t2                                       t3|---------app---------|---|---firstActivity---|---------...---------|---careActivity---||<--applicationCost-->||<--------------firstScreenCost-------------->||<---------------------------------------coldCost------------------------------------->|.                         |<-----warmCost---->|

AppMethodBeat.i第一次发生时,可以认为是Application创建的时间(t0);第一次Activity的启动或者Service、Receiver的创建认为是Application创建的结束时间(t1)。这两者的时间差即为Application创建耗时。

第一个Activity的onWindowFocusChange发生时,即为时间t2,t2-t0就是首屏启动耗时。第二个Activity(一般是真正的MainActivity)的onWindowFocusChange发生时,即为时间t3,t3-t0就是冷启动的总耗时。

结果:

字段含义:

  • tag

Trace_StartUp

  • application_create

应用启动的耗时

  • first_activity_create

activity 启动耗时

  • startup_duration

启动总耗时

  • is_warm_start_up

是否是软启动:true(是) false(不是)

  • application_create_scene

100:代表activity拉起的。114:代表service拉起的。113:代表receiver拉起的。-100:代表未知的

性能检测工具:Matrix-TraceCanary 入门相关推荐

  1. 如何使用Lighthouse性能检测工具

    前言 最近做性能检测工具,很多知识点不清楚,打算查缺补漏,补一补. 接下来从官方提供的性能检测工具Lighthouse(灯塔)开始我们的学习,简单介绍了下Lighthouse的一些点. 阅读完本文,你 ...

  2. coverity代码检测工具介绍_FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具...

    FOREPOST:一种使用反馈驱动学习软件测试的性能检测工具 摘要 性能测试的一个目标是找出某些特定情况,在这些情况下对于某些输入值组合,应用程序意外地展示出更糟糕的特性.性能测试的一个基本问题是如何 ...

  3. 在debug模式下引入一些性能检测工具

    我们经常在debug模式下使用一些性能检测工具,例如blockCannary,leakCannary.Stetho等,但是我们release的时候又不需要这些检测工具,通常情况下我们的做法是在buil ...

  4. [Linux 性能检测工具]IOSTAT

    [Linux 性能检测工具]IOSTAT IOSTAT NAME:          Iostat, 报告CPU的统计,和 I/O的统计. 语法: iostat  [ -c ] [ -d ] [ -N ...

  5. [Linux 性能检测工具]DF

    [Linux 性能检测工具]DF DF NAME: df 报告了文件系统的使用 语法: df [OPTION]... [FILE]... 描述: Df显示了以文件名为从参数查看所在文件系统的可用空间. ...

  6. Unity官方性能检测工具UPR使用心得

    Unity官方性能检测工具UPR使用心得 UPR是Unity官方推出的免费性能检测工具,近来功能较之前版本完善了许多,经笔者在正式项目中试用,与UWA的免费性能检测工具GPM作对比,发现UPR相较GP ...

  7. 季冠CPM云平台监控系统——自动化链路的性能检测工具

    网络监测 自动化 智能化 在互联网高度发展的今天,我们每一个人都有过被网络支配的恐惧,除了手机没电,最害怕的就是网络卡顿和网络延迟. 尤其是对于线下实体门店.互联网公司.工厂及酒店等多分支网点企业来说 ...

  8. Unity性能优化之性能检测工具汇总

    Unity Profiler 简述:Unity自带的性能分析工具,可以分析CPU.GPU以及内存消耗,支持真机调试. 官方教程:https://docs.unity3d.com/Manual/Prof ...

  9. java 性能检测工具 检测死锁等

    为什么80%的码农都做不了架构师?>>>    死锁检测方法 1 JConsole 找到需要查看的进程,打开线程选项卡,点击检测死锁 2 jps查看java进程ID,使用jstack ...

最新文章

  1. python的编译器有哪些-python编译器有哪些
  2. java爬虫问题二: 使用jsoup爬取数据class选择器中空格多选择怎么解决
  3. 从蓝桥杯来谈Fibonacci数列
  4. Java源码研究之object to json string debug
  5. 拉格朗日乘数法_拉格朗日乘数法介绍(不含证明)
  6. Varnish 503错误
  7. 电机控制入门——学习路线规划以及学习书籍推荐
  8. 判断SuperView
  9. 易语言程序c0000005,教你四招彻底解决易语言程序误报问题
  10. l7sa008b故障代码_美国凯利冷机故障码表
  11. 拉格朗日插值和牛顿插值的龙格现象
  12. div 设置a4大小_javascript – 拉伸div到A4大小
  13. Edge 开发者日 · New Bing New Edge
  14. 【Linux】lftp客户端使用详解
  15. ucos移植到stm32上的中断小小改进
  16. digital_logic@一位全加器的真值表@画卡诺图@输出逻辑函数表达式
  17. Intellij IDEA Ultimate下载安装
  18. 二维码可以用哪款条码软件打印?
  19. 考试备战系列--软考--02基础知识复习
  20. AE文本复杂排列动画脚本TypeMonkey for mac

热门文章

  1. 如何发送和接收RTP封包的H264,用FFmpeg解码
  2. 视觉机器人+人体姿态识别项目总结
  3. 【评测】常规PCR(终点法,跑胶)的多重PCR的试剂
  4. 一场疫情,全民变厨子、医生变战士、教师变主播、只有孩子们,依然是神兽!...
  5. java js 高德api_地图-参考手册-地图 JS API | 高德地图API
  6. ESP32开发(1)----Espressif-IDE开发环境配置
  7. JEM software ticket45:Console output error of nQP when LCU level rate control is enabled
  8. python中的main函数可以被其他文件调用么_在Python中,如何在另一个py文件的[if\u name\uuuu='\uu main\uu']中调用子例程?...
  9. 两个向量夹角的cos值
  10. 基于微信教室预约小程序系统设计与实现 开题报告