Merge "BatteryStats: Better big-little CPU accounting" into mnc-dr-dev

This commit is contained in:
Adam Lesinski
2015-09-18 20:26:07 +00:00
committed by Android (Google) Code Review
8 changed files with 324 additions and 118 deletions

View File

@@ -463,13 +463,15 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getCpuPowerMaUs(int which); public abstract long getCpuPowerMaUs(int which);
/** /**
* Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a
* given CPU cluster.
* @param cluster the index of the CPU cluster.
* @param step the index of the CPU speed. This is not the actual speed of the CPU. * @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
* @see BatteryStats#getCpuSpeedSteps() * @see PowerProfile.getNumCpuClusters()
* @see PowerProfile.getNumSpeedStepsInCpuCluster(int)
*/ */
@Deprecated public abstract long getTimeAtCpuSpeed(int cluster, int step, int which);
public abstract long getTimeAtCpuSpeed(int step, int which);
public static abstract class Sensor { public static abstract class Sensor {
/* /*
@@ -2276,9 +2278,6 @@ public abstract class BatteryStats implements Parcelable {
public abstract Map<String, ? extends Timer> getKernelWakelockStats(); public abstract Map<String, ? extends Timer> getKernelWakelockStats();
/** Returns the number of different speeds that the CPU can run at */
public abstract int getCpuSpeedSteps();
public abstract void writeToParcelWithoutUids(Parcel out, int flags); public abstract void writeToParcelWithoutUids(Parcel out, int flags);
private final static void formatTimeRaw(StringBuilder out, long seconds) { private final static void formatTimeRaw(StringBuilder out, long seconds) {

View File

@@ -338,7 +338,7 @@ public final class BatteryStatsHelper {
} }
if (mCpuPowerCalculator == null) { if (mCpuPowerCalculator == null) {
mCpuPowerCalculator = new CpuPowerCalculator(); mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
} }
mCpuPowerCalculator.reset(); mCpuPowerCalculator.reset();

View File

@@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS' private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version // Current on-disk Parcel version
private static final int VERSION = 130 + (USE_OLD_HISTORY ? 1000 : 0); private static final int VERSION = 131 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history. // Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000; private static final int MAX_HISTORY_ITEMS = 2000;
@@ -118,8 +118,6 @@ public final class BatteryStatsImpl extends BatteryStats {
// in to one common name. // in to one common name.
private static final int MAX_WAKELOCKS_PER_UID = 100; private static final int MAX_WAKELOCKS_PER_UID = 100;
private static int sNumSpeedSteps;
private final JournaledFile mFile; private final JournaledFile mFile;
public final AtomicFile mCheckinFile; public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile; public final AtomicFile mDailyFile;
@@ -133,7 +131,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader(); private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
public interface BatteryCallback { public interface BatteryCallback {
public void batteryNeedsCpuUpdate(); public void batteryNeedsCpuUpdate();
@@ -4411,7 +4409,7 @@ public final class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase);
LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase);
LongSamplingCounter[] mSpeedBins; LongSamplingCounter[][] mCpuClusterSpeed;
/** /**
* The statistics we have collected for this uid's wake locks. * The statistics we have collected for this uid's wake locks.
@@ -4470,7 +4468,6 @@ public final class BatteryStatsImpl extends BatteryStats {
mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
mWifiMulticastTimers, mOnBatteryTimeBase); mWifiMulticastTimers, mOnBatteryTimeBase);
mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE]; mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()];
} }
@Override @Override
@@ -5008,10 +5005,18 @@ public final class BatteryStatsImpl extends BatteryStats {
} }
@Override @Override
public long getTimeAtCpuSpeed(int step, int which) { public long getTimeAtCpuSpeed(int cluster, int step, int which) {
if (step >= 0 && step < mSpeedBins.length) { if (mCpuClusterSpeed != null) {
if (mSpeedBins[step] != null) { if (cluster >= 0 && cluster < mCpuClusterSpeed.length) {
return mSpeedBins[step].getCountLocked(which); 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; return 0;
@@ -5128,10 +5133,16 @@ public final class BatteryStatsImpl extends BatteryStats {
mUserCpuTime.reset(false); mUserCpuTime.reset(false);
mSystemCpuTime.reset(false); mSystemCpuTime.reset(false);
mCpuPower.reset(false); mCpuPower.reset(false);
for (int i = 0; i < mSpeedBins.length; i++) {
LongSamplingCounter c = mSpeedBins[i]; if (mCpuClusterSpeed != null) {
if (c != null) { for (LongSamplingCounter[] speeds : mCpuClusterSpeed) {
c.reset(false); if (speeds != null) {
for (LongSamplingCounter speed : speeds) {
if (speed != null) {
speed.reset(false);
}
}
}
} }
} }
@@ -5280,10 +5291,16 @@ public final class BatteryStatsImpl extends BatteryStats {
mUserCpuTime.detach(); mUserCpuTime.detach();
mSystemCpuTime.detach(); mSystemCpuTime.detach();
mCpuPower.detach(); mCpuPower.detach();
for (int i = 0; i < mSpeedBins.length; i++) {
LongSamplingCounter c = mSpeedBins[i]; if (mCpuClusterSpeed != null) {
if (c != null) { for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
c.detach(); if (cpuSpeeds != null) {
for (LongSamplingCounter c : cpuSpeeds) {
if (c != null) {
c.detach();
}
}
}
} }
} }
} }
@@ -5461,15 +5478,27 @@ public final class BatteryStatsImpl extends BatteryStats {
mSystemCpuTime.writeToParcel(out); mSystemCpuTime.writeToParcel(out);
mCpuPower.writeToParcel(out); mCpuPower.writeToParcel(out);
out.writeInt(mSpeedBins.length); if (mCpuClusterSpeed != null) {
for (int i = 0; i < mSpeedBins.length; i++) { out.writeInt(1);
LongSamplingCounter c = mSpeedBins[i]; out.writeInt(mCpuClusterSpeed.length);
if (c != null) { for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) {
out.writeInt(1); if (cpuSpeeds != null) {
c.writeToParcel(out); out.writeInt(1);
} else { out.writeInt(cpuSpeeds.length);
out.writeInt(0); for (LongSamplingCounter c : cpuSpeeds) {
if (c != null) {
out.writeInt(1);
c.writeToParcel(out);
} else {
out.writeInt(0);
}
}
} else {
out.writeInt(0);
}
} }
} else {
out.writeInt(0);
} }
} }
@@ -5653,13 +5682,32 @@ public final class BatteryStatsImpl extends BatteryStats {
mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in); mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in);
int bins = in.readInt(); if (in.readInt() != 0) {
int steps = getCpuSpeedSteps(); int numCpuClusters = in.readInt();
mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps]; if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
for (int i = 0; i < bins; i++) { throw new ParcelFormatException("Incompatible number of cpu clusters");
if (in.readInt() != 0) {
mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
} }
mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][];
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
if (in.readInt() != 0) {
int numSpeeds = in.readInt();
if (mPowerProfile != null &&
mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
throw new ParcelFormatException("Incompatible number of cpu speeds");
}
final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
mCpuClusterSpeed[cluster] = cpuSpeeds;
for (int speed = 0; speed < numSpeeds; speed++) {
if (in.readInt() != 0) {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
}
}
}
} else {
mCpuClusterSpeed = null;
} }
} }
@@ -6874,6 +6922,19 @@ public final class BatteryStatsImpl extends BatteryStats {
public void setPowerProfile(PowerProfile profile) { public void setPowerProfile(PowerProfile profile) {
synchronized (this) { synchronized (this) {
mPowerProfile = profile; mPowerProfile = profile;
// We need to initialize the KernelCpuSpeedReaders to read from
// the first cpu of each core. Once we have the PowerProfile, we have access to this
// information.
final int numClusters = mPowerProfile.getNumCpuClusters();
mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
int firstCpuOfCluster = 0;
for (int i = 0; i < numClusters; i++) {
final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
numSpeedSteps);
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
} }
} }
@@ -6881,10 +6942,6 @@ public final class BatteryStatsImpl extends BatteryStats {
mCallback = cb; mCallback = cb;
} }
public void setNumSpeedSteps(int steps) {
if (sNumSpeedSteps == 0) sNumSpeedSteps = steps;
}
public void setRadioScanningTimeout(long timeout) { public void setRadioScanningTimeout(long timeout) {
if (mPhoneSignalScanningTimer != null) { if (mPhoneSignalScanningTimer != null) {
mPhoneSignalScanningTimer.setTimeout(timeout); mPhoneSignalScanningTimer.setTimeout(timeout);
@@ -7997,9 +8054,11 @@ public final class BatteryStatsImpl extends BatteryStats {
// If no app is holding a wakelock, then the distribution is normal. // If no app is holding a wakelock, then the distribution is normal.
final int wakelockWeight = 50; final int wakelockWeight = 50;
// Read the time spent at various cpu frequencies. // Read the time spent for each cluster at various cpu frequencies.
final int cpuSpeedSteps = getCpuSpeedSteps(); final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][];
final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta(); for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta();
}
int numWakelocks = 0; int numWakelocks = 0;
@@ -8072,11 +8131,23 @@ public final class BatteryStatsImpl extends BatteryStats {
// Add the cpu speeds to this UID. These are used as a ratio // Add the cpu speeds to this UID. These are used as a ratio
// for computing the power this UID used. // for computing the power this UID used.
for (int i = 0; i < cpuSpeedSteps; i++) { if (u.mCpuClusterSpeed == null) {
if (u.mSpeedBins[i] == null) { u.mCpuClusterSpeed = new LongSamplingCounter[clusterSpeeds.length][];
u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); }
for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) {
if (u.mCpuClusterSpeed[cluster] == null) {
u.mCpuClusterSpeed[cluster] =
new LongSamplingCounter[clusterSpeeds[cluster].length];
}
final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster];
for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) {
if (cpuSpeeds[speed] == null) {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase);
}
cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]);
} }
u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]);
} }
} }
}); });
@@ -8776,11 +8847,6 @@ public final class BatteryStatsImpl extends BatteryStats {
} }
} }
@Override
public int getCpuSpeedSteps() {
return sNumSpeedSteps;
}
/** /**
* Retrieve the statistics object for a particular uid, creating if needed. * Retrieve the statistics object for a particular uid, creating if needed.
*/ */
@@ -9216,11 +9282,6 @@ public final class BatteryStatsImpl extends BatteryStats {
} }
} }
sNumSpeedSteps = in.readInt();
if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
throw new ParcelFormatException("Bad speed steps in data: " + sNumSpeedSteps);
}
final int NU = in.readInt(); final int NU = in.readInt();
if (NU > 10000) { if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU); throw new ParcelFormatException("File corrupt: too many uids " + NU);
@@ -9304,17 +9365,33 @@ public final class BatteryStatsImpl extends BatteryStats {
u.mSystemCpuTime.readSummaryFromParcelLocked(in); u.mSystemCpuTime.readSummaryFromParcelLocked(in);
u.mCpuPower.readSummaryFromParcelLocked(in); u.mCpuPower.readSummaryFromParcelLocked(in);
int NSB = in.readInt(); if (in.readInt() != 0) {
if (NSB > 100) { final int numClusters = in.readInt();
throw new ParcelFormatException("File corrupt: too many speed bins " + NSB); if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) {
} throw new ParcelFormatException("Incompatible cpu cluster arrangement");
u.mSpeedBins = new LongSamplingCounter[NSB];
for (int i=0; i<NSB; i++) {
if (in.readInt() != 0) {
u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase);
u.mSpeedBins[i].readSummaryFromParcelLocked(in);
} }
u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
for (int cluster = 0; cluster < numClusters; cluster++) {
int NSB = in.readInt();
if (mPowerProfile != null &&
mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
}
if (in.readInt() != 0) {
u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
for (int speed = 0; speed < NSB; speed++) {
if (in.readInt() != 0) {
u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter(
mOnBatteryTimeBase);
u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
}
}
}
}
} else {
u.mCpuClusterSpeed = null;
} }
int NW = in.readInt(); int NW = in.readInt();
@@ -9531,7 +9608,6 @@ public final class BatteryStatsImpl extends BatteryStats {
} }
} }
out.writeInt(sNumSpeedSteps);
final int NU = mUidStats.size(); final int NU = mUidStats.size();
out.writeInt(NU); out.writeInt(NU);
for (int iu = 0; iu < NU; iu++) { for (int iu = 0; iu < NU; iu++) {
@@ -9640,15 +9716,27 @@ public final class BatteryStatsImpl extends BatteryStats {
u.mSystemCpuTime.writeSummaryFromParcelLocked(out); u.mSystemCpuTime.writeSummaryFromParcelLocked(out);
u.mCpuPower.writeSummaryFromParcelLocked(out); u.mCpuPower.writeSummaryFromParcelLocked(out);
out.writeInt(u.mSpeedBins.length); if (u.mCpuClusterSpeed != null) {
for (int i = 0; i < u.mSpeedBins.length; i++) { out.writeInt(1);
LongSamplingCounter speedBin = u.mSpeedBins[i]; out.writeInt(u.mCpuClusterSpeed.length);
if (speedBin != null) { for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) {
out.writeInt(1); if (cpuSpeeds != null) {
speedBin.writeSummaryFromParcelLocked(out); out.writeInt(1);
} else { out.writeInt(cpuSpeeds.length);
out.writeInt(0); for (LongSamplingCounter c : cpuSpeeds) {
if (c != null) {
out.writeInt(1);
c.writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
}
}
} else {
out.writeInt(0);
}
} }
} else {
out.writeInt(0);
} }
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
@@ -9897,8 +9985,6 @@ public final class BatteryStatsImpl extends BatteryStats {
mFlashlightTurnedOnTimers.clear(); mFlashlightTurnedOnTimers.clear();
mCameraTurnedOnTimers.clear(); mCameraTurnedOnTimers.clear();
sNumSpeedSteps = in.readInt();
int numUids = in.readInt(); int numUids = in.readInt();
mUidStats.clear(); mUidStats.clear();
for (int i = 0; i < numUids; i++) { for (int i = 0; i < numUids; i++) {
@@ -10037,8 +10123,6 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(0); out.writeInt(0);
} }
out.writeInt(sNumSpeedSteps);
if (inclUids) { if (inclUids) {
int size = mUidStats.size(); int size = mUidStats.size();
out.writeInt(size); out.writeInt(size);

View File

@@ -22,12 +22,47 @@ import android.util.Log;
public class CpuPowerCalculator extends PowerCalculator { public class CpuPowerCalculator extends PowerCalculator {
private static final String TAG = "CpuPowerCalculator"; private static final String TAG = "CpuPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
private final PowerProfile mProfile;
public CpuPowerCalculator(PowerProfile profile) {
mProfile = profile;
}
@Override @Override
public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) { long rawUptimeUs, int statsType) {
app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
app.cpuPowerMah = (double) u.getCpuPowerMaUs(statsType) / (60.0 * 60.0 * 1000.0 * 1000.0);
// Aggregate total time spent on each cluster.
long totalTime = 0;
final int numClusters = mProfile.getNumCpuClusters();
for (int cluster = 0; cluster < numClusters; cluster++) {
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType);
}
}
totalTime = Math.max(totalTime, 1);
double cpuPowerMaMs = 0;
for (int cluster = 0; cluster < numClusters; cluster++) {
final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster);
for (int speed = 0; speed < speedsForCluster; speed++) {
final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) /
totalTime;
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)));
}
cpuPowerMaMs += cpuSpeedStepPower;
}
}
app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000);
if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power=" Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power="
+ BatteryStatsHelper.makemAh(app.cpuPowerMah)); + BatteryStatsHelper.makemAh(app.cpuPowerMah));

