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

Commit d6d30bf7 authored by Wu Ahan's avatar Wu Ahan Committed by Android (Google) Code Review
Browse files

Merge "Support surface only instrumentation" into sc-v2-dev

parents c3e67055 7141ab26
Loading
Loading
Loading
Loading
+95 −64
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.view.ThreadedRenderer;
import android.view.ViewRootImpl;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;

@@ -69,6 +70,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    static final int REASON_CANCEL_NORMAL = 16;
    static final int REASON_CANCEL_NOT_BEGUN = 17;
    static final int REASON_CANCEL_SAME_VSYNC = 18;
    static final int REASON_CANCEL_TIMEOUT = 19;

    /** @hide */
    @IntDef({
@@ -97,6 +99,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    private final Handler mHandler;
    private final ChoreographerWrapper mChoreographer;

    @VisibleForTesting
    public final boolean mSurfaceOnly;

    private long mBeginVsyncId = INVALID_ID;
    private long mEndVsyncId = INVALID_ID;
    private boolean mMetricsFinalized;
@@ -136,34 +141,47 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
    }

    public FrameTracker(@NonNull Session session, @NonNull Handler handler,
            @NonNull ThreadedRendererWrapper renderer, @NonNull ViewRootWrapper viewRootWrapper,
            @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
            @NonNull SurfaceControlWrapper surfaceControlWrapper,
            @NonNull ChoreographerWrapper choreographer,
            @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
            int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
            @Nullable FrameMetricsWrapper metrics,
            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
            @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
        mSurfaceOnly = config.isSurfaceOnly();
        mSession = session;
        mRendererWrapper = renderer;
        mMetricsWrapper = metrics;
        mViewRoot = viewRootWrapper;
        mHandler = handler;
        mChoreographer = choreographer;
        mSurfaceControlWrapper = surfaceControlWrapper;
        mHandler = handler;
        mObserver = new HardwareRendererObserver(
                this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/);

        // HWUI instrumentation init.
        mRendererWrapper = mSurfaceOnly ? null : renderer;
        mMetricsWrapper = mSurfaceOnly ? null : metrics;
        mViewRoot = mSurfaceOnly ? null : viewRootWrapper;
        mObserver = mSurfaceOnly
                ? null
                : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
                        handler, /* waitForPresentTime= */ false);

        mTraceThresholdMissedFrames = traceThresholdMissedFrames;
        mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
        mListener = listener;

        if (mSurfaceOnly) {
            mSurfaceControl = config.getSurfaceControl();
            mSurfaceChangedCallback = null;
        } else {
            // HWUI instrumentation init.
            // If the surface isn't valid yet, wait until it's created.
        if (viewRootWrapper.getSurfaceControl().isValid()) {
            mSurfaceControl = viewRootWrapper.getSurfaceControl();
        }
            if (mViewRoot.getSurfaceControl().isValid()) {
                mSurfaceControl = mViewRoot.getSurfaceControl();
                mSurfaceChangedCallback = null;
            } else {
                mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
                    @Override
                    public void surfaceCreated(SurfaceControl.Transaction t) {
                        synchronized (FrameTracker.this) {
                            if (mSurfaceControl == null) {
                        mSurfaceControl = viewRootWrapper.getSurfaceControl();
                                mSurfaceControl = mViewRoot.getSurfaceControl();
                                if (mBeginVsyncId != INVALID_ID) {
                                    mSurfaceControlWrapper.addJankStatsListener(
                                            FrameTracker.this, mSurfaceControl);
@@ -180,8 +198,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                    @Override
                    public void surfaceDestroyed() {

                // Wait a while to give the system a chance for the remaining frames to arrive, then
                // force finish the session.
                        // Wait a while to give the system a chance for the remaining
                        // frames to arrive, then force finish the session.
                        mHandler.postDelayed(() -> {
                            synchronized (FrameTracker.this) {
                                if (DEBUG) {
@@ -198,9 +216,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                        }, 50);
                    }
                };

        // This callback has a reference to FrameTracker, remember to remove it to avoid leakage.
        viewRootWrapper.addSurfaceChangedCallback(mSurfaceChangedCallback);
                // This callback has a reference to FrameTracker,
                // remember to remove it to avoid leakage.
                mViewRoot.addSurfaceChangedCallback(mSurfaceChangedCallback);
            }
        }
    }

    /**
@@ -208,16 +228,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     */
    public synchronized void begin() {
        mBeginVsyncId = mChoreographer.getVsyncId() + 1;
        if (mSurfaceControl != null) {
            postTraceStartMarker();
        }
        mRendererWrapper.addObserver(mObserver);
        if (DEBUG) {
            Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
        }
        if (mSurfaceControl != null) {
            postTraceStartMarker();
            mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
        }
        if (!mSurfaceOnly) {
            mRendererWrapper.addObserver(mObserver);
        }
        if (mListener != null) {
            mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
        }
@@ -273,11 +293,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     * Cancel the trace session of the CUJ.
     */
    public synchronized void cancel(@Reasons int reason) {
        mCancelled = true;

        // We don't need to end the trace section if it never begun.
        if (mTracingStarted) {
            Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
        }
        mCancelled = true;

        // Always remove the observers in cancel call to avoid leakage.
        removeObservers();
@@ -377,7 +398,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        for (int i = mJankInfos.size() - 1; i >= 0; i--) {
            JankInfo info = mJankInfos.valueAt(i);
            if (info.frameVsyncId >= mEndVsyncId) {
                if (info.hwuiCallbackFired && info.surfaceControlCallbackFired) {
                if (isLastIndexCandidate(info)) {
                    lastIndex = i;
                }
            } else {
@@ -395,6 +416,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        finish(indexOnOrAfterEnd);
    }

    private boolean isLastIndexCandidate(JankInfo info) {
        return mSurfaceOnly
                ? info.surfaceControlCallbackFired
                : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
    }

    private void finish(int indexOnOrAfterEnd) {

        mMetricsFinalized = true;
@@ -410,7 +437,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener

        for (int i = 0; i <= indexOnOrAfterEnd; i++) {
            JankInfo info = mJankInfos.valueAt(i);
            if (info.isFirstFrame) {
            final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame;
            if (isFirstDrawn) {
                continue;
            }
            if (info.surfaceControlCallbackFired) {
@@ -435,11 +463,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                }
                // TODO (b/174755489): Early latch currently gets fired way too often, so we have
                // to ignore it for now.
                if (!info.hwuiCallbackFired) {
                if (!mSurfaceOnly && !info.hwuiCallbackFired) {
                    Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
                }
            }
            if (info.hwuiCallbackFired) {
            if (!mSurfaceOnly && info.hwuiCallbackFired) {
                maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
                if (!info.surfaceControlCallbackFired) {
                    Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
@@ -462,7 +490,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
        // Trigger perfetto if necessary.
        boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
                && missedFramesCount >= mTraceThresholdMissedFrames;
        boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
        boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
                && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
        if (overMissedFramesThreshold || overFrameTimeThreshold) {
            triggerPerfetto();
@@ -473,7 +501,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
                    mSession.getStatsdInteractionType(),
                    totalFramesCount,
                    missedFramesCount,
                    maxFrameTimeNanos,
                    maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
                    missedSfFramesCount,
                    missedAppFramesCount);
            if (mListener != null) {
@@ -496,12 +524,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
     */
    @VisibleForTesting
    public void removeObservers() {
        mRendererWrapper.removeObserver(mObserver);
        mSurfaceControlWrapper.removeJankStatsListener(this);
        if (!mSurfaceOnly) {
            // HWUI part.
            mRendererWrapper.removeObserver(mObserver);
            if (mSurfaceChangedCallback != null) {
                mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
            }
        }
    }

    /**
     * Trigger the prefetto daemon.
+182 −47

File changed.

Preview size limit exceeded, changes collapsed.

+203 −77

File changed.

Preview size limit exceeded, changes collapsed.

+24 −17
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.internal.jank;

import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;

@@ -25,17 +23,17 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.provider.DeviceConfig;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -43,8 +41,12 @@ import android.view.ViewAttachTestActivity;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;

import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;

import org.junit.Before;
@@ -92,12 +94,15 @@ public class InteractionJankMonitorTest {
        verify(mWorker).start();

        Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
        Configuration config = mock(Configuration.class);
        when(config.isSurfaceOnly()).thenReturn(false);
        FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                new ThreadedRendererWrapper(mView.getThreadedRenderer()),
                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                mock(FrameTracker.ChoreographerWrapper.class),
                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
                /*traceThresholdFrameTimeMillis=*/ -1, null));
                new ViewRootWrapper(mView.getViewRootImpl()),
                new SurfaceControlWrapper(), mock(ChoreographerWrapper.class),
                new FrameMetricsWrapper(),
                /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
                /* FrameTrackerListener */ null, config));
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).triggerPerfetto();
        doNothing().when(tracker).postTraceStartMarker();
@@ -138,28 +143,30 @@ public class InteractionJankMonitorTest {
    public void testBeginCancel() {
        InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));

        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);

        Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
        Configuration config = mock(Configuration.class);
        when(config.isSurfaceOnly()).thenReturn(false);
        FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                new ThreadedRendererWrapper(mView.getThreadedRenderer()),
                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
                mock(FrameTracker.ChoreographerWrapper.class),
                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
                /*traceThresholdFrameTimeMillis=*/ -1, null));
                new ViewRootWrapper(mView.getViewRootImpl()),
                new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class),
                new FrameMetricsWrapper(),
                /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
                /* FrameTrackerListener */ null, config));
        doReturn(tracker).when(monitor).createFrameTracker(any(), any());
        doNothing().when(tracker).triggerPerfetto();
        doNothing().when(tracker).postTraceStartMarker();

        assertThat(monitor.begin(mView, session.getCuj())).isTrue();
        verify(tracker).begin();
        verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(),
                anyLong());
        Runnable runnable = captor.getValue().getCallback();
        verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
        Runnable runnable = captor.getValue();
        assertThat(runnable).isNotNull();
        mWorker.getThreadHandler().removeCallbacks(runnable);
        runnable.run();
        verify(tracker).cancel(FrameTracker.REASON_CANCEL_NORMAL);
        verify(tracker).cancel(FrameTracker.REASON_CANCEL_TIMEOUT);
    }

    @Test
+1 −2
Original line number Diff line number Diff line
@@ -80,8 +80,7 @@ public final class InteractionJankMonitorWrapper {
    public static void begin(View v, @CujType int cujType, long timeout) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
        Configuration.Builder builder =
                new Configuration.Builder(cujType)
                        .setView(v)
                Configuration.Builder.withView(cujType, v)
                        .setTimeout(timeout);
        InteractionJankMonitor.getInstance().begin(builder);
    }
Loading