blob: 5577daf7e4bfebddd0db777c47cf3dded419b82b [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.util.leak;
import android.os.SystemClock;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Map;
import java.util.function.Predicate;
/**
* Tracks the size of collections.
*/
public class TrackedCollections {
private static final long MILLIS_IN_MINUTE = 60 * 1000;
private static final long HALFWAY_DELAY = 30 * MILLIS_IN_MINUTE;
private final WeakIdentityHashMap<Collection<?>, CollectionState> mCollections
= new WeakIdentityHashMap<>();
/**
* @see LeakDetector#trackCollection(Collection, String)
*/
public synchronized void track(Collection<?> collection, String tag) {
CollectionState collectionState = mCollections.get(collection);
if (collectionState == null) {
collectionState = new CollectionState();
collectionState.tag = tag;
collectionState.startUptime = SystemClock.uptimeMillis();
mCollections.put(collection, collectionState);
}
if (collectionState.halfwayCount == -1
&& SystemClock.uptimeMillis() - collectionState.startUptime > HALFWAY_DELAY) {
collectionState.halfwayCount = collectionState.lastCount;
}
collectionState.lastCount = collection.size();
collectionState.lastUptime = SystemClock.uptimeMillis();
}
private static class CollectionState {
String tag;
long startUptime;
/** The number of elements in the collection at startUptime + HALFWAY_DELAY */
int halfwayCount = -1;
/** The number of elements in the collection at lastUptime */
int lastCount = -1;
long lastUptime;
/**
* Dump statistics about the tracked collection:
* - the tag
* - average elements inserted per hour during
* - the first 30min of its existence
* - after the first 30min
* - overall
* - the current size of the collection
*/
void dump(PrintWriter pw) {
long now = SystemClock.uptimeMillis();
pw.format("%s: %.2f (start-30min) / %.2f (30min-now) / %.2f (start-now)"
+ " (growth rate in #/hour); %d (current size)",
tag,
ratePerHour(startUptime, 0, startUptime + HALFWAY_DELAY, halfwayCount),
ratePerHour(startUptime + HALFWAY_DELAY, halfwayCount, now, lastCount),
ratePerHour(startUptime, 0, now, lastCount),
lastCount);
}
private float ratePerHour(long uptime1, int count1, long uptime2, int count2) {
if (uptime1 >= uptime2 || count1 < 0 || count2 < 0) {
return Float.NaN;
}
return ((float) count2 - count1) / (uptime2 - uptime1) * 60 * MILLIS_IN_MINUTE;
}
}
public synchronized void dump(PrintWriter pw, Predicate<Collection<?>> filter) {
for (Map.Entry<WeakReference<Collection<?>>, CollectionState> entry
: mCollections.entrySet()) {
Collection<?> key = entry.getKey().get();
if (filter == null || key != null && filter.test(key)) {
entry.getValue().dump(pw);
pw.println();
}
}
}
}