Loading core/java/android/os/BinderProxy.java +87 −41 Original line number Diff line number Diff line Loading @@ -225,10 +225,11 @@ public final class BinderProxy implements IBinder { } } /** * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps. */ private void dumpProxyInterfaceCounts() { private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) { if (maxToReturn < 0) { throw new IllegalArgumentException("negative interface count"); } Map<String, Integer> counts = new HashMap<>(); for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) { if (a != null) { Loading Loading @@ -258,13 +259,30 @@ public final class BinderProxy implements IBinder { } Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray( new Map.Entry[counts.size()]); Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> 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()); int returnCount = Math.min(maxToReturn, sorted.length); InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount]; for (int i = 0; i < returnCount; i++) { ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue()); } return ifaceCounts; } static final int MAX_NUM_INTERFACES_TO_DUMP = 10; /** * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps. */ private void dumpProxyInterfaceCounts() { final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP); Log.v(Binder.TAG, "BinderProxy descriptor histogram " + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):"); for (int i = 0; i < sorted.length; i++) { Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]); } } Loading Loading @@ -296,30 +314,57 @@ public final class BinderProxy implements IBinder { new ArrayList[MAIN_INDEX_SIZE]; } private static ProxyMap sProxyMap = new ProxyMap(); @GuardedBy("sProxyMap") private static final ProxyMap sProxyMap = new ProxyMap(); /** * Dump proxy debug information. * Simple pair-value class to store number of binder proxy interfaces live in this process. */ public static final class InterfaceCount { private final String mInterfaceName; private final int mCount; InterfaceCount(String interfaceName, int count) { mInterfaceName = interfaceName; mCount = count; } @Override public String toString() { return mInterfaceName + " x" + Integer.toString(mCount); } } /** * Get a sorted array with entries mapping proxy interface names to the number * of live proxies with those names. * * Note: this method is not thread-safe; callers must serialize with other * accesses to sProxyMap, in particular {@link #getInstance(long, long)}. * @param num maximum number of proxy interface counts to return. Use * Integer.MAX_VALUE to retrieve all * @hide */ public static InterfaceCount[] getSortedInterfaceCounts(int num) { synchronized (sProxyMap) { return sProxyMap.getSortedInterfaceCounts(num); } } /** * Dump proxy debug information. * * @hide */ private static void dumpProxyDebugInfo() { public static void dumpProxyDebugInfo() { if (Build.IS_DEBUGGABLE) { synchronized (sProxyMap) { sProxyMap.dumpProxyInterfaceCounts(); // Note that we don't call dumpPerUidProxyCounts(); this is because this // method may be called as part of the uid limit being hit, and calling // back into the UID tracking code would cause us to try to acquire a mutex // that is held during that callback. sProxyMap.dumpPerUidProxyCounts(); } } } /** * Return a BinderProxy for IBinder. * This method is thread-hostile! The (native) caller serializes getInstance() calls using * gProxyLock. * If we previously returned a BinderProxy bp for the same iBinder, and bp is still * in use, then we return the same bp. * Loading @@ -331,6 +376,7 @@ public final class BinderProxy implements IBinder { */ private static BinderProxy getInstance(long nativeData, long iBinder) { BinderProxy result; synchronized (sProxyMap) { try { result = sProxyMap.get(iBinder); if (result != null) { Loading @@ -346,6 +392,7 @@ public final class BinderProxy implements IBinder { NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData); // The registry now owns nativeData, even if registration threw an exception. sProxyMap.set(iBinder, result); } return result; } Loading Loading @@ -526,12 +573,11 @@ public final class BinderProxy implements IBinder { } } private static final void sendDeathNotice(DeathRecipient recipient) { private static void sendDeathNotice(DeathRecipient recipient) { if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient); try { recipient.binderDied(); } catch (RuntimeException exc) { } catch (RuntimeException exc) { Log.w("BinderNative", "Uncaught exception from death notification", exc); } Loading core/jni/android_util_Binder.cpp +0 −15 Original line number Diff line number Diff line Loading @@ -110,7 +110,6 @@ static struct binderproxy_offsets_t jclass mClass; jmethodID mGetInstance; jmethodID mSendDeathNotice; jmethodID mDumpProxyDebugInfo; // Object state. jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData. Loading Loading @@ -1038,18 +1037,6 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) static void android_os_BinderInternal_proxyLimitcallback(int uid) { JNIEnv *env = AndroidRuntime::getJNIEnv(); { // Calls into BinderProxy must be serialized AutoMutex _l(gProxyLock); env->CallStaticObjectMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mDumpProxyDebugInfo); } if (env->ExceptionCheck()) { ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); report_exception(env, excep.get(), "*** Uncaught exception in dumpProxyDebugInfo"); } env->CallStaticVoidMethod(gBinderInternalOffsets.mClass, gBinderInternalOffsets.mProxyLimitCallback, uid); Loading Loading @@ -1439,8 +1426,6 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) "(JJ)Landroid/os/BinderProxy;"); gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V"); gBinderProxyOffsets.mDumpProxyDebugInfo = GetStaticMethodIDOrDie(env, clazz, "dumpProxyDebugInfo", "()V"); gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J"); clazz = FindClassOrDie(env, "java/lang/Class"); Loading services/core/java/com/android/server/am/ActivityManagerService.java +14 −1 Original line number Diff line number Diff line Loading @@ -318,6 +318,7 @@ import android.net.ProxyInfo; import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; import android.os.BinderProxy; import android.os.Build; import android.os.Bundle; import android.os.Debug; Loading Loading @@ -15247,6 +15248,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void onLimitReached(int uid) { Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " + Process.myUid()); BinderProxy.dumpProxyDebugInfo(); if (uid == Process.SYSTEM_UID) { Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); } else { Loading Loading @@ -16098,8 +16100,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } else if ("binder-proxies".equals(cmd)) { if (opti >= args.length) { dumpBinderProxyInterfaceCounts(pw, "Top proxy interface names held by SYSTEM"); dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(), "Counts of Binder Proxies held by SYSTEM"); "Number of proxies per uid held by SYSTEM"); } else { String uid = args[opti]; opti++; Loading Loading @@ -16602,6 +16606,15 @@ public class ActivityManagerService extends IActivityManager.Stub return printed; } void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) { final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50); pw.println(header); for (int i = 0; i < proxyCounts.length; i++) { pw.println(" #" + (i + 1) + ": " + proxyCounts[i]); } } boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) { if(counts != null) { pw.println(header); Loading
core/java/android/os/BinderProxy.java +87 −41 Original line number Diff line number Diff line Loading @@ -225,10 +225,11 @@ public final class BinderProxy implements IBinder { } } /** * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps. */ private void dumpProxyInterfaceCounts() { private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) { if (maxToReturn < 0) { throw new IllegalArgumentException("negative interface count"); } Map<String, Integer> counts = new HashMap<>(); for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) { if (a != null) { Loading Loading @@ -258,13 +259,30 @@ public final class BinderProxy implements IBinder { } Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray( new Map.Entry[counts.size()]); Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> 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()); int returnCount = Math.min(maxToReturn, sorted.length); InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount]; for (int i = 0; i < returnCount; i++) { ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue()); } return ifaceCounts; } static final int MAX_NUM_INTERFACES_TO_DUMP = 10; /** * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps. */ private void dumpProxyInterfaceCounts() { final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP); Log.v(Binder.TAG, "BinderProxy descriptor histogram " + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):"); for (int i = 0; i < sorted.length; i++) { Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]); } } Loading Loading @@ -296,30 +314,57 @@ public final class BinderProxy implements IBinder { new ArrayList[MAIN_INDEX_SIZE]; } private static ProxyMap sProxyMap = new ProxyMap(); @GuardedBy("sProxyMap") private static final ProxyMap sProxyMap = new ProxyMap(); /** * Dump proxy debug information. * Simple pair-value class to store number of binder proxy interfaces live in this process. */ public static final class InterfaceCount { private final String mInterfaceName; private final int mCount; InterfaceCount(String interfaceName, int count) { mInterfaceName = interfaceName; mCount = count; } @Override public String toString() { return mInterfaceName + " x" + Integer.toString(mCount); } } /** * Get a sorted array with entries mapping proxy interface names to the number * of live proxies with those names. * * Note: this method is not thread-safe; callers must serialize with other * accesses to sProxyMap, in particular {@link #getInstance(long, long)}. * @param num maximum number of proxy interface counts to return. Use * Integer.MAX_VALUE to retrieve all * @hide */ public static InterfaceCount[] getSortedInterfaceCounts(int num) { synchronized (sProxyMap) { return sProxyMap.getSortedInterfaceCounts(num); } } /** * Dump proxy debug information. * * @hide */ private static void dumpProxyDebugInfo() { public static void dumpProxyDebugInfo() { if (Build.IS_DEBUGGABLE) { synchronized (sProxyMap) { sProxyMap.dumpProxyInterfaceCounts(); // Note that we don't call dumpPerUidProxyCounts(); this is because this // method may be called as part of the uid limit being hit, and calling // back into the UID tracking code would cause us to try to acquire a mutex // that is held during that callback. sProxyMap.dumpPerUidProxyCounts(); } } } /** * Return a BinderProxy for IBinder. * This method is thread-hostile! The (native) caller serializes getInstance() calls using * gProxyLock. * If we previously returned a BinderProxy bp for the same iBinder, and bp is still * in use, then we return the same bp. * Loading @@ -331,6 +376,7 @@ public final class BinderProxy implements IBinder { */ private static BinderProxy getInstance(long nativeData, long iBinder) { BinderProxy result; synchronized (sProxyMap) { try { result = sProxyMap.get(iBinder); if (result != null) { Loading @@ -346,6 +392,7 @@ public final class BinderProxy implements IBinder { NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData); // The registry now owns nativeData, even if registration threw an exception. sProxyMap.set(iBinder, result); } return result; } Loading Loading @@ -526,12 +573,11 @@ public final class BinderProxy implements IBinder { } } private static final void sendDeathNotice(DeathRecipient recipient) { private static void sendDeathNotice(DeathRecipient recipient) { if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient); try { recipient.binderDied(); } catch (RuntimeException exc) { } catch (RuntimeException exc) { Log.w("BinderNative", "Uncaught exception from death notification", exc); } Loading
core/jni/android_util_Binder.cpp +0 −15 Original line number Diff line number Diff line Loading @@ -110,7 +110,6 @@ static struct binderproxy_offsets_t jclass mClass; jmethodID mGetInstance; jmethodID mSendDeathNotice; jmethodID mDumpProxyDebugInfo; // Object state. jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData. Loading Loading @@ -1038,18 +1037,6 @@ static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz) static void android_os_BinderInternal_proxyLimitcallback(int uid) { JNIEnv *env = AndroidRuntime::getJNIEnv(); { // Calls into BinderProxy must be serialized AutoMutex _l(gProxyLock); env->CallStaticObjectMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mDumpProxyDebugInfo); } if (env->ExceptionCheck()) { ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred()); report_exception(env, excep.get(), "*** Uncaught exception in dumpProxyDebugInfo"); } env->CallStaticVoidMethod(gBinderInternalOffsets.mClass, gBinderInternalOffsets.mProxyLimitCallback, uid); Loading Loading @@ -1439,8 +1426,6 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) "(JJ)Landroid/os/BinderProxy;"); gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V"); gBinderProxyOffsets.mDumpProxyDebugInfo = GetStaticMethodIDOrDie(env, clazz, "dumpProxyDebugInfo", "()V"); gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J"); clazz = FindClassOrDie(env, "java/lang/Class"); Loading
services/core/java/com/android/server/am/ActivityManagerService.java +14 −1 Original line number Diff line number Diff line Loading @@ -318,6 +318,7 @@ import android.net.ProxyInfo; import android.net.Uri; import android.os.BatteryStats; import android.os.Binder; import android.os.BinderProxy; import android.os.Build; import android.os.Bundle; import android.os.Debug; Loading Loading @@ -15247,6 +15248,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void onLimitReached(int uid) { Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " + Process.myUid()); BinderProxy.dumpProxyDebugInfo(); if (uid == Process.SYSTEM_UID) { Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); } else { Loading Loading @@ -16098,8 +16100,10 @@ public class ActivityManagerService extends IActivityManager.Stub } } else if ("binder-proxies".equals(cmd)) { if (opti >= args.length) { dumpBinderProxyInterfaceCounts(pw, "Top proxy interface names held by SYSTEM"); dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(), "Counts of Binder Proxies held by SYSTEM"); "Number of proxies per uid held by SYSTEM"); } else { String uid = args[opti]; opti++; Loading Loading @@ -16602,6 +16606,15 @@ public class ActivityManagerService extends IActivityManager.Stub return printed; } void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) { final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50); pw.println(header); for (int i = 0; i < proxyCounts.length; i++) { pw.println(" #" + (i + 1) + ": " + proxyCounts[i]); } } boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) { if(counts != null) { pw.println(header);