diff options
| -rw-r--r-- | core/java/android/os/BinderProxy.java | 128 | ||||
| -rw-r--r-- | core/jni/android_util_Binder.cpp | 15 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 15 |
3 files changed, 101 insertions, 57 deletions
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index e492f88d4329..591370ff728b 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -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) { @@ -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]); } } @@ -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. - * - * Note: this method is not thread-safe; callers must serialize with other - * accesses to sProxyMap, in particular {@link #getInstance(long, long)}. - * - * @hide - */ - private static void dumpProxyDebugInfo() { + * 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. + * + * @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 + */ + public static void dumpProxyDebugInfo() { if (Build.IS_DEBUGGABLE) { - 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. + synchronized (sProxyMap) { + sProxyMap.dumpProxyInterfaceCounts(); + 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. * @@ -331,21 +376,23 @@ public final class BinderProxy implements IBinder { */ private static BinderProxy getInstance(long nativeData, long iBinder) { BinderProxy result; - try { - result = sProxyMap.get(iBinder); - if (result != null) { - return result; + synchronized (sProxyMap) { + try { + result = sProxyMap.get(iBinder); + if (result != null) { + return result; + } + result = new BinderProxy(nativeData); + } catch (Throwable e) { + // We're throwing an exception (probably OOME); don't drop nativeData. + NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer, + nativeData); + throw e; } - result = new BinderProxy(nativeData); - } catch (Throwable e) { - // We're throwing an exception (probably OOME); don't drop nativeData. - NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer, - nativeData); - throw e; + NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData); + // The registry now owns nativeData, even if registration threw an exception. + sProxyMap.set(iBinder, result); } - NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData); - // The registry now owns nativeData, even if registration threw an exception. - sProxyMap.set(iBinder, result); return result; } @@ -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); } diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 9341d9a48913..adff4d644c08 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -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. @@ -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); @@ -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"); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7e3c28a3b683..3c8970f0d859 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -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; @@ -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 { @@ -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++; @@ -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); |