Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c24ace3 authored by Olivier Gaillard's avatar Olivier Gaillard Committed by Android (Google) Code Review
Browse files

Merge "Optimize resolving transaction codes."

parents f96f79cc f31dfb94
Loading
Loading
Loading
Loading
+61 −19
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import java.util.Queue;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;

/**
@@ -185,14 +186,21 @@ public class BinderCallsStats implements BinderInternal.Observer {
    }

    @Nullable
    private String resolveTransactionCode(Class<? extends Binder> binder, int transactionCode) {
        final Method getDefaultTransactionName;
    private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
        try {
            getDefaultTransactionName = binder.getMethod("getDefaultTransactionName", int.class);
            return binder.getMethod("getDefaultTransactionName", int.class);
        } catch (NoSuchMethodException e) {
            // The method might not be present for stubs not generated with AIDL.
            return null;
        }
    }

    @Nullable
    private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) {
        if (getDefaultTransactionName == null) {
            return null;
        }

        try {
            return (String) getDefaultTransactionName.invoke(null, transactionCode);
        } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
@@ -218,11 +226,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
                    ExportedCallStat exported = new ExportedCallStat();
                    exported.uid = entry.uid;
                    exported.className = stat.binderClass.getName();
                    // TODO refactor in order to call resolveTransactionCode outside of the lock.
                    String methodName =
                            resolveTransactionCode(stat.binderClass, stat.transactionCode);
                    exported.methodName = methodName == null
                            ? String.valueOf(stat.transactionCode) : methodName;
                    exported.binderClass = stat.binderClass;
                    exported.transactionCode = stat.transactionCode;
                    exported.cpuTimeMicros = stat.cpuTimeMicros;
                    exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
                    exported.latencyMicros = stat.latencyMicros;
@@ -236,6 +241,36 @@ public class BinderCallsStats implements BinderInternal.Observer {
                }
            }
        }

        // Resolve codes outside of the lock since it can be slow.
        ExportedCallStat previous = null;
        // Cache the previous method/transaction code.
        Method getDefaultTransactionName = null;
        String previousMethodName = null;
        resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
        for (ExportedCallStat exported : resultCallStats) {
            final boolean isClassDifferent = previous == null
                    || !previous.className.equals(exported.className);
            if (isClassDifferent) {
                getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass);
            }

            final boolean isCodeDifferent = previous == null
                    || previous.transactionCode != exported.transactionCode;
            final String methodName;
            if (isClassDifferent || isCodeDifferent) {
                String resolvedCode = resolveTransactionCode(
                        getDefaultTransactionName, exported.transactionCode);
                methodName = resolvedCode == null
                        ? String.valueOf(exported.transactionCode)
                        : resolvedCode;
            } else {
                methodName = previousMethodName;
            }
            previousMethodName = methodName;
            exported.methodName = methodName;
        }

        return resultCallStats;
    }

@@ -280,7 +315,9 @@ public class BinderCallsStats implements BinderInternal.Observer {
                + "latency_time_micros, max_latency_time_micros, exception_count, "
                + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
                + "call_count):");
        for (ExportedCallStat e : sortByCpuDesc(getExportedCallStats())) {
        List<ExportedCallStat> exportedCallStats = getExportedCallStats();
        exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
        for (ExportedCallStat e : exportedCallStats) {
            sb.setLength(0);
            sb.append("    ")
                    .append(uidToString(e.uid, appIdToPkgNameMap))
@@ -397,6 +434,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
        public long maxRequestSizeBytes;
        public long maxReplySizeBytes;
        public long exceptionCount;

        // Used internally.
        Class<? extends Binder> binderClass;
        int transactionCode;
    }

    @VisibleForTesting
@@ -554,15 +595,16 @@ public class BinderCallsStats implements BinderInternal.Observer {
        return result;
    }

    private List<ExportedCallStat> sortByCpuDesc(List<ExportedCallStat> callStats) {
        callStats.sort((o1, o2) -> {
            if (o1.cpuTimeMicros < o2.cpuTimeMicros) {
                return 1;
            } else if (o1.cpuTimeMicros > o2.cpuTimeMicros) {
                return -1;
    private static int compareByCpuDesc(
            ExportedCallStat a, ExportedCallStat b) {
        return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
    }
            return 0;
        });
        return callStats;

    private static int compareByBinderClassAndCode(
            ExportedCallStat a, ExportedCallStat b) {
        int result = a.className.compareTo(b.className);
        return result != 0
                ? result
                : Integer.compare(a.transactionCode, b.transactionCode);
    }
}
+38 −0
Original line number Diff line number Diff line
@@ -240,6 +240,12 @@ public class BinderCallsStatsTest {
        }
    }

    private static class AnotherBinderWithGetTransactionName extends Binder {
        public static String getDefaultTransactionName(int code) {
            return "foo" + code;
        }
    }

    @Test
    public void testTransactionCodeResolved() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
@@ -254,6 +260,38 @@ public class BinderCallsStatsTest {
        assertEquals("resolved", callStatsList.get(0).methodName);
    }

    @Test
    public void testMultipleTransactionCodeResolved() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();
        bcs.setDetailedTracking(true);

        Binder binder = new AnotherBinderWithGetTransactionName();
        CallSession callSession = bcs.callStarted(binder, 1);
        bcs.time += 10;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        Binder binder2 = new BinderWithGetTransactionName();
        callSession = bcs.callStarted(binder2, 1);
        bcs.time += 10;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        callSession = bcs.callStarted(binder, 2);
        bcs.time += 10;
        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);

        List<BinderCallsStats.ExportedCallStat> callStatsList =
                bcs.getExportedCallStats();
        assertEquals("foo1", callStatsList.get(0).methodName);
        assertEquals(AnotherBinderWithGetTransactionName.class.getName(),
                callStatsList.get(0).className);
        assertEquals("foo2", callStatsList.get(1).methodName);
        assertEquals(AnotherBinderWithGetTransactionName.class.getName(),
                callStatsList.get(1).className);
        assertEquals("resolved", callStatsList.get(2).methodName);
        assertEquals(BinderWithGetTransactionName.class.getName(),
                callStatsList.get(2).className);
    }

    @Test
    public void testResolvingCodeDoesNotThrowWhenMethodNotPresent() {
        TestBinderCallsStats bcs = new TestBinderCallsStats();