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

Commit e2b64e5f authored by Pablo Gamito's avatar Pablo Gamito Committed by Automerger Merge Worker
Browse files

Merge "Add ability to trace shell transitions" into tm-qpr-dev am: 1abf4745

parents 1564fb88 1abf4745
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -542,6 +542,21 @@ interface IWindowManager
     */
    boolean isWindowTraceEnabled();

    /**
     * Starts a transition trace.
     */
    void startTransitionTrace();

    /**
     * Stops a transition trace.
     */
    void stopTransitionTrace();

    /**
     * Returns true if transition trace is enabled.
     */
    boolean isTransitionTraceEnabled();

    /**
     * Gets the windowing mode of the display.
     *
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

syntax = "proto3";

package com.android.server.wm.shell;

import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";

option java_multiple_files = true;

/* Represents a file full of transition entries.
   Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such
   that it can be easily identified. */
message TransitionTraceProto {

  /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
     (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
      constants into .proto files. */
  enum MagicNumber {
    INVALID = 0;
    MAGIC_NUMBER_L = 0x544e5254;  /* TRNT (little-endian ASCII) */
    MAGIC_NUMBER_H = 0x45434152;  /* RACE (little-endian ASCII) */
  }

  fixed64 magic_number = 1;  /* Must be the first field, set to value in MagicNumber */
  int64 timestamp = 2; /* The timestamp of when the trace was started. */
  repeated Transition transition = 3;
}

message Transition {

  enum State {
    COLLECTING = 0;
    PENDING = -1;
    STARTED = 1;
    PLAYING = 2;
    ABORT = 3;
    FINISHED = 4;
  }

  int32 id = 1;
  int32 transition_type = 2;
  int64 timestamp = 3;
  State state = 5;
  int32 flags = 6;
  repeated ChangeInfo change = 7;
}

message ChangeInfo {
  com.android.server.wm.IdentifierProto window_identifier = 1;
  int32 transit_mode = 2;
  bool has_changed = 3;
  int32 change_flags = 4;
}
+32 −1
Original line number Diff line number Diff line
@@ -131,18 +131,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
     */
    private static final int STATE_ABORT = 3;

    /**
     * This transition has finished playing successfully.
     */
    private static final int STATE_FINISHED = 4;

    @IntDef(prefix = { "STATE_" }, value = {
            STATE_PENDING,
            STATE_COLLECTING,
            STATE_STARTED,
            STATE_PLAYING,
            STATE_ABORT
            STATE_ABORT,
            STATE_FINISHED
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface TransitionState {}

    final @TransitionType int mType;
    private int mSyncId = -1;
    // Used for tracking a Transition throughout a lifecycle (i.e. from STATE_COLLECTING to
    // STATE_FINISHED or STATE_ABORT), and should only be used for testing and debugging.
    private int mDebugId = -1;
    private @TransitionFlags int mFlags;
    private final TransitionController mController;
    private final BLASTSyncEngine mSyncEngine;
@@ -202,6 +211,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        mFlags = flags;
        mController = controller;
        mSyncEngine = syncEngine;

        controller.mTransitionTracer.logState(this);
    }

    void addFlag(int flag) {
@@ -242,11 +253,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
    }

    @TransitionState
    int getState() {
        return mState;
    }

    @VisibleForTesting
    int getSyncId() {
        return mSyncId;
    }

    @VisibleForTesting
    int getDebugId() {
        return mDebugId;
    }

    @TransitionFlags
    int getFlags() {
        return mFlags;
@@ -259,6 +280,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        }
        mState = STATE_COLLECTING;
        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
        mDebugId = mSyncId;

        mController.mTransitionTracer.logState(this);
    }

    /**
@@ -276,6 +300,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
                mSyncId);
        applyReady();

        mController.mTransitionTracer.logState(this);
    }

    /**
@@ -644,6 +670,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
            dc.removeImeSurfaceImmediately();
            dc.handleCompleteDeferredRemoval();
        }

        mState = STATE_FINISHED;
        mController.mTransitionTracer.logState(this);
    }

    void abort() {
@@ -673,6 +702,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
            Slog.e(TAG, "Unexpected Sync ID " + syncId + ". Expected " + mSyncId);
            return;
        }

        if (mTargetDisplays.isEmpty()) {
            mTargetDisplays.add(mController.mAtm.mRootWindowContainer.getDefaultDisplay());
        }
@@ -784,6 +814,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        }
        mStartTransaction = transaction;
        mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();

        buildFinishTransaction(mFinishTransaction, info.getRootLeash());
        if (mController.getTransitionPlayer() != null) {
            mController.dispatchLegacyAppTransitionStarting(info);
+6 −1
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ class TransitionController {

    private ITransitionPlayer mTransitionPlayer;
    final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
    final TransitionTracer mTransitionTracer;

    private IApplicationThread mTransitionPlayerThread;
    final ActivityTaskManagerService mAtm;
@@ -100,10 +101,12 @@ class TransitionController {
    final StatusBarManagerInternal mStatusBar;

    TransitionController(ActivityTaskManagerService atm,
            TaskSnapshotController taskSnapshotController) {
            TaskSnapshotController taskSnapshotController,
            TransitionTracer transitionTracer) {
        mAtm = atm;
        mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
        mTaskSnapshotController = taskSnapshotController;
        mTransitionTracer = transitionTracer;
        mTransitionPlayerDeath = () -> {
            synchronized (mAtm.mGlobalLock) {
                // Clean-up/finish any playing transitions.
@@ -514,6 +517,7 @@ class TransitionController {
            setAnimationRunning(true /* running */);
        }
        mPlayingTransitions.add(transition);
        mTransitionTracer.logState(transition);
    }

    private void setAnimationRunning(boolean running) {
@@ -532,6 +536,7 @@ class TransitionController {
        }
        transition.abort();
        mCollectingTransition = null;
        mTransitionTracer.logState(transition);
    }

    /**
+234 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.server.wm;

import static android.os.Build.IS_USER;

import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS;
import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED;
import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE;
import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER;
import static com.android.server.wm.shell.Transition.CHANGE;
import static com.android.server.wm.shell.Transition.FLAGS;
import static com.android.server.wm.shell.Transition.ID;
import static com.android.server.wm.shell.Transition.STATE;
import static com.android.server.wm.shell.Transition.TIMESTAMP;
import static com.android.server.wm.shell.Transition.TRANSITION_TYPE;
import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER;
import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H;
import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L;
import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.os.Trace;
import android.util.Log;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.TraceBuffer;
import com.android.server.wm.Transition.ChangeInfo;
import com.android.server.wm.shell.TransitionTraceProto;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Helper class to collect and dump transition traces.
 */
