BlockCanary CpuSampler 适配方案验证

461 阅读5分钟

前言

记录寻找替换方案的过程

top 命令已经不可行

首先尝试使用 top 命令去获取 cpu 信息,在电脑上执行

adb shell top -n 1 

获取到的信息如下

C:\Users\Admin>adb shell top -n 1
[uTasks: 769 total,   3 running, 766 sleeping,   0 stopped,   0 zombie
Mem:      5.4G total,      5.4G used,       71M free,      239M buffers
Swap:      2.5G total,      610M used,      1.9G free,      2.2G cached
800%cpu  45%user   0%nice 100%sys 634%idle   0%iow  17%irq   3%sirq   0%host

看到可以获取 cpu 信息了,可是没高兴多久,当放到 Android App 中去执行时,就 G 了,在我 Android 13 的手机上一直获取到 cpu 信息都是 0 ,不甘心换了台 Android 10 测试也是相同结果

结果如下

line : 800%cpu   0%user   0%nice   0%sys 800%idle   0%iow   0%irq   0%sirq   0%host

看来 top 命令的方式已经不再可用了,去滴滴 Dokit 上看到也有人反馈 Dokit 获取不到 cpu 信息,只能另找方案了 github.com/didi/DoKit/…

HardwarePropertiesManager

尝试通过系统服务来直接获取 cpu 使用率,最后还是不行,同样也是需要系统应用才能访问到

cpufreq 路径

作为一个 sys 目录下的一个路径,看到有博客说这种方式可用

这种方案有两种方式获取

  1. 通过逐个获取每个 cpu 的运行时间,比如我的小米是 8 核 cpu,那我去获取 cpu[0-7] 共 8 份文件中的总和
  • 通过 cpu 策略,比如 8 核有 3 份策略,这种方案确实可以少读取几个文件,但有个疑问,怎么去获取策略数量,会不会有些 cpu 策略并不是 3 份,这种又怎么处理呢

先来看下第一种方式获取,获取 cpu7 的参数,前面是 cpu 频率,后面对应频率下的执行时间

C:\Users\Admin>adb shell cat /sys/devices/system/cpu/cpu7/cpufreq/stats/time_in_state
825600 55901351
902400 86570
979200 74804
1056000 213817
1209600 129256
1286400 52766
1363200 79177
1459200 43535
1536000 24624
1612800 87744
1689600 55199
1766400 18301
1843200 16066
1920000 13120
1996800 14409
2092800 59493
2169600 13362
2246400 21327
2323200 80973
2400000 8857
2476800 8298
2553600 10331
2649600 55135
2803200 543609

再看第二种方式获取,他俩其实还是有差别,比如 "2803200 " 这个频率下,我就没在 policy0/4/7 这三个策略中找到对应的

C:\Users\Admin>adb shell cat  /sys/devices/system/cpu/cpufreq/policy7/stats/time_in_state
806400 13732109
940800 60388
1056000 804627
1171200 189701
1286400 290102
1401600 158627
1497600 36420
1612800 79639
1728000 207300
1843200 24322
1958400 80042
2054400 691783
2169600 803
2284800 53212
2400000 94008
2515200 339
2630400 258
2726400 258
2822400 311
2841600 14242
2995200 1950

sys 中记录了所有策略下 CPU 使用时间,只要一个个累加起来就是 CPU 的总使用时长

然后找到 CPU 空闲,就可以计算出 CPU 的使用率了

具体流程

  1. 查找有几份策略
  2. 然后读取不同策略下的总时长

读取文件的方式

     private void getCpuTime(String fileName) {
           try {
                BufferedReader cpuReader = new BufferedReader(new InputStreamReader(
                            new FileInputStream(fileName)), 1000);
                String line = cpuReader.readLine();
                while (line != null) {
                    String[] cpuInfoArray = line.split(" ");
                    Log.e(TAG, " cpuRate"+ cpuInfoArray[1]);
                    cpuTime += Long.parseLong(cpuInfoArray[1]);
                    line = cpuReader.readLine();
                 }
              } catch (Throwable throwable) {
                  Log.e(TAG, "doSample: ", throwable);
              } finally {
                    try {
                        if (cpuReader != null) {
                            cpuReader.close();
                        }
                    } catch (IOException exception) {
                        Log.e(TAG, "doSample: ", exception);
                    }
                }
            }

获取空闲时间

获取 Cpu 空闲时间麻烦一点点,大概流程:

// 获取当前 cpuidle state
C:\Users\Admin>adb shell ls /sys/devices/system/cpu/cpu0/cpuidle
driver
state0
state1
state2

// state 对应的时长 (微秒)
C:\Users\Admin>adb shell cat /sys/devices/system/cpu/cpu0/cpuidle/state0/time
160453296988

  1. 先获取 cpu0 的 idleSate,通常由一两个,然后通过 state 获取当前空闲时间
  2. 然后获取当前 state 的进入次数
  3. 第一二步的值相乘,就获取到 cpu0 的空闲时间了(微秒)
  4. 重复 1-3 步,直到获取所有 cpu 的空闲时间

看作者的博文中提到,这样还会出现空闲时间计算不准的情况,原因是有些 state 的刷新延时大于我们通常的采样时间(比如 BlockCanary 默认为 200ms),在计算空闲时长的差值很小,接近 0 ,导致计算出来的 CPU 使用率很高,跟真实的偏差大

作者也给出了方案,具体可以看 Android 高版本采集系统CPU使用率的方式

小结

一通尝试下来,验证了确实有替换方案,来获取 cpu 使用率,接下来 fork BlockCanary 代码准备着手改造了

相关链接