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

Commit d19d9919 authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Add shell-side transition trace perfetto tracer

Bug: 309630341

Test: adb shell -t perfetto -c - --txt -o /data/misc/perfetto-traces/trace <<EOF
   unique_session_name: transitions_tracing
   buffers: {
       size_kb: 63488
       fill_policy: RING_BUFFER
   }
   data_sources: {
       config {
           name: com.android.wm.shell.transition
       }
   }
   duration_ms: 10000
EOF

Change-Id: If510fabd57fb7a9242235ad0b42d81133b96377f
parent 216586f9
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.tracing.transition;

import android.tracing.perfetto.CreateTlsStateArgs;
import android.tracing.perfetto.DataSource;
import android.tracing.perfetto.DataSourceInstance;
import android.tracing.perfetto.FlushCallbackArguments;
@@ -23,10 +24,14 @@ import android.tracing.perfetto.StartCallbackArguments;
import android.tracing.perfetto.StopCallbackArguments;
import android.util.proto.ProtoInputStream;

import java.util.HashMap;
import java.util.Map;

/**
 * @hide
 */
public class TransitionDataSource extends DataSource {
public class TransitionDataSource
        extends DataSource<DataSourceInstance, TransitionDataSource.TlsState, Void> {
    public static String DATA_SOURCE_NAME = "com.android.wm.shell.transition";

    private final Runnable mOnStartStaticCallback;
@@ -40,6 +45,15 @@ public class TransitionDataSource extends DataSource {
        this.mOnStopStaticCallback = onStop;
    }

    @Override
    protected TlsState createTlsState(CreateTlsStateArgs<DataSourceInstance> args) {
        return new TlsState();
    }

    public class TlsState {
        public final Map<String, Integer> handlerMapping = new HashMap<>();
    }

    @Override
    public DataSourceInstance createInstance(ProtoInputStream configStream, int instanceIndex) {
        return new DataSourceInstance(this, instanceIndex) {
+1 −0
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ android_library {
        "com_android_wm_shell_flags_lib",
        "com.android.window.flags.window-aconfig-java",
        "WindowManager-Shell-proto",
        "perfetto_trace_java_protos",
        "dagger2",
        "jsr330",
    ],
+27 −10
Original line number Diff line number Diff line
@@ -83,6 +83,9 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.tracing.LegacyTransitionTracer;
import com.android.wm.shell.transition.tracing.PerfettoTransitionTracer;
import com.android.wm.shell.transition.tracing.TransitionTracer;
import com.android.wm.shell.util.TransitionUtil;

import java.io.PrintWriter;
@@ -184,7 +187,7 @@ public class Transitions implements RemoteCallable<Transitions>,
    private final ShellController mShellController;
    private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
    private final SleepHandler mSleepHandler = new SleepHandler();
    private final Tracer mTracer = new Tracer();
    private final TransitionTracer mTransitionTracer;
    private boolean mIsRegistered = false;

    /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
@@ -307,6 +310,12 @@ public class Transitions implements RemoteCallable<Transitions>,
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
        shellInit.addInitCallback(this::onInit, this);
        mHomeTransitionObserver = observer;

        if (android.tracing.Flags.perfettoTransitionTracing()) {
            mTransitionTracer = new PerfettoTransitionTracer();
        } else {
            mTransitionTracer = new LegacyTransitionTracer();
        }
    }

    private void onInit() {
@@ -868,7 +877,7 @@ public class Transitions implements RemoteCallable<Transitions>,
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
                + " %s is still animating. Notify the animating transition"
                + " in case they can be merged", ready, playing);
        mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
        mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
        playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
                playing.mToken, (wct) -> onMerged(playing, ready));
    }
@@ -902,7 +911,7 @@ public class Transitions implements RemoteCallable<Transitions>,
        for (int i = 0; i < mObservers.size(); ++i) {
            mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken);
        }
        mTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
        mTransitionTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
        // See if we should merge another transition.
        processReadyQueue(track);
    }
@@ -923,7 +932,7 @@ public class Transitions implements RemoteCallable<Transitions>,
                    active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct));
            if (consumed) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
                mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
                mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
                return;
            }
        }
@@ -948,7 +957,7 @@ public class Transitions implements RemoteCallable<Transitions>,
            if (consumed) {
                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
                        mHandlers.get(i));
                mTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
                mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
                return mHandlers.get(i);
            }
        }
