Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License |
| 15 | */ |
| 16 | |
| 17 | package com.android.internal.os; |
| 18 | |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 19 | import android.annotation.NonNull; |
Olivier Gaillard | 103aae2 | 2018-06-07 18:20:10 +0100 | [diff] [blame] | 20 | import android.annotation.Nullable; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 21 | import android.os.Binder; |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 22 | import android.os.Process; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 23 | import android.os.SystemClock; |
Fyodor Kupolov | 8aa5124 | 2018-04-23 12:44:51 -0700 | [diff] [blame] | 24 | import android.text.format.DateFormat; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 25 | import android.util.ArrayMap; |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 26 | import android.util.Pair; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 27 | import android.util.Slog; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 28 | import android.util.SparseArray; |
| 29 | |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 30 | import com.android.internal.annotations.GuardedBy; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 31 | import com.android.internal.annotations.VisibleForTesting; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 32 | import com.android.internal.os.BinderInternal.CallSession; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 33 | |
| 34 | import java.io.PrintWriter; |
Olivier Gaillard | 1f93a77 | 2018-07-30 14:09:22 +0100 | [diff] [blame] | 35 | import java.lang.reflect.InvocationTargetException; |
| 36 | import java.lang.reflect.Method; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 37 | import java.util.ArrayList; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 38 | import java.util.Collection; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 39 | import java.util.Comparator; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 40 | import java.util.List; |
| 41 | import java.util.Map; |
| 42 | import java.util.Queue; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 43 | import java.util.Random; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 44 | import java.util.concurrent.ConcurrentLinkedQueue; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 45 | import java.util.function.ToDoubleFunction; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 46 | |
| 47 | /** |
| 48 | * Collects statistics about CPU time spent per binder call across multiple dimensions, e.g. |
| 49 | * per thread, uid or call description. |
| 50 | */ |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 51 | public class BinderCallsStats implements BinderInternal.Observer { |
Olivier Gaillard | 703420c | 2019-01-23 17:51:39 +0000 | [diff] [blame] | 52 | public static final boolean ENABLED_DEFAULT = true; |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 53 | public static final boolean DETAILED_TRACKING_DEFAULT = true; |
Olivier Gaillard | 51f669e | 2019-02-21 16:05:27 +0000 | [diff] [blame] | 54 | public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000; |
Olivier Gaillard | 36b80ca | 2019-02-11 11:41:39 +0000 | [diff] [blame] | 55 | public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; |
| 56 | public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 57 | public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000; |
Marcin Oczeretko | 9450171 | 2018-12-17 18:03:45 +0000 | [diff] [blame] | 58 | private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 59 | |
| 60 | private static class OverflowBinder extends Binder {} |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 61 | |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 62 | private static final String TAG = "BinderCallsStats"; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 63 | private static final int CALL_SESSIONS_POOL_SIZE = 100; |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 64 | private static final int MAX_EXCEPTION_COUNT_SIZE = 50; |
| 65 | private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow"; |
Olivier Gaillard | a3686dd | 2018-12-07 11:28:07 +0000 | [diff] [blame] | 66 | // Default values for overflow entry. The work source uid does not use a default value in order |
| 67 | // to have on overflow entry per work source uid. |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 68 | private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class; |
Olivier Gaillard | a3686dd | 2018-12-07 11:28:07 +0000 | [diff] [blame] | 69 | private static final boolean OVERFLOW_SCREEN_INTERACTIVE = false; |
| 70 | private static final int OVERFLOW_DIRECT_CALLING_UID = -1; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 71 | private static final int OVERFLOW_TRANSACTION_CODE = -1; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 72 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 73 | // Whether to collect all the data: cpu + exceptions + reply/request sizes. |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 74 | private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT; |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 75 | // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out |
| 76 | // of 100 requests. |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 77 | private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 78 | private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT; |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 79 | @GuardedBy("mLock") |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 80 | private final SparseArray<UidEntry> mUidEntries = new SparseArray<>(); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 81 | @GuardedBy("mLock") |
| 82 | private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>(); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 83 | private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>(); |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 84 | private final Object mLock = new Object(); |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 85 | private final Random mRandom; |
Olivier Gaillard | 28109b5 | 2018-12-14 15:14:14 +0000 | [diff] [blame] | 86 | private long mStartCurrentTime = System.currentTimeMillis(); |
| 87 | private long mStartElapsedTime = SystemClock.elapsedRealtime(); |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 88 | private long mCallStatsCount = 0; |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 89 | private boolean mAddDebugEntries = false; |
Olivier Gaillard | 36b80ca | 2019-02-11 11:41:39 +0000 | [diff] [blame] | 90 | private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID; |
| 91 | private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 92 | |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 93 | private CachedDeviceState.Readonly mDeviceState; |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 94 | private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 95 | |
| 96 | /** Injector for {@link BinderCallsStats}. */ |
| 97 | public static class Injector { |
| 98 | public Random getRandomGenerator() { |
| 99 | return new Random(); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | public BinderCallsStats(Injector injector) { |
| 104 | this.mRandom = injector.getRandomGenerator(); |
| 105 | } |
| 106 | |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 107 | public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 108 | if (mBatteryStopwatch != null) { |
| 109 | mBatteryStopwatch.close(); |
| 110 | } |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 111 | mDeviceState = deviceState; |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 112 | mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch(); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 113 | } |
| 114 | |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 115 | @Override |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 116 | @Nullable |
Olivier Gaillard | 76c231d | 2018-12-05 12:52:08 +0000 | [diff] [blame] | 117 | public CallSession callStarted(Binder binder, int code, int workSourceUid) { |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 118 | if (mDeviceState == null || mDeviceState.isCharging()) { |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 119 | return null; |
| 120 | } |
| 121 | |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 122 | final CallSession s = obtainCallSession(); |
| 123 | s.binderClass = binder.getClass(); |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 124 | s.transactionCode = code; |
Olivier Gaillard | 9429bf5 | 2018-05-15 23:25:03 +0100 | [diff] [blame] | 125 | s.exceptionThrown = false; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 126 | s.cpuTimeStarted = -1; |
| 127 | s.timeStarted = -1; |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 128 | if (shouldRecordDetailedData()) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 129 | s.cpuTimeStarted = getThreadTimeMicro(); |
| 130 | s.timeStarted = getElapsedRealtimeMicro(); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 131 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 132 | return s; |
| 133 | } |
| 134 | |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 135 | private CallSession obtainCallSession() { |
| 136 | CallSession s = mCallSessionsPool.poll(); |
| 137 | return s == null ? new CallSession() : s; |
| 138 | } |
| 139 | |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 140 | @Override |
Olivier Gaillard | 76c231d | 2018-12-05 12:52:08 +0000 | [diff] [blame] | 141 | public void callEnded(@Nullable CallSession s, int parcelRequestSize, |
| 142 | int parcelReplySize, int workSourceUid) { |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 143 | if (s == null) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 144 | return; |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 145 | } |
| 146 | |
Olivier Gaillard | 76c231d | 2018-12-05 12:52:08 +0000 | [diff] [blame] | 147 | processCallEnded(s, parcelRequestSize, parcelReplySize, workSourceUid); |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 148 | |
| 149 | if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) { |
| 150 | mCallSessionsPool.add(s); |
| 151 | } |
| 152 | } |
| 153 | |
Olivier Gaillard | 76c231d | 2018-12-05 12:52:08 +0000 | [diff] [blame] | 154 | private void processCallEnded(CallSession s, |
| 155 | int parcelRequestSize, int parcelReplySize, int workSourceUid) { |
Olivier Gaillard | 8a760b9 | 2018-07-26 12:21:55 +0100 | [diff] [blame] | 156 | // Non-negative time signals we need to record data for this call. |
| 157 | final boolean recordCall = s.cpuTimeStarted >= 0; |
| 158 | final long duration; |
| 159 | final long latencyDuration; |
| 160 | if (recordCall) { |
| 161 | duration = getThreadTimeMicro() - s.cpuTimeStarted; |
| 162 | latencyDuration = getElapsedRealtimeMicro() - s.timeStarted; |
| 163 | } else { |
| 164 | duration = 0; |
| 165 | latencyDuration = 0; |
| 166 | } |
Olivier Gaillard | 36b80ca | 2019-02-11 11:41:39 +0000 | [diff] [blame] | 167 | final boolean screenInteractive = mTrackScreenInteractive |
| 168 | ? mDeviceState.isScreenInteractive() |
| 169 | : OVERFLOW_SCREEN_INTERACTIVE; |
| 170 | final int callingUid = mTrackDirectCallingUid |
| 171 | ? getCallingUid() |
| 172 | : OVERFLOW_DIRECT_CALLING_UID; |
Olivier Gaillard | 8a760b9 | 2018-07-26 12:21:55 +0100 | [diff] [blame] | 173 | |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 174 | synchronized (mLock) { |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 175 | // This was already checked in #callStart but check again while synchronized. |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 176 | if (mDeviceState == null || mDeviceState.isCharging()) { |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 177 | return; |
| 178 | } |
| 179 | |
Olivier Gaillard | c17d280 | 2018-11-19 17:08:17 +0000 | [diff] [blame] | 180 | final UidEntry uidEntry = getUidEntry(workSourceUid); |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 181 | uidEntry.callCount++; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 182 | |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 183 | if (recordCall) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 184 | uidEntry.cpuTimeMicros += duration; |
| 185 | uidEntry.recordedCallCount++; |
| 186 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 187 | final CallStat callStat = uidEntry.getOrCreate( |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 188 | callingUid, s.binderClass, s.transactionCode, |
Olivier Gaillard | 36b80ca | 2019-02-11 11:41:39 +0000 | [diff] [blame] | 189 | screenInteractive, |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 190 | mCallStatsCount >= mMaxBinderCallStatsCount); |
| 191 | final boolean isNewCallStat = callStat.callCount == 0; |
| 192 | if (isNewCallStat) { |
| 193 | mCallStatsCount++; |
| 194 | } |
| 195 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 196 | callStat.callCount++; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 197 | callStat.recordedCallCount++; |
Olivier Gaillard | 11965ed | 2018-06-04 14:14:04 +0100 | [diff] [blame] | 198 | callStat.cpuTimeMicros += duration; |
| 199 | callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration); |
| 200 | callStat.latencyMicros += latencyDuration; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 201 | callStat.maxLatencyMicros = |
| 202 | Math.max(callStat.maxLatencyMicros, latencyDuration); |
| 203 | if (mDetailedTracking) { |
| 204 | callStat.exceptionCount += s.exceptionThrown ? 1 : 0; |
| 205 | callStat.maxRequestSizeBytes = |
| 206 | Math.max(callStat.maxRequestSizeBytes, parcelRequestSize); |
| 207 | callStat.maxReplySizeBytes = |
| 208 | Math.max(callStat.maxReplySizeBytes, parcelReplySize); |
| 209 | } |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 210 | } else { |
| 211 | // Only record the total call count if we already track data for this key. |
| 212 | // It helps to keep the memory usage down when sampling is enabled. |
| 213 | final CallStat callStat = uidEntry.get( |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 214 | callingUid, s.binderClass, s.transactionCode, |
| 215 | mDeviceState.isScreenInteractive()); |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 216 | if (callStat != null) { |
| 217 | callStat.callCount++; |
| 218 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 219 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 220 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 221 | } |
| 222 | |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 223 | private UidEntry getUidEntry(int uid) { |
| 224 | UidEntry uidEntry = mUidEntries.get(uid); |
| 225 | if (uidEntry == null) { |
| 226 | uidEntry = new UidEntry(uid); |
| 227 | mUidEntries.put(uid, uidEntry); |
| 228 | } |
| 229 | return uidEntry; |
| 230 | } |
| 231 | |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 232 | @Override |
| 233 | public void callThrewException(@Nullable CallSession s, Exception exception) { |
| 234 | if (s == null) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 235 | return; |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 236 | } |
Olivier Gaillard | 9429bf5 | 2018-05-15 23:25:03 +0100 | [diff] [blame] | 237 | s.exceptionThrown = true; |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 238 | try { |
| 239 | String className = exception.getClass().getName(); |
| 240 | synchronized (mLock) { |
| 241 | if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 242 | className = EXCEPTION_COUNT_OVERFLOW_NAME; |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 243 | } |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 244 | final Integer count = mExceptionCounts.get(className); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 245 | mExceptionCounts.put(className, count == null ? 1 : count + 1); |
| 246 | } |
| 247 | } catch (RuntimeException e) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 248 | // Do not propagate the exception. We do not want to swallow original exception. |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 249 | Slog.wtf(TAG, "Unexpected exception while updating mExceptionCounts"); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 250 | } |
Olivier Gaillard | 9429bf5 | 2018-05-15 23:25:03 +0100 | [diff] [blame] | 251 | } |
| 252 | |
Olivier Gaillard | 1f93a77 | 2018-07-30 14:09:22 +0100 | [diff] [blame] | 253 | @Nullable |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 254 | private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) { |
Olivier Gaillard | 1f93a77 | 2018-07-30 14:09:22 +0100 | [diff] [blame] | 255 | try { |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 256 | return binder.getMethod("getDefaultTransactionName", int.class); |
Olivier Gaillard | 1f93a77 | 2018-07-30 14:09:22 +0100 | [diff] [blame] | 257 | } catch (NoSuchMethodException e) { |
| 258 | // The method might not be present for stubs not generated with AIDL. |
| 259 | return null; |
| 260 | } |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 261 | } |
| 262 | |
| 263 | @Nullable |
| 264 | private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) { |
| 265 | if (getDefaultTransactionName == null) { |
| 266 | return null; |
| 267 | } |
| 268 | |
Olivier Gaillard | 1f93a77 | 2018-07-30 14:09:22 +0100 | [diff] [blame] | 269 | try { |
| 270 | return (String) getDefaultTransactionName.invoke(null, transactionCode); |
| 271 | } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) { |
| 272 | throw new RuntimeException(e); |
| 273 | } |
| 274 | } |
| 275 | |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 276 | /** |
| 277 | * This method is expensive to call. |
| 278 | */ |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 279 | public ArrayList<ExportedCallStat> getExportedCallStats() { |
| 280 | // We do not collect all the data if detailed tracking is off. |
| 281 | if (!mDetailedTracking) { |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 282 | return new ArrayList<>(); |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>(); |
| 286 | synchronized (mLock) { |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 287 | final int uidEntriesSize = mUidEntries.size(); |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 288 | for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) { |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 289 | final UidEntry entry = mUidEntries.valueAt(entryIdx); |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 290 | for (CallStat stat : entry.getCallStatsList()) { |
| 291 | ExportedCallStat exported = new ExportedCallStat(); |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 292 | exported.workSourceUid = entry.workSourceUid; |
| 293 | exported.callingUid = stat.callingUid; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 294 | exported.className = stat.binderClass.getName(); |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 295 | exported.binderClass = stat.binderClass; |
| 296 | exported.transactionCode = stat.transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 297 | exported.screenInteractive = stat.screenInteractive; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 298 | exported.cpuTimeMicros = stat.cpuTimeMicros; |
| 299 | exported.maxCpuTimeMicros = stat.maxCpuTimeMicros; |
| 300 | exported.latencyMicros = stat.latencyMicros; |
| 301 | exported.maxLatencyMicros = stat.maxLatencyMicros; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 302 | exported.recordedCallCount = stat.recordedCallCount; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 303 | exported.callCount = stat.callCount; |
| 304 | exported.maxRequestSizeBytes = stat.maxRequestSizeBytes; |
| 305 | exported.maxReplySizeBytes = stat.maxReplySizeBytes; |
| 306 | exported.exceptionCount = stat.exceptionCount; |
| 307 | resultCallStats.add(exported); |
| 308 | } |
| 309 | } |
| 310 | } |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 311 | |
| 312 | // Resolve codes outside of the lock since it can be slow. |
| 313 | ExportedCallStat previous = null; |
| 314 | // Cache the previous method/transaction code. |
| 315 | Method getDefaultTransactionName = null; |
| 316 | String previousMethodName = null; |
| 317 | resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode); |
| 318 | for (ExportedCallStat exported : resultCallStats) { |
| 319 | final boolean isClassDifferent = previous == null |
| 320 | || !previous.className.equals(exported.className); |
| 321 | if (isClassDifferent) { |
| 322 | getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass); |
| 323 | } |
| 324 | |
| 325 | final boolean isCodeDifferent = previous == null |
| 326 | || previous.transactionCode != exported.transactionCode; |
| 327 | final String methodName; |
| 328 | if (isClassDifferent || isCodeDifferent) { |
| 329 | String resolvedCode = resolveTransactionCode( |
| 330 | getDefaultTransactionName, exported.transactionCode); |
| 331 | methodName = resolvedCode == null |
| 332 | ? String.valueOf(exported.transactionCode) |
| 333 | : resolvedCode; |
| 334 | } else { |
| 335 | methodName = previousMethodName; |
| 336 | } |
| 337 | previousMethodName = methodName; |
| 338 | exported.methodName = methodName; |
| 339 | } |
| 340 | |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 341 | // Debug entries added to help validate the data. |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 342 | if (mAddDebugEntries && mBatteryStopwatch != null) { |
Olivier Gaillard | 28109b5 | 2018-12-14 15:14:14 +0000 | [diff] [blame] | 343 | resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime)); |
| 344 | resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime())); |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 345 | resultCallStats.add( |
| 346 | createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis())); |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 347 | } |
| 348 | |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 349 | return resultCallStats; |
| 350 | } |
| 351 | |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 352 | private ExportedCallStat createDebugEntry(String variableName, long value) { |
| 353 | final int uid = Process.myUid(); |
| 354 | final ExportedCallStat callStat = new ExportedCallStat(); |
| 355 | callStat.className = ""; |
| 356 | callStat.workSourceUid = uid; |
| 357 | callStat.callingUid = uid; |
| 358 | callStat.recordedCallCount = 1; |
| 359 | callStat.callCount = 1; |
Marcin Oczeretko | 9450171 | 2018-12-17 18:03:45 +0000 | [diff] [blame] | 360 | callStat.methodName = DEBUG_ENTRY_PREFIX + variableName; |
Marcin Oczeretko | 8d86174 | 2018-12-06 11:13:29 +0000 | [diff] [blame] | 361 | callStat.latencyMicros = value; |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 362 | return callStat; |
| 363 | } |
| 364 | |
Olivier Gaillard | 6f52d15 | 2018-07-25 12:13:12 +0100 | [diff] [blame] | 365 | /** @hide */ |
| 366 | public ArrayMap<String, Integer> getExportedExceptionStats() { |
| 367 | synchronized (mLock) { |
| 368 | return new ArrayMap(mExceptionCounts); |
| 369 | } |
| 370 | } |
| 371 | |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 372 | /** Writes the collected statistics to the supplied {@link PrintWriter}.*/ |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 373 | public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) { |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 374 | synchronized (mLock) { |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 375 | dumpLocked(pw, packageMap, verbose); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 376 | } |
| 377 | } |
| 378 | |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 379 | private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) { |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 380 | long totalCallsCount = 0; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 381 | long totalRecordedCallsCount = 0; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 382 | long totalCpuTime = 0; |
Fyodor Kupolov | 8aa5124 | 2018-04-23 12:44:51 -0700 | [diff] [blame] | 383 | pw.print("Start time: "); |
Olivier Gaillard | 28109b5 | 2018-12-14 15:14:14 +0000 | [diff] [blame] | 384 | pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime)); |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 385 | pw.print("On battery time (ms): "); |
| 386 | pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0); |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 387 | pw.println("Sampling interval period: " + mPeriodicSamplingInterval); |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 388 | final List<UidEntry> entries = new ArrayList<>(); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 389 | |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 390 | final int uidEntriesSize = mUidEntries.size(); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 391 | for (int i = 0; i < uidEntriesSize; i++) { |
| 392 | UidEntry e = mUidEntries.valueAt(i); |
| 393 | entries.add(e); |
| 394 | totalCpuTime += e.cpuTimeMicros; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 395 | totalRecordedCallsCount += e.recordedCallCount; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 396 | totalCallsCount += e.callCount; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 397 | } |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 398 | |
| 399 | entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed()); |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 400 | final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) "; |
| 401 | final StringBuilder sb = new StringBuilder(); |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 402 | pw.println("Per-UID raw data " + datasetSizeDesc |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 403 | + "(package/uid, worksource, call_desc, screen_interactive, " |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 404 | + "cpu_time_micros, max_cpu_time_micros, " |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 405 | + "latency_time_micros, max_latency_time_micros, exception_count, " |
| 406 | + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, " |
| 407 | + "call_count):"); |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 408 | final List<ExportedCallStat> exportedCallStats = getExportedCallStats(); |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 409 | exportedCallStats.sort(BinderCallsStats::compareByCpuDesc); |
| 410 | for (ExportedCallStat e : exportedCallStats) { |
Marcin Oczeretko | 9450171 | 2018-12-17 18:03:45 +0000 | [diff] [blame] | 411 | if (e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) { |
| 412 | // Do not dump debug entries. |
| 413 | continue; |
| 414 | } |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 415 | sb.setLength(0); |
| 416 | sb.append(" ") |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 417 | .append(packageMap.mapUid(e.callingUid)) |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 418 | .append(',') |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 419 | .append(packageMap.mapUid(e.workSourceUid)) |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 420 | .append(',').append(e.className) |
| 421 | .append('#').append(e.methodName) |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 422 | .append(',').append(e.screenInteractive) |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 423 | .append(',').append(e.cpuTimeMicros) |
| 424 | .append(',').append(e.maxCpuTimeMicros) |
| 425 | .append(',').append(e.latencyMicros) |
| 426 | .append(',').append(e.maxLatencyMicros) |
| 427 | .append(',').append(mDetailedTracking ? e.exceptionCount : '_') |
| 428 | .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_') |
| 429 | .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_') |
| 430 | .append(',').append(e.recordedCallCount) |
| 431 | .append(',').append(e.callCount); |
| 432 | pw.println(sb); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 433 | } |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 434 | pw.println(); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 435 | pw.println("Per-UID Summary " + datasetSizeDesc |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 436 | + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):"); |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 437 | final List<UidEntry> summaryEntries = verbose ? entries |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 438 | : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9); |
| 439 | for (UidEntry entry : summaryEntries) { |
Marcin Oczeretko | c4c45a8 | 2018-12-06 15:09:49 +0000 | [diff] [blame] | 440 | String uidStr = packageMap.mapUid(entry.workSourceUid); |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 441 | pw.println(String.format(" %10d %3.0f%% %8d %8d %s", |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 442 | entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime, |
| 443 | entry.recordedCallCount, entry.callCount, uidStr)); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 444 | } |
| 445 | pw.println(); |
| 446 | pw.println(String.format(" Summary: total_cpu_time=%d, " |
Marcin Oczeretko | c80c81a | 2018-08-30 20:15:52 +0100 | [diff] [blame] | 447 | + "calls_count=%d, avg_call_cpu_time=%.0f", |
| 448 | totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount)); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 449 | pw.println(); |
| 450 | |
| 451 | pw.println("Exceptions thrown (exception_count, class_name):"); |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 452 | final List<Pair<String, Integer>> exceptionEntries = new ArrayList<>(); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 453 | // We cannot use new ArrayList(Collection) constructor because MapCollections does not |
| 454 | // implement toArray method. |
| 455 | mExceptionCounts.entrySet().iterator().forEachRemaining( |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 456 | (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue()))); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 457 | exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second)); |
| 458 | for (Pair<String, Integer> entry : exceptionEntries) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 459 | pw.println(String.format(" %6d %s", entry.second, entry.first)); |
| 460 | } |
| 461 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 462 | if (mPeriodicSamplingInterval != 1) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 463 | pw.println(""); |
| 464 | pw.println("/!\\ Displayed data is sampled. See sampling interval at the top."); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 465 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 466 | } |
| 467 | |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 468 | protected long getThreadTimeMicro() { |
| 469 | return SystemClock.currentThreadTimeMicro(); |
| 470 | } |
| 471 | |
| 472 | protected int getCallingUid() { |
| 473 | return Binder.getCallingUid(); |
| 474 | } |
| 475 | |
Olivier Gaillard | 11965ed | 2018-06-04 14:14:04 +0100 | [diff] [blame] | 476 | protected long getElapsedRealtimeMicro() { |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 477 | return SystemClock.elapsedRealtimeNanos() / 1000; |
Olivier Gaillard | 121988e | 2018-05-15 20:49:47 +0100 | [diff] [blame] | 478 | } |
| 479 | |
Olivier Gaillard | 8a3e20a | 2018-10-25 13:17:51 +0100 | [diff] [blame] | 480 | protected boolean shouldRecordDetailedData() { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 481 | return mRandom.nextInt() % mPeriodicSamplingInterval == 0; |
| 482 | } |
| 483 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 484 | /** |
| 485 | * Sets to true to collect all the data. |
| 486 | */ |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 487 | public void setDetailedTracking(boolean enabled) { |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 488 | synchronized (mLock) { |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 489 | if (enabled != mDetailedTracking) { |
| 490 | mDetailedTracking = enabled; |
| 491 | reset(); |
| 492 | } |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 493 | } |
| 494 | } |
| 495 | |
Olivier Gaillard | 36b80ca | 2019-02-11 11:41:39 +0000 | [diff] [blame] | 496 | /** |
| 497 | * Whether to track the screen state. |
| 498 | */ |
| 499 | public void setTrackScreenInteractive(boolean enabled) { |
| 500 | synchronized (mLock) { |
| 501 | if (enabled != mTrackScreenInteractive) { |
| 502 | mTrackScreenInteractive = enabled; |
| 503 | reset(); |
| 504 | } |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | /** |
| 509 | * Whether to track direct caller uid. |
| 510 | */ |
| 511 | public void setTrackDirectCallerUid(boolean enabled) { |
| 512 | synchronized (mLock) { |
| 513 | if (enabled != mTrackDirectCallingUid) { |
| 514 | mTrackDirectCallingUid = enabled; |
| 515 | reset(); |
| 516 | } |
| 517 | } |
| 518 | } |
| 519 | |
Marcin Oczeretko | 772e8f2 | 2018-11-21 13:00:32 +0000 | [diff] [blame] | 520 | public void setAddDebugEntries(boolean addDebugEntries) { |
| 521 | mAddDebugEntries = addDebugEntries; |
| 522 | } |
| 523 | |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 524 | /** |
| 525 | * Sets the maximum number of items to track. |
| 526 | */ |
| 527 | public void setMaxBinderCallStats(int maxKeys) { |
| 528 | if (maxKeys <= 0) { |
| 529 | Slog.w(TAG, "Ignored invalid max value (value must be positive): " |
| 530 | + maxKeys); |
| 531 | return; |
| 532 | } |
| 533 | |
| 534 | synchronized (mLock) { |
| 535 | if (maxKeys != mMaxBinderCallStatsCount) { |
| 536 | mMaxBinderCallStatsCount = maxKeys; |
| 537 | reset(); |
| 538 | } |
| 539 | } |
| 540 | } |
| 541 | |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 542 | public void setSamplingInterval(int samplingInterval) { |
Marcin Oczeretko | 5a082f6 | 2018-10-02 16:17:43 +0100 | [diff] [blame] | 543 | if (samplingInterval <= 0) { |
| 544 | Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " |
| 545 | + samplingInterval); |
| 546 | return; |
| 547 | } |
| 548 | |
Olivier Gaillard | 1d7f615 | 2018-07-03 13:57:58 +0100 | [diff] [blame] | 549 | synchronized (mLock) { |
| 550 | if (samplingInterval != mPeriodicSamplingInterval) { |
| 551 | mPeriodicSamplingInterval = samplingInterval; |
| 552 | reset(); |
| 553 | } |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 554 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 555 | } |
| 556 | |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 557 | public void reset() { |
| 558 | synchronized (mLock) { |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 559 | mCallStatsCount = 0; |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 560 | mUidEntries.clear(); |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 561 | mExceptionCounts.clear(); |
Olivier Gaillard | 28109b5 | 2018-12-14 15:14:14 +0000 | [diff] [blame] | 562 | mStartCurrentTime = System.currentTimeMillis(); |
| 563 | mStartElapsedTime = SystemClock.elapsedRealtime(); |
Marcin Oczeretko | 6a2e524 | 2018-11-28 11:08:50 +0000 | [diff] [blame] | 564 | if (mBatteryStopwatch != null) { |
| 565 | mBatteryStopwatch.reset(); |
| 566 | } |
Fyodor Kupolov | 3f3af61 | 2018-04-18 17:26:43 -0700 | [diff] [blame] | 567 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 568 | } |
| 569 | |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 570 | /** |
Olivier Gaillard | 8652690 | 2019-02-27 10:39:18 +0000 | [diff] [blame] | 571 | * Aggregated data by uid/class/method to be sent through statsd. |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 572 | */ |
| 573 | public static class ExportedCallStat { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 574 | public int callingUid; |
| 575 | public int workSourceUid; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 576 | public String className; |
| 577 | public String methodName; |
Olivier Gaillard | 6f52d15 | 2018-07-25 12:13:12 +0100 | [diff] [blame] | 578 | public boolean screenInteractive; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 579 | public long cpuTimeMicros; |
| 580 | public long maxCpuTimeMicros; |
| 581 | public long latencyMicros; |
| 582 | public long maxLatencyMicros; |
| 583 | public long callCount; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 584 | public long recordedCallCount; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 585 | public long maxRequestSizeBytes; |
| 586 | public long maxReplySizeBytes; |
| 587 | public long exceptionCount; |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 588 | |
| 589 | // Used internally. |
| 590 | Class<? extends Binder> binderClass; |
| 591 | int transactionCode; |
Olivier Gaillard | 00bfb1b | 2018-07-10 11:25:09 +0100 | [diff] [blame] | 592 | } |
| 593 | |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 594 | @VisibleForTesting |
| 595 | public static class CallStat { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 596 | // The UID who executed the transaction (i.e. Binder#getCallingUid). |
| 597 | public final int callingUid; |
| 598 | public final Class<? extends Binder> binderClass; |
| 599 | public final int transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 600 | // True if the screen was interactive when the call ended. |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 601 | public final boolean screenInteractive; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 602 | // Number of calls for which we collected data for. We do not record data for all the calls |
| 603 | // when sampling is on. |
| 604 | public long recordedCallCount; |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 605 | // Roughly the real number of total calls. We only track only track the API call count once |
| 606 | // at least one non-sampled count happened. |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 607 | public long callCount; |
| 608 | // Total CPU of all for all the recorded calls. |
| 609 | // Approximate total CPU usage can be computed by |
| 610 | // cpuTimeMicros * callCount / recordedCallCount |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 611 | public long cpuTimeMicros; |
Olivier Gaillard | 11965ed | 2018-06-04 14:14:04 +0100 | [diff] [blame] | 612 | public long maxCpuTimeMicros; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 613 | // Total latency of all for all the recorded calls. |
| 614 | // Approximate average latency can be computed by |
| 615 | // latencyMicros * callCount / recordedCallCount |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 616 | public long latencyMicros; |
Olivier Gaillard | 58b56e3 | 2018-06-01 16:18:43 +0100 | [diff] [blame] | 617 | public long maxLatencyMicros; |
Olivier Gaillard | 11965ed | 2018-06-04 14:14:04 +0100 | [diff] [blame] | 618 | // The following fields are only computed if mDetailedTracking is set. |
Olivier Gaillard | 58b56e3 | 2018-06-01 16:18:43 +0100 | [diff] [blame] | 619 | public long maxRequestSizeBytes; |
| 620 | public long maxReplySizeBytes; |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 621 | public long exceptionCount; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 622 | |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 623 | CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode, |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 624 | boolean screenInteractive) { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 625 | this.callingUid = callingUid; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 626 | this.binderClass = binderClass; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 627 | this.transactionCode = transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 628 | this.screenInteractive = screenInteractive; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 629 | } |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 630 | } |
| 631 | |
| 632 | /** Key used to store CallStat object in a Map. */ |
| 633 | public static class CallStatKey { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 634 | public int callingUid; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 635 | public Class<? extends Binder> binderClass; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 636 | public int transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 637 | private boolean screenInteractive; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 638 | |
| 639 | @Override |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 640 | public boolean equals(Object o) { |
| 641 | if (this == o) { |
| 642 | return true; |
| 643 | } |
| 644 | |
Olivier Gaillard | b812229 | 2018-08-01 13:49:25 +0100 | [diff] [blame] | 645 | final CallStatKey key = (CallStatKey) o; |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 646 | return callingUid == key.callingUid |
| 647 | && transactionCode == key.transactionCode |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 648 | && screenInteractive == key.screenInteractive |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 649 | && (binderClass.equals(key.binderClass)); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 650 | } |
| 651 | |
| 652 | @Override |
| 653 | public int hashCode() { |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 654 | int result = binderClass.hashCode(); |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 655 | result = 31 * result + transactionCode; |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 656 | result = 31 * result + callingUid; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 657 | result = 31 * result + (screenInteractive ? 1231 : 1237); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 658 | return result; |
| 659 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 660 | } |
| 661 | |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 662 | |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 663 | @VisibleForTesting |
| 664 | public static class UidEntry { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 665 | // The UID who is responsible for the binder transaction. If the bluetooth process execute a |
| 666 | // transaction on behalf of app foo, the workSourceUid will be the uid of app foo. |
| 667 | public int workSourceUid; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 668 | // Number of calls for which we collected data for. We do not record data for all the calls |
| 669 | // when sampling is on. |
| 670 | public long recordedCallCount; |
| 671 | // Real number of total calls. |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 672 | public long callCount; |
Olivier Gaillard | 2c13c6f | 2018-07-16 16:55:58 +0100 | [diff] [blame] | 673 | // Total CPU of all for all the recorded calls. |
| 674 | // Approximate total CPU usage can be computed by |
| 675 | // cpuTimeMicros * callCount / recordedCallCount |
| 676 | public long cpuTimeMicros; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 677 | |
| 678 | UidEntry(int uid) { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 679 | this.workSourceUid = uid; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 680 | } |
| 681 | |
| 682 | // Aggregate time spent per each call name: call_desc -> cpu_time_micros |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 683 | private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>(); |
| 684 | private CallStatKey mTempKey = new CallStatKey(); |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 685 | |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 686 | @Nullable |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 687 | CallStat get(int callingUid, Class<? extends Binder> binderClass, int transactionCode, |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 688 | boolean screenInteractive) { |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 689 | // Use a global temporary key to avoid creating new objects for every lookup. |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 690 | mTempKey.callingUid = callingUid; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 691 | mTempKey.binderClass = binderClass; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 692 | mTempKey.transactionCode = transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 693 | mTempKey.screenInteractive = screenInteractive; |
Olivier Gaillard | 34ad8af | 2018-08-06 15:55:03 +0100 | [diff] [blame] | 694 | return mCallStats.get(mTempKey); |
| 695 | } |
| 696 | |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 697 | CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass, |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 698 | int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 699 | CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive); |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 700 | // Only create CallStat if it's a new entry, otherwise update existing instance. |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 701 | if (mapCallStat == null) { |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 702 | if (maxCallStatsReached) { |
Olivier Gaillard | a3686dd | 2018-12-07 11:28:07 +0000 | [diff] [blame] | 703 | mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER, |
| 704 | OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE); |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 705 | if (mapCallStat != null) { |
| 706 | return mapCallStat; |
| 707 | } |
| 708 | |
Olivier Gaillard | a3686dd | 2018-12-07 11:28:07 +0000 | [diff] [blame] | 709 | callingUid = OVERFLOW_DIRECT_CALLING_UID; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 710 | binderClass = OVERFLOW_BINDER; |
| 711 | transactionCode = OVERFLOW_TRANSACTION_CODE; |
Olivier Gaillard | a3686dd | 2018-12-07 11:28:07 +0000 | [diff] [blame] | 712 | screenInteractive = OVERFLOW_SCREEN_INTERACTIVE; |
Olivier Gaillard | 7949061 | 2018-11-30 16:22:23 +0000 | [diff] [blame] | 713 | } |
| 714 | |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 715 | mapCallStat = new CallStat(callingUid, binderClass, transactionCode, |
| 716 | screenInteractive); |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 717 | CallStatKey key = new CallStatKey(); |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 718 | key.callingUid = callingUid; |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 719 | key.binderClass = binderClass; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 720 | key.transactionCode = transactionCode; |
Olivier Gaillard | 86714d1 | 2018-08-01 15:05:36 +0100 | [diff] [blame] | 721 | key.screenInteractive = screenInteractive; |
Olivier Gaillard | 289ba40 | 2018-07-24 18:50:13 +0100 | [diff] [blame] | 722 | mCallStats.put(key, mapCallStat); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 723 | } |
| 724 | return mapCallStat; |
| 725 | } |
| 726 | |
| 727 | /** |
| 728 | * Returns list of calls sorted by CPU time |
| 729 | */ |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 730 | public Collection<CallStat> getCallStatsList() { |
| 731 | return mCallStats.values(); |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 732 | } |
| 733 | |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 734 | @Override |
| 735 | public String toString() { |
| 736 | return "UidEntry{" + |
Olivier Gaillard | 121988e | 2018-05-15 20:49:47 +0100 | [diff] [blame] | 737 | "cpuTimeMicros=" + cpuTimeMicros + |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 738 | ", callCount=" + callCount + |
| 739 | ", mCallStats=" + mCallStats + |
| 740 | '}'; |
| 741 | } |
| 742 | |
| 743 | @Override |
| 744 | public boolean equals(Object o) { |
| 745 | if (this == o) { |
| 746 | return true; |
| 747 | } |
| 748 | |
| 749 | UidEntry uidEntry = (UidEntry) o; |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 750 | return workSourceUid == uidEntry.workSourceUid; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 751 | } |
| 752 | |
| 753 | @Override |
| 754 | public int hashCode() { |
Olivier Gaillard | 88de48f | 2018-10-18 10:21:21 +0100 | [diff] [blame] | 755 | return workSourceUid; |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 756 | } |
| 757 | } |
| 758 | |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 759 | @VisibleForTesting |
| 760 | public SparseArray<UidEntry> getUidEntries() { |
| 761 | return mUidEntries; |
| 762 | } |
| 763 | |
| 764 | @VisibleForTesting |
Olivier Gaillard | f82d2e73 | 2018-06-07 11:45:35 +0100 | [diff] [blame] | 765 | public ArrayMap<String, Integer> getExceptionCounts() { |
| 766 | return mExceptionCounts; |
| 767 | } |
| 768 | |
| 769 | @VisibleForTesting |
Fyodor Kupolov | cf0fe2d | 2018-05-22 18:50:04 -0700 | [diff] [blame] | 770 | public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble, |
| 771 | double percentile) { |
| 772 | List<T> sortedList = new ArrayList<>(list); |
| 773 | sortedList.sort(Comparator.comparingDouble(toDouble).reversed()); |
| 774 | double total = 0; |
| 775 | for (T item : list) { |
| 776 | total += toDouble.applyAsDouble(item); |
| 777 | } |
| 778 | List<T> result = new ArrayList<>(); |
| 779 | double runningSum = 0; |
| 780 | for (T item : sortedList) { |
| 781 | if (runningSum > percentile * total) { |
| 782 | break; |
| 783 | } |
| 784 | result.add(item); |
| 785 | runningSum += toDouble.applyAsDouble(item); |
| 786 | } |
| 787 | return result; |
| 788 | } |
| 789 | |
Olivier Gaillard | f31dfb9 | 2018-07-31 12:59:20 +0100 | [diff] [blame] | 790 | private static int compareByCpuDesc( |
| 791 | ExportedCallStat a, ExportedCallStat b) { |
| 792 | return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros); |
| 793 | } |
| 794 | |
| 795 | private static int compareByBinderClassAndCode( |
| 796 | ExportedCallStat a, ExportedCallStat b) { |
| 797 | int result = a.className.compareTo(b.className); |
| 798 | return result != 0 |
| 799 | ? result |
| 800 | : Integer.compare(a.transactionCode, b.transactionCode); |
Olivier Gaillard | 7a2a98b | 2018-07-26 13:29:16 +0100 | [diff] [blame] | 801 | } |
Fyodor Kupolov | ca34851 | 2018-01-10 18:05:53 -0800 | [diff] [blame] | 802 | } |