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