diff options
| author | 2018-07-03 22:41:47 +0000 | |
|---|---|---|
| committer | 2018-07-03 22:41:47 +0000 | |
| commit | e89e466e9168f2816615ab5fb8dc550b0c3f28e2 (patch) | |
| tree | 144141d3c4873cc19d1a8251ef498a9705f8ac1c | |
| parent | 191f146b2db5c3c12c9543dce8edf9562dee5f84 (diff) | |
| parent | f82d2e7369d6eb3d0c30c0686ccfedb6df624da9 (diff) | |
Merge "Tracks exception count by class name."
| -rw-r--r-- | core/java/android/os/Binder.java | 2 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/BinderCallsStats.java | 42 | ||||
| -rw-r--r-- | core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java | 39 |
3 files changed, 81 insertions, 2 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index ac871055dc4a..5923529bdc21 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -730,7 +730,7 @@ public class Binder implements IBinder { } res = onTransact(code, data, reply, flags); } catch (RemoteException|RuntimeException e) { - binderCallsStats.callThrewException(callSession); + binderCallsStats.callThrewException(callSession, e); if (LOG_RUNTIME_EXCEPTION) { Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); } diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 75d714f3ba4c..96702a07fb8c 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -21,6 +21,8 @@ import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -29,10 +31,12 @@ import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.ToDoubleFunction; @@ -41,13 +45,18 @@ import java.util.function.ToDoubleFunction; * per thread, uid or call description. */ public class BinderCallsStats { + private static final String TAG = "BinderCallsStats"; private static final int CALL_SESSIONS_POOL_SIZE = 100; private static final int PERIODIC_SAMPLING_INTERVAL = 10; + private static final int MAX_EXCEPTION_COUNT_SIZE = 50; + private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow"; private static final BinderCallsStats sInstance = new BinderCallsStats(); private volatile boolean mDetailedTracking = false; @GuardedBy("mLock") private final SparseArray<UidEntry> mUidEntries = new SparseArray<>(); + @GuardedBy("mLock") + private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>(); private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>(); private final Object mLock = new Object(); private long mStartTime = System.currentTimeMillis(); @@ -158,9 +167,22 @@ public class BinderCallsStats { * <li>Do not throw an exception in this method, it will swallow the original exception thrown * by the binder transaction. */ - public void callThrewException(CallSession s) { + public void callThrewException(CallSession s, Exception exception) { Preconditions.checkNotNull(s); s.exceptionThrown = true; + try { + String className = exception.getClass().getName(); + synchronized (mLock) { + if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) { + className = EXCEPTION_COUNT_OVERFLOW_NAME; + } + Integer count = mExceptionCounts.get(className); + mExceptionCounts.put(className, count == null ? 1 : count + 1); + } + } catch (RuntimeException e) { + // Do not propagate the exception. We do not want to swallow original exception. + Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e); + } } public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) { @@ -244,6 +266,18 @@ public class BinderCallsStats { pw.println(String.format(" Summary: total_cpu_time=%d, " + "calls_count=%d, avg_call_cpu_time=%.0f", totalCpuTime, totalCallsCount, (double)totalCpuTime / totalCallsCount)); + pw.println(); + + pw.println("Exceptions thrown (exception_count, class_name):"); + List<Pair<String, Integer>> exceptionEntries = new ArrayList<>(); + // We cannot use new ArrayList(Collection) constructor because MapCollections does not + // implement toArray method. + mExceptionCounts.entrySet().iterator().forEachRemaining( + (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue()))); + exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second)); + for (Pair<String, Integer> entry : exceptionEntries) { + pw.println(String.format(" %6d %s", entry.second, entry.first)); + } } private static String uidToString(int uid, Map<Integer, String> pkgNameMap) { @@ -279,6 +313,7 @@ public class BinderCallsStats { public void reset() { synchronized (mLock) { mUidEntries.clear(); + mExceptionCounts.clear(); mSampledEntries.mCallStats.clear(); mStartTime = System.currentTimeMillis(); } @@ -414,6 +449,11 @@ public class BinderCallsStats { } @VisibleForTesting + public ArrayMap<String, Integer> getExceptionCounts() { + return mExceptionCounts; + } + + @VisibleForTesting public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble, double percentile) { List<T> sortedList = new ArrayList<>(list); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 18263ab0fa17..2f9f758fa793 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -20,14 +20,19 @@ import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.util.ArrayMap; import android.util.SparseArray; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.junit.Assert.assertEquals; @@ -198,6 +203,40 @@ public class BinderCallsStatsTest { assertEquals(Arrays.asList(4, 3, 2), highestValues); } + @Test + public void testExceptionCount() { + TestBinderCallsStats bcs = new TestBinderCallsStats(true); + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.callThrewException(callSession, new IllegalStateException()); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callThrewException(callSession, new IllegalStateException()); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + callSession = bcs.callStarted(binder, 1); + bcs.callThrewException(callSession, new RuntimeException()); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + ArrayMap<String, Integer> expected = new ArrayMap<>(); + expected.put("java.lang.IllegalStateException", 2); + expected.put("java.lang.RuntimeException", 1); + assertEquals(expected, bcs.getExceptionCounts()); + } + + @Test + public void testDumpDoesNotThrowException() { + TestBinderCallsStats bcs = new TestBinderCallsStats(true); + Binder binder = new Binder(); + BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1); + bcs.callThrewException(callSession, new IllegalStateException()); + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE); + + PrintWriter pw = new PrintWriter(new StringWriter()); + bcs.dump(pw, new HashMap<>(), true); + } + static class TestBinderCallsStats extends BinderCallsStats { int callingUid = TEST_UID; long time = 1234; |