Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 796ea6e5 authored by Yasin Kilicdere's avatar Yasin Kilicdere
Browse files

Wait for CPU idle between multiuser perf test iterations.

Bug: 342399089
Bug: 340812874
Test: atest UserLifecycleTests
Flag: None
Change-Id: I91579d8af35b4a7df76d6bc1bcfad328c107d7c2
parent b2b0c3af
Loading
Loading
Loading
Loading
+61 −5
Original line number Diff line number Diff line
@@ -19,13 +19,16 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.os.SystemClock;
import android.perftests.utils.ShellHelper;
import android.util.Log;

import java.util.ArrayList;
import java.util.concurrent.TimeoutException;

// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
public class BenchmarkRunner {

    private static final long COOL_OFF_PERIOD_MS = 1000;
    private static final String TAG = BenchmarkRunner.class.getSimpleName();
    private static final int TIMEOUT_IN_SECONDS = 45;
    private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90;

    private static final int NUM_ITERATIONS = 4;

@@ -79,8 +82,7 @@ public class BenchmarkRunner {
    }

    private void prepareForNextRun() {
        SystemClock.sleep(COOL_OFF_PERIOD_MS);
        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
        waitCoolDownPeriod();
        mStartTimeNs = System.nanoTime();
        mPausedDurationNs = 0;
    }
@@ -102,7 +104,7 @@ public class BenchmarkRunner {
     * to avoid unnecessary waiting.
     */
    public void resumeTiming() {
        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
        waitCoolDownPeriod();
        resumeTimer();
    }

@@ -162,4 +164,58 @@ public class BenchmarkRunner {
        }
        return null;
    }

    /** Waits for the CPU cores and the broadcast queue to be idle. */
    public void waitCoolDownPeriod() {
        waitForCpuIdle();
        waitForBroadcastIdle();
    }

    private void waitForBroadcastIdle() {
        try {
            ShellHelper.runShellCommandWithTimeout(
                    "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECONDS);
        } catch (TimeoutException e) {
            Log.e(TAG, "Ending waitForBroadcastIdle because it didn't finish in "
                    + TIMEOUT_IN_SECONDS + " seconds", e);
        }
    }

    private void waitForCpuIdle() {
        int count = 0;
        int idleCpuPercentage;
        while (count++ < TIMEOUT_IN_SECONDS) {
            idleCpuPercentage = getIdleCpuPercentage();
            Log.d(TAG, "Waiting for CPU idle #" + count + "=" + idleCpuPercentage + "%");
            if (idleCpuPercentage > CPU_IDLE_THRESHOLD_PERCENTAGE) {
                return;
            }
            SystemClock.sleep(1000);
        }
        Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in "
                + TIMEOUT_IN_SECONDS + " seconds");
    }

    private int getIdleCpuPercentage() {
        String output = ShellHelper.runShellCommand("top -m 1 -n 1");

        String[] tokens = output.split("\\s+");

        float totalCpu = -1;
        float idleCpu = -1;
        for (String token : tokens) {
            if (token.contains("%cpu")) {
                totalCpu = Float.parseFloat(token.split("%")[0]);
            } else if (token.contains("%idle")) {
                idleCpu = Float.parseFloat(token.split("%")[0]);
            }
        }

        if (totalCpu < 0 || idleCpu < 0) {
            Log.e(TAG, "Could not get idle cpu percentage, output=" + output);
            return -1;
        }

        return (int) (100 * idleCpu / totalCpu);
    }
}
 No newline at end of file
