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

Commit 54019ee8 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick Committed by Android (Google) Code Review
Browse files

Merge "StrictMode.Span support"

parents 53f0f363 e7520d89
Loading
Loading
Loading
Loading
+168 −0
Original line number Diff line number Diff line
@@ -112,6 +112,9 @@ public final class StrictMode {
    // Only show an annoying dialog at most every 30 seconds
    private static final long MIN_DIALOG_INTERVAL_MS = 30000;

    // How many Span tags (e.g. animations) to report.
    private static final int MAX_SPAN_TAGS = 20;

    // How many offending stacks to keep track of (and time) per loop
    // of the Looper.
    private static final int MAX_OFFENSES_PER_LOOP = 10;
@@ -1216,6 +1219,140 @@ public final class StrictMode {
        setBlockGuardPolicy(newPolicy);
    }

    /**
     * A tracked, critical time span.  (e.g. during an animation.)
     *
     * The object itself is a linked list node, to avoid any allocations
     * during rapid span entries and exits.
     *
     * @hide
     */
    public static class Span {
        private String mName;
        private long mCreateMillis;
        private Span mNext;
        private Span mPrev;  // not used when in freeList, only active
        private final ThreadSpanState mContainerState;

        Span(ThreadSpanState threadState) {
            mContainerState = threadState;
        }

        /**
         * To be called when the critical span is complete (i.e. the
         * animation is done animating).  This can be called on any
         * thread (even a different one from where the animation was
         * taking place), but that's only a defensive implementation
         * measure.  It really makes no sense for you to call this on
         * thread other than that where you created it.
         *
         * @hide
         */
        public void finish() {
            ThreadSpanState state = mContainerState;
            synchronized (state) {
                if (mName == null) {
                    // Duplicate finish call.  Ignore.
                    return;
                }

                // Remove ourselves from the active list.
                if (mPrev != null) {
                    mPrev.mNext = mNext;
                }
                if (mNext != null) {
                    mNext.mPrev = mPrev;
                }
                if (state.mActiveHead == this) {
                    state.mActiveHead = mNext;
                }

                this.mCreateMillis = -1;
                this.mName = null;
                this.mPrev = null;
                this.mNext = null;
                state.mActiveSize--;

                // Add ourselves to the freeList, if it's not already
                // too big.
                if (state.mFreeListSize < 5) {
                    this.mNext = state.mFreeListHead;
                    state.mFreeListHead = this;
                    state.mFreeListSize++;
                }
            }
        }
    }

    /**
     * Linked lists of active spans and a freelist.
     *
     * Locking notes: there's one of these structures per thread and
     * all members of this structure (as well as the Span nodes under
     * it) are guarded by the ThreadSpanState object instance.  While
     * in theory there'd be no locking required because it's all local
     * per-thread, the finish() method above is defensive against
     * people calling it on a different thread from where they created
     * the Span, hence the locking.
     */
    private static class ThreadSpanState {
        public Span mActiveHead;    // doubly-linked list.
        public int mActiveSize;
        public Span mFreeListHead;  // singly-linked list.  only changes at head.
        public int mFreeListSize;
    }

    private static final ThreadLocal<ThreadSpanState> sThisThreadSpanState =
            new ThreadLocal<ThreadSpanState>() {
        @Override protected ThreadSpanState initialValue() {
            return new ThreadSpanState();
        }
    };

    /**
     * Enter a named critical span (e.g. an animation)
     *
     * <p>The name is an arbitary label (or tag) that will be applied
     * to any strictmode violation that happens while this span is
     * active.  You must call finish() on the span when done.
     *
     * <p>This will never return null, but on devices without debugging
     * enabled, this may return a dummy object on which the finish()
     * method is a no-op.
     *
     * <p>TODO: add CloseGuard to this, verifying callers call finish.
     *
     * @hide
     */
    public static Span enterCriticalSpan(String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("name must be non-null and non-empty");
        }
        ThreadSpanState state = sThisThreadSpanState.get();
        Span span = null;
        synchronized (state) {
            if (state.mFreeListHead != null) {
                span = state.mFreeListHead;
                state.mFreeListHead = span.mNext;
                state.mFreeListSize--;
            } else {
                // Shouldn't have to do this often.
                span = new Span(state);
            }
            span.mName = name;
            span.mCreateMillis = SystemClock.uptimeMillis();
            span.mNext = state.mActiveHead;
            span.mPrev = null;
            state.mActiveHead = span;
            state.mActiveSize++;
            if (span.mNext != null) {
                span.mNext.mPrev = span;
            }
        }
        return span;
    }


    /**
     * Parcelable that gets sent in Binder call headers back to callers
     * to report violations that happened during a cross-process call.
@@ -1244,6 +1381,12 @@ public final class StrictMode {
         */
        public int numAnimationsRunning = 0;

        /**
         * List of tags from active Span instances during this
         * violation, or null for none.
         */
        public String[] tags;

        /**
         * Which violation number this was (1-based) since the last Looper loop,
         * from the perspective of the root caller (if it crossed any processes
@@ -1284,6 +1427,23 @@ public final class StrictMode {
            if (broadcastIntent != null) {
                broadcastIntentAction = broadcastIntent.getAction();
            }
            ThreadSpanState state = sThisThreadSpanState.get();
            synchronized (state) {
                int spanActiveCount = state.mActiveSize;
                if (spanActiveCount > MAX_SPAN_TAGS) {
                    spanActiveCount = MAX_SPAN_TAGS;
                }
                if (spanActiveCount != 0) {
                    this.tags = new String[spanActiveCount];
                    Span iter = state.mActiveHead;
                    int index = 0;
                    while (iter != null && index < spanActiveCount) {
                        this.tags[index] = iter.mName;
                        index++;
                        iter = iter.mNext;
                    }
                }
            }
        }

        /**
@@ -1312,6 +1472,7 @@ public final class StrictMode {
            numAnimationsRunning = in.readInt();
            violationUptimeMillis = in.readLong();
            broadcastIntentAction = in.readString();
            tags = in.readStringArray();
        }

        /**
@@ -1325,6 +1486,7 @@ public final class StrictMode {
            dest.writeInt(numAnimationsRunning);
            dest.writeLong(violationUptimeMillis);
            dest.writeString(broadcastIntentAction);
            dest.writeStringArray(tags);
        }


@@ -1347,6 +1509,12 @@ public final class StrictMode {
            if (broadcastIntentAction != null) {
                pw.println(prefix + "broadcastIntentAction: " + broadcastIntentAction);
            }
            if (tags != null) {
                int index = 0;
                for (String tag : tags) {
                    pw.println(prefix + "tag[" + (index++) + "]: " + tag);
                }
            }
        }

    }
+6 −1
Original line number Diff line number Diff line
@@ -6702,9 +6702,14 @@ public final class ActivityManagerService extends ActivityManagerNative
            if (info.broadcastIntentAction != null) {
                sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
            }
            if (info != null && info.durationMillis != -1) {
            if (info.durationMillis != -1) {
                sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
            }
            if (info.tags != null) {
                for (String tag : info.tags) {
                    sb.append("Span-Tag: ").append(tag).append("\n");
                }
            }
            sb.append("\n");
            if (info.crashInfo != null && info.crashInfo.stackTrace != null) {
                sb.append(info.crashInfo.stackTrace);