Merge "Revert "Revert "Add ability to trace shell transitions""" into tm-qpr-dev am: 9fd0212e95

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18619887

Change-Id: I0b891a8addb77fd5012cd98ed809133728a13637
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Pablo Gamito
2022-06-01 12:25:15 +00:00
committed by Automerger Merge Worker
9 changed files with 428 additions and 8 deletions

View File

@@ -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.
*

View File

@@ -0,0 +1,68 @@
/*
* 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;
}

View File

@@ -95,6 +95,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -131,18 +132,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 +212,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags = flags;
mController = controller;
mSyncEngine = syncEngine;
controller.mTransitionTracer.logState(this);
}
void addFlag(int flag) {
@@ -272,11 +284,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
@TransitionState
int getState() {
return mState;
}
@VisibleForTesting
int getSyncId() {
return mSyncId;
}
@VisibleForTesting
int getDebugId() {
return mDebugId;
}
@TransitionFlags
int getFlags() {
return mFlags;
@@ -289,6 +311,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mState = STATE_COLLECTING;
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
mDebugId = mSyncId;
mController.mTransitionTracer.logState(this);
}
/**
@@ -306,6 +331,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
mSyncId);
applyReady();
mController.mTransitionTracer.logState(this);
}
/**
@@ -674,6 +701,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
mState = STATE_FINISHED;
mController.mTransitionTracer.logState(this);
}
void abort() {

View File

@@ -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.
@@ -524,6 +527,7 @@ class TransitionController {
setAnimationRunning(true /* running */);
}
mPlayingTransitions.add(transition);
mTransitionTracer.logState(transition);
}
private void setAnimationRunning(boolean running) {
@@ -542,6 +546,7 @@ class TransitionController {
}
transition.abort();
mCollectingTransition = null;
mTransitionTracer.logState(transition);
}
/**

View File

@@ -0,0 +1,234 @@
/*
* 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();
}
}
}
}

View File

@@ -459,6 +459,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
final TransitionTracer mTransitionTracer;
private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
@@ -1245,6 +1246,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
Choreographer.getInstance());
mTransitionTracer = new TransitionTracer();
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -5887,6 +5889,21 @@ public class WindowManagerService extends IWindowManager.Stub
return mWindowTracing.isEnabled();
}
@Override
public void startTransitionTrace() {
mTransitionTracer.startTrace(null /* printwriter */);
}
@Override
public void stopTransitionTrace() {
mTransitionTracer.stopTrace(null /* printwriter */);
}
@Override
public boolean isTransitionTraceEnabled() {
return mTransitionTracer.isEnabled();
}
@Override
public boolean registerCrossWindowBlurEnabledListener(
ICrossWindowBlurEnabledListener listener) {

View File

@@ -149,6 +149,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
case "shell":
return runWmShellCommand(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -1235,6 +1237,47 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
private int runWmShellCommand(PrintWriter pw) {
String arg = getNextArg();
switch (arg) {
case "tracing":
return runWmShellTracing(pw);
case "help":
default:
return runHelp(pw);
}
}
private int runHelp(PrintWriter pw) {
pw.println("Window Manager Shell commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" tracing <start/stop>");
pw.println(" Start/stop shell transition tracing.");
return 0;
}
private int runWmShellTracing(PrintWriter pw) {
String arg = getNextArg();
switch (arg) {
case "start":
mInternal.mTransitionTracer.startTrace(pw);
break;
case "stop":
mInternal.mTransitionTracer.stopTrace(pw);
break;
default:
getErrPrintWriter()
.println("Error: expected 'start' or 'stop', but got '" + arg + "'");
return -1;
}
return 0;
}
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());

View File

@@ -149,7 +149,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
void setWindowManager(WindowManagerService wms) {
mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController,
wms.mTransitionTracer);
mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
}

View File

@@ -88,7 +88,11 @@ public class TransitionTests extends WindowTestsBase {
final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
private Transition createTestTransition(int transitType) {
TransitionController controller = mock(TransitionController.class);
TransitionTracer tracer = mock(TransitionTracer.class);
final TransitionController controller = new TransitionController(
mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class),
mock(TransitionTracer.class));
final BLASTSyncEngine sync = createTestBLASTSyncEngine();
final Transition t = new Transition(transitType, 0 /* flags */, controller, sync);
t.startCollecting(0 /* timeoutMs */);
@@ -584,7 +588,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTimeout() {
final TransitionController controller = new TransitionController(mAtm,
mock(TaskSnapshotController.class));
mock(TaskSnapshotController.class), mock(TransitionTracer.class));
final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
final CountDownLatch latch = new CountDownLatch(1);
// When the timeout is reached, it will finish the sync-group and notify transaction ready.
@@ -841,7 +845,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -905,7 +910,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTransientLaunch() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -968,7 +974,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testNotReadyPushPop() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
final TransitionController controller = new TransitionController(mAtm, snapshotController,
mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);