1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.util.leak; 18 19 import android.os.Build; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 import com.android.internal.util.IndentingPrintWriter; 23 import com.android.systemui.Dumpable; 24 25 import java.io.FileDescriptor; 26 import java.io.PrintWriter; 27 import java.util.Collection; 28 29 /** 30 * Detects leaks. 31 */ 32 public class LeakDetector implements Dumpable { 33 34 public static final boolean ENABLED = Build.IS_DEBUGGABLE; 35 36 private final TrackedCollections mTrackedCollections; 37 private final TrackedGarbage mTrackedGarbage; 38 private final TrackedObjects mTrackedObjects; 39 40 @VisibleForTesting LeakDetector(TrackedCollections trackedCollections, TrackedGarbage trackedGarbage, TrackedObjects trackedObjects)41 public LeakDetector(TrackedCollections trackedCollections, 42 TrackedGarbage trackedGarbage, 43 TrackedObjects trackedObjects) { 44 mTrackedCollections = trackedCollections; 45 mTrackedGarbage = trackedGarbage; 46 mTrackedObjects = trackedObjects; 47 } 48 49 /** 50 * Tracks an instance that has a high leak risk (i.e. has complex ownership and references 51 * a large amount of memory). 52 * 53 * The LeakDetector will monitor and keep weak references to such instances, dump statistics 54 * about them in a bugreport, and in the future dump the heap if their count starts growing 55 * unreasonably. 56 * 57 * This should be called when the instance is first constructed. 58 */ trackInstance(T object)59 public <T> void trackInstance(T object) { 60 if (mTrackedObjects != null) { 61 mTrackedObjects.track(object); 62 } 63 } 64 65 /** 66 * Tracks a collection that is at risk of leaking large objects, e.g. a collection of 67 * dynamically registered listeners. 68 * 69 * The LeakDetector will monitor and keep weak references to such collections, dump 70 * statistics about them in a bugreport, and in the future dump the heap if their size starts 71 * growing unreasonably. 72 * 73 * This should be called whenever the collection grows. 74 * 75 * @param tag A tag for labeling the collection in a bugreport 76 */ trackCollection(Collection<T> collection, String tag)77 public <T> void trackCollection(Collection<T> collection, String tag) { 78 if (mTrackedCollections != null) { 79 mTrackedCollections.track(collection, tag); 80 } 81 } 82 83 /** 84 * Tracks an instance that should become garbage soon. 85 * 86 * The LeakDetector will monitor and keep weak references to such garbage, dump 87 * statistics about them in a bugreport, and in the future dump the heap if it is not 88 * collected reasonably soon. 89 * 90 * This should be called when the last strong reference to the instance is dropped. 91 */ trackGarbage(Object o)92 public void trackGarbage(Object o) { 93 if (mTrackedGarbage != null) { 94 mTrackedGarbage.track(o); 95 } 96 } 97 getTrackedGarbage()98 TrackedGarbage getTrackedGarbage() { 99 return mTrackedGarbage; 100 } 101 102 @Override dump(FileDescriptor df, PrintWriter w, String[] args)103 public void dump(FileDescriptor df, PrintWriter w, String[] args) { 104 IndentingPrintWriter pw = new IndentingPrintWriter(w, " "); 105 106 pw.println("SYSUI LEAK DETECTOR"); 107 pw.increaseIndent(); 108 109 if (mTrackedCollections != null && mTrackedGarbage != null) { 110 pw.println("TrackedCollections:"); 111 pw.increaseIndent(); 112 mTrackedCollections.dump(pw, (col) -> !TrackedObjects.isTrackedObject(col)); 113 pw.decreaseIndent(); 114 pw.println(); 115 116 pw.println("TrackedObjects:"); 117 pw.increaseIndent(); 118 mTrackedCollections.dump(pw, TrackedObjects::isTrackedObject); 119 pw.decreaseIndent(); 120 pw.println(); 121 122 pw.print("TrackedGarbage:"); 123 pw.increaseIndent(); 124 mTrackedGarbage.dump(pw); 125 pw.decreaseIndent(); 126 } else { 127 pw.println("disabled"); 128 } 129 pw.decreaseIndent(); 130 pw.println(); 131 } 132 create()133 public static LeakDetector create() { 134 if (ENABLED) { 135 TrackedCollections collections = new TrackedCollections(); 136 return new LeakDetector(collections, new TrackedGarbage(collections), 137 new TrackedObjects(collections)); 138 } else { 139 return new LeakDetector(null, null, null); 140 } 141 } 142 } 143