@@ -978,7 +987,7 @@ public class Transitions implements RemoteCallable<Transitions>,
        final Track track = mTracks.get(transition.getTrack());
        transition.mAborted = true;

        mTracer.logAborted(transition.mInfo.getDebugId());
        mTransitionTracer.logAborted(transition.mInfo.getDebugId());

        if (transition.mHandler != null) {
            // Notifies to clean-up the aborted transition.
@@ -1506,12 +1515,18 @@ public class Transitions implements RemoteCallable<Transitions>,
        }
    }


    @Override
    public boolean onShellCommand(String[] args, PrintWriter pw) {
        switch (args[0]) {
            case "tracing": {
                mTracer.onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
                if (!android.tracing.Flags.perfettoTransitionTracing()) {
                    ((LegacyTransitionTracer) mTransitionTracer)
                            .onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
                } else {
                    pw.println("Command not supported. Use the Perfetto command instead to start "
                            + "and stop this trace instead.");
                    return false;
                }
                return true;
            }
            default: {
@@ -1524,8 +1539,10 @@ public class Transitions implements RemoteCallable<Transitions>,

    @Override
    public void printShellCommandHelp(PrintWriter pw, String prefix) {
        if (!android.tracing.Flags.perfettoTransitionTracing()) {
            pw.println(prefix + "tracing");
        mTracer.printShellCommandHelp(pw, prefix + "  ");
            ((LegacyTransitionTracer) mTransitionTracer).printShellCommandHelp(pw, prefix + "  ");
        }
    }

    private void dump(@NonNull PrintWriter pw, String prefix) {
+32 −26
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

package com.android.wm.shell.transition;
package com.android.wm.shell.transition.tracing;

import static android.os.Build.IS_USER;

@@ -29,6 +29,7 @@ import android.util.Log;

import com.android.internal.util.TraceBuffer;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.transition.Transitions;

import com.google.protobuf.nano.MessageNano;

@@ -45,7 +46,8 @@ import java.util.concurrent.TimeUnit;
/**
 * Helper class to collect and dump transition traces.
 */
public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
public class LegacyTransitionTracer
        implements ShellCommandHandler.ShellCommandActionHandler, TransitionTracer {
    private static final int ALWAYS_ON_TRACING_CAPACITY = 15 * 1024; // 15 KB
    private static final int ACTIVE_TRACING_BUFFER_CAPACITY = 5000 * 1024; // 5 MB

@@ -84,9 +86,9 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
            };
    private final TraceBuffer<MessageNano,
            com.android.wm.shell.nano.WmShellTransitionTraceProto,
            com.android.wm.shell.nano.Transition> mTraceBuffer
                    = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider,
                            (proto) -> handleOnEntryRemovedFromTrace(proto));
            com.android.wm.shell.nano.Transition> mTraceBuffer =
                new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider,
                        this::handleOnEntryRemovedFromTrace);
    private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>();

    private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>();
@@ -99,6 +101,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
     * @param transitionId The id of the transition being dispatched.
     * @param handler The handler the transition is being dispatched to.
     */
    @Override
    public void logDispatched(int transitionId, Transitions.TransitionHandler handler) {
        final int handlerId;
        if (mHandlerIds.containsKey(handler)) {
@@ -130,6 +133,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
     *
     * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
     */
    @Override
    public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
        com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
        proto.id = mergeRequestedTransitionId;
@@ -145,6 +149,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
     * @param mergedTransitionId The id of the transition that was merged.
     * @param playingTransitionId The id of the transition the transition was merged into.
     */
    @Override
    public void logMerged(int mergedTransitionId, int playingTransitionId) {
        com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
        proto.id = mergedTransitionId;
@@ -159,6 +164,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
     *
     * @param transitionId The id of the transition that was aborted.
     */
    @Override
    public void logAborted(int transitionId) {
        com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
        proto.id = transitionId;
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.transition.tracing;

import android.internal.perfetto.protos.PerfettoTrace;
import android.os.SystemClock;
import android.tracing.perfetto.DataSourceInstance;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
import android.tracing.perfetto.TracingContext;
import android.tracing.transition.TransitionDataSource;
import android.util.proto.ProtoOutputStream;

import com.android.wm.shell.transition.Transitions;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Helper class to collect and dump transition traces.
 */
public class PerfettoTransitionTracer implements TransitionTracer {
    private final AtomicInteger mActiveTraces = new AtomicInteger(0);
    private final TransitionDataSource mDataSource = new TransitionDataSource(
            mActiveTraces::incrementAndGet,
            this::onFlush,
            mActiveTraces::decrementAndGet);

    public PerfettoTransitionTracer() {
        Producer.init(InitArguments.DEFAULTS);
        mDataSource.register(DataSourceParams.DEFAULTS);
    }

    /**
     * Adds an entry in the trace to log that a transition has been dispatched to a handler.
     *
     * @param transitionId The id of the transition being dispatched.
     * @param handler The handler the transition is being dispatched to.
     */
    @Override
    public void logDispatched(int transitionId, Transitions.TransitionHandler handler) {
        if (!isTracing()) {
            return;
        }

        mDataSource.trace(ctx -> {
            final int handlerId = getHandlerId(handler, ctx);

            final ProtoOutputStream os = ctx.newTracePacket();
            final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
            os.write(PerfettoTrace.ShellTransition.ID, transitionId);
            os.write(PerfettoTrace.ShellTransition.DISPATCH_TIME_NS,
                    SystemClock.elapsedRealtimeNanos());
            os.write(PerfettoTrace.ShellTransition.HANDLER, handlerId);
            os.end(token);
        });
    }

    private static int getHandlerId(Transitions.TransitionHandler handler,
            TracingContext<DataSourceInstance, TransitionDataSource.TlsState, Void> ctx) {
        final Map<String, Integer> handlerMapping =
                ctx.getCustomTlsState().handlerMapping;
        final int handlerId;
        if (handlerMapping.containsKey(handler.getClass().getName())) {
            handlerId = handlerMapping.get(handler.getClass().getName());
        } else {
            // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto
            handlerId = handlerMapping.size() + 1;
            handlerMapping.put(handler.getClass().getName(), handlerId);
        }
        return handlerId;
    }

    /**
     * Adds an entry in the trace to log that a request to merge a transition was made.
     *
     * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
     */
    @Override
    public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
        if (!isTracing()) {
            return;
        }

        mDataSource.trace(ctx -> {
            final ProtoOutputStream os = ctx.newTracePacket();
            final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
            os.write(PerfettoTrace.ShellTransition.ID, mergeRequestedTransitionId);
            os.write(PerfettoTrace.ShellTransition.MERGE_REQUEST_TIME_NS,
                    SystemClock.elapsedRealtimeNanos());
            os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
            os.end(token);
        });
    }

    /**
     * Adds an entry in the trace to log that a transition was merged by the handler.
     *
     * @param mergedTransitionId The id of the transition that was merged.
     * @param playingTransitionId The id of the transition the transition was merged into.
     */
    @Override
    public void logMerged(int mergedTransitionId, int playingTransitionId) {
        if (!isTracing()) {
            return;
        }

        mDataSource.trace(ctx -> {
            final ProtoOutputStream os = ctx.newTracePacket();
            final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
            os.write(PerfettoTrace.ShellTransition.ID, mergedTransitionId);
            os.write(PerfettoTrace.ShellTransition.MERGE_TIME_NS,
                    SystemClock.elapsedRealtimeNanos());
            os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
            os.end(token);
        });
    }

    /**
     * Adds an entry in the trace to log that a transition was aborted.
     *
     * @param transitionId The id of the transition that was aborted.
     */
    @Override
    public void logAborted(int transitionId) {
        if (!isTracing()) {
            return;
        }

        mDataSource.trace(ctx -> {
            final ProtoOutputStream os = ctx.newTracePacket();
            final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
            os.write(PerfettoTrace.ShellTransition.ID, transitionId);
            os.write(PerfettoTrace.ShellTransition.SHELL_ABORT_TIME_NS,
                    SystemClock.elapsedRealtimeNanos());
            os.end(token);
        });
    }

    private boolean isTracing() {
        return mActiveTraces.get() > 0;
    }

    private void onFlush() {
        mDataSource.trace(ctx -> {
            final ProtoOutputStream os = ctx.newTracePacket();

            final Map<String, Integer> handlerMapping = ctx.getCustomTlsState().handlerMapping;
            for (String handler : handlerMapping.keySet()) {
                final long token = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS);
                os.write(PerfettoTrace.ShellHandlerMapping.ID, handlerMapping.get(handler));
                os.write(PerfettoTrace.ShellHandlerMapping.NAME, handler);
                os.end(token);
            }

            ctx.flush();
        });
    }
}
Loading