From 3fc030f08354343885281becf91a1ce053620f86 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 27 Dec 2017 09:30:45 -0800 Subject: [PATCH] Binder: Log a histogram of binder proxies to logcat on death To help investigate potential BinderProxy leaks, attempt to log a histogram of BinderProxy descriptors to the logcat before asserting. Bug: 71353150 Test: m Change-Id: I47313814160678369b15e7dc5851d8096c286b9d --- core/java/android/os/Binder.java | 58 +++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 1b707bdf73a95..5c4a40e503dbc 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -35,6 +35,9 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; /** * Base class for a remotable object, the core part of a lightweight @@ -888,17 +891,62 @@ final class BinderProxy implements IBinder { keyArray[size] = key; } if (size >= mWarnBucketSize) { - final int total_size = size(); + final int totalSize = size(); Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size - + " total = " + total_size); + + " total = " + totalSize); mWarnBucketSize += WARN_INCREMENT; - if (Build.IS_DEBUGGABLE && total_size > CRASH_AT_SIZE) { - throw new AssertionError("Binder ProxyMap has too many entries. " - + "BinderProxy leak?"); + if (Build.IS_DEBUGGABLE && totalSize > CRASH_AT_SIZE) { + diagnosticCrash(); } } } + /** + * Dump a histogram to the logcat, then throw an assertion error. Used to diagnose + * abnormally large proxy maps. + */ + private void diagnosticCrash() { + Map counts = new HashMap<>(); + for (ArrayList> a : mMainIndexValues) { + if (a != null) { + for (WeakReference weakRef : a) { + BinderProxy bp = weakRef.get(); + String key; + if (bp == null) { + key = ""; + } else { + try { + key = bp.getInterfaceDescriptor(); + } catch (Throwable t) { + key = ""; + } + } + Integer i = counts.get(key); + if (i == null) { + counts.put(key, 1); + } else { + counts.put(key, i + 1); + } + } + } + } + Map.Entry[] sorted = counts.entrySet().toArray( + new Map.Entry[counts.size()]); + Arrays.sort(sorted, (Map.Entry a, Map.Entry b) + -> b.getValue().compareTo(a.getValue())); + Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):"); + int printLength = Math.min(10, sorted.length); + for (int i = 0; i < printLength; i++) { + Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x" + + sorted[i].getValue()); + } + + // Now throw an assertion. + final int totalSize = size(); + throw new AssertionError("Binder ProxyMap has too many entries: " + totalSize + + ". BinderProxy leak?"); + } + // Corresponding ArrayLists in the following two arrays always have the same size. // They contain no empty entries. However WeakReferences in the values ArrayLists // may have been cleared.