Merge "Reduce unnecessary overhead in CpuWakeupStats" into udc-dev

This commit is contained in:
Suprabh Shukla
2023-05-16 00:03:00 +00:00
committed by Android (Google) Code Review
4 changed files with 278 additions and 109 deletions

View File

@@ -28,6 +28,8 @@
- Wifi: Use this to denote network traffic that uses the wifi transport.
- Sound_trigger: Use this to denote sound phrase detection, like the ones supported by
SoundTriggerManager.
- Sensor: Use this to denote wakeups due to sensor events.
- Cellular_data: Use this to denote network traffic on the cellular transport.
The overlay should use tags <device> and <subsystem> to describe this mapping in the
following way:

View File

@@ -23,6 +23,7 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
@@ -48,6 +49,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.LongSupplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -62,15 +64,14 @@ public class CpuWakeupStats {
private static final String SUBSYSTEM_SENSOR_STRING = "Sensor";
private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data";
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.SECONDS.toMillis(30);
private final Handler mHandler;
private final IrqDeviceMap mIrqDeviceMap;
@VisibleForTesting
final Config mConfig = new Config();
private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
private final WakingActivityHistory mRecentWakingActivity;
@VisibleForTesting
final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
@@ -85,6 +86,8 @@ public class CpuWakeupStats {
public CpuWakeupStats(Context context, int mapRes, Handler handler) {
mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
mRecentWakingActivity = new WakingActivityHistory(
() -> mConfig.WAKING_ACTIVITY_RETENTION_MS);
mHandler = handler;
}
@@ -202,15 +205,14 @@ public class CpuWakeupStats {
/** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
String rawReason) {
final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime);
final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime,
mIrqDeviceMap);
if (parsedWakeup == null) {
// This wakeup is unsupported for attribution. Exit.
return;
}
mWakeupEvents.put(elapsedRealtime, parsedWakeup);
attemptAttributionFor(parsedWakeup);
// Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order,
// we can delete all history that will not be useful in attributing future wakeups.
mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
// Limit history of wakeups and their attribution to the last retentionDuration. Note that
// the last wakeup and its attribution (if computed) is always stored, even if that wakeup
@@ -244,25 +246,22 @@ public class CpuWakeupStats {
}
private synchronized void attemptAttributionFor(Wakeup wakeup) {
final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
if (subsystems == null) {
// We don't support attribution for this kind of wakeup yet.
return;
}
final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems;
SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
if (attribution == null) {
attribution = new SparseArray<>();
mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
}
final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;
for (int subsystemIdx = 0; subsystemIdx < subsystems.size(); subsystemIdx++) {
final int subsystem = subsystems.keyAt(subsystemIdx);
// Blame all activity that happened WAKEUP_REASON_HALF_WINDOW_MS before or after
// Blame all activity that happened matchingWindowMillis before or after
// the wakeup from each responsible subsystem.
final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
final long startTime = wakeup.mElapsedMillis - matchingWindowMillis;
final long endTime = wakeup.mElapsedMillis + matchingWindowMillis;
final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
startTime, endTime);
@@ -272,18 +271,16 @@ public class CpuWakeupStats {
private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
SparseIntArray uidProcStates) {
final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;
final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
activityElapsed - matchingWindowMillis);
final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS);
activityElapsed + matchingWindowMillis);
for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
final Wakeup wakeup = mWakeupEvents.valueAt(wakeupIdx);
final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
if (subsystems == null) {
// Unsupported for attribution
continue;
}
final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems;
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
@@ -405,11 +402,13 @@ public class CpuWakeupStats {
*/
@VisibleForTesting
static final class WakingActivityHistory {
private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
private LongSupplier mRetentionSupplier;
@VisibleForTesting
final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity =
new SparseArray<>();
final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();
WakingActivityHistory(LongSupplier retentionSupplier) {
mRetentionSupplier = retentionSupplier;
}
void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) {
if (uidProcStates == null) {
@@ -433,27 +432,13 @@ public class CpuWakeupStats {
}
}
}
// 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
// WAKING_ACTIVITY_RETENTION_MS.
// Limit activity history per subsystem to the last retention period as supplied by
// mRetentionSupplier. Note that the last activity is always present, even if it
// occurred before the retention period.
final int endIdx = wakingActivity.closestIndexOnOrBefore(
elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS);
elapsedRealtime - mRetentionSupplier.getAsLong());
for (int i = endIdx; i >= 0; i--) {
wakingActivity.removeAt(endIdx);
}
}
void clearAllBefore(long elapsedRealtime) {
for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
final TimeSparseArray<SparseIntArray> activityPerSubsystem =
mWakingActivity.valueAt(subsystemIdx);
final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
activityPerSubsystem.removeAt(removeIdx);
}
// Generally waking activity is a high frequency occurrence for any subsystem, so we
// don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
// This will leave one TimeSparseArray per subsystem, which are few right now.
wakingActivity.removeAt(i);
}
}
@@ -515,33 +500,6 @@ public class CpuWakeupStats {
}
}
private SparseBooleanArray getResponsibleSubsystemsForWakeup(Wakeup wakeup) {
if (ArrayUtils.isEmpty(wakeup.mDevices)) {
return null;
}
final SparseBooleanArray result = new SparseBooleanArray();
for (final Wakeup.IrqDevice device : wakeup.mDevices) {
final List<String> rawSubsystems = mIrqDeviceMap.getSubsystemsForDevice(device.mDevice);
boolean anyKnownSubsystem = false;
if (rawSubsystems != null) {
for (int i = 0; i < rawSubsystems.size(); i++) {
final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i));
if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) {
// Just in case the xml had arbitrary subsystem names, we want to make sure
// that we only put the known ones into our attribution map.
result.put(subsystem, true);
anyKnownSubsystem = true;
}
}
}
if (!anyKnownSubsystem) {
result.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
}
}
return result;
}
static int stringToKnownSubsystem(String rawSubsystem) {
switch (rawSubsystem) {
case SUBSYSTEM_ALARM_STRING:
@@ -598,15 +556,19 @@ public class CpuWakeupStats {
long mElapsedMillis;
long mUptimeMillis;
IrqDevice[] mDevices;
SparseBooleanArray mResponsibleSubsystems;
private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis,
SparseBooleanArray responsibleSubsystems) {
mType = type;
mDevices = devices;
mElapsedMillis = elapsedMillis;
mUptimeMillis = uptimeMillis;
mResponsibleSubsystems = responsibleSubsystems;
}
static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis,
IrqDeviceMap deviceMap) {
final String[] components = rawReason.split(":");
if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
// Accounting of aborts is not supported yet.
@@ -616,6 +578,7 @@ public class CpuWakeupStats {
int type = TYPE_IRQ;
int parsedDeviceCount = 0;
final IrqDevice[] parsedDevices = new IrqDevice[components.length];
final SparseBooleanArray responsibleSubsystems = new SparseBooleanArray();
for (String component : components) {
final Matcher matcher = sIrqPattern.matcher(component.trim());
@@ -635,13 +598,35 @@ public class CpuWakeupStats {
continue;
}
parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
final List<String> rawSubsystems = deviceMap.getSubsystemsForDevice(device);
boolean anyKnownSubsystem = false;
if (rawSubsystems != null) {
for (int i = 0; i < rawSubsystems.size(); i++) {
final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i));
if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) {
// Just in case the xml had arbitrary subsystem names, we want to
// make sure that we only put the known ones into our map.
responsibleSubsystems.put(subsystem, true);
anyKnownSubsystem = true;
}
}
}
if (!anyKnownSubsystem) {
responsibleSubsystems.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
}
}
}
if (parsedDeviceCount == 0) {
return null;
}
if (responsibleSubsystems.size() == 1 && responsibleSubsystems.get(
CPU_WAKEUP_SUBSYSTEM_UNKNOWN, false)) {
// There is no attributable subsystem here, so we do not support it.
return null;
}
return new Wakeup(type, Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
uptimeMillis);
uptimeMillis, responsibleSubsystems);
}
@Override
@@ -651,6 +636,7 @@ public class CpuWakeupStats {
+ ", mElapsedMillis=" + mElapsedMillis
+ ", mUptimeMillis=" + mUptimeMillis
+ ", mDevices=" + Arrays.toString(mDevices)
+ ", mResponsibleSubsystems=" + mResponsibleSubsystems
+ '}';
}
@@ -672,18 +658,28 @@ public class CpuWakeupStats {
static final class Config implements DeviceConfig.OnPropertiesChangedListener {
static final String KEY_WAKEUP_STATS_RETENTION_MS = "wakeup_stats_retention_ms";
static final String KEY_WAKEUP_MATCHING_WINDOW_MS = "wakeup_matching_window_ms";
static final String KEY_WAKING_ACTIVITY_RETENTION_MS = "waking_activity_retention_ms";
private static final String[] PROPERTY_NAMES = {
KEY_WAKEUP_STATS_RETENTION_MS,
KEY_WAKEUP_MATCHING_WINDOW_MS,
KEY_WAKING_ACTIVITY_RETENTION_MS,
};
static final long DEFAULT_WAKEUP_STATS_RETENTION_MS = TimeUnit.DAYS.toMillis(3);
private static final long DEFAULT_WAKEUP_MATCHING_WINDOW_MS = TimeUnit.SECONDS.toMillis(1);
private static final long DEFAULT_WAKING_ACTIVITY_RETENTION_MS =
TimeUnit.MINUTES.toMillis(5);
/**
* Wakeup stats are retained only for this duration.
*/
public volatile long WAKEUP_STATS_RETENTION_MS = DEFAULT_WAKEUP_STATS_RETENTION_MS;
public volatile long WAKEUP_MATCHING_WINDOW_MS = DEFAULT_WAKEUP_MATCHING_WINDOW_MS;
public volatile long WAKING_ACTIVITY_RETENTION_MS = DEFAULT_WAKING_ACTIVITY_RETENTION_MS;
@SuppressLint("MissingPermission")
void register(Executor executor) {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_STATS,
executor, this);
@@ -702,6 +698,15 @@ public class CpuWakeupStats {
WAKEUP_STATS_RETENTION_MS = properties.getLong(
KEY_WAKEUP_STATS_RETENTION_MS, DEFAULT_WAKEUP_STATS_RETENTION_MS);
break;
case KEY_WAKEUP_MATCHING_WINDOW_MS:
WAKEUP_MATCHING_WINDOW_MS = properties.getLong(
KEY_WAKEUP_MATCHING_WINDOW_MS, DEFAULT_WAKEUP_MATCHING_WINDOW_MS);
break;
case KEY_WAKING_ACTIVITY_RETENTION_MS:
WAKING_ACTIVITY_RETENTION_MS = properties.getLong(
KEY_WAKING_ACTIVITY_RETENTION_MS,
DEFAULT_WAKING_ACTIVITY_RETENTION_MS);
break;
}
}
}
@@ -711,7 +716,19 @@ public class CpuWakeupStats {
pw.increaseIndent();
pw.print(KEY_WAKEUP_STATS_RETENTION_MS, WAKEUP_STATS_RETENTION_MS);
pw.print(KEY_WAKEUP_STATS_RETENTION_MS);
pw.print("=");
TimeUtils.formatDuration(WAKEUP_STATS_RETENTION_MS, pw);
pw.println();
pw.print(KEY_WAKEUP_MATCHING_WINDOW_MS);
pw.print("=");
TimeUtils.formatDuration(WAKEUP_MATCHING_WINDOW_MS, pw);
pw.println();
pw.print(KEY_WAKING_ACTIVITY_RETENTION_MS);
pw.print("=");
TimeUtils.formatDuration(WAKING_ACTIVITY_RETENTION_MS, pw);
pw.println();
pw.decreaseIndent();

View File

@@ -23,8 +23,6 @@ import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
import static com.android.server.power.stats.wakeups.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
@@ -55,7 +53,7 @@ public class CpuWakeupStatsTest {
private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device";
private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device";
private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
private static final String KERNEL_REASON_UNKNOWN_FORMAT = "free-form-reason test.alarm.device";
private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
@@ -85,30 +83,29 @@ public class CpuWakeupStatsTest {
@Test
public void removesOldWakeups() {
// The xml resource doesn't matter for this test.
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1, mHandler);
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long retention = obj.mConfig.WAKEUP_STATS_RETENTION_MS;
final Set<Long> timestamps = new HashSet<>();
final long firstWakeup = 453192;
obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ);
// Reasons do not matter for this test, as long as they map to known subsystems
obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_ALARM_IRQ);
timestamps.add(firstWakeup);
for (int i = 1; i < 1000; i++) {
final long delta = mRandom.nextLong(retention);
if (timestamps.add(firstWakeup + delta)) {
obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ);
obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_SENSOR_IRQ);
}
}
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231,
KERNEL_REASON_UNKNOWN_IRQ);
obj.noteWakeupTimeAndReason(firstWakeup + retention, 231, KERNEL_REASON_WIFI_IRQ);
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
for (int i = 0; i < 100; i++) {
final long now = mRandom.nextLong(retention + 1, 100 * retention);
obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ);
obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_SOUND_TRIGGER_IRQ);
assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
}
}
@@ -201,9 +198,9 @@ public class CpuWakeupStatsTest {
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
@@ -234,9 +231,9 @@ public class CpuWakeupStatsTest {
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3,
TEST_UID_5);
@@ -268,9 +265,9 @@ public class CpuWakeupStatsTest {
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
@@ -300,9 +297,9 @@ public class CpuWakeupStatsTest {
// Alarm activity
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
@@ -311,9 +308,9 @@ public class CpuWakeupStatsTest {
// Wifi activity
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_4);
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_3);
wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_3);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
@@ -347,33 +344,67 @@ public class CpuWakeupStatsTest {
}
@Test
public void unknownIrqAttribution() {
public void unknownIrqSoloIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 92123410;
obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
// Unrelated subsystems, should not be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
TEST_UID_5);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
// Any nearby activity should not end up in the attribution map.
assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
}
@Test
public void unknownWakeupIgnored() {
public void unknownAndWifiAttribution() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 92123410;
populateDefaultProcStates(obj);
obj.noteWakeupTimeAndReason(wakeupTime, 24,
KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_WIFI_IRQ);
// Wifi activity
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_3,
TEST_UID_5);
// Unrelated, should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(2);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(
TEST_PROC_STATE_1);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(
TEST_PROC_STATE_3);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0);
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
TEST_PROC_STATE_5);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isNull();
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isFalse();
}
@Test
public void unknownFormatWakeupIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 72123210;
obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN_FORMAT);
// Should be ignored as this type of wakeup is not known.
assertThat(obj.mWakeupEvents.size()).isEqualTo(0);