View File

@@ -24,8 +24,8 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
/** /**
* Reads CPU time spent at various frequencies and provides a delta from the last call to * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
* {@link #readDelta}. Each line in the proc file has the format: * last call to {@link #readDelta}. Each line in the proc file has the format:
* *
* freq time * freq time
* *
@@ -33,12 +33,20 @@ import java.util.Arrays;
*/ */
public class KernelCpuSpeedReader { public class KernelCpuSpeedReader {
private static final String TAG = "KernelCpuSpeedReader"; private static final String TAG = "KernelCpuSpeedReader";
private static final String sProcFile =
"/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state";
private static final int MAX_SPEEDS = 60;
private long[] mLastSpeedTimes = new long[MAX_SPEEDS]; private final String mProcFile;
private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS]; private final long[] mLastSpeedTimes;
private final long[] mDeltaSpeedTimes;
/**
* @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];
}
/** /**
* The returned array is modified in subsequent calls to {@link #readDelta}. * The returned array is modified in subsequent calls to {@link #readDelta}.
@@ -46,7 +54,7 @@ public class KernelCpuSpeedReader {
* {@link #readDelta}. * {@link #readDelta}.
*/ */
public long[] readDelta() { public long[] readDelta() {
try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
String line; String line;
int speedIndex = 0; int speedIndex = 0;
@@ -56,12 +64,18 @@ public class KernelCpuSpeedReader {
// The proc file reports time in 1/100 sec, so convert to milliseconds. // The proc file reports time in 1/100 sec, so convert to milliseconds.
long time = Long.parseLong(splitter.next()) * 10; long time = Long.parseLong(splitter.next()) * 10;
mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; if (time < mLastSpeedTimes[speedIndex]) {
// The stats reset when the cpu hotplugged. That means that the time
// we read is offset from 0, so the time is the delta.
mDeltaSpeedTimes[speedIndex] = time;
} else {
mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex];
}
mLastSpeedTimes[speedIndex] = time; mLastSpeedTimes[speedIndex] = time;
speedIndex++; speedIndex++;
} }
} catch (IOException e) { } catch (IOException e) {
Slog.e(TAG, "Failed to read cpu-freq", e); Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage());
Arrays.fill(mDeltaSpeedTimes, 0); Arrays.fill(mDeltaSpeedTimes, 0);
} }
return mDeltaSpeedTimes; return mDeltaSpeedTimes;

