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

Commit c29cc2e1 authored by Jing Ji's avatar Jing Ji Committed by Automerger Merge Worker
Browse files

Merge "Fix memory leak in StrictMode violation throttling" into rvc-dev am:...

Merge "Fix memory leak in StrictMode violation throttling" into rvc-dev am: 471ef84e am: 341fd0c6

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

Change-Id: Ic0b256cd4765f0427acabc07a051d2e66b60b983
parents 37e9b4e7 341fd0c6
Loading
Loading
Loading
Loading
+47 −16
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -189,6 +191,9 @@ public final class StrictMode {
    // Only show an annoying dialog at most every 30 seconds
    private static final long MIN_DIALOG_INTERVAL_MS = 30000;

    // Only log a dropbox entry at most every 30 seconds
    private static final long MIN_DROPBOX_INTERVAL_MS = 3000;

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

@@ -1752,16 +1757,20 @@ public final class StrictMode {
            // Not perfect, but fast and good enough for dup suppression.
            Integer crashFingerprint = info.hashCode();
            long lastViolationTime = 0;
            long now = SystemClock.uptimeMillis();
            if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
                if (mLastViolationTime != null) {
                    Long vtime = mLastViolationTime.get(crashFingerprint);
                    if (vtime != null) {
                        lastViolationTime = vtime;
                    }
                    clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS,
                                Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS)));
                } else {
                    mLastViolationTime = new ArrayMap<>(1);
                }
            long now = SystemClock.uptimeMillis();
                mLastViolationTime.put(crashFingerprint, now);
            }
            long timeSinceLastViolationMillis =
                    lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime);

@@ -1780,7 +1789,8 @@ public final class StrictMode {
                penaltyMask |= PENALTY_DIALOG;
            }

            if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
            if (info.penaltyEnabled(PENALTY_DROPBOX)
                    && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) {
                penaltyMask |= PENALTY_DROPBOX;
            }

@@ -2215,6 +2225,23 @@ public final class StrictMode {
    @UnsupportedAppUsage
    private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();

    /**
     * Clamp the given map by removing elements with timestamp older than the given retainSince.
     */
    private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime,
            final long retainSince) {
        final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, Long> e = iterator.next();
            if (e.getValue() < retainSince) {
                // Remove stale entries
                iterator.remove();
            }
        }
        // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK,
        // seems not worth it (saving some space immediately but they will be obsoleted soon anyway)
    }

    /** @hide */
    public static void onVmPolicyViolation(Violation originStack) {
        onVmPolicyViolation(originStack, false);
@@ -2238,6 +2265,7 @@ public final class StrictMode {
        final long now = SystemClock.uptimeMillis();
        long lastViolationTime;
        long timeSinceLastViolationMillis = Long.MAX_VALUE;
        if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger
            synchronized (sLastVmViolationTime) {
                if (sLastVmViolationTime.containsKey(fingerprint)) {
                    lastViolationTime = sLastVmViolationTime.get(fingerprint);
@@ -2246,6 +2274,9 @@ public final class StrictMode {
                if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
                    sLastVmViolationTime.put(fingerprint, now);
                }
                clampViolationTimeMap(sLastVmViolationTime,
                        now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS));
            }
        }
        if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
            // Rate limit all penalties.
+51 −0
Original line number Diff line number Diff line
@@ -18,7 +18,58 @@ package android.os.strictmode;

/** Root class for all StrictMode violations. */
public abstract class Violation extends Throwable {
    private int mHashCode;
    private boolean mHashCodeValid;

    Violation(String message) {
        super(message);
    }

    @Override
    public int hashCode() {
        synchronized (this) {
            if (mHashCodeValid) {
                return mHashCode;
            }
            final String message = getMessage();
            final Throwable cause = getCause();
            int hashCode = message != null ? message.hashCode() : getClass().hashCode();
            hashCode = hashCode * 37 + calcStackTraceHashCode(getStackTrace());
            hashCode = hashCode * 37 + (cause != null ? cause.toString().hashCode() : 0);
            mHashCodeValid = true;
            return mHashCode = hashCode;
        }
    }

    @Override
    public synchronized Throwable initCause(Throwable cause) {
        mHashCodeValid = false;
        return super.initCause(cause);
    }

    @Override
    public void setStackTrace(StackTraceElement[] stackTrace) {
        super.setStackTrace(stackTrace);
        synchronized (this) {
            mHashCodeValid = false;
        }
    }

    @Override
    public synchronized Throwable fillInStackTrace() {
        mHashCodeValid = false;
        return super.fillInStackTrace();
    }

    private static int calcStackTraceHashCode(final StackTraceElement[] stackTrace) {
        int hashCode = 17;
        if (stackTrace != null) {
            for (int i = 0; i < stackTrace.length; i++) {
                if (stackTrace[i] != null) {
                    hashCode = hashCode * 37 + stackTrace[i].hashCode();
                }
            }
        }
        return hashCode;
    }
}