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