View File

@@ -59,6 +59,7 @@ public class PowerProfile {
/** /**
* Power consumption when CPU is in power collapse mode. * Power consumption when CPU is in power collapse mode.
*/ */
@Deprecated
public static final String POWER_CPU_ACTIVE = "cpu.active"; public static final String POWER_CPU_ACTIVE = "cpu.active";
/** /**
@@ -163,6 +164,7 @@ public class PowerProfile {
*/ */
public static final String POWER_CAMERA = "camera.avg"; public static final String POWER_CAMERA = "camera.avg";
@Deprecated
public static final String POWER_CPU_SPEEDS = "cpu.speeds"; public static final String POWER_CPU_SPEEDS = "cpu.speeds";
/** /**
@@ -191,6 +193,7 @@ public class PowerProfile {
if (sPowerMap.size() == 0) { if (sPowerMap.size() == 0) {
readPowerValuesFromXml(context); readPowerValuesFromXml(context);
} }
initCpuClusters();
} }
private void readPowerValuesFromXml(Context context) { private void readPowerValuesFromXml(Context context) {
@@ -249,7 +252,7 @@ public class PowerProfile {
} }
// Now collect other config variables. // Now collect other config variables.
int[] configResIds = new int[] { int[] configResIds = new int[]{
com.android.internal.R.integer.config_bluetooth_idle_cur_ma, com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
com.android.internal.R.integer.config_bluetooth_rx_cur_ma, com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
com.android.internal.R.integer.config_bluetooth_tx_cur_ma, com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
@@ -260,7 +263,7 @@ public class PowerProfile {
com.android.internal.R.integer.config_wifi_operating_voltage_mv, com.android.internal.R.integer.config_wifi_operating_voltage_mv,
}; };
String[] configResIdKeys = new String[] { String[] configResIdKeys = new String[]{
POWER_BLUETOOTH_CONTROLLER_IDLE, POWER_BLUETOOTH_CONTROLLER_IDLE,
POWER_BLUETOOTH_CONTROLLER_RX, POWER_BLUETOOTH_CONTROLLER_RX,
POWER_BLUETOOTH_CONTROLLER_TX, POWER_BLUETOOTH_CONTROLLER_TX,
@@ -279,6 +282,69 @@ public class PowerProfile {
} }
} }
private CpuClusterKey[] mCpuClusters;
private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
@SuppressWarnings("deprecated")
private void initCpuClusters() {
// Figure out how many CPU clusters we're dealing with
final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
if (obj == null || !(obj instanceof Double[])) {
// Default to single.
mCpuClusters = new CpuClusterKey[1];
mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
} else {
final Double[] array = (Double[]) obj;
mCpuClusters = new CpuClusterKey[array.length];
for (int cluster = 0; cluster < array.length; cluster++) {
int numCpusInCluster = (int) Math.round(array[cluster]);
mCpuClusters[cluster] = new CpuClusterKey(
POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
numCpusInCluster);
}
}
}
public static class CpuClusterKey {
private final String timeKey;
private final String powerKey;
private final int numCpus;
private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
this.timeKey = timeKey;
this.powerKey = powerKey;
this.numCpus = numCpus;
}
}
public int getNumCpuClusters() {
return mCpuClusters.length;
}
public int getNumCoresInCpuCluster(int index) {
return mCpuClusters[index].numCpus;
}
public int getNumSpeedStepsInCpuCluster(int index) {
Object value = sPowerMap.get(mCpuClusters[index].timeKey);
if (value != null && value instanceof Double[]) {
return ((Double[])value).length;
}
return 1; // Only one speed
}
public double getAveragePowerForCpu(int cluster, int step) {
if (cluster >= 0 && cluster < mCpuClusters.length) {
return getAveragePower(mCpuClusters[cluster].powerKey, step);
}
return 0;
}
/** /**
* Returns the average current in mA consumed by the subsystem, or the given * Returns the average current in mA consumed by the subsystem, or the given
* default value if the subsystem has no recorded value. * default value if the subsystem has no recorded value.
@@ -344,16 +410,4 @@ public class PowerProfile {
public double getBatteryCapacity() { public double getBatteryCapacity() {
return getAveragePower(POWER_BATTERY_CAPACITY); return getAveragePower(POWER_BATTERY_CAPACITY);
} }
/**
* Returns the number of speeds that the CPU can be run at.
* @return
*/
public int getNumSpeedSteps() {
Object value = sPowerMap.get(POWER_CPU_SPEEDS);
if (value != null && value instanceof Double[]) {
return ((Double[])value).length;
}
return 1; // Only one speed
}
} }

