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