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

Commit 1abf4745 authored by Pablo Gamito's avatar Pablo Gamito Committed by Android (Google) Code Review
Browse files

Merge "Add ability to trace shell transitions" into tm-qpr-dev

parents 9e44f19c 6ae3b6c6
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