View File

@@ -28,8 +28,11 @@ import com.android.server.power.stats.wakeups.CpuWakeupStats.WakingActivityHisto
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.ThreadLocalRandom;
@RunWith(AndroidJUnit4.class)
public class WakingActivityHistoryTest {
private volatile long mTestRetention = 54;
private static boolean areSame(SparseIntArray a, SparseIntArray b) {
if (a == b) {
@@ -55,7 +58,7 @@ public class WakingActivityHistoryTest {
@Test
public void recordActivityAppendsUids() {
final WakingActivityHistory history = new WakingActivityHistory();
final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
final int subsystem = 42;
final long timestamp = 54;
@@ -100,7 +103,7 @@ public class WakingActivityHistoryTest {
@Test
public void recordActivityDoesNotDeleteExistingUids() {
final WakingActivityHistory history = new WakingActivityHistory();
final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
final int subsystem = 42;
long timestamp = 101;
@@ -151,4 +154,120 @@ public class WakingActivityHistoryTest {
assertThat(recordedUids.get(62, -1)).isEqualTo(31);
assertThat(recordedUids.get(85, -1)).isEqualTo(39);
}
@Test
public void removeBetween() {
final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
final int subsystem = 43;
final SparseIntArray uids = new SparseIntArray();
uids.put(1, 17);
uids.put(15, 2);
uids.put(62, 31);
history.recordActivity(subsystem, 123, uids);
uids.put(54, 91);
history.recordActivity(subsystem, 150, uids);
uids.put(101, 32);
uids.delete(1);
history.recordActivity(subsystem, 191, uids);
SparseIntArray removedUids = history.removeBetween(subsystem, 100, 122);
assertThat(removedUids).isNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
removedUids = history.removeBetween(subsystem, 124, 149);
assertThat(removedUids).isNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
removedUids = history.removeBetween(subsystem, 151, 190);
assertThat(removedUids).isNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
removedUids = history.removeBetween(subsystem, 192, 240);
assertThat(removedUids).isNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
// Removing from a different subsystem should do nothing.
removedUids = history.removeBetween(subsystem + 1, 0, 300);
assertThat(removedUids).isNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
removedUids = history.removeBetween(subsystem, 0, 300);
assertThat(removedUids.size()).isEqualTo(5);
assertThat(removedUids.get(1, -1)).isEqualTo(17);
assertThat(removedUids.get(15, -1)).isEqualTo(2);
assertThat(removedUids.get(62, -1)).isEqualTo(31);
assertThat(removedUids.get(54, -1)).isEqualTo(91);
assertThat(removedUids.get(101, -1)).isEqualTo(32);
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0);
history.recordActivity(subsystem, 23, uids);
uids.put(31, 123);
history.recordActivity(subsystem, 49, uids);
uids.put(177, 432);
history.recordActivity(subsystem, 89, uids);
removedUids = history.removeBetween(subsystem, 23, 23);
assertThat(removedUids.size()).isEqualTo(4);
assertThat(removedUids.get(15, -1)).isEqualTo(2);
assertThat(removedUids.get(62, -1)).isEqualTo(31);
assertThat(removedUids.get(54, -1)).isEqualTo(91);
assertThat(removedUids.get(101, -1)).isEqualTo(32);
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(2);
removedUids = history.removeBetween(subsystem, 49, 54);
assertThat(removedUids.size()).isEqualTo(5);
assertThat(removedUids.get(15, -1)).isEqualTo(2);
assertThat(removedUids.get(62, -1)).isEqualTo(31);
assertThat(removedUids.get(54, -1)).isEqualTo(91);
assertThat(removedUids.get(101, -1)).isEqualTo(32);
assertThat(removedUids.get(31, -1)).isEqualTo(123);
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(1);
removedUids = history.removeBetween(subsystem, 23, 89);
assertThat(removedUids.size()).isEqualTo(6);
assertThat(removedUids.get(15, -1)).isEqualTo(2);
assertThat(removedUids.get(62, -1)).isEqualTo(31);
assertThat(removedUids.get(54, -1)).isEqualTo(91);
assertThat(removedUids.get(101, -1)).isEqualTo(32);
assertThat(removedUids.get(31, -1)).isEqualTo(123);
assertThat(removedUids.get(177, -1)).isEqualTo(432);
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0);
}
@Test
public void deletesActivityPastRetention() {
final WakingActivityHistory history = new WakingActivityHistory(() -> mTestRetention);
final int subsystem = 49;
mTestRetention = 454;
final long firstTime = 342;
for (int i = 0; i < mTestRetention; i++) {
history.recordActivity(subsystem, firstTime + i, new SparseIntArray());
}
assertThat(history.mWakingActivity.get(subsystem)).isNotNull();
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention);
history.recordActivity(subsystem, firstTime + mTestRetention + 7, new SparseIntArray());
assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention - 7);
final ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < 100; i++) {
final long time = random.nextLong(firstTime + mTestRetention + 100,
456 * mTestRetention);
history.recordActivity(subsystem, time, new SparseIntArray());
assertThat(history.mWakingActivity.get(subsystem).closestIndexOnOrBefore(
time - mTestRetention)).isLessThan(0);
}
}
}