View File

@@ -47,17 +47,38 @@
<value>0.2</value> <!-- ~2mA --> <value>0.2</value> <!-- ~2mA -->
<value>0.1</value> <!-- ~1mA --> <value>0.1</value> <!-- ~1mA -->
</array> </array>
<!-- Different CPU speeds as reported in
/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --> <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
<array name="cpu.speeds"> number of CPU cores for that cluster.
Ex:
<array name="cpu.clusters.cores">
<value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3
<value>2</value> // cluster 1 has cpu4, cpu5
</array> -->
<array name="cpu.clusters.cores">
<value>1</value> <!-- cluster 0 has cpu0 -->
</array>
<!-- Different CPU speeds for cluster 0 as reported in
/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state.
There must be one of these for each cluster, labeled:
cpu.speeds.cluster0, cpu.speeds.cluster1, etc... -->
<array name="cpu.speeds.cluster0">
<value>400000</value> <!-- 400 MHz CPU speed --> <value>400000</value> <!-- 400 MHz CPU speed -->
</array> </array>
<!-- Current when CPU is idle -->
<item name="cpu.idle">0.1</item> <!-- Current at each CPU speed for cluster 0, as per 'cpu.speeds.cluster0'.
<!-- Current at each CPU speed, as per 'cpu.speeds' --> Like cpu.speeds.cluster0, there must be one of these present for
<array name="cpu.active"> each heterogeneous CPU cluster. -->
<array name="cpu.active.cluster0">
<value>0.1</value> <!-- ~100mA --> <value>0.1</value> <!-- ~100mA -->
</array> </array>
<!-- Current when CPU is idle -->
<item name="cpu.idle">0.1</item>
<!-- This is the battery capacity in mAh (measured at nominal voltage) --> <!-- This is the battery capacity in mAh (measured at nominal voltage) -->
<item name="battery.capacity">1000</item> <item name="battery.capacity">1000</item>

View File

@@ -171,12 +171,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public void publish(Context context) { public void publish(Context context) {
mContext = context; mContext = context;
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps());
mStats.setRadioScanningTimeout(mContext.getResources().getInteger( mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) com.android.internal.R.integer.config_radioScanningTimeout)
* 1000L); * 1000L);
mStats.setPowerProfile(new PowerProfile(context)); mStats.setPowerProfile(new PowerProfile(context));
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
} }
/** /**