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.SystemClock; 20 21 import java.io.PrintWriter; 22 import java.lang.ref.WeakReference; 23 import java.util.Collection; 24 import java.util.Map; 25 import java.util.function.Predicate; 26 27 /** 28 * Tracks the size of collections. 29 */ 30 public class TrackedCollections { 31 private static final long MILLIS_IN_MINUTE = 60 * 1000; 32 private static final long HALFWAY_DELAY = 30 * MILLIS_IN_MINUTE; 33 34 private final WeakIdentityHashMap<Collection<?>, CollectionState> mCollections 35 = new WeakIdentityHashMap<>(); 36 37 /** 38 * @see LeakDetector#trackCollection(Collection, String) 39 */ track(Collection<?> collection, String tag)40 public synchronized void track(Collection<?> collection, String tag) { 41 CollectionState collectionState = mCollections.get(collection); 42 if (collectionState == null) { 43 collectionState = new CollectionState(); 44 collectionState.tag = tag; 45 collectionState.startUptime = SystemClock.uptimeMillis(); 46 mCollections.put(collection, collectionState); 47 } 48 if (collectionState.halfwayCount == -1 49 && SystemClock.uptimeMillis() - collectionState.startUptime > HALFWAY_DELAY) { 50 collectionState.halfwayCount = collectionState.lastCount; 51 } 52 collectionState.lastCount = collection.size(); 53 collectionState.lastUptime = SystemClock.uptimeMillis(); 54 } 55 56 private static class CollectionState { 57 String tag; 58 long startUptime; 59 /** The number of elements in the collection at startUptime + HALFWAY_DELAY */ 60 int halfwayCount = -1; 61 /** The number of elements in the collection at lastUptime */ 62 int lastCount = -1; 63 long lastUptime; 64 65 /** 66 * Dump statistics about the tracked collection: 67 * - the tag 68 * - average elements inserted per hour during 69 * - the first 30min of its existence 70 * - after the first 30min 71 * - overall 72 * - the current size of the collection 73 */ dump(PrintWriter pw)74 void dump(PrintWriter pw) { 75 long now = SystemClock.uptimeMillis(); 76 77 pw.format("%s: %.2f (start-30min) / %.2f (30min-now) / %.2f (start-now)" 78 + " (growth rate in #/hour); %d (current size)", 79 tag, 80 ratePerHour(startUptime, 0, startUptime + HALFWAY_DELAY, halfwayCount), 81 ratePerHour(startUptime + HALFWAY_DELAY, halfwayCount, now, lastCount), 82 ratePerHour(startUptime, 0, now, lastCount), 83 lastCount); 84 } 85 ratePerHour(long uptime1, int count1, long uptime2, int count2)86 private float ratePerHour(long uptime1, int count1, long uptime2, int count2) { 87 if (uptime1 >= uptime2 || count1 < 0 || count2 < 0) { 88 return Float.NaN; 89 } 90 return ((float) count2 - count1) / (uptime2 - uptime1) * 60 * MILLIS_IN_MINUTE; 91 } 92 } 93 dump(PrintWriter pw, Predicate<Collection<?>> filter)94 public synchronized void dump(PrintWriter pw, Predicate<Collection<?>> filter) { 95 for (Map.Entry<WeakReference<Collection<?>>, CollectionState> entry 96 : mCollections.entrySet()) { 97 Collection<?> key = entry.getKey().get(); 98 if (filter == null || key != null && filter.test(key)) { 99 entry.getValue().dump(pw); 100 pw.println(); 101 } 102 } 103 } 104 } 105