Skip to content

Commit

Permalink
Swiching to HAHA 2.0.2 / Perflib
Browse files Browse the repository at this point in the history
* Switched to HAHA 2.0.2 which is based on perflib instead of MAT
* AnalysisResult.failure is now a `Throwable` instead of an `Exception`. Main goal is to catch and correctly report OOMs while parsing.
* Added ARRAY_ENTRY to LeakTraceElement.Type for references through array entries.
* Huge speed improvements when performing the shortest path with excluded references: it's now tailored to our needs instead of doing it on a loop.
* Renamed `ExcludedRefs` fields.
* Each `ExcludedRef` entry can now be ignored entirely or "kept only if no other path".
* Added support for ignoring all fields (static and non static) for a given class.
* Improved the heap dump analyzer test and added a heap dump for M Preview 2.
  • Loading branch information
pyricau committed Jul 22, 2015
1 parent f8bcc4e commit 66dddbc
Show file tree
Hide file tree
Showing 15 changed files with 834 additions and 392 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ subprojects {
// maven {
// url 'https://oss.sonatype.org/content/repositories/snapshots/'
// }
// mavenLocal()
}

buildscript {
Expand Down
2 changes: 1 addition & 1 deletion leakcanary-analyzer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repositories {
}

dependencies {
compile 'com.squareup.haha:haha:1.3'
compile 'com.squareup.haha:haha:2.0.2'
compile project(':leakcanary-watcher')
testCompile 'junit:junit:4.12'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.haha.perflib;

public final class HahaSpy {

public static Instance allocatingThread(Instance instance) {
Snapshot snapshot = instance.mHeap.mSnapshot;
int threadSerialNumber;
if (instance instanceof RootObj) {
threadSerialNumber = ((RootObj) instance).mThread;
} else {
threadSerialNumber = instance.mStack.mThreadSerialNumber;
}
ThreadObj thread = snapshot.getThread(threadSerialNumber);
return snapshot.findInstance(thread.mId);
}

private HahaSpy() {
throw new AssertionError();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public static AnalysisResult leakDetected(boolean excludedLeak, String className
return new AnalysisResult(true, excludedLeak, className, leakTrace, null, analysisDurationMs);
}

public static AnalysisResult failure(Exception exception, long analysisDurationMs) {
return new AnalysisResult(false, false, null, null, exception, analysisDurationMs);
public static AnalysisResult failure(Throwable failure, long analysisDurationMs) {
return new AnalysisResult(false, false, null, null, failure, analysisDurationMs);
}

/** True if a leak was found in the heap dump. */
Expand All @@ -54,13 +54,13 @@ public static AnalysisResult failure(Exception exception, long analysisDurationM
public final LeakTrace leakTrace;

/** Null unless the analysis failed. */
public final Exception failure;
public final Throwable failure;

/** Total time spent analyzing the heap. */
public final long analysisDurationMs;

private AnalysisResult(boolean leakFound, boolean excludedLeak, String className,
LeakTrace leakTrace, Exception failure, long analysisDurationMs) {
LeakTrace leakTrace, Throwable failure, long analysisDurationMs) {
this.leakFound = leakFound;
this.excludedLeak = excludedLeak;
this.className = className;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.leakcanary;

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.Heap;
import com.squareup.haha.perflib.Instance;
import com.squareup.haha.perflib.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.squareup.leakcanary.Preconditions.checkNotNull;
import static java.util.Arrays.asList;

public final class HahaHelper {

private static final Set<String> WRAPPER_TYPES = new HashSet<>(
asList(Boolean.class.getName(), Character.class.getName(), Float.class.getName(),
Double.class.getName(), Byte.class.getName(), Short.class.getName(),
Integer.class.getName(), Long.class.getName()));

static String fieldToString(Map.Entry<Field, Object> entry) {
return fieldToString(entry.getKey(), entry.getValue());
}

static String fieldToString(ClassInstance.FieldValue fieldValue) {
return fieldToString(fieldValue.getField(), fieldValue.getValue());
}

static String fieldToString(Field field, Object value) {
return field.getName() + " = " + value;
}

static String threadName(Instance holder) {
List<ClassInstance.FieldValue> values = classInstanceValues(holder);
Object nameField = fieldValue(values, "name");
return asString(nameField);
}

static boolean extendsThread(ClassObj clazz) {
boolean extendsThread = false;
ClassObj parentClass = clazz;
while (parentClass.getSuperClassObj() != null) {
if (clazz.getClassName().equals(Thread.class.getName())) {
extendsThread = true;
break;
}
parentClass = parentClass.getSuperClassObj();
}
return extendsThread;
}

static String asString(Object stringObject) {
Instance instance = (Instance) stringObject;
List<ClassInstance.FieldValue> values = classInstanceValues(instance);

Integer count = fieldValue(values, "count");
Object value = fieldValue(values, "value");
Integer offset;
ArrayInstance charArray;
if (isCharArray(value)) {
charArray = (ArrayInstance) value;
offset = fieldValue(values, "offset");
} else {
// In M preview 2+, the underlying char buffer resides in the heap with ID equalling the
// String's ID + 16.
// https://android-review.googlesource.com/#/c/160380/2/android/src/com/android/tools/idea/
// editors/hprof/descriptors/InstanceFieldDescriptorImpl.java
Heap heap = instance.getHeap();
Instance inlineInstance = heap.getInstance(instance.getId() + 16);
if (isCharArray(inlineInstance)) {
charArray = (ArrayInstance) inlineInstance;
offset = 0;
} else {
throw new UnsupportedOperationException("Could not find char array in " + instance);
}
}
checkNotNull(count, "count");
checkNotNull(charArray, "charArray");
checkNotNull(offset, "offset");

if (count == 0) {
return "";
}

char[] chars = charArray.asCharArray(offset, count);

return new String(chars);
}

public static boolean isPrimitiveWrapper(Object value) {
if (!(value instanceof ClassInstance)) {
return false;
}
return WRAPPER_TYPES.contains(((ClassInstance) value).getClassObj().getClassName());
}

public static boolean isPrimitiveOrWrapperArray(Object value) {
if (!(value instanceof ArrayInstance)) {
return false;
}
ArrayInstance arrayInstance = (ArrayInstance) value;
if (arrayInstance.getArrayType() != Type.OBJECT) {
return true;
}
return WRAPPER_TYPES.contains(arrayInstance.getClassObj().getClassName());
}

private static boolean isCharArray(Object value) {
return value instanceof ArrayInstance && ((ArrayInstance) value).getArrayType() == Type.CHAR;
}

static List<ClassInstance.FieldValue> classInstanceValues(Instance instance) {
ClassInstance classInstance = (ClassInstance) instance;
return classInstance.getValues();
}

static <T> T fieldValue(List<ClassInstance.FieldValue> values, String fieldName) {
for (ClassInstance.FieldValue fieldValue : values) {
if (fieldValue.getField().getName().equals(fieldName)) {
//noinspection unchecked
return (T) fieldValue.getValue();
}
}
throw new IllegalArgumentException("Field " + fieldName + " does not exists");
}

private HahaHelper() {
throw new AssertionError();
}
}
Loading

0 comments on commit 66dddbc

Please sign in to comment.