jimu-decompiled/sources/com/squareup/leakcanary/HeapAnalyzer.java
2025-05-13 19:24:51 +02:00

353 lines
16 KiB
Java

package com.squareup.leakcanary;
import android.os.Build;
import com.squareup.haha.perflib.ArrayInstance;
import com.squareup.haha.perflib.ClassInstance;
import com.squareup.haha.perflib.ClassObj;
import com.squareup.haha.perflib.Field;
import com.squareup.haha.perflib.HprofParser;
import com.squareup.haha.perflib.Instance;
import com.squareup.haha.perflib.RootObj;
import com.squareup.haha.perflib.RootType;
import com.squareup.haha.perflib.Snapshot;
import com.squareup.haha.perflib.Type;
import com.squareup.haha.perflib.io.MemoryMappedFileBuffer;
import com.squareup.leakcanary.AnalyzerProgressListener;
import com.squareup.leakcanary.LeakTraceElement;
import com.squareup.leakcanary.Reachability;
import com.squareup.leakcanary.ShortestPathFinder;
import com.unity3d.ads.metadata.MediationMetaData;
import gnu.trove.THashMap;
import gnu.trove.TObjectProcedure;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/* loaded from: classes.dex */
public final class HeapAnalyzer {
private static final String ANONYMOUS_CLASS_NAME_PATTERN = "^.+\\$\\d+$";
private final ExcludedRefs excludedRefs;
private final AnalyzerProgressListener listener;
private final List<Reachability.Inspector> reachabilityInspectors;
@Deprecated
public HeapAnalyzer(ExcludedRefs excludedRefs) {
this(excludedRefs, AnalyzerProgressListener.NONE, Collections.emptyList());
}
private LeakTraceElement buildLeakElement(LeakNode leakNode) {
LeakTraceElement.Holder holder;
String str;
LeakTraceElement.Holder holder2;
LeakNode leakNode2 = leakNode.parent;
String str2 = null;
if (leakNode2 == null) {
return null;
}
Instance instance = leakNode2.instance;
if (instance instanceof RootObj) {
return null;
}
List<LeakReference> describeFields = describeFields(instance);
String className = getClassName(instance);
ArrayList arrayList = new ArrayList();
arrayList.add(className);
String name = Object.class.getName();
if (instance instanceof ClassInstance) {
ClassObj classObj = instance.getClassObj();
while (true) {
classObj = classObj.getSuperClassObj();
if (classObj.getClassName().equals(name)) {
break;
}
arrayList.add(classObj.getClassName());
}
}
if (instance instanceof ClassObj) {
holder = LeakTraceElement.Holder.CLASS;
} else if (instance instanceof ArrayInstance) {
holder = LeakTraceElement.Holder.ARRAY;
} else {
ClassObj classObj2 = instance.getClassObj();
if (HahaHelper.extendsThread(classObj2)) {
LeakTraceElement.Holder holder3 = LeakTraceElement.Holder.THREAD;
str = "(named '" + HahaHelper.threadName(instance) + "')";
holder2 = holder3;
return new LeakTraceElement(leakNode.leakReference, holder2, arrayList, str, leakNode.exclusion, describeFields);
}
if (className.matches(ANONYMOUS_CLASS_NAME_PATTERN)) {
String className2 = classObj2.getSuperClassObj().getClassName();
if (name.equals(className2)) {
holder = LeakTraceElement.Holder.OBJECT;
try {
Class<?>[] interfaces = Class.forName(classObj2.getClassName()).getInterfaces();
if (interfaces.length > 0) {
str2 = "(anonymous implementation of " + interfaces[0].getName() + ")";
} else {
str2 = "(anonymous subclass of java.lang.Object)";
}
} catch (ClassNotFoundException unused) {
}
} else {
str2 = "(anonymous subclass of " + className2 + ")";
holder = LeakTraceElement.Holder.OBJECT;
}
} else {
holder = LeakTraceElement.Holder.OBJECT;
}
}
holder2 = holder;
str = str2;
return new LeakTraceElement(leakNode.leakReference, holder2, arrayList, str, leakNode.exclusion, describeFields);
}
private LeakTrace buildLeakTrace(LeakNode leakNode) {
ArrayList arrayList = new ArrayList();
for (LeakNode leakNode2 = new LeakNode(null, null, leakNode, null); leakNode2 != null; leakNode2 = leakNode2.parent) {
LeakTraceElement buildLeakElement = buildLeakElement(leakNode2);
if (buildLeakElement != null) {
arrayList.add(0, buildLeakElement);
}
}
return new LeakTrace(arrayList, computeExpectedReachability(arrayList));
}
private List<Reachability> computeExpectedReachability(List<LeakTraceElement> list) {
Reachability expectedReachability;
int i = 1;
int size = list.size() - 1;
int i2 = 0;
int i3 = 0;
loop0: while (true) {
if (i >= size) {
break;
}
LeakTraceElement leakTraceElement = list.get(i);
Iterator<Reachability.Inspector> it = this.reachabilityInspectors.iterator();
do {
if (!it.hasNext()) {
break;
}
expectedReachability = it.next().expectedReachability(leakTraceElement);
if (expectedReachability == Reachability.REACHABLE) {
i3 = i;
}
i++;
} while (expectedReachability != Reachability.UNREACHABLE);
size = i;
break loop0;
}
ArrayList arrayList = new ArrayList();
while (i2 < list.size()) {
arrayList.add(i2 <= i3 ? Reachability.REACHABLE : i2 >= size ? Reachability.UNREACHABLE : Reachability.UNKNOWN);
i2++;
}
return arrayList;
}
private long computeIgnoredBitmapRetainedSize(Snapshot snapshot, Instance instance) {
ArrayInstance arrayInstance;
long j = 0;
for (Instance instance2 : snapshot.findClass("android.graphics.Bitmap").getInstancesList()) {
if (isIgnoredDominator(instance, instance2) && (arrayInstance = (ArrayInstance) HahaHelper.fieldValue(HahaHelper.classInstanceValues(instance2), "mBuffer")) != null) {
long totalRetainedSize = arrayInstance.getTotalRetainedSize();
long totalRetainedSize2 = instance2.getTotalRetainedSize();
if (totalRetainedSize2 < totalRetainedSize) {
totalRetainedSize2 += totalRetainedSize;
}
j += totalRetainedSize2;
}
}
return j;
}
private List<LeakReference> describeFields(Instance instance) {
ArrayList arrayList = new ArrayList();
if (instance instanceof ClassObj) {
for (Map.Entry<Field, Object> entry : ((ClassObj) instance).getStaticFieldValues().entrySet()) {
arrayList.add(new LeakReference(LeakTraceElement.Type.STATIC_FIELD, entry.getKey().getName(), HahaHelper.valueAsString(entry.getValue())));
}
} else if (instance instanceof ArrayInstance) {
ArrayInstance arrayInstance = (ArrayInstance) instance;
if (arrayInstance.getArrayType() == Type.OBJECT) {
Object[] values = arrayInstance.getValues();
for (int i = 0; i < values.length; i++) {
arrayList.add(new LeakReference(LeakTraceElement.Type.ARRAY_ENTRY, Integer.toString(i), HahaHelper.valueAsString(values[i])));
}
}
} else {
for (Map.Entry<Field, Object> entry2 : instance.getClassObj().getStaticFieldValues().entrySet()) {
arrayList.add(new LeakReference(LeakTraceElement.Type.STATIC_FIELD, entry2.getKey().getName(), HahaHelper.valueAsString(entry2.getValue())));
}
for (ClassInstance.FieldValue fieldValue : ((ClassInstance) instance).getValues()) {
arrayList.add(new LeakReference(LeakTraceElement.Type.INSTANCE_FIELD, fieldValue.getField().getName(), HahaHelper.valueAsString(fieldValue.getValue())));
}
}
return arrayList;
}
private AnalysisResult findLeakTrace(long j, Snapshot snapshot, Instance instance, boolean z) {
long j2;
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.FINDING_SHORTEST_PATH);
ShortestPathFinder.Result findPath = new ShortestPathFinder(this.excludedRefs).findPath(snapshot, instance);
String className = instance.getClassObj().getClassName();
if (findPath.leakingNode == null) {
return AnalysisResult.noLeak(className, since(j));
}
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.BUILDING_LEAK_TRACE);
LeakTrace buildLeakTrace = buildLeakTrace(findPath.leakingNode);
if (z) {
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.COMPUTING_DOMINATORS);
snapshot.computeDominators();
Instance instance2 = findPath.leakingNode.instance;
j2 = instance2.getTotalRetainedSize();
if (Build.VERSION.SDK_INT <= 25) {
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.COMPUTING_BITMAP_SIZE);
j2 += computeIgnoredBitmapRetainedSize(snapshot, instance2);
}
} else {
j2 = -1;
}
return AnalysisResult.leakDetected(findPath.excludingKnownLeaks, className, buildLeakTrace, j2, since(j));
}
private Instance findLeakingReference(String str, Snapshot snapshot) {
ClassObj findClass = snapshot.findClass(KeyedWeakReference.class.getName());
if (findClass == null) {
throw new IllegalStateException("Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
}
ArrayList arrayList = new ArrayList();
Iterator<Instance> it = findClass.getInstancesList().iterator();
while (it.hasNext()) {
List<ClassInstance.FieldValue> classInstanceValues = HahaHelper.classInstanceValues(it.next());
Object fieldValue = HahaHelper.fieldValue(classInstanceValues, "key");
if (fieldValue == null) {
arrayList.add(null);
} else {
String asString = HahaHelper.asString(fieldValue);
if (asString.equals(str)) {
return (Instance) HahaHelper.fieldValue(classInstanceValues, "referent");
}
arrayList.add(asString);
}
}
throw new IllegalStateException("Could not find weak reference with key " + str + " in " + arrayList);
}
private String generateRootKey(RootObj rootObj) {
return String.format("%s@0x%08x", rootObj.getRootType().getName(), Long.valueOf(rootObj.getId()));
}
private String getClassName(Instance instance) {
return instance instanceof ClassObj ? ((ClassObj) instance).getClassName() : instance instanceof ArrayInstance ? ((ArrayInstance) instance).getClassObj().getClassName() : instance.getClassObj().getClassName();
}
private boolean isIgnoredDominator(Instance instance, Instance instance2) {
boolean z = false;
do {
Instance immediateDominator = instance2.getImmediateDominator();
if ((immediateDominator instanceof RootObj) && ((RootObj) immediateDominator).getRootType() == RootType.UNKNOWN) {
instance2 = instance2.getNextInstanceToGcRoot();
z = true;
} else {
instance2 = immediateDominator;
}
if (instance2 == null) {
return false;
}
} while (instance2 != instance);
return z;
}
private long since(long j) {
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - j);
}
@Deprecated
public AnalysisResult checkForLeak(File file, String str) {
return checkForLeak(file, str, true);
}
void deduplicateGcRoots(Snapshot snapshot) {
final THashMap tHashMap = new THashMap();
final Collection<RootObj> gCRoots = snapshot.getGCRoots();
for (RootObj rootObj : gCRoots) {
String generateRootKey = generateRootKey(rootObj);
if (!tHashMap.containsKey(generateRootKey)) {
tHashMap.put(generateRootKey, rootObj);
}
}
gCRoots.clear();
tHashMap.forEach(new TObjectProcedure<String>() { // from class: com.squareup.leakcanary.HeapAnalyzer.1
@Override // gnu.trove.TObjectProcedure
public boolean execute(String str) {
return gCRoots.add(tHashMap.get(str));
}
});
}
public List<TrackedReference> findTrackedReferences(File file) {
if (!file.exists()) {
throw new IllegalArgumentException("File does not exist: " + file);
}
try {
Snapshot parse = new HprofParser(new MemoryMappedFileBuffer(file)).parse();
deduplicateGcRoots(parse);
ClassObj findClass = parse.findClass(KeyedWeakReference.class.getName());
ArrayList arrayList = new ArrayList();
Iterator<Instance> it = findClass.getInstancesList().iterator();
while (it.hasNext()) {
List<ClassInstance.FieldValue> classInstanceValues = HahaHelper.classInstanceValues(it.next());
String asString = HahaHelper.asString(HahaHelper.fieldValue(classInstanceValues, "key"));
String asString2 = HahaHelper.hasField(classInstanceValues, MediationMetaData.KEY_NAME) ? HahaHelper.asString(HahaHelper.fieldValue(classInstanceValues, MediationMetaData.KEY_NAME)) : "(No name field)";
Instance instance = (Instance) HahaHelper.fieldValue(classInstanceValues, "referent");
if (instance != null) {
arrayList.add(new TrackedReference(asString, asString2, getClassName(instance), describeFields(instance)));
}
}
return arrayList;
} catch (Throwable th) {
throw new RuntimeException(th);
}
}
public AnalysisResult checkForLeak(File file, String str, boolean z) {
long nanoTime = System.nanoTime();
if (!file.exists()) {
return AnalysisResult.failure(new IllegalArgumentException("File does not exist: " + file), since(nanoTime));
}
try {
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.READING_HEAP_DUMP_FILE);
HprofParser hprofParser = new HprofParser(new MemoryMappedFileBuffer(file));
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.PARSING_HEAP_DUMP);
Snapshot parse = hprofParser.parse();
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.DEDUPLICATING_GC_ROOTS);
deduplicateGcRoots(parse);
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.FINDING_LEAKING_REF);
Instance findLeakingReference = findLeakingReference(str, parse);
return findLeakingReference == null ? AnalysisResult.noLeak(findLeakingReference.getClassObj().getClassName(), since(nanoTime)) : findLeakTrace(nanoTime, parse, findLeakingReference, z);
} catch (Throwable th) {
return AnalysisResult.failure(th, since(nanoTime));
}
}
public HeapAnalyzer(ExcludedRefs excludedRefs, AnalyzerProgressListener analyzerProgressListener, List<Class<? extends Reachability.Inspector>> list) {
this.excludedRefs = excludedRefs;
this.listener = analyzerProgressListener;
this.reachabilityInspectors = new ArrayList();
Iterator<Class<? extends Reachability.Inspector>> it = list.iterator();
while (it.hasNext()) {
try {
this.reachabilityInspectors.add(it.next().getDeclaredConstructor(new Class[0]).newInstance(new Object[0]));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}