1e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos/*
2e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * Copyright (C) 2017 The Android Open Source Project
3e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos *
4e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * Licensed under the Apache License, Version 2.0 (the "License");
5e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * you may not use this file except in compliance with the License.
6e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * You may obtain a copy of the License at
7e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos *
8e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos *      http://www.apache.org/licenses/LICENSE-2.0
9e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos *
10e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * Unless required by applicable law or agreed to in writing, software
11e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * distributed under the License is distributed on an "AS IS" BASIS,
12e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * See the License for the specific language governing permissions and
14e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * limitations under the License.
15e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos */
16e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
17e1e0b483be87d23383832aa6b558364a730c690fAdrian Roospackage com.android.systemui.util.leak;
18e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
19e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport android.os.SystemClock;
20e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
21e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport java.io.PrintWriter;
22e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport java.lang.ref.WeakReference;
23e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport java.util.Collection;
24e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport java.util.Map;
25e1e0b483be87d23383832aa6b558364a730c690fAdrian Roosimport java.util.function.Predicate;
26e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
27e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos/**
28e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos * Tracks the size of collections.
29e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos */
30e1e0b483be87d23383832aa6b558364a730c690fAdrian Roospublic class TrackedCollections {
31e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    private static final long MILLIS_IN_MINUTE = 60 * 1000;
32e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    private static final long HALFWAY_DELAY = 30 * MILLIS_IN_MINUTE;
33e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
34e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    private final WeakIdentityHashMap<Collection<?>, CollectionState> mCollections
35e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            = new WeakIdentityHashMap<>();
36e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
37e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    /**
38e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos     * @see LeakDetector#trackCollection(Collection, String)
39e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos     */
40e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    public synchronized void track(Collection<?> collection, String tag) {
41e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        CollectionState collectionState = mCollections.get(collection);
42e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        if (collectionState == null) {
43e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            collectionState = new CollectionState();
44e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            collectionState.tag = tag;
45e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            collectionState.startUptime = SystemClock.uptimeMillis();
46e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            mCollections.put(collection, collectionState);
47e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        }
48e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        if (collectionState.halfwayCount == -1
49e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                && SystemClock.uptimeMillis() - collectionState.startUptime > HALFWAY_DELAY) {
50e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            collectionState.halfwayCount = collectionState.lastCount;
51e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        }
52e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        collectionState.lastCount = collection.size();
53e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        collectionState.lastUptime = SystemClock.uptimeMillis();
54e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    }
55e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
56e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    private static class CollectionState {
57e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        String tag;
58e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        long startUptime;
59e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        /** The number of elements in the collection at startUptime + HALFWAY_DELAY */
60e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        int halfwayCount = -1;
61e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        /** The number of elements in the collection at lastUptime */
62e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        int lastCount = -1;
63e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        long lastUptime;
64e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
65e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        /**
66e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         * Dump statistics about the tracked collection:
67e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         * - the tag
68e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         * - average elements inserted per hour during
69e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         *   - the first 30min of its existence
70e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         *   - after the first 30min
71e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         *   - overall
72e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         * - the current size of the collection
73e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos         */
74e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        void dump(PrintWriter pw) {
75e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            long now = SystemClock.uptimeMillis();
76e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
77e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            pw.format("%s: %.2f (start-30min) / %.2f (30min-now) / %.2f (start-now)"
78e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                            + " (growth rate in #/hour); %d (current size)",
79e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                    tag,
80e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                    ratePerHour(startUptime, 0, startUptime + HALFWAY_DELAY, halfwayCount),
81e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                    ratePerHour(startUptime + HALFWAY_DELAY, halfwayCount, now, lastCount),
82e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                    ratePerHour(startUptime, 0, now, lastCount),
83e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                    lastCount);
84e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        }
85e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
86e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        private float ratePerHour(long uptime1, int count1, long uptime2, int count2) {
87e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            if (uptime1 >= uptime2 || count1 < 0 || count2 < 0) {
88e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                return Float.NaN;
89e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            }
90e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            return ((float) count2 - count1) / (uptime2 - uptime1) * 60 * MILLIS_IN_MINUTE;
91e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        }
92e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    }
93e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos
94e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    public synchronized void dump(PrintWriter pw, Predicate<Collection<?>> filter) {
95e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        for (Map.Entry<WeakReference<Collection<?>>, CollectionState> entry
96e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                : mCollections.entrySet()) {
97e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            Collection<?> key = entry.getKey().get();
98e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            if (filter == null || key != null && filter.test(key)) {
99e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                entry.getValue().dump(pw);
100e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos                pw.println();
101e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos            }
102e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos        }
103e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos    }
104e1e0b483be87d23383832aa6b558364a730c690fAdrian Roos}
105