From 2b29041f04076a3ba38a2980eb9f47e4a9f4f26e Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Wed, 27 Sep 2023 11:28:35 -0700 Subject: Add EnergyConsumer collection to CpuPowerStatsCollector Bug: 302013436 Test: atest PowerStatsTests Change-Id: Ie1c4c3e8f98609174a5317a88e51ddc3c70a86e2 --- .../java/com/android/internal/os/PowerProfile.java | 128 ++---- .../res/xml/power_profile_test_power_brackets.xml | 37 -- .../com/android/internal/os/PowerProfileTest.java | 66 --- .../server/power/stats/BatteryStatsImpl.java | 22 +- .../stats/CpuAggregatedPowerStatsProcessor.java | 51 ++- .../server/power/stats/CpuPowerStatsCollector.java | 449 ++++++++++++++++++--- .../server/power/stats/PowerStatsCollector.java | 8 + .../powerstatstests/res/xml/power_profile_test.xml | 101 +++++ .../res/xml/power_profile_test_power_brackets.xml | 37 ++ .../CpuAggregatedPowerStatsProcessorTest.java | 2 +- .../power/stats/CpuPowerStatsCollectorTest.java | 286 ++++++++++++- 11 files changed, 868 insertions(+), 319 deletions(-) delete mode 100644 core/tests/coretests/res/xml/power_profile_test_power_brackets.xml create mode 100644 services/tests/powerstatstests/res/xml/power_profile_test.xml create mode 100644 services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 503e689c0d5d..2298cbde10d1 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -44,7 +44,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Locale; /** * Reports power consumption values for various device activities. Reads values from an XML file. @@ -295,7 +294,7 @@ public class PowerProfile { private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF; - private static final int DEFAULT_CPU_POWER_BRACKET_NUMBER = 3; + public static final int POWER_BRACKETS_UNSPECIFIED = -1; /** * A map from Power Use Item to its power consumption. @@ -361,7 +360,7 @@ public class PowerProfile { } initCpuClusters(); initCpuScalingPolicies(); - initCpuPowerBrackets(DEFAULT_CPU_POWER_BRACKET_NUMBER); + initCpuPowerBrackets(); initDisplays(); initModem(); } @@ -560,8 +559,7 @@ public class PowerProfile { /** * Parses or computes CPU power brackets: groups of states with similar power requirements. */ - @VisibleForTesting - public void initCpuPowerBrackets(int defaultCpuPowerBracketNumber) { + private void initCpuPowerBrackets() { boolean anyBracketsSpecified = false; boolean allBracketsSpecified = true; for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) { @@ -580,79 +578,32 @@ public class PowerProfile { "Power brackets should be specified for all scaling policies or none"); } - mCpuPowerBracketCount = 0; - if (allBracketsSpecified) { - for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) { - int policy = mCpuScalingPolicies.keyAt(i); - CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); - final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy); - if (data.length != cpuScalingPolicyPower.powerBrackets.length) { - throw new RuntimeException( - "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy - + ", expected: " - + cpuScalingPolicyPower.powerBrackets.length); - } + if (!allBracketsSpecified) { + mCpuPowerBracketCount = POWER_BRACKETS_UNSPECIFIED; + return; + } - for (int j = 0; j < data.length; j++) { - final int bracket = (int) Math.round(data[j]); - cpuScalingPolicyPower.powerBrackets[j] = bracket; - if (bracket > mCpuPowerBracketCount) { - mCpuPowerBracketCount = bracket; - } - } - } - mCpuPowerBracketCount++; - } else { - double minPower = Double.MAX_VALUE; - double maxPower = Double.MIN_VALUE; - int stateCount = 0; - for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) { - int policy = mCpuScalingPolicies.keyAt(i); - CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); - final int steps = cpuScalingPolicyPower.stepPower.length; - for (int step = 0; step < steps; step++) { - final double power = getAveragePowerForCpuScalingStep(policy, step); - if (power < minPower) { - minPower = power; - } - if (power > maxPower) { - maxPower = power; - } - } - stateCount += steps; + mCpuPowerBracketCount = 0; + for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) { + int policy = mCpuScalingPolicies.keyAt(i); + CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); + final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy); + if (data.length != cpuScalingPolicyPower.powerBrackets.length) { + throw new RuntimeException( + "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy + + ", expected: " + + cpuScalingPolicyPower.powerBrackets.length); } - if (stateCount <= defaultCpuPowerBracketNumber) { - mCpuPowerBracketCount = stateCount; - int bracket = 0; - for (int i = 0; i < mCpuScalingPolicies.size(); i++) { - CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); - final int steps = cpuScalingPolicyPower.stepPower.length; - for (int step = 0; step < steps; step++) { - cpuScalingPolicyPower.powerBrackets[step] = bracket++; - } - } - } else { - mCpuPowerBracketCount = defaultCpuPowerBracketNumber; - final double minLogPower = Math.log(minPower); - final double logBracket = (Math.log(maxPower) - minLogPower) - / defaultCpuPowerBracketNumber; - - for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) { - int policy = mCpuScalingPolicies.keyAt(i); - CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); - final int steps = cpuScalingPolicyPower.stepPower.length; - for (int step = 0; step < steps; step++) { - final double power = getAveragePowerForCpuScalingStep(policy, step); - int bracket = (int) ((Math.log(power) - minLogPower) / logBracket); - if (bracket >= defaultCpuPowerBracketNumber) { - bracket = defaultCpuPowerBracketNumber - 1; - } - cpuScalingPolicyPower.powerBrackets[step] = bracket; - } + for (int j = 0; j < data.length; j++) { + final int bracket = (int) Math.round(data[j]); + cpuScalingPolicyPower.powerBrackets[j] = bracket; + if (bracket > mCpuPowerBracketCount) { + mCpuPowerBracketCount = bracket; } } } + mCpuPowerBracketCount++; } private static class CpuScalingPolicyPower { @@ -771,43 +722,12 @@ public class PowerProfile { /** * Returns the number of CPU power brackets: groups of states with similar power requirements. + * If power brackets are not specified, returns {@link #POWER_BRACKETS_UNSPECIFIED} */ public int getCpuPowerBracketCount() { return mCpuPowerBracketCount; } - /** - * Description of a CPU power bracket: which cluster/frequency combinations are included. - */ - public String getCpuPowerBracketDescription(CpuScalingPolicies cpuScalingPolicies, - int powerBracket) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < mCpuScalingPolicies.size(); i++) { - int policy = mCpuScalingPolicies.keyAt(i); - CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i); - int[] brackets = cpuScalingPolicyPower.powerBrackets; - int[] freqs = cpuScalingPolicies.getFrequencies(policy); - for (int step = 0; step < brackets.length; step++) { - if (brackets[step] == powerBracket) { - if (sb.length() != 0) { - sb.append(", "); - } - if (mCpuScalingPolicies.size() > 1) { - sb.append(policy).append('/'); - } - if (step < freqs.length) { - sb.append(freqs[step] / 1000); - } - sb.append('('); - sb.append(String.format(Locale.US, "%.1f", - getAveragePowerForCpuScalingStep(policy, step))); - sb.append(')'); - } - } - } - return sb.toString(); - } - /** * Returns the CPU power bracket corresponding to the specified scaling policy and frequency * step diff --git a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml b/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml deleted file mode 100644 index a46c6083009c..000000000000 --- a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - 10 - - - - 25 - 35 - - - - 1 - - - - 1 - 0 - - diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java index 8fa63760d231..77202d1faa31 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -21,16 +21,12 @@ import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT; import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL; import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON; -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.annotation.XmlRes; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; -import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -540,66 +536,4 @@ public class PowerProfileTest { private void assertEquals(double expected, double actual) { Assert.assertEquals(expected, actual, 0.1); } - - @Test - public void powerBrackets_specifiedInPowerProfile() { - mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets); - mProfile.initCpuPowerBrackets(8); - - int cpuPowerBracketCount = mProfile.getCpuPowerBracketCount(); - assertThat(cpuPowerBracketCount).isEqualTo(2); - assertThat(new int[]{ - mProfile.getCpuPowerBracketForScalingStep(0, 0), - mProfile.getCpuPowerBracketForScalingStep(4, 0), - mProfile.getCpuPowerBracketForScalingStep(4, 1), - }).isEqualTo(new int[]{1, 1, 0}); - } - - @Test - public void powerBrackets_automatic() { - mProfile.forceInitForTesting(mContext, R.xml.power_profile_test); - CpuScalingPolicies scalingPolicies = new CpuScalingPolicies( - new SparseArray<>() {{ - put(0, new int[]{0, 1, 2}); - put(3, new int[]{3, 4}); - }}, - new SparseArray<>() {{ - put(0, new int[]{300000, 1000000, 2000000}); - put(3, new int[]{300000, 1000000, 2500000, 3000000}); - }}); - - assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(3); - assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 0)) - .isEqualTo("0/300(10.0)"); - assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 1)) - .isEqualTo("0/1000(20.0), 0/2000(30.0), 3/300(25.0)"); - assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 2)) - .isEqualTo("3/1000(35.0), 3/2500(50.0), 3/3000(60.0)"); - assertThat(new int[]{ - mProfile.getCpuPowerBracketForScalingStep(0, 0), - mProfile.getCpuPowerBracketForScalingStep(0, 1), - mProfile.getCpuPowerBracketForScalingStep(0, 2), - mProfile.getCpuPowerBracketForScalingStep(3, 0), - mProfile.getCpuPowerBracketForScalingStep(3, 1), - mProfile.getCpuPowerBracketForScalingStep(3, 2), - mProfile.getCpuPowerBracketForScalingStep(3, 3), - }).isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2}); - } - - @Test - public void powerBrackets_moreBracketsThanStates() { - mProfile.forceInitForTesting(mContext, R.xml.power_profile_test); - mProfile.initCpuPowerBrackets(8); - - assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(7); - assertThat(new int[]{ - mProfile.getCpuPowerBracketForScalingStep(0, 0), - mProfile.getCpuPowerBracketForScalingStep(0, 1), - mProfile.getCpuPowerBracketForScalingStep(0, 2), - mProfile.getCpuPowerBracketForScalingStep(3, 0), - mProfile.getCpuPowerBracketForScalingStep(3, 1), - mProfile.getCpuPowerBracketForScalingStep(3, 2), - mProfile.getCpuPowerBracketForScalingStep(3, 3), - }).isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6}); - } } diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index a9c2bc28b7a0..a6558e07b2aa 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -10937,7 +10937,8 @@ public class BatteryStatsImpl extends BatteryStats { } mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile, - mHandler, mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu()); + () -> mBatteryVoltageMv, mHandler, + mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu()); mCpuPowerStatsCollector.addConsumer(this::recordPowerStats); mStartCount++; @@ -14437,6 +14438,7 @@ public class BatteryStatsImpl extends BatteryStats { final int level, /* not final */ int temp, final int voltageMv, final int chargeUah, final int chargeFullUah, final long chargeTimeToFullSeconds, final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) { + // Temperature is encoded without the signed bit, so clamp any negative temperatures to 0. temp = Math.max(0, temp); @@ -15621,18 +15623,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - @GuardedBy("this") - private void dumpCpuPowerBracketsLocked(PrintWriter pw) { - pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):"); - final int bracketCount = mPowerProfile.getCpuPowerBracketCount(); - for (int bracket = 0; bracket < bracketCount; bracket++) { - pw.print(" "); - pw.print(bracket); - pw.print(": "); - pw.println(mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, bracket)); - } - } - /** * Dump EnergyConsumer stats */ @@ -16989,8 +16979,10 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(); dumpConstantsLocked(pw); - pw.println(); - dumpCpuPowerBracketsLocked(pw); + if (mCpuPowerStatsCollector != null) { + pw.println(); + mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw); + } pw.println(); dumpEnergyConsumerStatsLocked(pw); diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java index 9887a77b06e4..8dc232fbfc81 100644 --- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java @@ -36,10 +36,6 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private final int mCpuClusterCount; // Total number of CPU scaling steps across all clusters private final int mCpuScalingStepCount; - // Number of CPU power brackets used for compression of time-in-state data - private final int mCpuPowerBracketCount; - // Map of scaling step to the corresponding power brackets mScalingStepToBracket[step]->bracket - private final int[] mScalingStepToBracket; // Map of scaling step to the corresponding core cluster mScalingStepToCluster[step]->cluster private final int[] mScalingStepToCluster; // Average power consumed by the CPU when it is powered up (per power_profile.xml) @@ -64,13 +60,10 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) { - int[] scalingStepToPowerBracketMap = CpuPowerStatsCollector.getScalingStepToPowerBracketMap( - powerProfile, scalingPolicies); mCpuScalingStepCount = scalingPolicies.getScalingStepCount(); - mCpuPowerBracketCount = powerProfile.getCpuPowerBracketCount(); - mScalingStepToCluster = new int[scalingStepToPowerBracketMap.length]; - mScalingStepToBracket = new int[scalingStepToPowerBracketMap.length]; - mPowerMultipliersByScalingStep = new double[scalingStepToPowerBracketMap.length]; + mScalingStepToCluster = new int[mCpuScalingStepCount]; + mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount]; + int step = 0; int[] policies = scalingPolicies.getPolicies(); mCpuClusterCount = policies.length; @@ -84,8 +77,6 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces mScalingStepToCluster[step] = cluster; mPowerMultipliersByScalingStep[step] = powerProfile.getAveragePowerForCpuScalingStep(policy, i) / HOUR_IN_MILLIS; - mScalingStepToBracket[step] = - powerProfile.getCpuPowerBracketForScalingStep(policy, i); step++; } } @@ -248,14 +239,16 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces private void estimatePowerByDeviceState(PowerComponentAggregatedPowerStats stats, Intermediates intermediates) { int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount(); + int powerBracketCount = mStatsLayout.getCpuPowerBracketCount(); + int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap(); List deviceStateEstimations = mPlan.deviceStateEstimations; for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) { DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse); deviceStateEstimation.intermediates = new DeviceStatsIntermediates(); DeviceStatsIntermediates deviceStatsIntermediates = (DeviceStatsIntermediates) deviceStateEstimation.intermediates; - deviceStatsIntermediates.timeByBracket = new long[mCpuPowerBracketCount]; - deviceStatsIntermediates.powerByBracket = new double[mCpuPowerBracketCount]; + deviceStatsIntermediates.timeByBracket = new long[powerBracketCount]; + deviceStatsIntermediates.powerByBracket = new double[powerBracketCount]; stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues); double power = 0; @@ -265,12 +258,13 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces } long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step); - deviceStatsIntermediates.timeByBracket[mScalingStepToBracket[step]] += timeInStep; - double stepPower = intermediates.powerByScalingStep[step] * timeInStep / intermediates.timeByScalingStep[step]; power += stepPower; - deviceStatsIntermediates.powerByBracket[mScalingStepToBracket[step]] += stepPower; + + int bracket = scalingStepToBracketMap[step]; + deviceStatsIntermediates.timeByBracket[bracket] += timeInStep; + deviceStatsIntermediates.powerByBracket[bracket] += stepPower; } deviceStatsIntermediates.power = power; mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power); @@ -283,8 +277,9 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i); DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates(); - cdseIntermediates.timeByBracket = new long[mCpuPowerBracketCount]; - cdseIntermediates.powerByBracket = new double[mCpuPowerBracketCount]; + int bracketCount = mStatsLayout.getCpuPowerBracketCount(); + cdseIntermediates.timeByBracket = new long[bracketCount]; + cdseIntermediates.powerByBracket = new double[bracketCount]; cdse.intermediates = cdseIntermediates; List deviceStateEstimations = cdse.deviceStateEstimations; for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) { @@ -292,7 +287,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces DeviceStatsIntermediates intermediates = (DeviceStatsIntermediates) dse.intermediates; cdseIntermediates.power += intermediates.power; - for (int k = 0; k < intermediates.powerByBracket.length; k++) { + for (int k = 0; k < bracketCount; k++) { cdseIntermediates.timeByBracket[k] += intermediates.timeByBracket[k]; cdseIntermediates.powerByBracket[k] += intermediates.powerByBracket[k]; } @@ -319,7 +314,8 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces continue; } - long timeInBracket = mStatsLayout.getTimeByPowerBracket(mTmpUidStatsArray, bracket); + long timeInBracket = mStatsLayout.getUidTimeByPowerBracket(mTmpUidStatsArray, + bracket); if (timeInBracket == 0) { continue; } @@ -354,6 +350,17 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces sb.append(mStatsLayout.getTimeByCluster(stats, cluster)); } sb.append("] uptime: ").append(mStatsLayout.getUptime(stats)); + int energyConsumerCount = mStatsLayout.getEnergyConsumerCount(); + if (energyConsumerCount > 0) { + sb.append(" energy: ["); + for (int i = 0; i < energyConsumerCount; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(mStatsLayout.getConsumedEnergy(stats, i)); + } + sb.append("]"); + } sb.append(" power: ").append( BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats))); return sb.toString(); @@ -369,7 +376,7 @@ public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProces if (bracket != 0) { sb.append(", "); } - sb.append(mStatsLayout.getTimeByPowerBracket(stats, bracket)); + sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket)); } sb.append("] power: ").append( BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats))); diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index 47efc6f2a28c..7bb7ee10670c 100644 --- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java @@ -16,20 +16,39 @@ package com.android.server.power.stats; -import android.annotation.NonNull; +import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerResult; +import android.hardware.power.stats.EnergyConsumerType; import android.os.BatteryConsumer; import android.os.Handler; import android.os.PersistableBundle; +import android.power.PowerStatsInternal; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.Keep; import com.android.internal.annotations.VisibleForNative; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.Clock; import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; +import com.android.server.LocalServices; import com.android.server.power.optimization.Flags; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + /** * Collects snapshots of power-related system statistics. *

@@ -37,21 +56,36 @@ import com.android.server.power.optimization.Flags; * constructor. Thus the object is not thread-safe except where noted. */ public class CpuPowerStatsCollector extends PowerStatsCollector { + private static final String TAG = "CpuPowerStatsCollector"; private static final long NANOS_PER_MILLIS = 1000000; + private static final long ENERGY_UNSPECIFIED = -1; + private static final int DEFAULT_CPU_POWER_BRACKETS = 3; + private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2; + private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000; private boolean mIsInitialized; + private final CpuScalingPolicies mCpuScalingPolicies; + private final PowerProfile mPowerProfile; private final KernelCpuStatsReader mKernelCpuStatsReader; - private final long[] mCpuTimeByScalingStep; - private final long[] mTempCpuTimeByScalingStep; - private final int[] mScalingStepToPowerBracketMap; - private final long[] mTempUidStats; + private final Supplier mPowerStatsSupplier; + private final IntSupplier mVoltageSupplier; + private final int mDefaultCpuPowerBrackets; + private final int mDefaultCpuPowerBracketsPerEnergyConsumer; + private long[] mCpuTimeByScalingStep; + private long[] mTempCpuTimeByScalingStep; + private long[] mTempUidStats; private final SparseArray mUidStats = new SparseArray<>(); - private boolean mIsSupportedFeature; + private boolean mIsPerUidTimeInStateSupported; + private PowerStatsInternal mPowerStatsInternal; + private int[] mCpuEnergyConsumerIds; + private PowerStats.Descriptor mPowerStatsDescriptor; // Reusable instance - private final PowerStats mCpuPowerStats; - private final StatsArrayLayout mLayout; + private PowerStats mCpuPowerStats; + private StatsArrayLayout mLayout; private long mLastUpdateTimestampNanos; private long mLastUpdateUptimeMillis; + private int mLastVoltageMv; + private long[] mLastConsumedEnergyUws; /** * Captures the positions and lengths of sections of the stats array, such as time-in-state, @@ -64,8 +98,10 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc"; private static final String EXTRA_DEVICE_POWER_POSITION = "dp"; private static final String EXTRA_DEVICE_UPTIME_POSITION = "du"; + private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de"; + private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec"; private static final String EXTRA_UID_BRACKETS_POSITION = "ub"; - private static final String EXTRA_UID_BRACKET_COUNT = "ubc"; + private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us"; private static final String EXTRA_UID_POWER_POSITION = "up"; private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0; @@ -78,12 +114,16 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { private int mDeviceCpuTimeByClusterPosition; private int mDeviceCpuTimeByClusterCount; private int mDeviceCpuUptimePosition; + private int mDeviceEnergyConsumerPosition; + private int mDeviceEnergyConsumerCount; private int mDevicePowerEstimatePosition; private int mUidPowerBracketsPosition; private int mUidPowerBracketCount; private int mUidPowerEstimatePosition; + private int[] mScalingStepToPowerBracketMap; + public int getDeviceStatsArrayLength() { return mDeviceStatsArrayLength; } @@ -171,6 +211,36 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { return stats[mDeviceCpuUptimePosition]; } + /** + * Declares that the stats array has a section capturing EnergyConsumer data from + * PowerStatsService. + */ + public void addDeviceSectionEnergyConsumers(int energyConsumerCount) { + mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength; + mDeviceEnergyConsumerCount = energyConsumerCount; + mDeviceStatsArrayLength += energyConsumerCount; + } + + public int getEnergyConsumerCount() { + return mDeviceEnergyConsumerCount; + } + + /** + * Saves the accumulated energy for the specified rail the corresponding + * stats element. + */ + public void setConsumedEnergy(long[] stats, int index, long energy) { + stats[mDeviceEnergyConsumerPosition + index] = energy; + } + + /** + * Extracts the EnergyConsumer data from a device stats array for the specified + * EnergyConsumer. + */ + public long getConsumedEnergy(long[] stats, int index) { + return stats[mDeviceEnergyConsumerPosition + index]; + } + /** * Declare that the stats array has a section capturing a power estimate */ @@ -196,10 +266,24 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { /** * Declare that the UID stats array has a section capturing CPU time per power bracket. */ - public void addUidSectionCpuTimeByPowerBracket(int cpuPowerBracketCount) { + public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) { + mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap; mUidPowerBracketsPosition = mUidStatsArrayLength; - mUidPowerBracketCount = cpuPowerBracketCount; - mUidStatsArrayLength += cpuPowerBracketCount; + updatePowerBracketCount(); + mUidStatsArrayLength += mUidPowerBracketCount; + } + + private void updatePowerBracketCount() { + mUidPowerBracketCount = 1; + for (int bracket : mScalingStepToPowerBracketMap) { + if (bracket >= mUidPowerBracketCount) { + mUidPowerBracketCount = bracket + 1; + } + } + } + + public int[] getScalingStepToPowerBracketMap() { + return mScalingStepToPowerBracketMap; } public int getCpuPowerBracketCount() { @@ -209,14 +293,14 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { /** * Saves time in bracket in the corresponding section of stats. */ - public void setTimeByPowerBracket(long[] stats, int bracket, long value) { + public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) { stats[mUidPowerBracketsPosition + bracket] = value; } /** * Extracts the time in bracket from a UID stats array. */ - public long getTimeByPowerBracket(long[] stats, int bracket) { + public long getUidTimeByPowerBracket(long[] stats, int bracket) { return stats[mUidPowerBracketsPosition + bracket]; } @@ -255,9 +339,14 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT, mDeviceCpuTimeByClusterCount); extras.putInt(EXTRA_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, + mDeviceEnergyConsumerPosition); + extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, + mDeviceEnergyConsumerCount); extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition); extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition); - extras.putInt(EXTRA_UID_BRACKET_COUNT, mUidPowerBracketCount); + extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET, + mScalingStepToPowerBracketMap); extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition); } @@ -274,94 +363,303 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { mDeviceCpuTimeByClusterCount = extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT); mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION); + mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION); + mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT); mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION); mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION); - mUidPowerBracketCount = extras.getInt(EXTRA_UID_BRACKET_COUNT); + mScalingStepToPowerBracketMap = + extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET); + if (mScalingStepToPowerBracketMap == null) { + mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount]; + } + updatePowerBracketCount(); mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION); } } public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, - Handler handler, long throttlePeriodMs) { + IntSupplier voltageSupplier, Handler handler, long throttlePeriodMs) { this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), - throttlePeriodMs, Clock.SYSTEM_CLOCK); + () -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier, + throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS, + DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER); } public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile, Handler handler, KernelCpuStatsReader kernelCpuStatsReader, - long throttlePeriodMs, Clock clock) { + Supplier powerStatsSupplier, + IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock, + int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) { super(handler, throttlePeriodMs, clock); + mCpuScalingPolicies = cpuScalingPolicies; + mPowerProfile = powerProfile; mKernelCpuStatsReader = kernelCpuStatsReader; + mPowerStatsSupplier = powerStatsSupplier; + mVoltageSupplier = voltageSupplier; + mDefaultCpuPowerBrackets = defaultCpuPowerBrackets; + mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer; + + } + + /** + * Initializes the collector during the boot sequence. + */ + public void onSystemReady() { + setEnabled(Flags.streamlinedBatteryStats()); + } + + private void ensureInitialized() { + if (mIsInitialized) { + return; + } - int cpuScalingStepCount = cpuScalingPolicies.getScalingStepCount(); + if (!isEnabled()) { + return; + } + + mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature(); + mPowerStatsInternal = mPowerStatsSupplier.get(); + + if (mPowerStatsInternal != null) { + readCpuEnergyConsumerIds(); + } else { + mCpuEnergyConsumerIds = new int[0]; + } + + int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount(); mCpuTimeByScalingStep = new long[cpuScalingStepCount]; mTempCpuTimeByScalingStep = new long[cpuScalingStepCount]; - mScalingStepToPowerBracketMap = - getScalingStepToPowerBracketMap(powerProfile, cpuScalingPolicies); - - int cpuPowerBracketCount = powerProfile.getCpuPowerBracketCount(); - mTempUidStats = new long[cpuPowerBracketCount]; + int[] scalingStepToPowerBracketMap = initPowerBrackets(); mLayout = new StatsArrayLayout(); mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount); - mLayout.addDeviceSectionCpuTimeByCluster(cpuScalingPolicies.getPolicies().length); + mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length); mLayout.addDeviceSectionUptime(); + mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length); mLayout.addDeviceSectionPowerEstimate(); - mLayout.addUidSectionCpuTimeByPowerBracket(cpuPowerBracketCount); + mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap); mLayout.addUidSectionPowerEstimate(); PersistableBundle extras = new PersistableBundle(); mLayout.toExtras(extras); - mCpuPowerStats = new PowerStats( - new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, - mLayout.getDeviceStatsArrayLength(), - mLayout.getUidStatsArrayLength(), extras)); + mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, + mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras); + mCpuPowerStats = new PowerStats(mPowerStatsDescriptor); + + mTempUidStats = new long[mLayout.getCpuPowerBracketCount()]; + + mIsInitialized = true; } - @NonNull - static int[] getScalingStepToPowerBracketMap(PowerProfile powerProfile, - CpuScalingPolicies cpuScalingPolicies) { - final int[] map = new int[cpuScalingPolicies.getScalingStepCount()]; + private void readCpuEnergyConsumerIds() { + EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo(); + if (energyConsumerInfo == null) { + mCpuEnergyConsumerIds = new int[0]; + return; + } + + List cpuEnergyConsumers = new ArrayList<>(); + for (EnergyConsumer energyConsumer : energyConsumerInfo) { + if (energyConsumer.type == EnergyConsumerType.CPU_CLUSTER) { + cpuEnergyConsumers.add(energyConsumer); + } + } + if (cpuEnergyConsumers.isEmpty()) { + return; + } + + cpuEnergyConsumers.sort(Comparator.comparing(c -> c.ordinal)); + + mCpuEnergyConsumerIds = new int[cpuEnergyConsumers.size()]; + for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) { + mCpuEnergyConsumerIds[i] = cpuEnergyConsumers.get(i).id; + } + mLastConsumedEnergyUws = new long[cpuEnergyConsumers.size()]; + Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED); + } + + private int[] initPowerBrackets() { + if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) { + return initPowerBracketsFromPowerProfile(); + } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) { + return initDefaultPowerBrackets(mDefaultCpuPowerBrackets); + } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) { + return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer); + } else { + Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer." + + " Number of CPU clusters (" + + mCpuScalingPolicies.getPolicies().length + + ") does not match the number of energy consumers (" + + mCpuEnergyConsumerIds.length + "). " + + " Please specify power bracket assignment explicitly in" + + " power_profile.xml"); + return initPowerBracketsByCluster(1); + } + } + + private int[] initPowerBracketsFromPowerProfile() { + int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; + int index = 0; + for (int policy : mCpuScalingPolicies.getPolicies()) { + int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); + for (int step = 0; step < frequencies.length; step++) { + int bracket = mPowerProfile.getCpuPowerBracketForScalingStep(policy, step); + stepToBracketMap[index++] = bracket; + } + } + return stepToBracketMap; + } + + private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) { + int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; + int index = 0; + int bracketBase = 0; + int[] policies = mCpuScalingPolicies.getPolicies(); + for (int policy : policies) { + int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); + double[] powerByStep = new double[frequencies.length]; + for (int step = 0; step < frequencies.length; step++) { + powerByStep[step] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step); + } + + int[] policyStepToBracketMap = new int[frequencies.length]; + mapScalingStepsToDefaultBrackets(policyStepToBracketMap, powerByStep, + defaultBracketCountPerCluster); + int maxBracket = 0; + for (int step = 0; step < frequencies.length; step++) { + int bracket = bracketBase + policyStepToBracketMap[step]; + stepToBracketMap[index++] = bracket; + if (bracket > maxBracket) { + maxBracket = bracket; + } + } + bracketBase = maxBracket + 1; + } + return stepToBracketMap; + } + + private int[] initDefaultPowerBrackets(int defaultCpuPowerBracketCount) { + int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()]; + double[] powerByStep = new double[mCpuScalingPolicies.getScalingStepCount()]; int index = 0; - for (int policy : cpuScalingPolicies.getPolicies()) { - int[] frequencies = cpuScalingPolicies.getFrequencies(policy); + int[] policies = mCpuScalingPolicies.getPolicies(); + for (int policy : policies) { + int[] frequencies = mCpuScalingPolicies.getFrequencies(policy); for (int step = 0; step < frequencies.length; step++) { - int bracket = powerProfile.getCpuPowerBracketForScalingStep(policy, step); - map[index++] = bracket; + powerByStep[index++] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step); + } + } + mapScalingStepsToDefaultBrackets(stepToBracketMap, powerByStep, + defaultCpuPowerBracketCount); + return stepToBracketMap; + } + + private static void mapScalingStepsToDefaultBrackets(int[] stepToBracketMap, + double[] powerByStep, int defaultCpuPowerBracketCount) { + double minPower = Double.MAX_VALUE; + double maxPower = Double.MIN_VALUE; + for (final double power : powerByStep) { + if (power < minPower) { + minPower = power; + } + if (power > maxPower) { + maxPower = power; + } + } + if (powerByStep.length <= defaultCpuPowerBracketCount) { + for (int index = 0; index < stepToBracketMap.length; index++) { + stepToBracketMap[index] = index; + } + } else { + final double minLogPower = Math.log(minPower); + final double logBracket = (Math.log(maxPower) - minLogPower) + / defaultCpuPowerBracketCount; + + for (int step = 0; step < powerByStep.length; step++) { + int bracket = (int) ((Math.log(powerByStep[step]) - minLogPower) / logBracket); + if (bracket >= defaultCpuPowerBracketCount) { + bracket = defaultCpuPowerBracketCount - 1; + } + stepToBracketMap[step] = bracket; } } - return map; } /** - * Initializes the collector during the boot sequence. + * Prints the definitions of power brackets. */ - public void onSystemReady() { - setEnabled(Flags.streamlinedBatteryStats()); + public void dumpCpuPowerBracketsLocked(PrintWriter pw) { + ensureInitialized(); + + pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):"); + for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) { + pw.print(" "); + pw.print(bracket); + pw.print(": "); + pw.println(getCpuPowerBracketDescription(bracket)); + } } - private void ensureInitialized() { - if (mIsInitialized) { - return; + /** + * Description of a CPU power bracket: which cluster/frequency combinations are included. + */ + @VisibleForTesting + public String getCpuPowerBracketDescription(int powerBracket) { + ensureInitialized(); + + int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap(); + StringBuilder sb = new StringBuilder(); + int index = 0; + int[] policies = mCpuScalingPolicies.getPolicies(); + for (int policy : policies) { + int[] freqs = mCpuScalingPolicies.getFrequencies(policy); + for (int step = 0; step < freqs.length; step++) { + if (stepToPowerBracketMap[index] != powerBracket) { + index++; + continue; + } + + if (sb.length() != 0) { + sb.append(", "); + } + if (policies.length > 1) { + sb.append(policy).append('/'); + } + sb.append(freqs[step] / 1000); + sb.append('('); + sb.append(String.format(Locale.US, "%.1f", + mPowerProfile.getAveragePowerForCpuScalingStep(policy, step))); + sb.append(')'); + + index++; + } } + return sb.toString(); + } - mIsSupportedFeature = mKernelCpuStatsReader.nativeIsSupportedFeature(); - mIsInitialized = true; + /** + * Returns the descriptor of PowerStats produced by this collector. + */ + @VisibleForTesting + public PowerStats.Descriptor getPowerStatsDescriptor() { + ensureInitialized(); + + return mPowerStatsDescriptor; } @Override protected PowerStats collectStats() { ensureInitialized(); - if (!mIsSupportedFeature) { + if (!mIsPerUidTimeInStateSupported) { return null; } mCpuPowerStats.uidStats.clear(); // TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster - long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats( - this::processUidStats, mScalingStepToPowerBracketMap, mLastUpdateTimestampNanos, + long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats, + mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos, mTempCpuTimeByScalingStep, mTempUidStats); for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) { mLayout.setTimeByScalingStep(mCpuPowerStats.stats, step, @@ -382,9 +680,56 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { } mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta); + if (mCpuEnergyConsumerIds.length != 0) { + collectEnergyConsumers(); + } + return mCpuPowerStats; } + private void collectEnergyConsumers() { + int voltageMv = mVoltageSupplier.getAsInt(); + if (voltageMv <= 0) { + Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv + + " mV) when querying energy consumers"); + return; + } + + int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv; + mLastVoltageMv = voltageMv; + + CompletableFuture future = + mPowerStatsInternal.getEnergyConsumedAsync(mCpuEnergyConsumerIds); + EnergyConsumerResult[] results = null; + try { + results = future.get( + POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e); + } + if (results == null) { + return; + } + + for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) { + int id = mCpuEnergyConsumerIds[i]; + for (EnergyConsumerResult result : results) { + if (result.id == id) { + long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED + ? result.energyUWs - mLastConsumedEnergyUws[i] : 0; + if (energyDelta < 0) { + // Likely, restart of powerstats HAL + energyDelta = 0; + } + mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, + uJtoUc(energyDelta, averageVoltage)); + mLastConsumedEnergyUws[i] = result.energyUWs; + break; + } + } + } + } + @VisibleForNative interface KernelCpuStatsCallback { @Keep // Called from native @@ -408,7 +753,7 @@ public class CpuPowerStatsCollector extends PowerStatsCollector { if (delta != 0) { nonzero = true; } - mLayout.setTimeByPowerBracket(uidStats.stats, bracket, delta); + mLayout.setUidTimeByPowerBracket(uidStats.stats, bracket, delta); uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket]; } if (nonzero) { diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index 0f3a767ca8b6..84cc21e81536 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -38,6 +38,7 @@ import java.util.stream.Stream; * except where noted. */ public abstract class PowerStatsCollector { + private static final int MILLIVOLTS_PER_VOLT = 1000; private final Handler mHandler; protected final Clock mClock; private final long mThrottlePeriodMs; @@ -183,4 +184,11 @@ public abstract class PowerStatsCollector { mHandler.post(done::open); done.block(); } + + /** Calculate charge consumption (in microcoulombs) from a given energy and voltage */ + protected long uJtoUc(long deltaEnergyUj, int avgVoltageMv) { + // To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times + // since the last snapshot. Round off to the nearest whole long. + return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv; + } } diff --git a/services/tests/powerstatstests/res/xml/power_profile_test.xml b/services/tests/powerstatstests/res/xml/power_profile_test.xml new file mode 100644 index 000000000000..ecd88614d920 --- /dev/null +++ b/services/tests/powerstatstests/res/xml/power_profile_test.xml @@ -0,0 +1,101 @@ + + + + + + 3000 + + + 5 + + 1.11 + + 2.55 + + + 2.11 + + 2.22 + + + + 10 + 20 + 30 + + + + 25 + 35 + 50 + 60 + + + + 0.5 + + 100 + + 800 + + + 500 + + 600 + + + 100.0 + + + 150.0 + + + 10 + + + 60 + + 3 + + + 6 + 5 + 4 + 3 + 3 + + + + 123 + 456 + 789 + + 10 + 20 + 30 + 40 + 50 + + \ No newline at end of file diff --git a/services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml b/services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml new file mode 100644 index 000000000000..a46c6083009c --- /dev/null +++ b/services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml @@ -0,0 +1,37 @@ + + + + + + + 10 + + + + 25 + 35 + + + + 1 + + + + 1 + 0 + + diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java index ca68c7b614c2..a3b69ff4f684 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java @@ -162,7 +162,7 @@ public class CpuAggregatedPowerStatsProcessorTest { mStatsLayout.addDeviceSectionCpuTimeByCluster(2); mStatsLayout.addDeviceSectionUptime(); mStatsLayout.addDeviceSectionPowerEstimate(); - mStatsLayout.addUidSectionCpuTimeByPowerBracket(3); + mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2}); mStatsLayout.addUidSectionPowerEstimate(); PersistableBundle extras = new PersistableBundle(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java index 929b258656d0..f3362429a412 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java @@ -20,16 +20,26 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.content.Context; +import android.hardware.power.stats.EnergyConsumer; +import android.hardware.power.stats.EnergyConsumerResult; +import android.hardware.power.stats.EnergyConsumerType; +import android.os.BatteryConsumer; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; +import android.power.PowerStatsInternal; import android.util.SparseArray; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.frameworks.powerstatstests.R; import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; @@ -40,44 +50,155 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + @RunWith(AndroidJUnit4.class) @SmallTest public class CpuPowerStatsCollectorTest { + private Context mContext; private final MockClock mMockClock = new MockClock(); private final HandlerThread mHandlerThread = new HandlerThread("test"); private Handler mHandler; - private CpuPowerStatsCollector mCollector; private PowerStats mCollectedStats; - @Mock private PowerProfile mPowerProfile; @Mock private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader; + @Mock + private PowerStatsInternal mPowerStatsInternal; + private CpuScalingPolicies mCpuScalingPolicies; @Before public void setup() { MockitoAnnotations.initMocks(this); + mContext = InstrumentationRegistry.getContext(); mHandlerThread.start(); mHandler = mHandlerThread.getThreadHandler(); - when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2); - when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0); - when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1); - when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 2)).thenReturn(1); when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true); - mCollector = new CpuPowerStatsCollector(new CpuScalingPolicies( + } + + @Test + public void powerBrackets_specifiedInPowerProfile() { + mPowerProfile = new PowerProfile(mContext); + mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets); + mCpuScalingPolicies = new CpuScalingPolicies( new SparseArray<>() {{ - put(0, new int[]{0}); + put(0, new int[]{0}); + put(4, new int[]{4}); }}, new SparseArray<>() {{ - put(0, new int[]{1, 12, 24}); - }}), - mPowerProfile, mHandler, mMockKernelCpuStatsReader, 60_000, mMockClock); - mCollector.addConsumer(stats -> mCollectedStats = stats); - mCollector.setEnabled(true); + put(0, new int[]{100}); + put(4, new int[]{400, 500}); + }}); + + CpuPowerStatsCollector collector = createCollector(8, 0); + + assertThat(getScalingStepToPowerBracketMap(collector)) + .isEqualTo(new int[]{1, 1, 0}); + } + + @Test + public void powerBrackets_default_noEnergyConsumers() { + mPowerProfile = new PowerProfile(mContext); + mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mockCpuScalingPolicies(2); + + CpuPowerStatsCollector collector = createCollector(3, 0); + + assertThat(new String[]{ + collector.getCpuPowerBracketDescription(0), + collector.getCpuPowerBracketDescription(1), + collector.getCpuPowerBracketDescription(2)}) + .isEqualTo(new String[]{ + "0/300(10.0)", + "0/1000(20.0), 0/2000(30.0), 4/300(25.0)", + "4/1000(35.0), 4/2500(50.0), 4/3000(60.0)"}); + assertThat(getScalingStepToPowerBracketMap(collector)) + .isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2}); } @Test - public void collectStats() { + public void powerBrackets_moreBracketsThanStates() { + mPowerProfile = new PowerProfile(mContext); + mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mockCpuScalingPolicies(2); + + CpuPowerStatsCollector collector = createCollector(8, 0); + + assertThat(getScalingStepToPowerBracketMap(collector)) + .isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6}); + } + + @Test + public void powerBrackets_energyConsumers() throws Exception { + mPowerProfile = new PowerProfile(mContext); + mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mockCpuScalingPolicies(2); + mockEnergyConsumers(); + + CpuPowerStatsCollector collector = createCollector(8, 2); + + assertThat(getScalingStepToPowerBracketMap(collector)) + .isEqualTo(new int[]{0, 1, 1, 2, 2, 3, 3}); + } + + @Test + public void powerStatsDescriptor() throws Exception { + mPowerProfile = new PowerProfile(mContext); + mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test); + mockCpuScalingPolicies(2); + mockEnergyConsumers(); + + CpuPowerStatsCollector collector = createCollector(8, 2); + PowerStats.Descriptor descriptor = collector.getPowerStatsDescriptor(); + assertThat(descriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU); + assertThat(descriptor.name).isEqualTo("cpu"); + assertThat(descriptor.statsArrayLength).isEqualTo(13); + assertThat(descriptor.uidStatsArrayLength).isEqualTo(5); + CpuPowerStatsCollector.StatsArrayLayout layout = + new CpuPowerStatsCollector.StatsArrayLayout(); + layout.fromExtras(descriptor.extras); + + long[] deviceStats = new long[descriptor.statsArrayLength]; + layout.setTimeByScalingStep(deviceStats, 2, 42); + layout.setConsumedEnergy(deviceStats, 1, 43); + layout.setUptime(deviceStats, 44); + layout.setDevicePowerEstimate(deviceStats, 45); + + long[] uidStats = new long[descriptor.uidStatsArrayLength]; + layout.setUidTimeByPowerBracket(uidStats, 3, 46); + layout.setUidPowerEstimate(uidStats, 47); + + assertThat(layout.getCpuScalingStepCount()).isEqualTo(7); + assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42); + + assertThat(layout.getEnergyConsumerCount()).isEqualTo(2); + assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43); + + assertThat(layout.getUptime(deviceStats)).isEqualTo(44); + + assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45); + + assertThat(layout.getScalingStepToPowerBracketMap()).isEqualTo( + new int[]{0, 1, 1, 2, 2, 3, 3}); + assertThat(layout.getCpuPowerBracketCount()).isEqualTo(4); + + assertThat(layout.getUidTimeByPowerBracket(uidStats, 3)).isEqualTo(46); + assertThat(layout.getUidPowerEstimate(uidStats)).isEqualTo(47); + } + + @Test + public void collectStats() throws Exception { + mockCpuScalingPolicies(1); + mockPowerProfile(); + mockEnergyConsumers(); + + CpuPowerStatsCollector collector = createCollector(8, 0); + CpuPowerStatsCollector.StatsArrayLayout layout = + new CpuPowerStatsCollector.StatsArrayLayout(); + layout.fromExtras(collector.getPowerStatsDescriptor().extras); + mockKernelCpuStats(new long[]{1111, 2222, 3333}, new SparseArray<>() {{ put(42, new long[]{100, 200}); @@ -85,13 +206,27 @@ public class CpuPowerStatsCollectorTest { }}, 0, 1234); mMockClock.uptime = 1000; - mCollector.forceSchedule(); + collector.forceSchedule(); waitForIdle(); assertThat(mCollectedStats.durationMs).isEqualTo(1234); - assertThat(mCollectedStats.stats).isEqualTo(new long[]{1111, 2222, 3333, 1000, 0}); - assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{100, 200, 0}); - assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{300, 600, 0}); + + assertThat(layout.getCpuScalingStepCount()).isEqualTo(3); + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(1111); + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222); + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(3333); + + assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(0); + assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(0); + + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0)) + .isEqualTo(100); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1)) + .isEqualTo(200); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0)) + .isEqualTo(300); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1)) + .isEqualTo(600); mockKernelCpuStats(new long[]{5555, 4444, 3333}, new SparseArray<>() {{ @@ -100,13 +235,59 @@ public class CpuPowerStatsCollectorTest { }}, 1234, 3421); mMockClock.uptime = 2000; - mCollector.forceSchedule(); + collector.forceSchedule(); waitForIdle(); assertThat(mCollectedStats.durationMs).isEqualTo(3421 - 1234); - assertThat(mCollectedStats.stats).isEqualTo(new long[]{4444, 2222, 0, 1000, 0}); - assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{23, 34, 0}); - assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{45, 78, 0}); + + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(4444); + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222); + assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(0); + + // 500 * 1000 / 3500 + assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(143); + // 700 * 1000 / 3500 + assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(200); + + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0)) + .isEqualTo(23); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1)) + .isEqualTo(34); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0)) + .isEqualTo(45); + assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1)) + .isEqualTo(78); + } + + private void mockCpuScalingPolicies(int clusterCount) { + SparseArray cpus = new SparseArray<>(); + SparseArray freqs = new SparseArray<>(); + cpus.put(0, new int[]{0, 1, 2, 3}); + freqs.put(0, new int[]{300000, 1000000, 2000000}); + if (clusterCount == 2) { + cpus.put(4, new int[]{4, 5}); + freqs.put(4, new int[]{300000, 1000000, 2500000, 3000000}); + } + mCpuScalingPolicies = new CpuScalingPolicies(cpus, freqs); + } + + private void mockPowerProfile() { + mPowerProfile = mock(PowerProfile.class); + when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2); + when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0); + when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1); + when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 2)).thenReturn(1); + } + + private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets, + int defaultCpuPowerBracketsPerEnergyConsumer) { + CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies, + mPowerProfile, mHandler, mMockKernelCpuStatsReader, () -> mPowerStatsInternal, + () -> 3500, 60_000, mMockClock, defaultCpuPowerBrackets, + defaultCpuPowerBracketsPerEnergyConsumer); + collector.addConsumer(stats -> mCollectedStats = stats); + collector.setEnabled(true); + return collector; } private void mockKernelCpuStats(long[] deviceStats, SparseArray uidToCpuStats, @@ -139,6 +320,67 @@ public class CpuPowerStatsCollectorTest { }); } + @SuppressWarnings("unchecked") + private void mockEnergyConsumers() throws Exception { + when(mPowerStatsInternal.getEnergyConsumerInfo()) + .thenReturn(new EnergyConsumer[]{ + new EnergyConsumer() {{ + id = 1; + type = EnergyConsumerType.CPU_CLUSTER; + ordinal = 0; + name = "CPU0"; + }}, + new EnergyConsumer() {{ + id = 2; + type = EnergyConsumerType.CPU_CLUSTER; + ordinal = 1; + name = "CPU4"; + }}, + new EnergyConsumer() {{ + id = 3; + type = EnergyConsumerType.BLUETOOTH; + name = "BT"; + }}, + }); + + CompletableFuture future1 = mock(CompletableFuture.class); + when(future1.get(anyLong(), any(TimeUnit.class))) + .thenReturn(new EnergyConsumerResult[]{ + new EnergyConsumerResult() {{ + id = 1; + energyUWs = 1000; + }}, + new EnergyConsumerResult() {{ + id = 2; + energyUWs = 2000; + }} + }); + + CompletableFuture future2 = mock(CompletableFuture.class); + when(future2.get(anyLong(), any(TimeUnit.class))) + .thenReturn(new EnergyConsumerResult[]{ + new EnergyConsumerResult() {{ + id = 1; + energyUWs = 1500; + }}, + new EnergyConsumerResult() {{ + id = 2; + energyUWs = 2700; + }} + }); + + when(mPowerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2}))) + .thenReturn(future1) + .thenReturn(future2); + } + + private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) { + CpuPowerStatsCollector.StatsArrayLayout layout = + new CpuPowerStatsCollector.StatsArrayLayout(); + layout.fromExtras(collector.getPowerStatsDescriptor().extras); + return layout.getScalingStepToPowerBracketMap(); + } + private void waitForIdle() { ConditionVariable done = new ConditionVariable(); mHandler.post(done::open); -- cgit v1.2.3-59-g8ed1b