Android APM性能监控

数据的价值

APM

  目标

  对应用的性能、业务可靠性进行线上的监控和预警

  采集内容

  系统指标,应用性能指标,Crash,自定义日志等

用户行为 

   目标

   精细化运营

   采集内容

    从用户属性——性别、地域、收入、家庭状况 

    从用户生命周期——注册、活跃、流失

    从用户行为——功能、内容、产品的喜好等

技术手段

Java层实现功能

1.自定义业务数据链路化

2.内存指标

3.CPU指标

4.FPS 指标

5.ANR日志

6.卡顿检测

7.GC日志

8.Crash日志

9.Http指标数据(暂时只支持OkHttp)

10.电量指标

11.MAOP,使用注解和配置文件AOP指定方法执行前,执行后,异常插入指定代码的功能(项目也实现动态日志功能,但是好像没有很好的使用场景)

12.Remote下发命令,执行shell和动态执行代码功能

13.交互分析:分析Activity生命周期耗时

概述

因为涉及的知识点太多,受篇幅限制,此篇就只列出关键的知识点和步骤,具体知识点会列出参考的链接或者书籍,相信这些作者都讲的很详细和通透。

Android AOP

在Android编译过程中,使用自定义的gradle插件,注册新的tranform任务,在java compile之后,修改编译后class文件的内容,在切入点增加相应的代码来实现AOP,实现无埋点功能。

Android的编译流程


Gradle

Transform API

Android Gradle 工具在 1.5.0 版本后提供了 Transfrom API, 允许第三方 Plugin 在打包 dex 文件之前的编译过程中操作 .class 文件。目前 jarMerge、proguard、multi-dex、Instant-Run 都已经换成 Transform 实现,我们注册自己的transform实现对class文件的修改

Transform具体的详解

需要注意的是App Module依赖的library Module都是以jar的形式存在的,处理jar包时需要通过jarInput.getName()来判断是否是":xx"形式开头的jar,这部分jar包需要当成目录代码来访问。

自定义Gradle插件

自定义Gradle Plugin使用的是Groovy语言,需要对groovy有一定的了解,着重了解它的闭包概念,和各种省略写法规则。

深入理解Android之Gradle

具体操作详解

调试gradle插件

1.创建新的Configuration,Run->Edit Configurations->Remote

2.终端调用命令

./gradlew clean

./gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true

3.App栏选择gradleDebug,点击debug按钮,AS会断点的地方停下来等待调试

Class文件格式

Class文件的总的格式

Class的method属性

Method的code属性

Class文件格式分析

深入理解Java虚拟机(第2版)  提取码: tmnh

字节码指令

Java AOP

常见的字节码生成工具有: ASM, AspectJ,Javassit ,Javapoet ,Spring,CGLib等,Spring,CGLib这些应为他们的作用机制原因,在 Android中无法使用,ASM可对指令流程有很好的分析能力,非常适用我们的场景,最终选择了ASM,其他的工具都都有各自的使用场景。

ASM

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类,ASM通过 “Visitor” 模式将 “.class” 类文件的内容从头到尾扫描一遍.

ASM详解

ASM4使用指南

ASM开发的高效工具

Decompiler

字节码反编译的工具,as自带,用来检查生成的代码是否满足class 文件格式

Classpy

图形化的class 文件分析工具,功能和 javap类似,界面参考了Java Class Viewer

GitHub地址

ASM Bytecode Outline

它有一个Bytecode视图,该视图可以查看当前激活的源码视图所对应的类 用asm应该如何生成。也就是说,如果你知道改造后的类的字节码,就可以通过该视图得到改造的过程,asm的调用语句应该怎么写。研究asm时特别有用,值得推荐。

IDEA Plugin

dx命令

强烈推荐,当你ASM写完之后,就是dx不过时,可以使用这个命令,它可以提示你具体哪条指令出现了什么问题,非常提高效率。

dx --debug --dex —output=~/Desktop/out.dex  ~/Desktop/dex

日志

数据链路化

1.事件中定义id和parentId属性来建立事件关系

2.同一个线程里执行的事件本身就存在链路

3.不同线程的事件,需要传递ParentId

MAOP

提供简单使用配置文件或者使用注解指定哪个类的哪个方法需要在方法执行前,执行后,执行异常时进行拦截,实现日志代码和业务代码的分离,如果业务觉得匹配规则不够灵活和丰富,也可以选择匹配语法和场景更多的AspectJ,同时需要一定学习成本。

注解方式

配置文件方式

插桩代码

交互分析

一般的交互分析需求,一般业务方法中插入代码或者监听系统提供的回调接口就能实现,Mas实现了:App启动,App结束,页面浏览,OnClick几种基本事件监控。

点击事件

通过Aop在View.OnClickListener.onClick插入代码实现,具体实现查看项目 plugin下的 MasAnalyticsClassVisitor实现

App启动,App结束,页面浏览

Activity页面浏览都可以通过 Application.ActivityLifecycleCallbacks来实现,具体查看项目中的MasDataActivityLifecycleCallbacks

