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