Merge "Fix system service power calculation when measured energy is available" into sc-dev

This commit is contained in:
Dmitri Plotnikov
2021-05-26 20:48:55 +00:00
committed by Android (Google) Code Review
3 changed files with 182 additions and 104 deletions

View File

@@ -201,7 +201,10 @@ public class CpuPowerCalculator extends PowerCalculator {
result.packageWithHighestDrain = packageWithHighestDrain; result.packageWithHighestDrain = packageWithHighestDrain;
} }
private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { /**
* Calculates CPU power consumed by the specified app, using the PowerProfile model.
*/
public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
// Constant battery drain when CPU is active // Constant battery drain when CPU is active
double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());

View File

@@ -40,8 +40,10 @@ public class SystemServicePowerCalculator extends PowerCalculator {
// to this layout: // to this layout:
// {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
private final UsageBasedPowerEstimator[] mPowerEstimators; private final UsageBasedPowerEstimator[] mPowerEstimators;
private final CpuPowerCalculator mCpuPowerCalculator;
public SystemServicePowerCalculator(PowerProfile powerProfile) { public SystemServicePowerCalculator(PowerProfile powerProfile) {
mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
int numFreqs = 0; int numFreqs = 0;
final int numCpuClusters = powerProfile.getNumCpuClusters(); final int numCpuClusters = powerProfile.getNumCpuClusters();
for (int cluster = 0; cluster < numCpuClusters; cluster++) { for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -62,7 +64,22 @@ public class SystemServicePowerCalculator extends PowerCalculator {
@Override @Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
double systemServicePowerMah = calculateSystemServicePower(batteryStats); final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
if (systemUid == null) {
return;
}
final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC, query);
double systemServicePowerMah;
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
systemUid, consumptionUC);
} else {
systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
}
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders(); builder.getUidBatteryConsumerBuilders();
final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get( final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
@@ -76,7 +93,7 @@ public class SystemServicePowerCalculator extends PowerCalculator {
// distributed to applications // distributed to applications
systemServerConsumer.setConsumedPower( systemServerConsumer.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
-systemServicePowerMah); -systemServicePowerMah, powerModel);
} }
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -84,7 +101,8 @@ public class SystemServicePowerCalculator extends PowerCalculator {
if (app != systemServerConsumer) { if (app != systemServerConsumer) {
final BatteryStats.Uid uid = app.getBatteryStatsUid(); final BatteryStats.Uid uid = app.getBatteryStatsUid();
app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
systemServicePowerMah * uid.getProportionalSystemServiceUsage()); systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
powerModel);
} }
} }
@@ -102,7 +120,20 @@ public class SystemServicePowerCalculator extends PowerCalculator {
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, long rawRealtimeUs, long rawUptimeUs, int statsType,
SparseArray<UserHandle> asUsers) { SparseArray<UserHandle> asUsers) {
double systemServicePowerMah = calculateSystemServicePower(batteryStats); final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
if (systemUid == null) {
return;
}
final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
double systemServicePowerMah;
if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
systemUid, consumptionUC);
} else {
systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
}
BatterySipper systemServerSipper = null; BatterySipper systemServerSipper = null;
for (int i = sippers.size() - 1; i >= 0; i--) { for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i); final BatterySipper app = sippers.get(i);
@@ -134,7 +165,21 @@ public class SystemServicePowerCalculator extends PowerCalculator {
} }
} }
private double calculateSystemServicePower(BatteryStats batteryStats) { private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
BatteryStats.Uid systemUid, long consumptionUC) {
// Use the PowerProfile based model to estimate the ratio between the power consumed
// while handling incoming binder calls and the entire System UID power consumption.
// Apply that ratio to the _measured_ system UID power consumption to get a more
// accurate estimate of the power consumed by incoming binder calls.
final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
systemUid, BatteryStats.STATS_SINCE_CHARGED);
return uCtoMah(consumptionUC) * systemServiceModeledPowerMah
/ systemUidModeledPowerMah;
}
private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
if (systemServiceTimeAtCpuSpeeds == null) { if (systemServiceTimeAtCpuSpeeds == null) {
return 0; return 0;
@@ -145,13 +190,12 @@ public class SystemServicePowerCalculator extends PowerCalculator {
double powerMah = 0; double powerMah = 0;
final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length); final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]); powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
} }
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "System service power:" + powerMah); Log.d(TAG, "System service power:" + powerMah);
} }
return powerMah; return powerMah;
} }
} }

View File

