页面整体
- App选择(Android、iOS)
- 版本选择(生产版本、测试版本)
- 时间段(1天、1周、1个月,最多支持3个月)
对于选中的
- App启动次数、网络请求次数(地域分布)
- App崩溃次数,崩溃率(可选时间段)
- ANR分析、卡顿分析
- http请求错误率、CDN性能
- 服务响应时间(平均0.4s 的水平)
- Webview(响应最差的接口、http错误率最高的接口)
- js错误类型
- 交互信息:设备型号分布、操作系统版本、App版本(各个版本占比多少)
以上内容对照现有的APM系统界面编写
360开源的 ArgusAPM 线上监控
主要功能
ArgusAPM 主要支持如下性能指标:
- 交互分析:分析 Activity 生命周期耗时
- 网络请求分析:监控流量使用情况,发现并定位各种网络问题
- 内存分析:全面监控内存的使用
- 进程监控:针对多进程应用,统计进程启动情况,发现启动异常(耗电、存活率等)
- 文件监控: 监控 App 私有文件大小/变化,避免私有文件过大导致的卡顿
- 卡顿分析:监控并发现卡顿原因
- ANR 分析: 捕获 ANR 异常
结构
主要注意有一个
数据采集
1、卡顿(block) 信息
通过使用 :
1 | Looper.getMainLooper().setMessageLogging() |
方法设置自定义的 Printer,监听消息的开始和消息的结束。
2、FPS
主要通过监听系统执行下一帧的回调来做到:
1 | //这个方法的使用方式 |
在 doFrame 回调里面,每次都重新注册这个监听,当到了定时任务,就计算当前 mFpsCount 数量(总帧数),我们直到 FPS 无非就是每秒绘制的帧数,所以,我们可以计算出 FPS 的值:总帧数/时间。
3、Memory
内存收集,在 ArgusAPM 中是通过 getMemoryInfo 方法来获得的:
1 |
|
通过 Debug 类的相关功能,最后在Native层面实现。 Debug 这个类很有用,我们可以看下主要的方法,会有意想不到的收获。比如启动时等待调试就是调用的 waitForDebugger 方法;获取当前的虚拟机信息可以通过 getVmFeatureList 方法。此外,这个比较耗时和cpu,所以谨慎调用,需要合理降低采集的次数。
4、watchdog
WatchDogTask 做的事情和 blockTask 类似,都是卡顿检测,不过采用另外的思路,代码如下:
1 | private Runnable runnable = new Runnable() { |
主要思路是:往主线程post 一个任务,对一个变量 mTick 执行 ++ 操作,然后再当前线程中 sleep 一段时间,然后再去检测 mTick,假如主线程没有卡顿的话,那么 ++ 操作肯定会得到执行,这时候 mTick 就不会与初始值相等。如果相等就可以认为这个等待时间里面,主线程发生了卡顿,这个时候就采集数据,采集的主要是堆栈。
5、ANR
发生 ANR 时都会在 data/anr 下产生 trace 文件,因此 anr 就是以 trace 文件为核心。ArgusAPM 提供了两种思路: 1)通过Fileobserver 的方式监听 data/anr 这个目录;2)定时采样方式(隔一段时间就保存一下现场)。
但是这里需要注意,如果没有权限就拿不到 trace 文件,这里并没有解决方案。
6、Net
有个 gradle 的 plugin,通过这个 gradle ,在编译 APK 的时候,读取所有的 class 文件,如果发现 Class 文件是 Okhttp 的时候,在方法里面,拿到 interceptors 字段,添加自己的 Interceptor ,这样就完成了 用 ASM 写入一段字节码,这样就可以采集 Okhttp 信息。
7、Activity
Activity 的启动常规方式不好采集,ArgusAPM 采用 Hook 的方式,这里采用的是 Hook Instrumentation 这个类:
1 | private static void hookInstrumentation() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { |
hook 当前currentActivityThread 的 mInstrumentation。每次执行 Activity 的任何生命周期都会先调用到 Instrumentation 。在后续可以通过 Hook 方式将自定义的 Instrumentation 代理系统原来的 Instrumentation,这样,就能统计 Activity 中的每个 onXXX 方法执行的耗时,而没有嵌入任何代码。
8、Appstart
ArgusAPM 中采用的是 application 的启动到第一个 Activity 的创建结束,因为已经 Hook 了 Instrumentation ,因此在 Instrumentation 的 callApplicationOnCreate 方法执行时,就记录下 Application 启动的时间,然后 callActivityOnCreate 记录下第一个 Activity 的启动即可获得冷启动的启动时间。
总结
除了net 和 Activity 之外,其他的采集并不难,只是细节会非常多,所以需要很精细化的控制,降低对 App 的影响。
关于线上监控,还可以参考爱奇艺的 xCrash框架,介绍如下:
xCarash源码解析
xCrash 整体分为两部分: 运行于崩溃的App进程楼内的部分、以及独立进程的部分(称为dumper)
- App 进程内的部分分为 Java 部分 和 Native 部分,前者主要用于 Java 层的崩溃捕捉,后者用于在Native 负责信号捕捉
- Dumper 独立进程是纯 Native 实现,主要用于负责凤奎进程中线程相关数据的收集和控制。
Java层的 Exception 捕捉
在 Java 层主要是还是通过 Java 自身提供的方法来捕捉,只需要自己自定义 UncaughtExceptionHandler 即可,在 xCrash 中定义了一个 JavaCrashHandler 类专门用来干这个事情,之后,在里面实现了注册捕捉:
1 | Thread.setDefaultUncaughtExceptionHandler(this); |
针对 ANR 的捕捉
ANR 捕捉主要是用 AnrHandler 实现,主要就是针对 /data/anr 路径的监听,但是,这只适合低版本的手机,在高版本(Android 版本 5.0 以上)的手机上,应用已经访问不到 /data/anr 目录了。xCrash 是怎么实现呢?实际上它捕获了 SIGQUIT 信号,其原理是:Android App 发生 ANR 时, AMS 会向 App 发送 SIGQUIT 信号!
当接在 Native 层收到 SIGQUIT 信号时,就开始 dump 现场信息。
Native 层的 Exception 捕捉
在Native 崩溃发生时,生成 tombstone 文件(与官方的格式兼容),方便查看