Add some client side logging for interrupted draws to the dump

In particular, these are to try and narrow down why the app window isn't
drawing in b/235463625 where the draw state for launcher is stuck in
DRAW_PENDING.

- mLastReportNextDrawReason: the reason the last draw was requested,
  maybe useful in telling if wm is not requesting a draw if not set
- mLastPerformTraversalsSkipDrawReason: the reason perform traversals
  returned without drawing (includes wm/predraw cancel reasons)
- mLastPerformDrawSkippedReason: the reason perform draw returned
  without drawing
- mLocalSyncState: extra logging to confirm that the last sync actually
  completes

Bug: 235463625
Test: dumpsys -T 60000 activity -v all
Test: dumpsys activity activities
Change-Id: I10118470fe62075920e9ce29fe449b0cfc8570c9
This commit is contained in:
Winson Chung
2022-08-04 22:43:36 +00:00
parent 9c2b0e1cf0
commit 8564942f36
3 changed files with 79 additions and 11 deletions

View File

@@ -422,7 +422,7 @@ public class SurfaceControlViewHost {
public void relayout(WindowManager.LayoutParams attrs, public void relayout(WindowManager.LayoutParams attrs,
WindowlessWindowManager.ResizeCompleteCallback callback) { WindowlessWindowManager.ResizeCompleteCallback callback) {
mViewRoot.setLayoutParams(attrs, false); mViewRoot.setLayoutParams(attrs, false);
mViewRoot.setReportNextDraw(true /* syncBuffer */); mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout");
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
} }

View File

@@ -587,8 +587,21 @@ public final class ViewRootImpl implements ViewParent,
int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
boolean mPerformContentCapture; boolean mPerformContentCapture;
boolean mReportNextDraw; boolean mReportNextDraw;
/** Set only while mReportNextDraw=true, indicating the last reason that was triggered */
String mLastReportNextDrawReason;
/** The reaason the last call to performDraw() returned false */
String mLastPerformDrawSkippedReason;
/** The reason the last call to performTraversals() returned without drawing */
String mLastPerformTraversalsSkipDrawReason;
/** The state of the local sync, if one is in progress. Can be one of the states below. */
int mLocalSyncState;
// The possible states of the local sync, see createSyncIfNeeded()
private final int LOCAL_SYNC_NONE = 0;
private final int LOCAL_SYNC_PENDING = 1;
private final int LOCAL_SYNC_RETURNED = 2;
private final int LOCAL_SYNC_MERGED = 3;
/** /**
* Set whether the draw should send the buffer to system server. When set to true, VRI will * Set whether the draw should send the buffer to system server. When set to true, VRI will
@@ -1836,7 +1849,7 @@ public final class ViewRootImpl implements ViewParent,
mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId; mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
if (msg == MSG_RESIZED_REPORT) { if (msg == MSG_RESIZED_REPORT) {
reportNextDraw(); reportNextDraw("resized");
} }
if (mView != null && (frameChanged || configChanged)) { if (mView != null && (frameChanged || configChanged)) {
@@ -2741,6 +2754,8 @@ public final class ViewRootImpl implements ViewParent,
} }
private void performTraversals() { private void performTraversals() {
mLastPerformTraversalsSkipDrawReason = null;
// cache mView since it is used so much below... // cache mView since it is used so much below...
final View host = mView; final View host = mView;
if (DBG) { if (DBG) {
@@ -2750,12 +2765,14 @@ public final class ViewRootImpl implements ViewParent,
} }
if (host == null || !mAdded) { if (host == null || !mAdded) {
mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
return; return;
} }
mIsInTraversal = true; mIsInTraversal = true;
mWillDrawSoon = true; mWillDrawSoon = true;
boolean cancelDraw = false; boolean cancelDraw = false;
String cancelReason = null;
boolean isSyncRequest = false; boolean isSyncRequest = false;
boolean windowSizeMayChange = false; boolean windowSizeMayChange = false;
@@ -3038,13 +3055,14 @@ public final class ViewRootImpl implements ViewParent,
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW) cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
== RELAYOUT_RES_CANCEL_AND_REDRAW; == RELAYOUT_RES_CANCEL_AND_REDRAW;
cancelReason = "relayout";
final boolean dragResizing = mPendingDragResizing; final boolean dragResizing = mPendingDragResizing;
if (mSyncSeqId > mLastSyncSeqId) { if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId; mLastSyncSeqId = mSyncSeqId;
if (DEBUG_BLAST) { if (DEBUG_BLAST) {
Log.d(mTag, "Relayout called with blastSync"); Log.d(mTag, "Relayout called with blastSync");
} }
reportNextDraw(); reportNextDraw("relayout");
mSyncBuffer = true; mSyncBuffer = true;
isSyncRequest = true; isSyncRequest = true;
if (!cancelDraw) { if (!cancelDraw) {
@@ -3147,6 +3165,7 @@ public final class ViewRootImpl implements ViewParent,
} }
} catch (OutOfResourcesException e) { } catch (OutOfResourcesException e) {
handleOutOfResourcesException(e); handleOutOfResourcesException(e);
mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer";
return; return;
} }
} }
@@ -3184,6 +3203,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.updateSurface(mSurface); mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) { } catch (OutOfResourcesException e) {
handleOutOfResourcesException(e); handleOutOfResourcesException(e);
mLastPerformTraversalsSkipDrawReason = "oom_update_surface";
return; return;
} }
} }
@@ -3349,6 +3369,7 @@ public final class ViewRootImpl implements ViewParent,
if (mCheckIfCanDraw) { if (mCheckIfCanDraw) {
try { try {
cancelDraw = mWindowSession.cancelDraw(mWindow); cancelDraw = mWindowSession.cancelDraw(mWindow);
cancelReason = "wm_sync";
if (DEBUG_BLAST) { if (DEBUG_BLAST) {
Log.d(mTag, "cancelDraw returned " + cancelDraw); Log.d(mTag, "cancelDraw returned " + cancelDraw);
} }
@@ -3571,19 +3592,21 @@ public final class ViewRootImpl implements ViewParent,
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw(); reportNextDraw("first_relayout");
} }
mCheckIfCanDraw = isSyncRequest || cancelDraw; mCheckIfCanDraw = isSyncRequest || cancelDraw;
boolean cancelAndRedraw = boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync); boolean cancelAndRedraw = cancelDueToPreDrawListener
|| (cancelDraw && mDrewOnceForSync);
if (!cancelAndRedraw) { if (!cancelAndRedraw) {
createSyncIfNeeded(); createSyncIfNeeded();
mDrewOnceForSync = true; mDrewOnceForSync = true;
} }
if (!isViewVisible) { if (!isViewVisible) {
mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) { for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations(); mPendingTransitions.get(i).endChangingAnimations();
@@ -3595,6 +3618,9 @@ public final class ViewRootImpl implements ViewParent,
mSyncBufferCallback.onBufferReady(null); mSyncBufferCallback.onBufferReady(null);
} }
} else if (cancelAndRedraw) { } else if (cancelAndRedraw) {
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
: "cancel_" + cancelReason;
// Try again // Try again
scheduleTraversals(); scheduleTraversals();
} else { } else {
@@ -3618,11 +3644,13 @@ public final class ViewRootImpl implements ViewParent,
if (!cancelAndRedraw) { if (!cancelAndRedraw) {
mReportNextDraw = false; mReportNextDraw = false;
mLastReportNextDrawReason = null;
mSyncBufferCallback = null; mSyncBufferCallback = null;
mSyncBuffer = false; mSyncBuffer = false;
if (isInLocalSync()) { if (isInLocalSync()) {
mSyncGroup.markSyncReady(); mSyncGroup.markSyncReady();
mSyncGroup = null; mSyncGroup = null;
mLocalSyncState = LOCAL_SYNC_NONE;
} }
} }
} }
@@ -3634,12 +3662,15 @@ public final class ViewRootImpl implements ViewParent,
} }
final int seqId = mSyncSeqId; final int seqId = mSyncSeqId;
mLocalSyncState = LOCAL_SYNC_PENDING;
mSyncGroup = new SurfaceSyncGroup(transaction -> { mSyncGroup = new SurfaceSyncGroup(transaction -> {
mLocalSyncState = LOCAL_SYNC_RETURNED;
// Callback will be invoked on executor thread so post to main thread. // Callback will be invoked on executor thread so post to main thread.
mHandler.postAtFrontOfQueue(() -> { mHandler.postAtFrontOfQueue(() -> {
if (transaction != null) { if (transaction != null) {
mSurfaceChangedTransaction.merge(transaction); mSurfaceChangedTransaction.merge(transaction);
} }
mLocalSyncState = LOCAL_SYNC_MERGED;
reportDrawFinished(seqId); reportDrawFinished(seqId);
}); });
}); });
@@ -4353,9 +4384,12 @@ public final class ViewRootImpl implements ViewParent,
} }
private boolean performDraw() { private boolean performDraw() {
mLastPerformDrawSkippedReason = null;
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
mLastPerformDrawSkippedReason = "screen_off";
return false; return false;
} else if (mView == null) { } else if (mView == null) {
mLastPerformDrawSkippedReason = "no_root_view";
return false; return false;
} }
@@ -8458,6 +8492,21 @@ public final class ViewRootImpl implements ViewParent,
if (mTraversalScheduled) { if (mTraversalScheduled) {
writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")"); writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")");
} }
writer.println(innerPrefix + "mReportNextDraw=" + mReportNextDraw);
if (mReportNextDraw) {
writer.println(innerPrefix + " (reason=" + mLastReportNextDrawReason + ")");
}
if (mLastPerformTraversalsSkipDrawReason != null) {
writer.println(innerPrefix + "mLastPerformTraversalsFailedReason="
+ mLastPerformTraversalsSkipDrawReason);
}
if (mLastPerformDrawSkippedReason != null) {
writer.println(innerPrefix + "mLastPerformDrawFailedReason="
+ mLastPerformDrawSkippedReason);
}
if (mLocalSyncState != LOCAL_SYNC_NONE) {
writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState);
}
writer.println(innerPrefix + "mIsAmbientMode=" + mIsAmbientMode); writer.println(innerPrefix + "mIsAmbientMode=" + mIsAmbientMode);
writer.println(innerPrefix + "mUnbufferedInputSource=" writer.println(innerPrefix + "mUnbufferedInputSource="
+ Integer.toHexString(mUnbufferedInputSource)); + Integer.toHexString(mUnbufferedInputSource));
@@ -9958,11 +10007,12 @@ public final class ViewRootImpl implements ViewParent,
} }
} }
private void reportNextDraw() { private void reportNextDraw(String reason) {
if (DEBUG_BLAST) { if (DEBUG_BLAST) {
Log.d(mTag, "reportNextDraw " + Debug.getCallers(5)); Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
} }
mReportNextDraw = true; mReportNextDraw = true;
mLastReportNextDrawReason = reason;
} }
/** /**
@@ -9975,11 +10025,12 @@ public final class ViewRootImpl implements ViewParent,
* @param syncBuffer If true, the transaction that contains the buffer from the draw should be * @param syncBuffer If true, the transaction that contains the buffer from the draw should be
* sent to system to be synced. If false, VRI will not try to sync the buffer, * sent to system to be synced. If false, VRI will not try to sync the buffer,
* but only report back that a buffer was drawn. * but only report back that a buffer was drawn.
* @param reason A debug string indicating the reason for reporting the next draw
* @hide * @hide
*/ */
public void setReportNextDraw(boolean syncBuffer) { public void setReportNextDraw(boolean syncBuffer, String reason) {
mSyncBuffer = syncBuffer; mSyncBuffer = syncBuffer;
reportNextDraw(); reportNextDraw(reason);
invalidate(); invalidate();
} }

View File

@@ -74,6 +74,9 @@ public final class ViewTreeObserver {
* that the listener will be immediately called. */ * that the listener will be immediately called. */
private boolean mWindowShown; private boolean mWindowShown;
// The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw
private String mLastDispatchOnPreDrawCanceledReason;
private boolean mAlive = true; private boolean mAlive = true;
/** /**
@@ -1161,6 +1164,7 @@ public final class ViewTreeObserver {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() { public final boolean dispatchOnPreDraw() {
mLastDispatchOnPreDrawCanceledReason = null;
boolean cancelDraw = false; boolean cancelDraw = false;
final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
if (listeners != null && listeners.size() > 0) { if (listeners != null && listeners.size() > 0) {
@@ -1168,7 +1172,11 @@ public final class ViewTreeObserver {
try { try {
int count = access.size(); int count = access.size();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
cancelDraw |= !(access.get(i).onPreDraw()); final OnPreDrawListener preDrawListener = access.get(i);
cancelDraw |= !(preDrawListener.onPreDraw());
if (cancelDraw) {
mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName();
}
} }
} finally { } finally {
listeners.end(); listeners.end();
@@ -1177,6 +1185,15 @@ public final class ViewTreeObserver {
return cancelDraw; return cancelDraw;
} }
/**
* @return the reason that the last call to dispatchOnPreDraw() returned true to cancel the
* current draw, or null if the last call did not cancel.
* @hide
*/
final String getLastDispatchOnPreDrawCanceledReason() {
return mLastDispatchOnPreDrawCanceledReason;
}
/** /**
* Notifies registered listeners that the window is now shown * Notifies registered listeners that the window is now shown
* @hide * @hide