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