public class TransitionTracer {

    private static final String LOG_TAG = "TransitionTracer";

    /**
     * Maximum buffer size, currently defined as 5 MB
     */
    private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB
    static final String WINSCOPE_EXT = ".winscope";
    private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT;
    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;

    private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer();

    private final Object mEnabledLock = new Object();
    private volatile boolean mEnabled = false;

    private long mTraceStartTimestamp;

    private class TransitionTraceBuffer {
        private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY);

        private void pushTransitionState(Transition transition) {
            final ProtoOutputStream outputStream = new ProtoOutputStream();
            final long transitionEntryToken = outputStream.start(TRANSITION);

            outputStream.write(ID, transition.getDebugId());
            outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
            outputStream.write(TRANSITION_TYPE, transition.mType);
            outputStream.write(STATE, transition.getState());
            outputStream.write(FLAGS, transition.getFlags());

            for (int i = 0; i < transition.mChanges.size(); ++i) {
                final WindowContainer window = transition.mChanges.keyAt(i);
                final ChangeInfo changeInfo = transition.mChanges.valueAt(i);
                writeChange(outputStream, window, changeInfo);
            }

            outputStream.end(transitionEntryToken);

            mBuffer.add(outputStream);
        }

        private void writeChange(ProtoOutputStream outputStream, WindowContainer window,
                ChangeInfo changeInfo) {
            Trace.beginSection("TransitionProto#addChange");
            final long changeEntryToken = outputStream.start(CHANGE);

            final int transitMode = changeInfo.getTransitMode(window);
            final boolean hasChanged = changeInfo.hasChanged(window);
            final int changeFlags = changeInfo.getChangeFlags(window);

            outputStream.write(TRANSIT_MODE, transitMode);
            outputStream.write(HAS_CHANGED, hasChanged);
            outputStream.write(CHANGE_FLAGS, changeFlags);
            window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER);

