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