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

Commit 4a725004 authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Capture method tracing for window test

It will generate method profiling trace data on the last 2
iterations that won't be counted in result metrics.

Bug: 131727899
Test: atest WindowAddRemovePerfTest

Change-Id: Ifd6f594481785deed2ebeb6e1d198f6f8728d3f4
parent c3423e23
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -25,4 +25,9 @@
        <option name="package" value="com.android.perftests.core" />
        <option name="hidden-api-checks" value="false"/>
    </test>

    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
        <option name="directory-keys" value="/data/local/CorePerfTests" />
        <option name="collect-on-run-ended-only" value="true" />
    </metrics_collector>
</configuration>
+21 −2
Original line number Diff line number Diff line
@@ -44,7 +44,11 @@ import org.junit.Rule;
import org.junit.Test;

@LargeTest
public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase {
public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
        implements ManualBenchmarkState.CustomizedIterationListener {

    private static final int PROFILED_ITERATIONS = 2;

    @Rule
    public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();

@@ -59,10 +63,24 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase {
        sUiAutomation.dropShellPermissionIdentity();
    }

    /** The last {@link #PROFILED_ITERATIONS} will provide the information of method profiling. */
    @Override
    public void onStart(int iteration) {
        startProfiling(WindowAddRemovePerfTest.class.getSimpleName()
                + "_MethodTracing_" + iteration + ".trace");
    }

    @Override
    public void onFinished(int iteration) {
        stopProfiling();
    }

    @Test
    @ManualBenchmarkTest(warmupDurationNs = TIME_1_S_IN_NS, targetTestDurationNs = TIME_5_S_IN_NS)
    public void testAddRemoveWindow() throws Throwable {
        new TestWindow().runBenchmark(mPerfStatusReporter.getBenchmarkState());
        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        state.setCustomizedIterations(PROFILED_ITERATIONS, this);
        new TestWindow().runBenchmark(state);
    }

    private static class TestWindow extends BaseIWindow {
@@ -102,6 +120,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase {
                state.addExtraResult("remove", elapsedTimeNsOfRemove);

                elapsedTimeNs = elapsedTimeNsOfAdd + elapsedTimeNsOfRemove;
                inputChannel.dispose();
            }
        }
    }
+45 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import android.app.Activity;
import android.app.UiAutomation;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
import android.perftests.utils.PerfTestActivity;

import androidx.test.rule.ActivityTestRule;
@@ -32,6 +33,10 @@ import org.junit.BeforeClass;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class WindowManagerPerfTestBase {
@@ -40,15 +45,53 @@ public class WindowManagerPerfTestBase {
    static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
    static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;

    /**
     * The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
     * is in /data because while enabling method profling of system server, it cannot write the
     * trace to external storage.
     */
    static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");

    @BeforeClass
    public static void setUpOnce() {
        if (!BASE_OUT_PATH.exists()) {
            executeShellCommand("mkdir -p " + BASE_OUT_PATH);
        }
        // In order to be closer to the real use case.
        sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
        sUiAutomation.executeShellCommand("wm dismiss-keyguard");
        executeShellCommand("input keyevent KEYCODE_WAKEUP");
        executeShellCommand("wm dismiss-keyguard");
        getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
    }

    /**
     * Executes shell command with reading the output. It may also used to block until the current
     * command is completed.
     */
    static ByteArrayOutputStream executeShellCommand(String command) {
        final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand(command);
        final byte[] buf = new byte[512];
        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        int bytesRead;
        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
            while ((bytesRead = fis.read(buf)) != -1) {
                bytes.write(buf, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bytes;
    }

    /** Starts method tracing on system server. */
    void startProfiling(String subPath) {
        executeShellCommand("am profile start system " + new File(BASE_OUT_PATH, subPath));
    }

    void stopProfiling() {
        executeShellCommand("am profile stop system");
    }

    /**
     * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
     */
+47 −4
Original line number Diff line number Diff line
@@ -88,6 +88,15 @@ public final class ManualBenchmarkState {
        int[] percentiles() default {};
    }

    /** The interface to receive the events of customized iteration. */
    public interface CustomizedIterationListener {
        /** The customized iteration starts. */
        void onStart(int iteration);

        /** The customized iteration finished. */
        void onFinished(int iteration);
    }

    /** It means the entire {@link StatsReport} is not given. */
    private static final int DEFAULT_STATS_REPORT = -2;

@@ -105,7 +114,8 @@ public final class ManualBenchmarkState {
    private static final int NOT_STARTED = 0;  // The benchmark has not started yet.
    private static final int WARMUP = 1; // The benchmark is warming up.
    private static final int RUNNING = 2;  // The benchmark is running.
    private static final int FINISHED = 3;  // The benchmark has stopped.
    private static final int RUNNING_CUSTOMIZED = 3;  // Running for customized measurement.
    private static final int FINISHED = 4;  // The benchmark has stopped.

    private int mState = NOT_STARTED;  // Current benchmark state.

@@ -116,6 +126,14 @@ public final class ManualBenchmarkState {

    private int mMaxIterations = 0;

    /**
     * Additinal iteration that used to apply customized measurement. The result during these
     * iterations won't be counted into {@link #mStats}.
     */
    private int mMaxCustomizedIterations;
    private int mCustomizedIterations;
    private CustomizedIterationListener mCustomizedIterationListener;

    // Individual duration in nano seconds.
    private ArrayList<Long> mResults = new ArrayList<>();

@@ -189,10 +207,25 @@ public final class ManualBenchmarkState {
                final boolean keepRunning = mResults.size() < mMaxIterations;
                if (!keepRunning) {
                    mStats = new Stats(mResults);
                    if (mMaxCustomizedIterations > 0 && mCustomizedIterationListener != null) {
                        mState = RUNNING_CUSTOMIZED;
                        mCustomizedIterationListener.onStart(mCustomizedIterations);
                        return true;
                    }
                    mState = FINISHED;
                }
                return keepRunning;
            }
            case RUNNING_CUSTOMIZED: {
                mCustomizedIterationListener.onFinished(mCustomizedIterations);
                mCustomizedIterations++;
                if (mCustomizedIterations >= mMaxCustomizedIterations) {
                    mState = FINISHED;
                    return false;
                }
                mCustomizedIterationListener.onStart(mCustomizedIterations);
                return true;
            }
            case FINISHED:
                throw new IllegalStateException("The benchmark has finished.");
            default:
@@ -210,11 +243,21 @@ public final class ManualBenchmarkState {
    }

    /**
     * Adds additional result while this benchmark isn't warming up. It is used when a sequence of
     * operations is executed consecutively, the duration of each operation can also be recorded.
     * This is used to run the benchmark with more information by enabling some debug mechanism but
     * we don't want to account the special runs (slower) in the stats report.
     */
    public void setCustomizedIterations(int iterations, CustomizedIterationListener listener) {
        mMaxCustomizedIterations = iterations;
        mCustomizedIterationListener = listener;
    }

    /**
     * Adds additional result while this benchmark isn't warming up or running in customized state.
     * It is used when a sequence of operations is executed consecutively, the duration of each
     * operation can also be recorded.
     */
    public void addExtraResult(String key, long duration) {
        if (isWarmingUp()) {
        if (isWarmingUp() || mState == RUNNING_CUSTOMIZED) {
            return;
        }
        if (mExtraResults == null) {