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.io.Writer; 28 import java.util.Collection; 29 30 /** 31 * Detects leaks. 32 */ 33 public class LeakDetector implements Dumpable { 34 35 public static final boolean ENABLED = Build.IS_DEBUGGABLE; 36 37 private final TrackedCollections mTrackedCollections; 38 private final TrackedGarbage mTrackedGarbage; 39 private final TrackedObjects mTrackedObjects; 40 41 @VisibleForTesting LeakDetector(TrackedCollections trackedCollections, TrackedGarbage trackedGarbage, TrackedObjects trackedObjects)42 public LeakDetector(TrackedCollections trackedCollections, 43 TrackedGarbage trackedGarbage, 44 TrackedObjects trackedObjects) { 45 mTrackedCollections = trackedCollections; 46 mTrackedGarbage = trackedGarbage; 47 mTrackedObjects = trackedObjects; 48 } 49 50 /** 51 * Tracks an instance that has a high leak risk (i.e. has complex ownership and references 52 * a large amount of memory). 53 * 54 * The LeakDetector will monitor and keep weak references to such instances, dump statistics 55 * about them in a bugreport, and in the future dump the heap if their count starts growing 56 * unreasonably. 57 * 58 * This should be called when the instance is first constructed. 59 */ trackInstance(T object)60 public <T> void trackInstance(T object) { 61 if (mTrackedObjects != null) { 62 mTrackedObjects.track(object); 63 } 64 } 65 66 /** 67 * Tracks a collection that is at risk of leaking large objects, e.g. a collection of 68 * dynamically registered listeners. 69 * 70 * The LeakDetector will monitor and keep weak references to such collections, dump 71 * statistics about them in a bugreport, and in the future dump the heap if their size starts 72 * growing unreasonably. 73 * 74 * This should be called whenever the collection grows. 75 * 76 * @param tag A tag for labeling the collection in a bugreport 77 */ trackCollection(Collection<T> collection, String tag)78 public <T> void trackCollection(Collection<T> collection, String tag) { 79 if (mTrackedCollections != null) { 80 mTrackedCollections.track(collection, tag); 81 } 82 } 83 84 /** 85 * Tracks an instance that should become garbage soon. 86 * 87 * The LeakDetector will monitor and keep weak references to such garbage, dump 88 * statistics about them in a bugreport, and in the future dump the heap if it is not 89 * collected reasonably soon. 90 * 91 * This should be called when the last strong reference to the instance is dropped. 92 */ trackGarbage(Object o)93 public void trackGarbage(Object o) { 94 if (mTrackedGarbage != null) { 95 mTrackedGarbage.track(o); 96 } 97 } 98 getTrackedGarbage()99 TrackedGarbage getTrackedGarbage() { 100 return mTrackedGarbage; 101 } 102 103 @Override dump(FileDescriptor df, PrintWriter w, String[] args)104 public void dump(FileDescriptor df, PrintWriter w, String[] args) { 105 IndentingPrintWriter pw = new IndentingPrintWriter(w, " "); 106 107 pw.println("SYSUI LEAK DETECTOR"); 108 pw.increaseIndent(); 109 110 if (mTrackedCollections != null && mTrackedGarbage != null) { 111 pw.println("TrackedCollections:"); 112 pw.increaseIndent(); 113 mTrackedCollections.dump(pw, (col) -> !TrackedObjects.isTrackedObject(col)); 114 pw.decreaseIndent(); 115 pw.println(); 116 117 pw.println("TrackedObjects:"); 118 pw.increaseIndent(); 119 mTrackedCollections.dump(pw, TrackedObjects::isTrackedObject); 120 pw.decreaseIndent(); 121 pw.println(); 122 123 pw.print("TrackedGarbage:"); 124 pw.increaseIndent(); 125 mTrackedGarbage.dump(pw); 126 pw.decreaseIndent(); 127 } else { 128 pw.println("disabled"); 129 } 130 pw.decreaseIndent(); 131 pw.println(); 132 } 133 create()134 public static LeakDetector create() { 135 if (ENABLED) { 136 TrackedCollections collections = new TrackedCollections(); 137 return new LeakDetector(collections, new TrackedGarbage(collections), 138 new TrackedObjects(collections)); 139 } else { 140 return new LeakDetector(null, null, null); 141 } 142 } 143 } 144