Android进程前后台运行总时长测量

3,705 阅读4分钟
原文链接: suojingchao.github.io

Android进程前后台运行总时长测量

背景知识

Android在5.0之后通过UsageStats能拿到应用的运行时长,但是这里对运行的定义就比较尴尬了,它指的是应用在前台的时间,而当用户按home或back键切到后台时的时间UsageStats是没有统计在运行时长内。那我想知道的是我的应用包括前台和后台的整个运行时长怎么办呢,嗯哼,自己造个轮子吧。

Linux设计的目的就是尽可能的充分利用内存,所以在Android设备上剩余内存总是看上去很紧张。原因就在于Android缓存了大部分的信息,其中包括启动过的App等等各种资源,目的就是为了用户下次能够更快的启动App,以此来提升用户体验。
然而内存是有限的,不可能不加管束的任由App运行。在Android中进程管理的大任就主要落在了AMS的身上。另外还有LMK也参与其中。他们二者的工作都与oom_adj紧密相关。

  • oom_adj:一个表示进程重要程度的整数值,值越大,代表进程越不重要,越容易被系统杀死。其计算的依据主要是根据进程所处的状态,占用的资源等等。具体可参考AMS的updateOomAdjLocked方法。
  • AMS:AMS主要通过计算并识别每个进程的oom_adj值得知某个进程当前所处的状态及重要程度。计算完毕后真正的记录oom_adj的工作是通知lmkd来完成的,该值会记录到/proc/pid/oom_adj 。在AMS中进程管理的场景主要如下:

    1. 用户主动从任务栏关闭应用或从设置中强制停止(Force-Stop)。
    2. 应用崩溃或ANR等异常 。
    3. 由于应用间的依赖导致的kill。
    4. 针对Cache和Empty状态的进程在进程数量超过一定阈值时主动kill。
  • LMK:lowmemoeykiller,其真正driver的实现在kernel中drivers/staging/Android/lowmemorykiller.c。其工作原理也是围绕oom_adj展开的。当系统的剩余内存低于一定的阈值时,会触发lmk开始扫描进程列表,它会在运行的进程中找到一个oom_adj最大并且内存占用最多(这里是用的rss来计算,不是pss)的进程,kill它。它提供了两个数组可灵活配置:

    1. /sys/module/lowmemorykiller/parameters/minfree
    2. /sys/module/lowmemorykiller/parameters/adj

    两个数组的大小是一致的,都为6。minfree用于配置不同阶段最小剩余内存的阈值,可以配置6个阈值。adj用于配置oom_adj值。二者是一起搭配使用的,minfree数组中配置的最小剩余内存决定lmk什么时候开始触发扫描,adj数组中配置的oom_adj值帮助lmk在扫描时选择一个要杀死的进程。
    比如:

    • minfree配置如下:
      [18432,23040,27648,32256,55296,97280]
    • adj配置如下:
      [0,10,58,117,176,1000]

    则表示当系统剩余内存低于18432kb时lmk会在进程列表中找出一个oom_adj值大于0并且内存占用最多的进程并将其杀死。依次类推,可配置6个阈值。但lmk的缺点在于一次只能杀死一个进程…

运用实践

原理:
跟踪代码中进程管理的point会发现无论是AMS还是LMK在进程管理过程中都会输出相应的log,目前我的实现方案主要关注AMS输出的event log,并没有加入对LMK输出log的分析,原因在后面缺陷中有说明。在AMS对进程的管理过程中主要通过这几个event log tag来定位进程的运行时长:

  • 进程启动:am_proc_start / am_proc_bound
  • 进程结束:am_kill / am_proc_died

通过对log的分析能够准确的得到一个进程声明周期的起始时间点。进而得到进程的整个运行时长。

方案:

  1. 收集log:通过monkey来模拟用户对手机的高强度使用。并在过程中收集event log。
  2. 利用脚本分析log得到在该统计区间内的进程运行时长。

效果:

Alt text

缺陷:
目前只对AMS模块直接kill进程的log做了解析,LMK触发的kill进程的log并没有加入其中(因为各手机厂商对LMK的配置的阈值相对都比较宽松,真正使用中由于触发LMK扫描而导致的kill进程动作其实并不常见。手机厂商对进程管理的优化也多集中于直接在Framework层针对AMS来添油加醋)。

源码:
python不熟,
写的太屎。
是我的锅,
大神勿喷。
/鞠躬 /鞠躬 /鞠躬
TTools