From 6832f39416cbe2cc9655af4eabefe39c5d272254 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Sat, 5 Sep 2015 18:05:40 -0700 Subject: BatteryStats: Better big-little CPU accounting Generalize cpu clusters so we can measure frequency and power usage across heterogeneous cpu clusters. This also brings back reading of cpu-times for power calculation. Bug:22773176 Change-Id: I9c794ae9756c782c0e971c7f5fcebbe70374b269 --- core/java/android/os/BatteryStats.java | 13 +- .../android/internal/os/BatteryStatsHelper.java | 2 +- .../com/android/internal/os/BatteryStatsImpl.java | 234 ++++++++++++++------- .../android/internal/os/CpuPowerCalculator.java | 37 +++- .../android/internal/os/KernelCpuSpeedReader.java | 34 ++- .../java/com/android/internal/os/PowerProfile.java | 82 ++++++-- 6 files changed, 294 insertions(+), 108 deletions(-) (limited to 'core/java') diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index bad94fce20aa..8e86a53072e7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -463,13 +463,15 @@ public abstract class BatteryStats implements Parcelable { 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 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 step, int which); + public abstract long getTimeAtCpuSpeed(int cluster, int step, int which); public static abstract class Sensor { /* @@ -2276,9 +2278,6 @@ public abstract class BatteryStats implements Parcelable { public abstract Map 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); private final static void formatTimeRaw(StringBuilder out, long seconds) { diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 4f4d3e0faa44..f178c8cf7ece 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -338,7 +338,7 @@ public final class BatteryStatsHelper { } if (mCpuPowerCalculator == null) { - mCpuPowerCalculator = new CpuPowerCalculator(); + mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } mCpuPowerCalculator.reset(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 4ff786987028..6ccdd08498ce 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // 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. private static final int MAX_HISTORY_ITEMS = 2000; @@ -118,8 +118,6 @@ public final class BatteryStatsImpl extends BatteryStats { // in to one common name. private static final int MAX_WAKELOCKS_PER_UID = 100; - private static int sNumSpeedSteps; - private final JournaledFile mFile; public final AtomicFile mCheckinFile; public final AtomicFile mDailyFile; @@ -133,7 +131,7 @@ public final class BatteryStatsImpl extends BatteryStats { private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); - private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader(); + private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; public interface BatteryCallback { public void batteryNeedsCpuUpdate(); @@ -4411,7 +4409,7 @@ public final class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase); - LongSamplingCounter[] mSpeedBins; + LongSamplingCounter[][] mCpuClusterSpeed; /** * 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, mWifiMulticastTimers, mOnBatteryTimeBase); mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE]; - mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()]; } @Override @@ -5008,10 +5005,18 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public long getTimeAtCpuSpeed(int step, int which) { - if (step >= 0 && step < mSpeedBins.length) { - if (mSpeedBins[step] != null) { - return mSpeedBins[step].getCountLocked(which); + public long getTimeAtCpuSpeed(int cluster, int step, int which) { + if (mCpuClusterSpeed != null) { + if (cluster >= 0 && cluster < mCpuClusterSpeed.length) { + final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster]; + if (cpuSpeeds != null) { + if (step >= 0 && step < cpuSpeeds.length) { + final LongSamplingCounter c = cpuSpeeds[step]; + if (c != null) { + return c.getCountLocked(which); + } + } + } } } return 0; @@ -5128,10 +5133,16 @@ public final class BatteryStatsImpl extends BatteryStats { mUserCpuTime.reset(false); mSystemCpuTime.reset(false); mCpuPower.reset(false); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.reset(false); + + if (mCpuClusterSpeed != null) { + for (LongSamplingCounter[] speeds : mCpuClusterSpeed) { + 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(); mSystemCpuTime.detach(); mCpuPower.detach(); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.detach(); + + if (mCpuClusterSpeed != null) { + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + 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); mCpuPower.writeToParcel(out); - out.writeInt(mSpeedBins.length); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); + if (mCpuClusterSpeed != null) { + out.writeInt(1); + out.writeInt(mCpuClusterSpeed.length); + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + if (cpuSpeeds != null) { + out.writeInt(1); + out.writeInt(cpuSpeeds.length); + 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); mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in); - int bins = in.readInt(); - int steps = getCpuSpeedSteps(); - mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps]; - for (int i = 0; i < bins; i++) { - if (in.readInt() != 0) { - mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in); + if (in.readInt() != 0) { + int numCpuClusters = in.readInt(); + if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numCpuClusters) { + throw new ParcelFormatException("Incompatible number of cpu clusters"); + } + + 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) { synchronized (this) { 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; } - public void setNumSpeedSteps(int steps) { - if (sNumSpeedSteps == 0) sNumSpeedSteps = steps; - } - public void setRadioScanningTimeout(long timeout) { if (mPhoneSignalScanningTimer != null) { 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. final int wakelockWeight = 50; - // Read the time spent at various cpu frequencies. - final int cpuSpeedSteps = getCpuSpeedSteps(); - final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta(); + // Read the time spent for each cluster at various cpu frequencies. + final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][]; + for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { + clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta(); + } 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 // for computing the power this UID used. - for (int i = 0; i < cpuSpeedSteps; i++) { - if (u.mSpeedBins[i] == null) { - u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + if (u.mCpuClusterSpeed == null) { + u.mCpuClusterSpeed = new LongSamplingCounter[clusterSpeeds.length][]; + } + + 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. */ @@ -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(); if (NU > 10000) { throw new ParcelFormatException("File corrupt: too many uids " + NU); @@ -9304,17 +9365,33 @@ public final class BatteryStatsImpl extends BatteryStats { u.mSystemCpuTime.readSummaryFromParcelLocked(in); u.mCpuPower.readSummaryFromParcelLocked(in); - int NSB = in.readInt(); - if (NSB > 100) { - throw new ParcelFormatException("File corrupt: too many speed bins " + NSB); - } + if (in.readInt() != 0) { + final int numClusters = in.readInt(); + if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) { + throw new ParcelFormatException("Incompatible cpu cluster arrangement"); + } - u.mSpeedBins = new LongSamplingCounter[NSB]; - for (int i=0; i wakeStats = u.mWakelockStats.getMap(); @@ -9897,8 +9985,6 @@ public final class BatteryStatsImpl extends BatteryStats { mFlashlightTurnedOnTimers.clear(); mCameraTurnedOnTimers.clear(); - sNumSpeedSteps = in.readInt(); - int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { @@ -10037,8 +10123,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } - out.writeInt(sNumSpeedSteps); - if (inclUids) { int size = mUidStats.size(); out.writeInt(size); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index d62f7a689157..8417856d16d5 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -22,12 +22,47 @@ import android.util.Log; public class CpuPowerCalculator extends PowerCalculator { private static final String TAG = "CpuPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + private final PowerProfile mProfile; + + public CpuPowerCalculator(PowerProfile profile) { + mProfile = profile; + } @Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { + 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)) { Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power=" + BatteryStatsHelper.makemAh(app.cpuPowerMah)); diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java index c30df28c5c1f..e16ae0bd8bff 100644 --- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -24,8 +24,8 @@ import java.io.IOException; import java.util.Arrays; /** - * Reads CPU time spent at various frequencies and provides a delta from the last call to - * {@link #readDelta}. Each line in the proc file has the format: + * Reads CPU time of a specific core spent at various frequencies and provides a delta from the + * last call to {@link #readDelta}. Each line in the proc file has the format: * * freq time * @@ -33,12 +33,20 @@ import java.util.Arrays; */ public class 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 long[] mDeltaSpeedTimes = new long[MAX_SPEEDS]; + private final String mProcFile; + 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}. @@ -46,7 +54,7 @@ public class KernelCpuSpeedReader { * {@link #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(' '); String line; int speedIndex = 0; @@ -56,12 +64,18 @@ public class KernelCpuSpeedReader { // The proc file reports time in 1/100 sec, so convert to milliseconds. 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; speedIndex++; } } 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); } return mDeltaSpeedTimes; diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 4ede8ddab3bc..aaa9f734aba8 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -59,6 +59,7 @@ public class PowerProfile { /** * Power consumption when CPU is in power collapse mode. */ + @Deprecated public static final String POWER_CPU_ACTIVE = "cpu.active"; /** @@ -163,6 +164,7 @@ public class PowerProfile { */ public static final String POWER_CAMERA = "camera.avg"; + @Deprecated public static final String POWER_CPU_SPEEDS = "cpu.speeds"; /** @@ -191,6 +193,7 @@ public class PowerProfile { if (sPowerMap.size() == 0) { readPowerValuesFromXml(context); } + initCpuClusters(); } private void readPowerValuesFromXml(Context context) { @@ -249,7 +252,7 @@ public class PowerProfile { } // 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_rx_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, }; - String[] configResIdKeys = new String[] { + String[] configResIdKeys = new String[]{ POWER_BLUETOOTH_CONTROLLER_IDLE, POWER_BLUETOOTH_CONTROLLER_RX, 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 * default value if the subsystem has no recorded value. @@ -344,16 +410,4 @@ public class PowerProfile { public double getBatteryCapacity() { 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 - } } -- cgit v1.2.3