@@ -18,25 +18,28 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.os.BatteryConsumer; import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder; import android.os.Binder;
import android.os.Process; import android.os.Process;
import android.os.UidBatteryConsumer;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.internal.power.MeasuredEnergyStats;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -47,6 +50,8 @@ import java.util.Collection;
public class SystemServicePowerCalculatorTest { public class SystemServicePowerCalculatorTest {
private static final double PRECISION = 0.000001; private static final double PRECISION = 0.000001;
private static final int APP_UID1 = 100;
private static final int APP_UID2 = 200;
@Rule @Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -61,73 +66,52 @@ public class SystemServicePowerCalculatorTest {
.setAveragePowerForCpuCore(1, 0, 500) .setAveragePowerForCpuCore(1, 0, 500)
.setAveragePowerForCpuCore(1, 1, 600); .setAveragePowerForCpuCore(1, 1, 600);
@Mock
private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider; private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
@Mock
private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
@Mock
private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
@Mock
private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
@Mock
private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
@Mock
private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
mock(KernelCpuSpeedReader.class),
mock(KernelCpuSpeedReader.class),
};
private MockBatteryStatsImpl mMockBatteryStats; private MockBatteryStatsImpl mMockBatteryStats;
private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class); MockitoAnnotations.initMocks(this);
mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
mMockBatteryStats = mStatsRule.getBatteryStats() mMockBatteryStats = mStatsRule.getBatteryStats()
.setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) .setUserInfoProvider(mMockUserInfoProvider)
.setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
.setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
.setUserInfoProvider(mMockUserInfoProvider); .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
.setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
.setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
.setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
} }
@Test @Test
public void testPowerProfileBasedModel() { public void testPowerProfileBasedModel() {
when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); prepareBatteryStats(null);
// Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total
mMockSystemServerCpuThreadReader.setCpuTimes(
new long[] {30000, 40000, 50000, 60000},
new long[] {20000, 30000, 40000, 50000});
mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
new long[] {10000, 20000, 30000, 40000}
);
mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false, null);
int workSourceUid1 = 100;
int workSourceUid2 = 200;
int transactionCode = 42;
Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
Binder.class, transactionCode, true /*screenInteractive */);
stat1.incrementalCallCount = 100;
stat1.recordedCallCount = 100;
stat1.cpuTimeMicros = 1000000;
callStats.add(stat1);
mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
Binder.class, transactionCode, true /*screenInteractive */);
stat2.incrementalCallCount = 100;
stat2.recordedCallCount = 100;
stat2.cpuTimeMicros = 9000000;
callStats.add(stat2);
mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats);
mMockBatteryStats.updateSystemServiceCallStats();
mMockBatteryStats.updateSystemServerThreadStats();
SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
mStatsRule.getPowerProfile()); mStatsRule.getPowerProfile());
mStatsRule.apply(new FakeCpuPowerCalculator(), calculator); mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1) assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(1.888888); .isWithin(PRECISION).of(1.888888);
assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2) assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(17.0); .isWithin(PRECISION).of(17.0);
assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
@@ -141,58 +125,105 @@ public class SystemServicePowerCalculatorTest {
.isWithin(PRECISION).of(18.888888); .isWithin(PRECISION).of(18.888888);
} }
private static class MockKernelCpuUidFreqTimeReader extends @Test
KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { public void testMeasuredEnergyBasedModel() {
private long[] mSystemServerCpuTimes; final boolean[] supportedPowerBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
mStatsRule.getBatteryStats()
.initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]);
MockKernelCpuUidFreqTimeReader() { prepareBatteryStats(new long[]{50000000, 100000000});
super(/*throttle */false);
SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
mStatsRule.getPowerProfile());
mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(1.979351);
assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(17.814165);
assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
.isWithin(PRECISION).of(-19.793517);
assertThat(mStatsRule.getDeviceBatteryConsumer()
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(19.793517);
assertThat(mStatsRule.getAppsBatteryConsumer()
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
.isWithin(PRECISION).of(19.793517);
} }
void setSystemServerCpuTimes(long[] systemServerCpuTimes) { private void prepareBatteryStats(long[] clusterChargesUc) {
mSystemServerCpuTimes = systemServerCpuTimes; when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
}
@Override when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
public boolean perClusterTimesAvailable() { when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
return true;
}
@Override when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
if (cb != null) {
cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
}
}
}
private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader { // User/System CPU time in microseconds
private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); doAnswer(invocation -> {
final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000});
callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000});
callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000});
return null;
}).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
MockSystemServerCpuThreadReader() { // Active CPU time in milliseconds
super(null); doAnswer(invocation -> {
} final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
callback.onUidCpuTime(APP_UID1, 1111L);
callback.onUidCpuTime(APP_UID2, 3333L);
callback.onUidCpuTime(Process.SYSTEM_UID, 10000L);
return null;
}).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { // Per-cluster CPU time in milliseconds
mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; doAnswer(invocation -> {
mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
} callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000});
return null;
}).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
@Override // System service CPU time
public SystemServiceCpuThreadTimes readDelta() { final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes =
return mThreadTimes; new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes();
} threadTimes.binderThreadCpuTimesUs =
} new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000};
private static class FakeCpuPowerCalculator extends PowerCalculator { when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes);
@Override
protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, int transactionCode = 42;
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
if (u.getUid() == Process.SYSTEM_UID) { Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
// SystemServer must be attributed at least as much power as the total BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1,
// of all system services requested by apps. Binder.class, transactionCode, true /*screenInteractive */);
app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000); stat1.incrementalCallCount = 100;
} stat1.recordedCallCount = 100;
} stat1.cpuTimeMicros = 1_000_000;
callStats.add(stat1);
mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2,
Binder.class, transactionCode, true /*screenInteractive */);
stat2.incrementalCallCount = 100;
stat2.recordedCallCount = 100;
stat2.cpuTimeMicros = 9_000_000;
callStats.add(stat2);
mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats);
mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc);
mMockBatteryStats.prepareForDumpLocked();
} }
} }