Skip to content

Commit

Permalink
Merge pull request square#30 from square/py/custom_excluded_refs
Browse files Browse the repository at this point in the history
Customizable ExcludedRef
  • Loading branch information
swankjesse committed May 10, 2015
2 parents 554140c + 23a4793 commit 72535c0
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 82 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public final class HeapAnalyzer {
private final ExcludedRefs excludedRefs;

public HeapAnalyzer(ExcludedRefs excludedRefs) {
this(new ExcludedRefs(), excludedRefs);
this(new ExcludedRefs.Builder().build(), excludedRefs);
}

public HeapAnalyzer(ExcludedRefs baseExcludedRefs, ExcludedRefs excludedRefs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public final class AndroidExcludedRefs {
/**
* This returns the references in the leak path that should be ignored by all on Android.
*/
public static ExcludedRefs createAndroidDefaults() {
ExcludedRefs excluded = new ExcludedRefs();
public static ExcludedRefs.Builder createAndroidDefaults() {
ExcludedRefs.Builder excluded = new ExcludedRefs.Builder();
// If the FinalizerWatchdogDaemon thread is on the shortest path, then there was no other
// reference to the object and it was about to be GCed.
excluded.thread("FinalizerWatchdogDaemon");
Expand All @@ -66,8 +66,8 @@ public static ExcludedRefs createAndroidDefaults() {
* in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
* developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
*/
public static ExcludedRefs createAppDefaults() {
ExcludedRefs excluded = createAndroidDefaults();
public static ExcludedRefs.Builder createAppDefaults() {
ExcludedRefs.Builder excluded = createAndroidDefaults();
if (SDK_INT >= KITKAT && SDK_INT <= LOLLIPOP) {
// Android AOSP sometimes keeps a reference to a destroyed activity as a "nextIdle" client
// record in the android.app.ActivityThread.mActivities map.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ public static RefWatcher androidWatcher(HeapDump.Listener heapDumpListener) {
DebuggerControl debuggerControl = new AndroidDebuggerControl();
AndroidHeapDumper heapDumper = new AndroidHeapDumper();
heapDumper.cleanup();
ExcludedRefs excludedRefs = AndroidExcludedRefs.createAndroidDefaults().build();
return new RefWatcher(new AndroidWatchExecutor(), debuggerControl, GcTrigger.DEFAULT,
heapDumper, heapDumpListener);
heapDumper, heapDumpListener, excludedRefs);
}

public static void enableDisplayLeakActivity(Context context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
import android.content.Intent;
import com.squareup.leakcanary.AbstractAnalysisResultService;
import com.squareup.leakcanary.AnalysisResult;
import com.squareup.leakcanary.ExcludedRefs;
import com.squareup.leakcanary.HeapAnalyzer;
import com.squareup.leakcanary.HeapDump;

import static com.squareup.leakcanary.AndroidExcludedRefs.createAndroidDefaults;
import static com.squareup.leakcanary.AndroidExcludedRefs.createAppDefaults;

/**
* This service runs in a separate process to avoid slowing down the app process or making it run
Expand All @@ -43,16 +43,17 @@ public static void runAnalysis(Context context, HeapDump heapDump,
context.startService(intent);
}

private final HeapAnalyzer heapAnalyzer;

public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName());
heapAnalyzer = new HeapAnalyzer(createAndroidDefaults(), createAppDefaults());
}

@Override protected void onHandleIntent(Intent intent) {
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

ExcludedRefs androidExcludedDefault = createAndroidDefaults().build();
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(androidExcludedDefault, heapDump.excludedRefs);

AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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 java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import static com.squareup.leakcanary.Preconditions.checkNotNull;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;

/**
* Prevents specific references from being taken into account when computing the shortest strong
* reference path from a suspected leaking instance to the GC roots.
*
* This class lets you ignore known memory leaks that you known about. If the shortest path
* matches {@link ExcludedRefs}, than the heap analyzer should look for a longer path with nothing
* matching in {@link ExcludedRefs}.
*/
public final class ExcludedRefs implements Serializable {

public final Map<String, Set<String>> excludeFieldMap;
public final Map<String, Set<String>> excludeStaticFieldMap;
public final Set<String> excludedThreads;

private ExcludedRefs(Map<String, Set<String>> excludeFieldMap,
Map<String, Set<String>> excludeStaticFieldMap, Set<String> excludedThreads) {
// Copy + unmodifiable.
this.excludeFieldMap = unmodifiableMap(new LinkedHashMap<>(excludeFieldMap));
this.excludeStaticFieldMap = unmodifiableMap(new LinkedHashMap<>(excludeStaticFieldMap));
this.excludedThreads = unmodifiableSet(new LinkedHashSet<>(excludedThreads));
}

public static final class Builder {
private final Map<String, Set<String>> excludeFieldMap = new LinkedHashMap<>();
private final Map<String, Set<String>> excludeStaticFieldMap = new LinkedHashMap<>();
private final Set<String> excludedThreads = new LinkedHashSet<>();

public Builder instanceField(String className, String fieldName) {
checkNotNull(className, "className");
checkNotNull(fieldName, "fieldName");
Set<String> excludedFields = excludeFieldMap.get(className);
if (excludedFields == null) {
excludedFields = new LinkedHashSet<>();
excludeFieldMap.put(className, excludedFields);
}
excludedFields.add(fieldName);
return this;
}

public Builder staticField(String className, String fieldName) {
checkNotNull(className, "className");
checkNotNull(fieldName, "fieldName");
Set<String> excludedFields = excludeStaticFieldMap.get(className);
if (excludedFields == null) {
excludedFields = new LinkedHashSet<>();
excludeStaticFieldMap.put(className, excludedFields);
}
excludedFields.add(fieldName);
return this;
}

public Builder thread(String threadName) {
checkNotNull(threadName, "threadName");
excludedThreads.add(threadName);
return this;
}

public ExcludedRefs build() {
return new ExcludedRefs(excludeFieldMap, excludeStaticFieldMap, excludedThreads);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,20 @@ public interface Listener {
*/
public final String referenceName;

/** References that should be ignored when analyzing this heap dump. */
public final ExcludedRefs excludedRefs;

/** Time from the request to watch the reference until the GC was triggered. */
public final long watchDurationMs;
public final long gcDurationMs;
public final long heapDumpDurationMs;

public HeapDump(File heapDumpFile, String referenceKey, String referenceName,
long watchDurationMs, long gcDurationMs, long heapDumpDurationMs) {
ExcludedRefs excludedRefs, long watchDurationMs, long gcDurationMs, long heapDumpDurationMs) {
this.heapDumpFile = checkNotNull(heapDumpFile, "heapDumpFile");
this.referenceKey = checkNotNull(referenceKey, "referenceKey");
this.referenceName = checkNotNull(referenceName, "referenceName");
this.excludedRefs = checkNotNull(excludedRefs, "excludedRefs");
this.watchDurationMs = watchDurationMs;
this.gcDurationMs = gcDurationMs;
this.heapDumpDurationMs = heapDumpDurationMs;
Expand All @@ -61,7 +65,7 @@ public HeapDump(File heapDumpFile, String referenceKey, String referenceName,
/** Renames the heap dump file and creates a new {@link HeapDump} pointing to it. */
public HeapDump renameFile(File newFile) {
heapDumpFile.renameTo(newFile);
return new HeapDump(newFile, referenceKey, referenceName, watchDurationMs, gcDurationMs,
heapDumpDurationMs);
return new HeapDump(newFile, referenceKey, referenceName, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public final class RefWatcher {
}, new HeapDump.Listener() {
@Override public void analyze(HeapDump heapDump) {
}
});
}, new ExcludedRefs.Builder().build());

private final Executor watchExecutor;
private final DebuggerControl debuggerControl;
Expand All @@ -57,20 +57,23 @@ public final class RefWatcher {
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
private final ExcludedRefs excludedRefs;

public RefWatcher(Executor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
HeapDumper heapDumper, HeapDump.Listener heapdumpListener) {
HeapDumper heapDumper, HeapDump.Listener heapdumpListener, ExcludedRefs excludedRefs) {
this.watchExecutor = checkNotNull(watchExecutor, "watchExecutor");
this.debuggerControl = checkNotNull(debuggerControl, "debuggerControl");
this.gcTrigger = checkNotNull(gcTrigger, "gcTrigger");
this.heapDumper = checkNotNull(heapDumper, "heapDumper");
this.heapdumpListener = checkNotNull(heapdumpListener, "heapdumpListener");
this.excludedRefs = checkNotNull(excludedRefs, "excludedRefs");
retainedKeys = new CopyOnWriteArraySet<>();
queue = new ReferenceQueue<>();
}

/**
* Identical to {@link #watch(Object, String)} with an empty string reference name.
*
* @see #watch(Object, String)
*/
public void watch(Object watchedReference) {
Expand Down Expand Up @@ -124,8 +127,8 @@ void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
heapDumpDurationMs));
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
}

Expand Down

0 comments on commit 72535c0

Please sign in to comment.