Loading services/core/java/com/android/server/am/BatteryStatsService.java +2 −0 Original line number Diff line number Diff line Loading @@ -654,6 +654,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); mHandler.post(() -> { mCpuWakeupStats.onUidRemoved(uid); synchronized (mStats) { mStats.removeUidStatsLocked(uid, elapsedRealtime); } Loading Loading @@ -764,6 +765,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { mCpuWakeupStats.noteUidProcessState(uid, state); synchronized (mStats) { mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime); } Loading services/core/java/com/android/server/power/stats/CpuWakeupStats.java +115 −59 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI; import android.app.ActivityManager; import android.content.Context; import android.os.Handler; import android.os.HandlerExecutor; Loading @@ -27,11 +28,10 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeSparseArray; import android.util.TimeUtils; Loading Loading @@ -59,7 +59,7 @@ public class CpuWakeupStats { private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution"; @VisibleForTesting static final long WAKEUP_REASON_HALF_WINDOW_MS = 500; private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2); private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30); private final Handler mHandler; private final IrqDeviceMap mIrqDeviceMap; Loading @@ -69,10 +69,15 @@ public class CpuWakeupStats { @VisibleForTesting final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>(); /* Maps timestamp -> {subsystem -> {uid -> procState}} */ @VisibleForTesting final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution = final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution = new TimeSparseArray<>(); final SparseIntArray mUidProcStates = new SparseIntArray(); private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4); public CpuWakeupStats(Context context, int mapRes, Handler handler) { mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes); mHandler = handler; Loading Loading @@ -102,13 +107,14 @@ public class CpuWakeupStats { FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN, FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN, null, wakeupToLog.mElapsedMillis); wakeupToLog.mElapsedMillis, null); Trace.instantForTrack(Trace.TRACE_TAG_POWER, TRACE_TRACK_WAKEUP_ATTRIBUTION, wakeupToLog.mElapsedMillis + " --"); return; } final SparseArray<SparseBooleanArray> wakeupAttribution = mWakeupAttribution.get( final SparseArray<SparseIntArray> wakeupAttribution = mWakeupAttribution.get( wakeupToLog.mElapsedMillis); if (wakeupAttribution == null) { // This is not expected but can theoretically happen in extreme situations, e.g. if we Loading @@ -121,24 +127,28 @@ public class CpuWakeupStats { for (int i = 0; i < wakeupAttribution.size(); i++) { final int subsystem = wakeupAttribution.keyAt(i); final SparseBooleanArray uidMap = wakeupAttribution.valueAt(i); final SparseIntArray uidProcStates = wakeupAttribution.valueAt(i); final int[] uids; if (uidMap == null || uidMap.size() == 0) { uids = new int[0]; final int[] procStatesProto; if (uidProcStates == null || uidProcStates.size() == 0) { uids = procStatesProto = new int[0]; } else { final IntArray tmp = new IntArray(uidMap.size()); for (int j = 0; j < uidMap.size(); j++) { if (uidMap.valueAt(j)) { tmp.add(uidMap.keyAt(j)); } final int numUids = uidProcStates.size(); uids = new int[numUids]; procStatesProto = new int[numUids]; for (int j = 0; j < numUids; j++) { uids[j] = uidProcStates.keyAt(j); procStatesProto[j] = ActivityManager.processStateAmToProto( uidProcStates.valueAt(j)); } uids = tmp.toArray(); } FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED, FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ, subsystemToStatsReason(subsystem), uids, wakeupToLog.mElapsedMillis); wakeupToLog.mElapsedMillis, procStatesProto); if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) { if (i == 0) { Loading @@ -154,6 +164,20 @@ public class CpuWakeupStats { traceEventBuilder.toString().trim()); } /** * Clean up data for a uid that is being removed. */ public synchronized void onUidRemoved(int uid) { mUidProcStates.delete(uid); } /** * Notes a procstate change for the given uid to maintain the mapping internally. */ public synchronized void noteUidProcessState(int uid, int state) { mUidProcStates.put(uid, state); } /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */ public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime, String rawReason) { Loading Loading @@ -184,8 +208,17 @@ public class CpuWakeupStats { /** Notes a waking activity that could have potentially woken up the CPU. */ public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) { if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) { mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids); if (uids == null) { return; } mReusableUidProcStates.clear(); for (int i = 0; i < uids.length; i++) { mReusableUidProcStates.put(uids[i], mUidProcStates.get(uids[i], ActivityManager.PROCESS_STATE_UNKNOWN)); } if (!attemptAttributionWith(subsystem, elapsedRealtime, mReusableUidProcStates)) { mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, mReusableUidProcStates); } } Loading @@ -196,7 +229,7 @@ public class CpuWakeupStats { return; } SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis); SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis); if (attribution == null) { attribution = new SparseArray<>(); mWakeupAttribution.put(wakeup.mElapsedMillis, attribution); Loading @@ -210,14 +243,14 @@ public class CpuWakeupStats { final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS; final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS; final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem, final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem, startTime, endTime); attribution.put(subsystem, uidsToBlame); } } private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed, int... uids) { SparseIntArray uidProcStates) { final int startIdx = mWakeupEvents.closestIndexOnOrAfter( activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS); final int endIdx = mWakeupEvents.closestIndexOnOrBefore( Loading @@ -233,19 +266,19 @@ public class CpuWakeupStats { if (subsystems.get(subsystem)) { // We don't expect more than one wakeup to be found within such a short window, so // just attribute this one and exit SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get( SparseArray<SparseIntArray> attribution = mWakeupAttribution.get( wakeup.mElapsedMillis); if (attribution == null) { attribution = new SparseArray<>(); mWakeupAttribution.put(wakeup.mElapsedMillis, attribution); } SparseBooleanArray uidsToBlame = attribution.get(subsystem); SparseIntArray uidsToBlame = attribution.get(subsystem); if (uidsToBlame == null) { uidsToBlame = new SparseBooleanArray(uids.length); attribution.put(subsystem, uidsToBlame); attribution.put(subsystem, uidProcStates.clone()); } else { for (int i = 0; i < uidProcStates.size(); i++) { uidsToBlame.put(uidProcStates.keyAt(i), uidProcStates.valueAt(i)); } for (final int uid : uids) { uidsToBlame.put(uid, true); } return true; } Loading @@ -267,6 +300,19 @@ public class CpuWakeupStats { mRecentWakingActivity.dump(pw, nowElapsed); pw.println(); pw.println("Current proc-state map (" + mUidProcStates.size() + "):"); pw.increaseIndent(); for (int i = 0; i < mUidProcStates.size(); i++) { if (i > 0) { pw.print(", "); } UserHandle.formatUid(pw, mUidProcStates.keyAt(i)); pw.print(":" + ActivityManager.procStateToString(mUidProcStates.valueAt(i))); } pw.println(); pw.decreaseIndent(); pw.println(); final SparseLongArray attributionStats = new SparseLongArray(); pw.println("Wakeup events:"); pw.increaseIndent(); Loading @@ -278,7 +324,7 @@ public class CpuWakeupStats { final Wakeup wakeup = mWakeupEvents.valueAt(i); pw.println(wakeup); pw.print("Attribution: "); final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get( final SparseArray<SparseIntArray> attribution = mWakeupAttribution.get( wakeup.mElapsedMillis); if (attribution == null) { pw.println("N/A"); Loading @@ -292,15 +338,17 @@ public class CpuWakeupStats { int attributed = IntPair.first(counters); final int total = IntPair.second(counters) + 1; pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx))); pw.print(", uids: ["); final SparseBooleanArray uids = attribution.valueAt(subsystemIdx); if (uids != null) { for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) { pw.print(subsystemToString(attribution.keyAt(subsystemIdx))); pw.print(" ["); final SparseIntArray uidProcStates = attribution.valueAt(subsystemIdx); if (uidProcStates != null) { for (int uidIdx = 0; uidIdx < uidProcStates.size(); uidIdx++) { if (uidIdx > 0) { pw.print(", "); } UserHandle.formatUid(pw, uids.keyAt(uidIdx)); UserHandle.formatUid(pw, uidProcStates.keyAt(uidIdx)); pw.print(" " + ActivityManager.procStateToString( uidProcStates.valueAt(uidIdx))); } attributed++; } Loading Loading @@ -330,28 +378,38 @@ public class CpuWakeupStats { pw.println(); } /** * This class stores recent unattributed activity history per subsystem. * The activity is stored as a mapping of subsystem to timestamp to uid to procstate. */ private static final class WakingActivityHistory { private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10); private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity = private SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>(); void recordActivity(int subsystem, long elapsedRealtime, int... uids) { if (uids == null) { void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) { if (uidProcStates == null) { return; } TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem); TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem); if (wakingActivity == null) { wakingActivity = new TimeSparseArray<>(); mWakingActivity.put(subsystem, wakingActivity); } SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime); final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime); if (uidsToBlame == null) { uidsToBlame = new SparseBooleanArray(uids.length); wakingActivity.put(elapsedRealtime, uidsToBlame); wakingActivity.put(elapsedRealtime, uidProcStates.clone()); } else { for (int i = 0; i < uidProcStates.size(); i++) { final int uid = uidProcStates.keyAt(i); // Just in case there are duplicate uids reported with the same timestamp, // keep the processState which was reported first. if (uidsToBlame.indexOfKey(uid) < 0) { uidsToBlame.put(uid, uidProcStates.valueAt(i)); } for (int i = 0; i < uids.length; i++) { uidsToBlame.put(uids[i], true); } wakingActivity.put(elapsedRealtime, uidsToBlame); } // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS. // Note that the last activity is always present, even if it occurred before Loading @@ -365,7 +423,7 @@ public class CpuWakeupStats { void clearAllBefore(long elapsedRealtime) { for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) { final TimeSparseArray<SparseBooleanArray> activityPerSubsystem = final TimeSparseArray<SparseIntArray> activityPerSubsystem = mWakingActivity.valueAt(subsystemIdx); final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime); for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) { Loading @@ -377,20 +435,20 @@ public class CpuWakeupStats { } } SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) { final SparseBooleanArray uidsToReturn = new SparseBooleanArray(); SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) { final SparseIntArray uidsToReturn = new SparseIntArray(); final TimeSparseArray<SparseBooleanArray> activityForSubsystem = final TimeSparseArray<SparseIntArray> activityForSubsystem = mWakingActivity.get(subsystem); if (activityForSubsystem != null) { final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed); final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed); for (int i = endIdx; i >= startIdx; i--) { final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i); final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i); for (int j = 0; j < uidsForTime.size(); j++) { if (uidsForTime.valueAt(j)) { uidsToReturn.put(uidsForTime.keyAt(j), true); } // In case the same uid appears in different uidsForTime maps, there is no // good way to choose one processState, so just arbitrarily pick any. uidsToReturn.put(uidsForTime.keyAt(j), uidsForTime.valueAt(j)); } } // More efficient to remove in a separate loop as it avoids repeatedly calling gc(). Loading @@ -409,25 +467,23 @@ public class CpuWakeupStats { pw.increaseIndent(); for (int i = 0; i < mWakingActivity.size(); i++) { pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":"); final LongSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.valueAt(i); final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i); if (wakingActivity == null) { continue; } pw.increaseIndent(); for (int j = wakingActivity.size() - 1; j >= 0; j--) { TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw); final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j); final SparseIntArray uidsToBlame = wakingActivity.valueAt(j); if (uidsToBlame == null) { pw.println(); continue; } pw.print(": "); for (int k = 0; k < uidsToBlame.size(); k++) { if (uidsToBlame.valueAt(k)) { UserHandle.formatUid(pw, uidsToBlame.keyAt(k)); pw.print(", "); } pw.print(" [" + ActivityManager.procStateToString(uidsToBlame.valueAt(k))); pw.print("], "); } pw.println(); } Loading services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java +98 −27 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/am/BatteryStatsService.java +2 −0 Original line number Diff line number Diff line Loading @@ -654,6 +654,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); mHandler.post(() -> { mCpuWakeupStats.onUidRemoved(uid); synchronized (mStats) { mStats.removeUidStatsLocked(uid, elapsedRealtime); } Loading Loading @@ -764,6 +765,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { mCpuWakeupStats.noteUidProcessState(uid, state); synchronized (mStats) { mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime); } Loading
services/core/java/com/android/server/power/stats/CpuWakeupStats.java +115 −59 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN; import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI; import android.app.ActivityManager; import android.content.Context; import android.os.Handler; import android.os.HandlerExecutor; Loading @@ -27,11 +28,10 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeSparseArray; import android.util.TimeUtils; Loading Loading @@ -59,7 +59,7 @@ public class CpuWakeupStats { private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution"; @VisibleForTesting static final long WAKEUP_REASON_HALF_WINDOW_MS = 500; private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2); private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30); private final Handler mHandler; private final IrqDeviceMap mIrqDeviceMap; Loading @@ -69,10 +69,15 @@ public class CpuWakeupStats { @VisibleForTesting final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>(); /* Maps timestamp -> {subsystem -> {uid -> procState}} */ @VisibleForTesting final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution = final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution = new TimeSparseArray<>(); final SparseIntArray mUidProcStates = new SparseIntArray(); private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4); public CpuWakeupStats(Context context, int mapRes, Handler handler) { mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes); mHandler = handler; Loading Loading @@ -102,13 +107,14 @@ public class CpuWakeupStats { FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN, FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN, null, wakeupToLog.mElapsedMillis); wakeupToLog.mElapsedMillis, null); Trace.instantForTrack(Trace.TRACE_TAG_POWER, TRACE_TRACK_WAKEUP_ATTRIBUTION, wakeupToLog.mElapsedMillis + " --"); return; } final SparseArray<SparseBooleanArray> wakeupAttribution = mWakeupAttribution.get( final SparseArray<SparseIntArray> wakeupAttribution = mWakeupAttribution.get( wakeupToLog.mElapsedMillis); if (wakeupAttribution == null) { // This is not expected but can theoretically happen in extreme situations, e.g. if we Loading @@ -121,24 +127,28 @@ public class CpuWakeupStats { for (int i = 0; i < wakeupAttribution.size(); i++) { final int subsystem = wakeupAttribution.keyAt(i); final SparseBooleanArray uidMap = wakeupAttribution.valueAt(i); final SparseIntArray uidProcStates = wakeupAttribution.valueAt(i); final int[] uids; if (uidMap == null || uidMap.size() == 0) { uids = new int[0]; final int[] procStatesProto; if (uidProcStates == null || uidProcStates.size() == 0) { uids = procStatesProto = new int[0]; } else { final IntArray tmp = new IntArray(uidMap.size()); for (int j = 0; j < uidMap.size(); j++) { if (uidMap.valueAt(j)) { tmp.add(uidMap.keyAt(j)); } final int numUids = uidProcStates.size(); uids = new int[numUids]; procStatesProto = new int[numUids]; for (int j = 0; j < numUids; j++) { uids[j] = uidProcStates.keyAt(j); procStatesProto[j] = ActivityManager.processStateAmToProto( uidProcStates.valueAt(j)); } uids = tmp.toArray(); } FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED, FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ, subsystemToStatsReason(subsystem), uids, wakeupToLog.mElapsedMillis); wakeupToLog.mElapsedMillis, procStatesProto); if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) { if (i == 0) { Loading @@ -154,6 +164,20 @@ public class CpuWakeupStats { traceEventBuilder.toString().trim()); } /** * Clean up data for a uid that is being removed. */ public synchronized void onUidRemoved(int uid) { mUidProcStates.delete(uid); } /** * Notes a procstate change for the given uid to maintain the mapping internally. */ public synchronized void noteUidProcessState(int uid, int state) { mUidProcStates.put(uid, state); } /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */ public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime, String rawReason) { Loading Loading @@ -184,8 +208,17 @@ public class CpuWakeupStats { /** Notes a waking activity that could have potentially woken up the CPU. */ public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) { if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) { mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids); if (uids == null) { return; } mReusableUidProcStates.clear(); for (int i = 0; i < uids.length; i++) { mReusableUidProcStates.put(uids[i], mUidProcStates.get(uids[i], ActivityManager.PROCESS_STATE_UNKNOWN)); } if (!attemptAttributionWith(subsystem, elapsedRealtime, mReusableUidProcStates)) { mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, mReusableUidProcStates); } } Loading @@ -196,7 +229,7 @@ public class CpuWakeupStats { return; } SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis); SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis); if (attribution == null) { attribution = new SparseArray<>(); mWakeupAttribution.put(wakeup.mElapsedMillis, attribution); Loading @@ -210,14 +243,14 @@ public class CpuWakeupStats { final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS; final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS; final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem, final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem, startTime, endTime); attribution.put(subsystem, uidsToBlame); } } private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed, int... uids) { SparseIntArray uidProcStates) { final int startIdx = mWakeupEvents.closestIndexOnOrAfter( activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS); final int endIdx = mWakeupEvents.closestIndexOnOrBefore( Loading @@ -233,19 +266,19 @@ public class CpuWakeupStats { if (subsystems.get(subsystem)) { // We don't expect more than one wakeup to be found within such a short window, so // just attribute this one and exit SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get( SparseArray<SparseIntArray> attribution = mWakeupAttribution.get( wakeup.mElapsedMillis); if (attribution == null) { attribution = new SparseArray<>(); mWakeupAttribution.put(wakeup.mElapsedMillis, attribution); } SparseBooleanArray uidsToBlame = attribution.get(subsystem); SparseIntArray uidsToBlame = attribution.get(subsystem); if (uidsToBlame == null) { uidsToBlame = new SparseBooleanArray(uids.length); attribution.put(subsystem, uidsToBlame); attribution.put(subsystem, uidProcStates.clone()); } else { for (int i = 0; i < uidProcStates.size(); i++) { uidsToBlame.put(uidProcStates.keyAt(i), uidProcStates.valueAt(i)); } for (final int uid : uids) { uidsToBlame.put(uid, true); } return true; } Loading @@ -267,6 +300,19 @@ public class CpuWakeupStats { mRecentWakingActivity.dump(pw, nowElapsed); pw.println(); pw.println("Current proc-state map (" + mUidProcStates.size() + "):"); pw.increaseIndent(); for (int i = 0; i < mUidProcStates.size(); i++) { if (i > 0) { pw.print(", "); } UserHandle.formatUid(pw, mUidProcStates.keyAt(i)); pw.print(":" + ActivityManager.procStateToString(mUidProcStates.valueAt(i))); } pw.println(); pw.decreaseIndent(); pw.println(); final SparseLongArray attributionStats = new SparseLongArray(); pw.println("Wakeup events:"); pw.increaseIndent(); Loading @@ -278,7 +324,7 @@ public class CpuWakeupStats { final Wakeup wakeup = mWakeupEvents.valueAt(i); pw.println(wakeup); pw.print("Attribution: "); final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get( final SparseArray<SparseIntArray> attribution = mWakeupAttribution.get( wakeup.mElapsedMillis); if (attribution == null) { pw.println("N/A"); Loading @@ -292,15 +338,17 @@ public class CpuWakeupStats { int attributed = IntPair.first(counters); final int total = IntPair.second(counters) + 1; pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx))); pw.print(", uids: ["); final SparseBooleanArray uids = attribution.valueAt(subsystemIdx); if (uids != null) { for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) { pw.print(subsystemToString(attribution.keyAt(subsystemIdx))); pw.print(" ["); final SparseIntArray uidProcStates = attribution.valueAt(subsystemIdx); if (uidProcStates != null) { for (int uidIdx = 0; uidIdx < uidProcStates.size(); uidIdx++) { if (uidIdx > 0) { pw.print(", "); } UserHandle.formatUid(pw, uids.keyAt(uidIdx)); UserHandle.formatUid(pw, uidProcStates.keyAt(uidIdx)); pw.print(" " + ActivityManager.procStateToString( uidProcStates.valueAt(uidIdx))); } attributed++; } Loading Loading @@ -330,28 +378,38 @@ public class CpuWakeupStats { pw.println(); } /** * This class stores recent unattributed activity history per subsystem. * The activity is stored as a mapping of subsystem to timestamp to uid to procstate. */ private static final class WakingActivityHistory { private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10); private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity = private SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>(); void recordActivity(int subsystem, long elapsedRealtime, int... uids) { if (uids == null) { void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) { if (uidProcStates == null) { return; } TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem); TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem); if (wakingActivity == null) { wakingActivity = new TimeSparseArray<>(); mWakingActivity.put(subsystem, wakingActivity); } SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime); final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime); if (uidsToBlame == null) { uidsToBlame = new SparseBooleanArray(uids.length); wakingActivity.put(elapsedRealtime, uidsToBlame); wakingActivity.put(elapsedRealtime, uidProcStates.clone()); } else { for (int i = 0; i < uidProcStates.size(); i++) { final int uid = uidProcStates.keyAt(i); // Just in case there are duplicate uids reported with the same timestamp, // keep the processState which was reported first. if (uidsToBlame.indexOfKey(uid) < 0) { uidsToBlame.put(uid, uidProcStates.valueAt(i)); } for (int i = 0; i < uids.length; i++) { uidsToBlame.put(uids[i], true); } wakingActivity.put(elapsedRealtime, uidsToBlame); } // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS. // Note that the last activity is always present, even if it occurred before Loading @@ -365,7 +423,7 @@ public class CpuWakeupStats { void clearAllBefore(long elapsedRealtime) { for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) { final TimeSparseArray<SparseBooleanArray> activityPerSubsystem = final TimeSparseArray<SparseIntArray> activityPerSubsystem = mWakingActivity.valueAt(subsystemIdx); final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime); for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) { Loading @@ -377,20 +435,20 @@ public class CpuWakeupStats { } } SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) { final SparseBooleanArray uidsToReturn = new SparseBooleanArray(); SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) { final SparseIntArray uidsToReturn = new SparseIntArray(); final TimeSparseArray<SparseBooleanArray> activityForSubsystem = final TimeSparseArray<SparseIntArray> activityForSubsystem = mWakingActivity.get(subsystem); if (activityForSubsystem != null) { final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed); final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed); for (int i = endIdx; i >= startIdx; i--) { final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i); final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i); for (int j = 0; j < uidsForTime.size(); j++) { if (uidsForTime.valueAt(j)) { uidsToReturn.put(uidsForTime.keyAt(j), true); } // In case the same uid appears in different uidsForTime maps, there is no // good way to choose one processState, so just arbitrarily pick any. uidsToReturn.put(uidsForTime.keyAt(j), uidsForTime.valueAt(j)); } } // More efficient to remove in a separate loop as it avoids repeatedly calling gc(). Loading @@ -409,25 +467,23 @@ public class CpuWakeupStats { pw.increaseIndent(); for (int i = 0; i < mWakingActivity.size(); i++) { pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":"); final LongSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.valueAt(i); final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i); if (wakingActivity == null) { continue; } pw.increaseIndent(); for (int j = wakingActivity.size() - 1; j >= 0; j--) { TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw); final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j); final SparseIntArray uidsToBlame = wakingActivity.valueAt(j); if (uidsToBlame == null) { pw.println(); continue; } pw.print(": "); for (int k = 0; k < uidsToBlame.size(); k++) { if (uidsToBlame.valueAt(k)) { UserHandle.formatUid(pw, uidsToBlame.keyAt(k)); pw.print(", "); } pw.print(" [" + ActivityManager.procStateToString(uidsToBlame.valueAt(k))); pw.print("], "); } pw.println(); } Loading
services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java +98 −27 File changed.Preview size limit exceeded, changes collapsed. Show changes