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

Commit 471ef84e authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

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

parents bc334c00 6985fb39
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;
    }
}