+10 −91
Original line number Diff line number Diff line
@@ -188,21 +188,6 @@ public class UserLifecycleTests {
        }
    }

    /** Tests creating a new user, with wait times between iterations. */
    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
    public void createUser_realistic() throws RemoteException {
        while (mRunner.keepRunning()) {
            Log.i(TAG, "Starting timer");
            final int userId = createUserNoFlags();

            mRunner.pauseTiming();
            Log.i(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }

    /** Tests creating and starting a new user. */
    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
    public void createAndStartUser() throws RemoteException {
@@ -239,7 +224,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -254,7 +238,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            final int userId = createUserNoFlags();

            waitForBroadcastIdle();
            runThenWaitForBroadcasts(userId, () -> {
                mRunner.resumeTiming();
                Log.i(TAG, "Starting timer");
@@ -309,9 +292,6 @@ public class UserLifecycleTests {

            preStartUser(userId, numberOfIterationsToSkip);

            waitForBroadcastIdle();
            waitCoolDownPeriod();

            runThenWaitForBroadcasts(userId, () -> {
                mRunner.resumeTiming();
                Log.i(TAG, "Starting timer");
@@ -353,9 +333,6 @@ public class UserLifecycleTests {
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();

            waitForBroadcastIdle();
            waitCoolDownPeriod();

            runThenWaitForBroadcasts(userId, () -> {
                mRunner.resumeTiming();
                Log.i(TAG, "Starting timer");
@@ -420,7 +397,6 @@ public class UserLifecycleTests {
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();

            waitCoolDownPeriod();
            mRunner.resumeTiming();
            Log.i(TAG, "Starting timer");

@@ -454,7 +430,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -466,6 +441,7 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            final int startUser = mAm.getCurrentUser();
            final int userId = createUserNoFlags();

            mRunner.resumeTiming();
            Log.i(TAG, "Starting timer");

@@ -479,27 +455,6 @@ public class UserLifecycleTests {
        }
    }

    /** Tests switching to an uninitialized user with wait times between iterations. */
    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
    public void switchUser_realistic() throws Exception {
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            final int startUser = ActivityManager.getCurrentUser();
            final int userId = createUserNoFlags();
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

            switchUser(userId);

            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            switchUserNoCheck(startUser);
            removeUser(userId);
            mRunner.resumeTimingForNextIteration();
        }
    }

    /** Tests switching to a previously-started, but no-longer-running, user. */
    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
    public void switchUser_stopped() throws RemoteException {
@@ -507,6 +462,7 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            final int startUser = mAm.getCurrentUser();
            final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);

            mRunner.resumeTiming();
            Log.i(TAG, "Starting timer");

@@ -536,7 +492,6 @@ public class UserLifecycleTests {

        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

@@ -562,7 +517,6 @@ public class UserLifecycleTests {
                /* useStaticWallpaper */true);
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

@@ -606,7 +560,6 @@ public class UserLifecycleTests {
        final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

@@ -614,7 +567,6 @@ public class UserLifecycleTests {

            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            waitForBroadcastIdle();
            switchUserNoCheck(startUser);
            mRunner.resumeTimingForNextIteration();
        }
@@ -631,7 +583,6 @@ public class UserLifecycleTests {
                /* useStaticWallpaper */ true);
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

@@ -639,7 +590,6 @@ public class UserLifecycleTests {

            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            waitForBroadcastIdle();
            switchUserNoCheck(startUser);
            mRunner.resumeTimingForNextIteration();
        }
@@ -675,13 +625,11 @@ public class UserLifecycleTests {
    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
    public void stopUser_realistic() throws RemoteException {
        final int userId = createUserNoFlags();
        waitCoolDownPeriod();
        while (mRunner.keepRunning()) {
            mRunner.pauseTiming();
            runThenWaitForBroadcasts(userId, ()-> {
                mIam.startUserInBackground(userId);
            }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
            waitCoolDownPeriod();
            Log.d(TAG, "Starting timer");
            mRunner.resumeTiming();

@@ -703,7 +651,7 @@ public class UserLifecycleTests {
            final int startUser = mAm.getCurrentUser();
            final int userId = createUserNoFlags();

            waitForBroadcastIdle();
            mRunner.waitCoolDownPeriod();
            mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                mRunner.resumeTiming();
                Log.i(TAG, "Starting timer");
@@ -726,7 +674,7 @@ public class UserLifecycleTests {
            final int startUser = ActivityManager.getCurrentUser();
            final int userId = createUserNoFlags();

            waitCoolDownPeriod();
            mRunner.waitCoolDownPeriod();
            mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                mRunner.resumeTiming();
                Log.d(TAG, "Starting timer");
@@ -752,7 +700,7 @@ public class UserLifecycleTests {
                switchUser(userId);
            }, Intent.ACTION_MEDIA_MOUNTED);

            waitForBroadcastIdle();
            mRunner.waitCoolDownPeriod();
            mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                runThenWaitForBroadcasts(userId, () -> {
                    mRunner.resumeTiming();
@@ -781,7 +729,7 @@ public class UserLifecycleTests {
                switchUser(userId);
            }, Intent.ACTION_MEDIA_MOUNTED);

            waitCoolDownPeriod();
            mRunner.waitCoolDownPeriod();
            mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                runThenWaitForBroadcasts(userId, () -> {
                    mRunner.resumeTiming();
@@ -827,7 +775,6 @@ public class UserLifecycleTests {
            Log.d(TAG, "Stopping timer");
            attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -868,7 +815,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -913,7 +859,6 @@ public class UserLifecycleTests {

            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
        removeUser(userId);
@@ -965,7 +910,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -1030,7 +974,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -1071,7 +1014,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -1124,7 +1066,6 @@ public class UserLifecycleTests {
            mRunner.pauseTiming();
            Log.d(TAG, "Stopping timer");
            removeUser(userId);
            waitCoolDownPeriod();
            mRunner.resumeTimingForNextIteration();
        }
    }
@@ -1164,7 +1105,6 @@ public class UserLifecycleTests {
            runThenWaitForBroadcasts(userId, () -> {
                startUserInBackgroundAndWaitForUnlock(userId);
            }, Intent.ACTION_MEDIA_MOUNTED);
            waitCoolDownPeriod();
            mRunner.resumeTiming();
            Log.d(TAG, "Starting timer");

@@ -1280,6 +1220,7 @@ public class UserLifecycleTests {
     * If lack of success should fail the test, use {@link #switchUser(int)} instead.
     */
    private boolean switchUserNoCheck(int userId) throws RemoteException {
        mRunner.waitCoolDownPeriod();
        final boolean[] success = {true};
        mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> {
            mAm.switchUser(userId);
@@ -1296,7 +1237,7 @@ public class UserLifecycleTests {
     */
    private void stopUserAfterWaitingForBroadcastIdle(int userId)
            throws RemoteException {
        waitForBroadcastIdle();
        mRunner.waitCoolDownPeriod();
        stopUser(userId);
    }

@@ -1438,6 +1379,8 @@ public class UserLifecycleTests {
     */
    private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
            String... actions) {
        mRunner.waitCoolDownPeriod();

        final String unreceivedAction =
                mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);

@@ -1538,28 +1481,4 @@ public class UserLifecycleTests {
        assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
        return TextUtils.firstNotEmpty(oldValue, "invalid");
    }

    private void waitForBroadcastIdle() {
        try {
            ShellHelper.runShellCommandWithTimeout(
                    "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
        } catch (TimeoutException e) {
            Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
        }
    }

    private void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            // Ignore
        }
    }

    private void waitCoolDownPeriod() {
        // Heuristic value based on local tests. Stability increased compared to no waiting.
        final int tenSeconds = 1000 * 10;
        waitForBroadcastIdle();
        sleep(tenSeconds);
    }
}