CamScanner.apk(点击下载) / ResourceLeakDetector.java


package io.netty.util;

import com.applovin.sdk.AppLovinEventTypes;
import com.lzy.okgo.cache.CacheEntity;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.ObjectUtil;
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.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class ResourceLeakDetector<T> {
    private static final Level DEFAULT_LEVEL;
    private static final int DEFAULT_SAMPLING_INTERVAL = 128;
    private static final int DEFAULT_TARGET_RECORDS = 4;
    private static final String PROP_LEVEL = "io.netty.leakDetection.level";
    private static final String PROP_LEVEL_OLD = "io.netty.leakDetectionLevel";
    private static final String PROP_SAMPLING_INTERVAL = "io.netty.leakDetection.samplingInterval";
    private static final String PROP_TARGET_RECORDS = "io.netty.leakDetection.targetRecords";
    static final int SAMPLING_INTERVAL = SystemPropertyUtil.getInt(PROP_SAMPLING_INTERVAL, 128);
    private static final int TARGET_RECORDS;
    private 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 Set<String> reportedLeaks;
    private final String resourceType;
    private final int samplingInterval;

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

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

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

        private void record0(Object obj) {
            AtomicReferenceFieldUpdater<DefaultResourceLeak<?>, TraceRecord> atomicReferenceFieldUpdater;
            TraceRecord traceRecord;
            boolean z10;
            TraceRecord traceRecord2;
            TraceRecord traceRecord3;
            if (ResourceLeakDetector.TARGET_RECORDS > 0) {
                do {
                    atomicReferenceFieldUpdater = headUpdater;
                    traceRecord = atomicReferenceFieldUpdater.get(this);
                    if (traceRecord != null) {
                        boolean z11 = true;
                        int i10 = traceRecord.pos + 1;
                        z10 = false;
                        if (i10 >= ResourceLeakDetector.TARGET_RECORDS) {
                            if (PlatformDependent.threadLocalRandom().nextInt(1 << Math.min(i10 - ResourceLeakDetector.TARGET_RECORDS, 30)) == 0) {
                                z11 = false;
                            }
                            traceRecord2 = z11 ? traceRecord.next : traceRecord;
                            z10 = z11;
                        } else {
                            traceRecord2 = traceRecord;
                        }
                        if (obj == null) {
                            traceRecord3 = new TraceRecord(traceRecord2);
                        }
                    } else {
                        return;
                    }
                } while (!atomicReferenceFieldUpdater.compareAndSet(this, traceRecord, traceRecord3));
                if (z10) {
                    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;
        }

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

        /* access modifiers changed from: package-private */
        public boolean dispose() {
            clear();
            return this.allLeaks.remove(this);
        }

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

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

        public String toString() {
            TraceRecord andSet = headUpdater.getAndSet(this, null);
            if (andSet == null) {
                return "";
            }
            int i10 = droppedRecordsUpdater.get(this);
            int i11 = 0;
            int i12 = 1;
            int i13 = andSet.pos + 1;
            StringBuilder sb2 = new StringBuilder(i13 * 2048);
            String str = StringUtil.NEWLINE;
            sb2.append(str);
            sb2.append("Recent access records: ");
            sb2.append(str);
            HashSet hashSet = new HashSet(i13);
            while (andSet != TraceRecord.BOTTOM) {
                String traceRecord = andSet.toString();
                if (!hashSet.add(traceRecord)) {
                    i11++;
                } else if (andSet.next == TraceRecord.BOTTOM) {
                    sb2.append("Created at:");
                    sb2.append(StringUtil.NEWLINE);
                    sb2.append(traceRecord);
                } else {
                    sb2.append('#');
                    sb2.append(i12);
                    sb2.append(':');
                    sb2.append(StringUtil.NEWLINE);
                    sb2.append(traceRecord);
                    i12++;
                }
                andSet = andSet.next;
            }
            if (i11 > 0) {
                sb2.append(": ");
                sb2.append(i11);
                sb2.append(" leak records were discarded because they were duplicates");
                sb2.append(StringUtil.NEWLINE);
            }
            if (i10 > 0) {
                sb2.append(": ");
                sb2.append(i10);
                sb2.append(" leak records were discarded because the leak record count is targeted to ");
                sb2.append(ResourceLeakDetector.TARGET_RECORDS);
                sb2.append(". Use system property ");
                sb2.append(ResourceLeakDetector.PROP_TARGET_RECORDS);
                sb2.append(" to increase the limit.");
                sb2.append(StringUtil.NEWLINE);
            }
            sb2.setLength(sb2.length() - StringUtil.NEWLINE.length());
            return sb2.toString();
        }
    }

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

        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;
        }
    }

    /* access modifiers changed from: private */
    public static class TraceRecord extends Throwable {
        private static final TraceRecord BOTTOM = new TraceRecord() {
            /* class io.netty.util.ResourceLeakDetector.TraceRecord.AnonymousClass1 */
            private static final long serialVersionUID = 7396077602074694571L;

            public Throwable fillInStackTrace() {
                return this;
            }
        };
        private static final long serialVersionUID = 6065153674892850720L;
        private final String hintString;
        private final TraceRecord next;
        private final int pos;

        private TraceRecord() {
            this.hintString = null;
            this.next = null;
            this.pos = -1;
        }

        TraceRecord(TraceRecord traceRecord) {
            this.hintString = null;
            this.next = traceRecord;
            this.pos = traceRecord.pos + 1;
        }

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

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

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

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

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

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

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

    @Deprecated
    public ResourceLeakDetector(String str, int i10, long j2) {
        this.allLeaks = Collections.newSetFromMap(new ConcurrentHashMap());
        this.refQueue = new ReferenceQueue<>();
        this.reportedLeaks = Collections.newSetFromMap(new ConcurrentHashMap());
        this.resourceType = (String) ObjectUtil.checkNotNull(str, "resourceType");
        this.samplingInterval = i10;
    }

    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 i10 = 0;
        while (i10 < length && (!hashSet.remove(declaredMethods[i10].getName()) || !hashSet.isEmpty())) {
            i10++;
        }
        if (hashSet.isEmpty()) {
            do {
                strArr2 = excludedMethods.get();
                strArr3 = (String[]) Arrays.copyOf(strArr2, strArr2.length + (strArr.length * 2));
                for (int i11 = 0; i11 < strArr.length; i11++) {
                    int i12 = i11 * 2;
                    strArr3[strArr2.length + i12] = cls.getName();
                    strArr3[strArr2.length + i12 + 1] = strArr[i11];
                }
            } while (!excludedMethods.compareAndSet(strArr2, strArr3));
            return;
        }
        throw new IllegalArgumentException("Can't find '" + hashSet + "' in " + cls.getName());
    }

    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 (!needReport()) {
            clearRefQueue();
            return;
        }
        while (true) {
            DefaultResourceLeak defaultResourceLeak = (DefaultResourceLeak) this.refQueue.poll();
            if (defaultResourceLeak != null) {
                if (defaultResourceLeak.dispose()) {
                    String defaultResourceLeak2 = defaultResourceLeak.toString();
                    if (this.reportedLeaks.add(defaultResourceLeak2)) {
                        if (defaultResourceLeak2.isEmpty()) {
                            reportUntracedLeak(this.resourceType);
                        } else {
                            reportTracedLeak(this.resourceType, defaultResourceLeak2);
                        }
                    }
                }
            } else {
                return;
            }
        }
    }

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

    public static void setLevel(Level level2) {
        level = (Level) ObjectUtil.checkNotNull(level2, AppLovinEventTypes.USER_COMPLETED_LEVEL);
    }

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

    /* access modifiers changed from: protected */
    public boolean needReport() {
        return logger.isErrorEnabled();
    }

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

    /* access modifiers changed from: protected */
    @Deprecated
    public void reportInstancesLeak(String str) {
    }

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

    /* access modifiers changed from: protected */
    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 https://netty.io/wiki/reference-counted-objects.html for more information.", str, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), StringUtil.simpleClassName(this));
    }

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