Android 7.0 耗电详情-应用消耗CPU电量统计原理

1. 原理

应用CPU耗电计算 = APP运行中的每个CPU消耗时间和电量的总和,具体算法如下

  1. 获取每个CPU频率运行时间接口-BatteryStatsImpl.getTimeAtCpuSpeed
  2. 获取每个CPU频率消耗的电量-Profile.getAveragePowerForCpu

其中一些应用没有电量消耗问题或者电量不准确,重点看下 time_in_state 节点有没有生成和power_profile.xml是否被正确配置

  • 源码
  • frameworks\base\core\java\com\android\internal\os\CpuPowerCalculator.java
    @Override
    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
                             long rawUptimeUs, int statsType) {
        app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;

        // Aggregate total time spent on each cluster.
        long totalTime = 0;
        // 获取 CPU 群簇数量,即 cpu.clusters.cores 的size
        final int numClusters = mProfile.getNumCpuClusters();
        // 遍历每个 CPU 群簇数量 cpu.clusters.cores 
        for (int cluster = 0; cluster < numClusters; cluster++) {
            // 获取每个 CPU 群簇的CPU档位size,例如 cpu.speeds.cluster0 的档位数量
            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
            for (int speed = 0; speed < speedsForCluster; speed++) {
                // 累增每个CPU档位的消耗时间
                totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
            }
        }
        totalTime = Math.max(totalTime, 1);

        double cpuPowerMaMs = 0;
        // 遍历每个 CPU 群簇数量 cpu.clusters.cores 
        for (int cluster = 0; cluster < numClusters; cluster++) {
            // 获取每个 CPU 群簇的CPU档位size,例如 cpu.speeds.cluster0 的档位数量
            final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
            for (int speed = 0; speed < speedsForCluster; speed++) {
                // ratio = 每个档位的CPU消耗时间 / 总运行时间
                final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
                        totalTime;
                
                // cpuSpeedStepPower 计算每个CPU频率档位消耗的电流
                final double cpuSpeedStepPower = ratio * app.cpuTimeMs *
                        mProfile.getAveragePowerForCpu(cluster, speed);
                if (DEBUG && ratio != 0) {
                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
                            + speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
                }
                
                // 应用CPU消耗总电流等于应用在每个CPU单位消耗的电量之和
                cpuPowerMaMs += cpuSpeedStepPower;
            }
        }
        
        
        app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);

        if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
            Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
                    + BatteryStatsHelper.makemAh(app.cpuPowerMah));
        }

2. 获取每个CPU频率运行时间接口 BatteryStatsImpl.getTimeAtCpuSpeed

  • 源码
  • frameworks\base\core\java\com\android\internal\os\BatteryStatsImpl.java

这里主要是 LongSamplingCounter 进行CPU时间计算,这里主要是使用 time_in_state 的节点数据

        LongSamplingCounter mCpuClusterSpeed;

        @Override
        public long getTimeAtCpuSpeed(int cluster, int step, int which) {
            if (mCpuClusterSpeed != null) {
                if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
                    final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster];
                    if (cpuSpeeds != null) {
                        if (step >= 0 && step < cpuSpeeds.length) {
                            final LongSamplingCounter c = cpuSpeeds[step];
                            if (c != null) {
                                return c.getCountLocked(which);
                            }
                        }
                    }
                }
            }
            return 0;
        }

2.1 CPU时间来源 time_in_state

统计的对象是 time_in_state 节点的数据

adb shell "cat /sys/devices/system/cpu/cpu5/cpufreq/stats/time_in_state"
1500000 20000
1429000 495
1367000 549
1314000 528
1261000 578
1208000 12754
1155000 92
1102000 54
1050000 642
948000 186
846000 261
745000 142
643000 168
542000 182
460000 171
400000 31079
  • 源码
  • frameworks\base\core\java\com\android\internal\os\KernelCpuSpeedReader
/**
 * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
 * last call to {@link #readDelta}. Each line in the proc file has the format:
 *
 * freq time
 *
 * where time is measured in jiffies.
 */
public class KernelCpuSpeedReader {
    private static final String TAG = "KernelCpuSpeedReader";

    private final String mProcFile;
    private final long[] mLastSpeedTimes;
    private final long[] mDeltaSpeedTimes;

    // How long a CPU jiffy is in milliseconds.
    private final long mJiffyMillis;

    /**
     * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
     */
    public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
        mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
                cpuNumber);
        mLastSpeedTimes = new long[numSpeedSteps];
        mDeltaSpeedTimes = new long[numSpeedSteps];
        long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK);
        mJiffyMillis = 1000/jiffyHz;
    }

