得物.apk(点击下载) / ResourceLeakDetector.java


package io.netty.util;

import androidx.core.view.accessibility.AccessibilityEventCompat;
import g00.a;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class ResourceLeakDetector<T> {
    public static final Level DEFAULT_LEVEL;
    public static final int SAMPLING_INTERVAL = SystemPropertyUtil.getInt("io.netty.leakDetection.samplingInterval", 128);
    public static final int TARGET_RECORDS;
    public static final AtomicReference<String[]> excludedMethods = new AtomicReference<>(EmptyArrays.EMPTY_STRINGS);
    private static Level level;
    private static final InternalLogger logger;
    private final Set<DefaultResourceLeak<?>> allLeaks;
    private final ReferenceQueue<Object> refQueue;
    private final ConcurrentMap<String, Boolean> reportedLeaks;
    private final String resourceType;
    private final int samplingInterval;

    public static final class DefaultResourceLeak<T> extends WeakReference<Object> implements ResourceLeakTracker<T>, ResourceLeak {
        public static final /* synthetic */ boolean $assertionsDisabled = false;
        private static final AtomicIntegerFieldUpdater<DefaultResourceLeak<?>> droppedRecordsUpdater = AtomicIntegerFieldUpdater.newUpdater(DefaultResourceLeak.class, "droppedRecords");
        private static final AtomicReferenceFieldUpdater<DefaultResourceLeak<?>, Record> headUpdater = AtomicReferenceFieldUpdater.newUpdater(DefaultResourceLeak.class, Record.class, "head");
        private final Set<DefaultResourceLeak<?>> allLeaks;
        private volatile int droppedRecords;
        private volatile Record head;
        private final int trackedHash;

        public DefaultResourceLeak(Object obj, ReferenceQueue<Object> referenceQueue, Set<DefaultResourceLeak<?>> set) {
            super(obj, referenceQueue);
            this.trackedHash = System.identityHashCode(obj);
            set.add(this);
            headUpdater.set(this, new Record(Record.BOTTOM));
            this.allLeaks = set;
        }

        private static void reachabilityFence0(Object obj) {
            if (obj != null) {
                synchronized (obj) {
                }
            }
        }

        private void record0(Object obj) {
            boolean z;
            Record record;
            boolean z4;
            Record record2;
            if (ResourceLeakDetector.TARGET_RECORDS > 0) {
                do {
                    AtomicReferenceFieldUpdater<DefaultResourceLeak<?>, Record> atomicReferenceFieldUpdater = headUpdater;
                    Record record3 = atomicReferenceFieldUpdater.get(this);
                    if (record3 != null) {
                        z = true;
                        int i = record3.pos + 1;
                        int i3 = ResourceLeakDetector.TARGET_RECORDS;
                        if (i >= i3) {
                            z4 = PlatformDependent.threadLocalRandom().nextInt(1 << Math.min(i - i3, 30)) != 0;
                            record = z4 ? record3.next : record3;
                        } else {
                            record = record3;
                            z4 = false;
                        }
                        if (obj == null) {
                            record2 = new Record(record);
                        }
                        while (true) {
                            if (!atomicReferenceFieldUpdater.compareAndSet(this, record3, record2)) {
                                if (atomicReferenceFieldUpdater.get(this) != record3) {
                                    z = false;
                                    continue;
                                    break;
                                }
                            } else {
                                break;
                            }
                        }
                    } else {
                        return;
                    }
                } while (!z);
                if (z4) {
                    droppedRecordsUpdater.incrementAndGet(this);
                }
            }
        }

        @Override // io.netty.util.ResourceLeak
        public boolean close() {
            if (!this.allLeaks.remove(this)) {
                return false;
            }
            clear();
            headUpdater.set(this, null);
            return true;
        }

        public boolean dispose() {
            clear();
            return this.allLeaks.remove(this);
        }

        @Override // io.netty.util.ResourceLeakTracker, io.netty.util.ResourceLeak
        public void record() {
            record0(null);
        }

        public String toString() {
            Record andSet = headUpdater.getAndSet(this, null);
            if (andSet == null) {
                return "";
            }
            int i = droppedRecordsUpdater.get(this);
            int i3 = 0;
            int i12 = 1;
            int i13 = andSet.pos + 1;
            StringBuilder sb2 = new StringBuilder(i13 * AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
            String str = StringUtil.NEWLINE;
            sb2.append(str);
            sb2.append("Recent access records: ");
            sb2.append(str);
            HashSet hashSet = new HashSet(i13);
            while (true) {
                Record record = Record.BOTTOM;
                if (andSet == record) {
                    break;
                }
                String record2 = andSet.toString();
                if (!hashSet.add(record2)) {
                    i3++;
                } else if (andSet.next == record) {
                    sb2.append("Created at:");
                    sb2.append(StringUtil.NEWLINE);
                    sb2.append(record2);
                } else {
                    sb2.append('#');
                    sb2.append(i12);
                    sb2.append(':');
                    sb2.append(StringUtil.NEWLINE);
                    sb2.append(record2);
                    i12++;
                }
                andSet = andSet.next;
            }
            if (i3 > 0) {
                a.r(sb2, ": ", i3, " leak records were discarded because they were duplicates");
                sb2.append(StringUtil.NEWLINE);
            }
            if (i > 0) {
                a.r(sb2, ": ", i, " leak records were discarded because the leak record count is targeted to ");
                sb2.append(ResourceLeakDetector.TARGET_RECORDS);
                sb2.append(". Use system property ");
                sb2.append("io.netty.leakDetection.targetRecords");
                sb2.append(" to increase the limit.");
                sb2.append(StringUtil.NEWLINE);
            }
            sb2.setLength(sb2.length() - StringUtil.NEWLINE.length());
            return sb2.toString();
        }

        @Override // io.netty.util.ResourceLeakTracker, io.netty.util.ResourceLeak
        public void record(Object obj) {
            record0(obj);
        }

        @Override // io.netty.util.ResourceLeakTracker
        public boolean close(T t) {
            try {
                return close();
            } finally {
                reachabilityFence0(t);
            }
        }
    }

    public enum Level {
        DISABLED,
        SIMPLE,
        ADVANCED,
        PARANOID;

        public static Level parseLevel(String str) {
            String trim = str.trim();
            Level[] values = values();
            for (Level level : values) {
                if (trim.equalsIgnoreCase(level.name()) || trim.equals(String.valueOf(level.ordinal()))) {
                    return level;
                }
            }
            return ResourceLeakDetector.DEFAULT_LEVEL;
        }
    }

    static {
        Level level2 = Level.SIMPLE;
        DEFAULT_LEVEL = level2;
        InternalLogger instance = InternalLoggerFactory.getInstance(ResourceLeakDetector.class);
        logger = instance;
        boolean z = false;
        if (SystemPropertyUtil.get("io.netty.noResourceLeakDetection") != null) {
            z = SystemPropertyUtil.getBoolean("io.netty.noResourceLeakDetection", false);
            instance.debug("-Dio.netty.noResourceLeakDetection: {}", Boolean.valueOf(z));
            instance.warn("-Dio.netty.noResourceLeakDetection is deprecated. Use '-D{}={}' instead.", "io.netty.leakDetection.level", level2.name().toLowerCase());
        }
        if (z) {
            level2 = Level.DISABLED;
        }
        Level parseLevel = Level.parseLevel(SystemPropertyUtil.get("io.netty.leakDetection.level", SystemPropertyUtil.get("io.netty.leakDetectionLevel", level2.name())));
        int i = SystemPropertyUtil.getInt("io.netty.leakDetection.targetRecords", 4);
        TARGET_RECORDS = i;
        level = parseLevel;
        if (instance.isDebugEnabled()) {
            instance.debug("-D{}: {}", "io.netty.leakDetection.level", parseLevel.name().toLowerCase());
            instance.debug("-D{}: {}", "io.netty.leakDetection.targetRecords", Integer.valueOf(i));
        }
    }

    @Deprecated
    public ResourceLeakDetector(Class<?> cls) {
        this(StringUtil.simpleClassName(cls));
    }

    public static void addExclusions(Class cls, String... strArr) {
        String[] strArr2;
        String[] strArr3;
        HashSet hashSet = new HashSet(Arrays.asList(strArr));
        Method[] declaredMethods = cls.getDeclaredMethods();
        int length = declaredMethods.length;
        int i = 0;
        while (i < length && (!hashSet.remove(declaredMethods[i].getName()) || !hashSet.isEmpty())) {
            i++;
        }
        if (hashSet.isEmpty()) {
            do {
                strArr2 = excludedMethods.get();
                strArr3 = (String[]) Arrays.copyOf(strArr2, (strArr.length * 2) + strArr2.length);
                for (int i3 = 0; i3 < strArr.length; i3++) {
                    int i12 = i3 * 2;
                    strArr3[strArr2.length + i12] = cls.getName();
                    strArr3[strArr2.length + i12 + 1] = strArr[i3];
                }
            } while (!excludedMethods.compareAndSet(strArr2, strArr3));
            return;
        }
        StringBuilder sb2 = new StringBuilder();
        sb2.append("Can't find '");
        sb2.append(hashSet);
        sb2.append("' in ");
        throw new IllegalArgumentException(androidx.appcompat.widget.a.h(cls, sb2));
    }

    private void clearRefQueue() {
        while (true) {
            DefaultResourceLeak defaultResourceLeak = (DefaultResourceLeak) this.refQueue.poll();
            if (defaultResourceLeak != null) {
                defaultResourceLeak.dispose();
            } else {
                return;
            }
        }
    }

    public static Level getLevel() {
        return level;
    }

    public static boolean isEnabled() {
        return getLevel().ordinal() > Level.DISABLED.ordinal();
    }

    private void reportLeak() {
        if (!logger.isErrorEnabled()) {
            clearRefQueue();
            return;
        }
        while (true) {
            DefaultResourceLeak defaultResourceLeak = (DefaultResourceLeak) this.refQueue.poll();
            if (defaultResourceLeak != null) {
                if (defaultResourceLeak.dispose()) {
                    String defaultResourceLeak2 = defaultResourceLeak.toString();
                    if (this.reportedLeaks.putIfAbsent(defaultResourceLeak2, Boolean.TRUE) == null) {
                        if (defaultResourceLeak2.isEmpty()) {
                            reportUntracedLeak(this.resourceType);
                        } else {
                            reportTracedLeak(this.resourceType, defaultResourceLeak2);
                        }
                    }
                }
            } else {
                return;
            }
        }
    }

    @Deprecated
    public static void setEnabled(boolean z) {
        setLevel(z ? Level.SIMPLE : Level.DISABLED);
    }

    public static void setLevel(Level level2) {
        level = level2;
    }

    private DefaultResourceLeak track0(T t) {
        Level level2 = level;
        if (level2 == Level.DISABLED) {
            return null;
        }
        if (level2.ordinal() >= Level.PARANOID.ordinal()) {
            reportLeak();
            return new DefaultResourceLeak(t, this.refQueue, this.allLeaks);
        } else if (PlatformDependent.threadLocalRandom().nextInt(this.samplingInterval) != 0) {
            return null;
        } else {
            reportLeak();
            return new DefaultResourceLeak(t, this.refQueue, this.allLeaks);
        }
    }

    @Deprecated
    public final ResourceLeak open(T t) {
        return track0(t);
    }

    @Deprecated
    public void reportInstancesLeak(String str) {
    }

    public void reportTracedLeak(String str, String str2) {
        logger.error("LEAK: {}.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.{}", str, str2);
    }

    public void reportUntracedLeak(String str) {
        logger.error("LEAK: {}.release() was not called before it's garbage-collected. Enable advanced leak reporting to find out where the leak occurred. To enable advanced leak reporting, specify the JVM option '-D{}={}' or call {}.setLevel() See http://netty.io/wiki/reference-counted-objects.html for more information.", str, "io.netty.leakDetection.level", Level.ADVANCED.name().toLowerCase(), StringUtil.simpleClassName(this));
    }

    public final ResourceLeakTracker<T> track(T t) {
        return track0(t);
    }

    @Deprecated
    public ResourceLeakDetector(String str) {
        this(str, 128, Long.MAX_VALUE);
    }

    @Deprecated
    public ResourceLeakDetector(Class<?> cls, int i, long j) {
        this(cls, i);
    }

    public static final class Record extends Throwable {
        public static final Record BOTTOM = new Record();
        private static final long serialVersionUID = 6065153674892850720L;
        private final String hintString;
        public final Record next;
        public final int pos;

        public Record(Record record, Object obj) {
            this.hintString = obj instanceof ResourceLeakHint ? ((ResourceLeakHint) obj).toHintString() : obj.toString();
            this.next = record;
            this.pos = record.pos + 1;
        }

        public String toString() {
            StringBuilder sb2 = new StringBuilder((int) AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
            if (this.hintString != null) {
                sb2.append("\tHint: ");
                sb2.append(this.hintString);
                sb2.append(StringUtil.NEWLINE);
            }
            StackTraceElement[] stackTrace = getStackTrace();
            for (int i = 3; i < stackTrace.length; i++) {
                StackTraceElement stackTraceElement = stackTrace[i];
                String[] strArr = ResourceLeakDetector.excludedMethods.get();
                int i3 = 0;
                while (true) {
                    if (i3 >= strArr.length) {
                        sb2.append('\t');
                        sb2.append(stackTraceElement.toString());
                        sb2.append(StringUtil.NEWLINE);
                        break;
                    }
                    if (strArr[i3].equals(stackTraceElement.getClassName()) && strArr[i3 + 1].equals(stackTraceElement.getMethodName())) {
                        break;
                    }
                    i3 += 2;
                }
            }
            return sb2.toString();
        }

        public Record(Record record) {
            this.next = record;
            this.pos = record.pos + 1;
        }

        private Record() {
            this.pos = -1;
        }
    }

    public ResourceLeakDetector(Class<?> cls, int i) {
        this(StringUtil.simpleClassName(cls), i, Long.MAX_VALUE);
    }

    @Deprecated
    public ResourceLeakDetector(String str, int i, long j) {
        this.allLeaks = Collections.newSetFromMap(new ConcurrentHashMap());
        this.refQueue = new ReferenceQueue<>();
        this.reportedLeaks = PlatformDependent.newConcurrentHashMap();
        this.resourceType = str;
        this.samplingInterval = i;
    }
}