Merge "Fix crash in jank monitor debug overlay" into udc-dev

This commit is contained in:
Peter Kalauskas
2023-05-11 18:14:20 +00:00
committed by Android (Google) Code Review
3 changed files with 77 additions and 45 deletions

View File

@@ -275,7 +275,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
} }
} }
@VisibleForTesting @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public Handler getHandler() { public Handler getHandler() {
return mHandler; return mHandler;
} }

View File

@@ -772,11 +772,12 @@ public class InteractionJankMonitor {
return true; return true;
} }
@UiThread
private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) { private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) {
synchronized (mLock) { synchronized (mLock) {
mRunningTrackers.put(cuj, tracker); mRunningTrackers.put(cuj, tracker);
if (mDebugOverlay != null) { if (mDebugOverlay != null) {
mDebugOverlay.onTrackerAdded(cuj, tracker.getViewRoot()); mDebugOverlay.onTrackerAdded(cuj, tracker);
} }
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj) Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj)
@@ -791,6 +792,7 @@ public class InteractionJankMonitor {
} }
} }
@UiThread
private void removeTracker(@CujType int cuj, int reason) { private void removeTracker(@CujType int cuj, int reason) {
synchronized (mLock) { synchronized (mLock) {
mRunningTrackers.remove(cuj); mRunningTrackers.remove(cuj);
@@ -818,7 +820,7 @@ public class InteractionJankMonitor {
SETTINGS_DEBUG_OVERLAY_ENABLED_KEY, SETTINGS_DEBUG_OVERLAY_ENABLED_KEY,
DEFAULT_DEBUG_OVERLAY_ENABLED); DEFAULT_DEBUG_OVERLAY_ENABLED);
if (debugOverlayEnabled && mDebugOverlay == null) { if (debugOverlayEnabled && mDebugOverlay == null) {
mDebugOverlay = new InteractionMonitorDebugOverlay(mDebugBgColor, mDebugYOffset); mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset);
} else if (!debugOverlayEnabled && mDebugOverlay != null) { } else if (!debugOverlayEnabled && mDebugOverlay != null) {
mDebugOverlay.dispose(); mDebugOverlay.dispose();
mDebugOverlay = null; mDebugOverlay = null;

View File

@@ -19,25 +19,30 @@ package com.android.internal.jank;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL; import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
import android.annotation.ColorInt; import android.annotation.ColorInt;
import android.annotation.UiThread;
import android.app.ActivityThread; import android.app.ActivityThread;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RecordingCanvas; import android.graphics.RecordingCanvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Handler;
import android.os.Trace; import android.os.Trace;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.WindowCallbacks; import android.view.WindowCallbacks;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.jank.FrameTracker.Reasons; import com.android.internal.jank.FrameTracker.Reasons;
import com.android.internal.jank.InteractionJankMonitor.CujType; import com.android.internal.jank.InteractionJankMonitor.CujType;
/** /**
* An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window
* associated with one of the CUJs being tracked. There's no guarantee which window it will * associated with one of the CUJs being tracked. There's no guarantee which window it will
* draw to. NOTE: sometimes the CUJ names will remain displayed on the screen longer than they * draw to. Traces that use the debug overlay should not be used for performance analysis.
* are actually running. * <p>
* To enable the overlay, run the following: <code>adb shell device_config put
* interaction_jank_monitor debug_overlay_enabled true</code>
* <p> * <p>
* CUJ names will be drawn as follows: * CUJ names will be drawn as follows:
* <ul> * <ul>
@@ -45,12 +50,16 @@ import com.android.internal.jank.InteractionJankMonitor.CujType;
* <li> Grey text indicates the CUJ ended normally and is no longer running * <li> Grey text indicates the CUJ ended normally and is no longer running
* <li> Red text with a strikethrough indicates the CUJ was canceled or ended abnormally * <li> Red text with a strikethrough indicates the CUJ was canceled or ended abnormally
* </ul> * </ul>
* @hide
*/ */
class InteractionMonitorDebugOverlay implements WindowCallbacks { class InteractionMonitorDebugOverlay implements WindowCallbacks {
private static final int REASON_STILL_RUNNING = -1000; private static final int REASON_STILL_RUNNING = -1000;
private final Object mLock;
// Sparse array where the key in the CUJ and the value is the session status, or null if // Sparse array where the key in the CUJ and the value is the session status, or null if
// it's currently running // it's currently running
@GuardedBy("mLock")
private final SparseIntArray mRunningCujs = new SparseIntArray(); private final SparseIntArray mRunningCujs = new SparseIntArray();
private Handler mHandler = null;
private FrameTracker.ViewRootWrapper mViewRoot = null; private FrameTracker.ViewRootWrapper mViewRoot = null;
private final Paint mDebugPaint; private final Paint mDebugPaint;
private final Paint.FontMetrics mDebugFontMetrics; private final Paint.FontMetrics mDebugFontMetrics;
@@ -59,8 +68,10 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
private final int mBgColor; private final int mBgColor;
private final double mYOffset; private final double mYOffset;
private final String mPackageName; private final String mPackageName;
private static final String TRACK_NAME = "InteractionJankMonitor";
InteractionMonitorDebugOverlay(@ColorInt int bgColor, double yOffset) { InteractionMonitorDebugOverlay(Object lock, @ColorInt int bgColor, double yOffset) {
mLock = lock;
mBgColor = bgColor; mBgColor = bgColor;
mYOffset = yOffset; mYOffset = yOffset;
mDebugPaint = new Paint(); mDebugPaint = new Paint();
@@ -70,18 +81,30 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
mPackageName = context.getPackageName(); mPackageName = context.getPackageName();
} }
@UiThread
void dispose() { void dispose() {
if (mViewRoot != null) { if (mViewRoot != null && mHandler != null) {
mViewRoot.removeWindowCallbacks(this); mHandler.runWithScissors(() -> mViewRoot.removeWindowCallbacks(this),
InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
forceRedraw(); forceRedraw();
} }
mHandler = null;
mViewRoot = null; mViewRoot = null;
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0);
} }
private boolean attachViewRootIfNeeded(FrameTracker.ViewRootWrapper viewRoot) { @UiThread
private boolean attachViewRootIfNeeded(FrameTracker tracker) {
FrameTracker.ViewRootWrapper viewRoot = tracker.getViewRoot();
if (mViewRoot == null && viewRoot != null) { if (mViewRoot == null && viewRoot != null) {
// Add a trace marker so we can identify traces that were captured while the debug
// overlay was enabled. Traces that use the debug overlay should NOT be used for
// performance analysis.
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
mHandler = tracker.getHandler();
mViewRoot = viewRoot; mViewRoot = viewRoot;
viewRoot.addWindowCallbacks(this); mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this),
InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
forceRedraw(); forceRedraw();
return true; return true;
} }
@@ -115,52 +138,61 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
} }
} }
@UiThread
private void forceRedraw() { private void forceRedraw() {
if (mViewRoot != null) { if (mViewRoot != null && mHandler != null) {
mViewRoot.requestInvalidateRootRenderNode(); mHandler.runWithScissors(() -> {
mViewRoot.getView().invalidate(); mViewRoot.requestInvalidateRootRenderNode();
mViewRoot.getView().invalidate();
}, InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
} }
} }
@UiThread
void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason, void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason,
SparseArray<FrameTracker> runningTrackers) { SparseArray<FrameTracker> runningTrackers) {
mRunningCujs.put(removedCuj, reason); synchronized (mLock) {
// If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended mRunningCujs.put(removedCuj, reason);
if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) { // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
mRunningCujs.clear(); if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
dispose(); mRunningCujs.clear();
} else {
boolean needsNewViewRoot = true;
if (mViewRoot != null) {
// Check to see if this viewroot is still associated with one of the running
// trackers
for (int i = 0; i < runningTrackers.size(); i++) {
if (mViewRoot.equals(
runningTrackers.valueAt(i).getViewRoot())) {
needsNewViewRoot = false;
break;
}
}
}
if (needsNewViewRoot) {
dispose(); dispose();
for (int i = 0; i < runningTrackers.size(); i++) { } else {
if (attachViewRootIfNeeded(runningTrackers.valueAt(i).getViewRoot())) { boolean needsNewViewRoot = true;
break; if (mViewRoot != null) {
// Check to see if this viewroot is still associated with one of the running
// trackers
for (int i = 0; i < runningTrackers.size(); i++) {
if (mViewRoot.equals(
runningTrackers.valueAt(i).getViewRoot())) {
needsNewViewRoot = false;
break;
}
} }
} }
} else { if (needsNewViewRoot) {
forceRedraw(); dispose();
for (int i = 0; i < runningTrackers.size(); i++) {
if (attachViewRootIfNeeded(runningTrackers.valueAt(i))) {
break;
}
}
} else {
forceRedraw();
}
} }
} }
} }
void onTrackerAdded(@CujType int addedCuj, FrameTracker.ViewRootWrapper viewRoot) { @UiThread
// Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ void onTrackerAdded(@CujType int addedCuj, FrameTracker tracker) {
// is still running synchronized (mLock) {
mRunningCujs.put(addedCuj, REASON_STILL_RUNNING); // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
attachViewRootIfNeeded(viewRoot); // is still running
forceRedraw(); mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
attachViewRootIfNeeded(tracker);
forceRedraw();
}
} }
@Override @Override
@@ -188,7 +220,6 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
@Override @Override
public void onPostDraw(RecordingCanvas canvas) { public void onPostDraw(RecordingCanvas canvas) {
Trace.beginSection("InteractionJankMonitor#drawDebug");
final int padding = dipToPx(5); final int padding = dipToPx(5);
final int h = canvas.getHeight(); final int h = canvas.getHeight();
final int w = canvas.getWidth(); final int w = canvas.getWidth();
@@ -235,6 +266,5 @@ class InteractionMonitorDebugOverlay implements WindowCallbacks {
canvas.translate(0, cujNameTextHeight); canvas.translate(0, cujNameTextHeight);
canvas.drawText(cujName, 0, 0, mDebugPaint); canvas.drawText(cujName, 0, 0, mDebugPaint);
} }
Trace.endSection();
} }
} }