            outputStream.end(changeEntryToken);
            Trace.endSection();
        }

        public void writeToFile(File file, ProtoOutputStream proto) throws IOException {
            mBuffer.writeTraceToFile(file, proto);
        }

        public void reset() {
            mBuffer.resetBuffer();
        }
    }

    /**
     * Records the current state of a transition in the transition trace (if it is running).
     * @param transition the transition that we want to record the state of.
     */
    public void logState(com.android.server.wm.Transition transition) {
        if (!mEnabled) {
            return;
        }

        Log.d(LOG_TAG, "Logging state of transition " + transition);
        mTraceBuffer.pushTransitionState(transition);
    }

    /**
     * Starts collecting transitions for the trace.
     * If called while a trace is already running, this will reset the trace.
     */
    public void startTrace(@Nullable PrintWriter pw) {
        if (IS_USER) {
            LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
            return;
        }
        Trace.beginSection("TransitionTracer#startTrace");
        LogAndPrintln.i(pw, "Starting shell transition trace.");
        synchronized (mEnabledLock) {
            mTraceStartTimestamp = SystemClock.elapsedRealtime();
            mEnabled = true;
            mTraceBuffer.reset();
        }
        Trace.endSection();
    }

    /**
     * Stops collecting the transition trace and dump to trace to file.
     *
     * Dumps the trace to @link{TRACE_FILE}.
     */
    public void stopTrace(@Nullable PrintWriter pw) {
        stopTrace(pw, new File(TRACE_FILE));
    }

    /**
     * Stops collecting the transition trace and dump to trace to file.
     * @param outputFile The file to dump the transition trace to.
     */
    public void stopTrace(@Nullable PrintWriter pw, File outputFile) {
        if (IS_USER) {
            LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
            return;
        }
        Trace.beginSection("TransitionTracer#stopTrace");
        LogAndPrintln.i(pw, "Stopping shell transition trace.");
        synchronized (mEnabledLock) {
            if (!mEnabled) {
                LogAndPrintln.e(pw,
                        "Error: Tracing can't be stopped because it hasn't been started.");
                return;
            }

            mEnabled = false;
            writeTraceToFileLocked(pw, outputFile);
        }
        Trace.endSection();
    }

    boolean isEnabled() {
        return mEnabled;
    }

    private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) {
        Trace.beginSection("TransitionTracer#writeTraceToFileLocked");
        try {
            ProtoOutputStream proto = new ProtoOutputStream();
            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
            proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp);
            int pid = android.os.Process.myPid();
            LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath()
                    + " from process " + pid);
            mTraceBuffer.writeToFile(file, proto);
        } catch (IOException e) {
            LogAndPrintln.e(pw, "Unable to write buffer to file", e);
        }
        Trace.endSection();
    }

    private static class LogAndPrintln {
        private static void i(@Nullable PrintWriter pw, String msg) {
            Log.i(LOG_TAG, msg);
            if (pw != null) {
                pw.println(msg);
                pw.flush();
            }
        }

        private static void e(@Nullable PrintWriter pw, String msg) {
            Log.e(LOG_TAG, msg);
            if (pw != null) {
                pw.println("ERROR: " + msg);
                pw.flush();
            }
        }

        private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) {
            Log.e(LOG_TAG, msg, e);
            if (pw != null) {
                pw.println("ERROR: " + msg + " ::\n " + e);
                pw.flush();
            }
        }
    }
}
Loading