2.2 如果没有 time_in_state 的解决方案

配置下 CONFIG_CPU_FREQ_STAT=y 宏即可

https://blog.csdn.net/su749520/article/details/83385322

2.3 更新CPU时间函数

2.3.1 刷新 updateCpuTimeLocked 不同CPU频率的运行时间

    /**
     * Read and distribute CPU usage across apps. If their are partial wakelocks being held
     * and we are on battery with screen off, we give more of the cpu time to those apps holding
     * wakelocks. If the screen is on, we just assign the actual cpu time an app used.
     */
    public void updateCpuTimeLocked() {
        if (mPowerProfile == null) {
            return;
        }

        if (DEBUG_ENERGY_CPU) {
            Slog.d(TAG, "!Cpu updating!");
        }

        // Holding a wakelock costs more than just using the cpu.
        // Currently, we assign only half the cpu time to an app that is running but
        // not holding a wakelock. The apps holding wakelocks get the rest of the blame.
        // If no app is holding a wakelock, then the distribution is normal.
        final int wakelockWeight = 50;

        // Read the time spent for each cluster at various cpu frequencies.
        final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
        for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
            clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
        }

2.3.2 刷新不同应用的CPU消耗时间

当 KernelUidCpuTimeReader 中的文件节点发生变化的时候刷新,对UID下的CPU运行时间

        // Read the CPU data for each UID. This will internally generate a snapshot so next time
        // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
        // we just ignore the data.
        final long startTimeMs = mClocks.elapsedRealtime();
        mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
                new KernelUidCpuTimeReader.Callback() {
                ...
                // Add the cpu speeds to this UID. These are used as a ratio
                // for computing the power this UID used.
                final int numClusters = mPowerProfile.getNumCpuClusters();
                if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length !=
                        numClusters) {
                    u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
                }

                for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
                    final int speedsInCluster = mPowerProfile.getNumSpeedStepsInCpuCluster(
                            cluster);
                    if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster !=
                            u.mCpuClusterSpeed[cluster].length) {
                        u.mCpuClusterSpeed[cluster] =
                                new LongSamplingCounter[speedsInCluster];
                    }

                    final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
                    for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) {
                        if (cpuSpeeds[speed] == null) {
                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
                        }
                        // 累加数据,最终会存了BatteryState.bin文件中
                        cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]);
                    }
                }

3. 获取每个CPU频率消耗的电量 Profile.getAveragePowerForCpu

  • 源码
  • frameworks\base\core\java\com\android\internal\os\PowerProfile.java

具体可以查看
https://blog.csdn.net/su749520/article/details/83340832

    /**
     * 获取cpu.active.cluster[cluster].[step] 对应下标的CPU耗电信息
     * 例如cpu.active.cluster0下第0下标的电流为100 mA
     */
    public double getAveragePowerForCpu(int cluster, int step) {
        if (cluster >= 0 && cluster < mCpuClusters.length) {
            return getAveragePower(mCpuClusters[cluster].powerKey, step);
        }
        return 0;
    }

上述数据,需要手机厂商根据整机进行测试,并填入power_profile文件中

3.1 power_profile.xml文件

  • frameworks\base\core\res\res\xml\power_profile.xml




  
  0
  200  
  300  
  66 
  0.5  
  4.83  
  166  
  20  
  39 
  145 
  109 
  659 
  50 

  
  200 
  32 
  
   
      2 
      1 
  


  
  0
  0
  0
  0

  
  
      4 
      4 
  

    
  
      900000
      979000
      1085000
      1218000
      1351000
      1484000
      1617000
      1750000
      1779000
      1809000
      1838000
      1868000
      1897000
      1927000
      1961000
      2001000
  

  
      400000
      460000
      542000
      643000
      745000
      846000
      948000
      1050000
      1102000
      1155000
      1208000
      1261000
      1314000
      1367000
      1429000
      1500000
  

  
  
      100
      110
      123
      134
      142
      153
      163
      171
      186
      190
      201
      213
      223
      236
      248
      256
  

  
      100
      114
      125
      134
      140
      147
      152
      158
      164
      169
      176
      181
      185
      190
      195
      200
  

  
  6

  
  
    22.7 
  

  
  3000

  
  
  0
  
  0
  
  0
  
   
  
  
  0

   
    .0002 
    .002  
    .02   
    .2    
    2    
  



你可能感兴趣的:(省电续航)