Crash

通过Thread.setDefaultUncaughtExceptionHandler(this)获取

内存监控

实现了监控内存使用随事件改变的曲线

OOM事件时检测fd,thread数目

Activity泄漏的检测

低内存状态检测

详细解析

Cpu

通过读取proc/pid/stat文件获取cpu数据进行计算,具体计算方法可以参考项目里CpuSnapshot类和CpuMonitor类实现,Android O 有权限读取自己pid下的proc文件

GC日志

1.GC日志可以通过Runtime.exc执行"logct -v time",抓取logcat日志通过匹配特定字符的方法,运行时可以捕捉自己进程打印的logcat日志

2.查看dalvik和art的源码可以知道,Gc打印都会调用系统的logcat接口,如果能在native层hook住方法调用,是否是更好的实现,可以使用xhook/bhook这类COT/PLT hook工具hook住int__android_log_write(intprio,constchar*tag,constchar*text),通过匹配特定字符来得到Gc的通知

电量

通过注册系统recevier,具体可查看BatteryMonitor类,但是只能知道系统电量的变化,对于应用电量消耗并不能很好的描述,如何描述应用电量的消耗还需后续实现,Google之后发现,Android4.4之后,系统将电量权限收掉之后参考其他项目的实现,好像都是模仿整个Android系统计算电量的公式重新做了一遍,这个还需要学习系统如何计算电量的。

ANR日志

1.通过FileObserver监听/data/anr/traces.txt读写事件,根据anr日志的固定格式解析出属于当前包名的Anr日志, anr数据一般保持最后一次数据, 但是 Huawei 有些机型最新的anr是写在最后的,采集的时候需要采集最新的ANR,具体实现查看ANR类日志内容。实践中发现FileObserver只能在第一次Anr发生的时候被通知,再次发生不会有通知,5.0之后已经基本上没有权限获取。

2.通过watchdog监测主线程实现,定义自己Anr的阀值

3.Ams在anr时会给应用进程发送SIG_QUIT信号,虚拟机在/art/runtime/signal_catcher.cc处理,把数据写入/data/anr/traces.txt,并打印Wrote stack traces to '/data/anr/traces.txt'日志,所以可以使用xhook这类 hook工具hook住int__android_log_write(intprio,constchar*tag,constchar*text),通过匹配特定字符来得到anr的通知


卡顿检测

使用Looper的print机制,计算当前主线操作执行的时间来确定是否卡顿,实现时可通过watchdog实现,但是正确的堆栈数据比较难获取,通过多次获取MainThread的堆栈来增加准确性,具体查看UIMonitor类

FPS

通过Choreographer.getInstance().postFrameCallback(frameCallback),设置回调来统计帧数,不过并不是十分准确,具体查看Fps类,在只有进程当下的权限的下还没有找到更好的方式,有更高权限的情况下可通过gxinfo实现。

Http指标数据

暂时只支持OkHttp3,不过其他httpclient实现原理也大致相同。项目当前实现的http 请求耗时,异常,数据大小,状态码 的获取,直接使用前面实现的MAOP,拦截OkHttpClient.Builder的build方法加入统计Interceptor ,DNSLookUp  耗时,连接耗时,ssl耗时,通过OkhttpClient 3.10之后设置EventListener.Factory,可以直接收集,首包时间需要拦截OkHttp读请求数据的方法来实现,OKHttpClient 最终调用CallServerInterceptor,关键代码就是读取readResponseHeaders的时机,详细的分析可以查看基于OkHttp的Http监控这篇文章。

实现

使用前面提供的MAOP功能,在AOP配置文件中加入,拦截OkHttpClient的builder方法和Http1Codec的readHeaderLine方法和okhttp3.internal.http2.Http2Stream的takeResponseHeaders方法的配置

在拦截OkHttpClient的Builder的build()方法中加入统计Interceptor和EventListenerFactory

首包的时间通过:认为第一次读响应头返回时为首包时间,拦截okhttp3.internal.http1.Http1Code.readHeaderLine的方法和okhttp3.internal.http2.Http2Stream.takeResponseHeaders计算首包时间

Remote执行

远程在日常开发维护实践过程中被证明是非常有用的功能,提供了开发直接触达用户的途径,改变出了问题,开发和用户低效沟通的情况。

不过,现项目暂时还没有实现功能,~.~~.~~.~。现有的思路是通过push通道下发命令,客户端解析执行,客户端集成脚本引擎 例如 lua ,quickjs等,同时可以通过runtime执行shell命令。项目中已经接入可用的lua库,但是shell执行,在开发过程中发现Android Runtime.exc执行带空格的shell命令,并不能像直接执行shell命令那样正确,这点还需要想办法解决。

后续

当前项目初步具备指标的采集的能力,但是指标数据和问题的解决之间天然横亘着巨大的鸿沟,这些还需要在实践过程中找到解决一个问题需要的全部数据,需要更加了解Android提供运行环境的实现原理,实现一个完善成熟的质量保障体系的道路还很漫长。

项目地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容