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