blob: bab478748467dd7af076c00344f88930d6351422 [file] [log] [blame]
Fyodor Kupolovca348512018-01-10 18:05:53 -08001/*
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
17package com.android.internal.os;
18
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +010019import android.annotation.NonNull;
Olivier Gaillard103aae22018-06-07 18:20:10 +010020import android.annotation.Nullable;
Fyodor Kupolovca348512018-01-10 18:05:53 -080021import android.os.Binder;
Marcin Oczeretko772e8f22018-11-21 13:00:32 +000022import android.os.Process;
Fyodor Kupolovca348512018-01-10 18:05:53 -080023import android.os.SystemClock;
Fyodor Kupolov8aa51242018-04-23 12:44:51 -070024import android.text.format.DateFormat;
Fyodor Kupolovca348512018-01-10 18:05:53 -080025import android.util.ArrayMap;
Olivier Gaillardf82d2e732018-06-07 11:45:35 +010026import android.util.Pair;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +010027import android.util.Slog;
Fyodor Kupolovca348512018-01-10 18:05:53 -080028import android.util.SparseArray;
29
Fyodor Kupolov3f3af612018-04-18 17:26:43 -070030import com.android.internal.annotations.GuardedBy;
Fyodor Kupolovca348512018-01-10 18:05:53 -080031import com.android.internal.annotations.VisibleForTesting;
Olivier Gaillard289ba402018-07-24 18:50:13 +010032import com.android.internal.os.BinderInternal.CallSession;
Fyodor Kupolovca348512018-01-10 18:05:53 -080033
34import java.io.PrintWriter;
Olivier Gaillard1f93a772018-07-30 14:09:22 +010035import java.lang.reflect.InvocationTargetException;
36import java.lang.reflect.Method;
Fyodor Kupolovca348512018-01-10 18:05:53 -080037import java.util.ArrayList;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +010038import java.util.Collection;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -070039import java.util.Comparator;
Fyodor Kupolovca348512018-01-10 18:05:53 -080040import java.util.List;
41import java.util.Map;
42import java.util.Queue;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +010043import java.util.Random;
Fyodor Kupolovca348512018-01-10 18:05:53 -080044import java.util.concurrent.ConcurrentLinkedQueue;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -070045import java.util.function.ToDoubleFunction;
Fyodor Kupolovca348512018-01-10 18:05:53 -080046
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 Gaillard289ba402018-07-24 18:50:13 +010051public class BinderCallsStats implements BinderInternal.Observer {
Olivier Gaillard703420c2019-01-23 17:51:39 +000052 public static final boolean ENABLED_DEFAULT = true;
Olivier Gaillard1d7f6152018-07-03 13:57:58 +010053 public static final boolean DETAILED_TRACKING_DEFAULT = true;
Olivier Gaillard51f669e2019-02-21 16:05:27 +000054 public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000;
Olivier Gaillard36b80ca2019-02-11 11:41:39 +000055 public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false;
56 public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true;
Olivier Gaillard79490612018-11-30 16:22:23 +000057 public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
Marcin Oczeretko94501712018-12-17 18:03:45 +000058 private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
Olivier Gaillard79490612018-11-30 16:22:23 +000059
60 private static class OverflowBinder extends Binder {}
Olivier Gaillard1d7f6152018-07-03 13:57:58 +010061
Olivier Gaillardf82d2e732018-06-07 11:45:35 +010062 private static final String TAG = "BinderCallsStats";
Fyodor Kupolovca348512018-01-10 18:05:53 -080063 private static final int CALL_SESSIONS_POOL_SIZE = 100;
Olivier Gaillardf82d2e732018-06-07 11:45:35 +010064 private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
65 private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
Olivier Gaillarda3686dd2018-12-07 11:28:07 +000066 // 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 Gaillard79490612018-11-30 16:22:23 +000068 private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
Olivier Gaillarda3686dd2018-12-07 11:28:07 +000069 private static final boolean OVERFLOW_SCREEN_INTERACTIVE = false;
70 private static final int OVERFLOW_DIRECT_CALLING_UID = -1;
Olivier Gaillard79490612018-11-30 16:22:23 +000071 private static final int OVERFLOW_TRANSACTION_CODE = -1;
Fyodor Kupolovca348512018-01-10 18:05:53 -080072
Olivier Gaillard34ad8af2018-08-06 15:55:03 +010073 // Whether to collect all the data: cpu + exceptions + reply/request sizes.
Olivier Gaillard289ba402018-07-24 18:50:13 +010074 private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
Olivier Gaillard34ad8af2018-08-06 15:55:03 +010075 // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
76 // of 100 requests.
Olivier Gaillard289ba402018-07-24 18:50:13 +010077 private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
Olivier Gaillard79490612018-11-30 16:22:23 +000078 private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT;
Fyodor Kupolov3f3af612018-04-18 17:26:43 -070079 @GuardedBy("mLock")
Fyodor Kupolovca348512018-01-10 18:05:53 -080080 private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
Olivier Gaillardf82d2e732018-06-07 11:45:35 +010081 @GuardedBy("mLock")
82 private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>();
Fyodor Kupolovca348512018-01-10 18:05:53 -080083 private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
Fyodor Kupolov3f3af612018-04-18 17:26:43 -070084 private final Object mLock = new Object();
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +010085 private final Random mRandom;
Olivier Gaillard28109b52018-12-14 15:14:14 +000086 private long mStartCurrentTime = System.currentTimeMillis();
87 private long mStartElapsedTime = SystemClock.elapsedRealtime();
Olivier Gaillard79490612018-11-30 16:22:23 +000088 private long mCallStatsCount = 0;
Marcin Oczeretko772e8f22018-11-21 13:00:32 +000089 private boolean mAddDebugEntries = false;
Olivier Gaillard36b80ca2019-02-11 11:41:39 +000090 private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID;
91 private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE;
Fyodor Kupolovca348512018-01-10 18:05:53 -080092
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +010093 private CachedDeviceState.Readonly mDeviceState;
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +000094 private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
Olivier Gaillard86714d12018-08-01 15:05:36 +010095
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 Oczeretkoc80c81a2018-08-30 20:15:52 +0100107 public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000108 if (mBatteryStopwatch != null) {
109 mBatteryStopwatch.close();
110 }
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100111 mDeviceState = deviceState;
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000112 mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
Fyodor Kupolovca348512018-01-10 18:05:53 -0800113 }
114
Olivier Gaillard289ba402018-07-24 18:50:13 +0100115 @Override
Olivier Gaillard86714d12018-08-01 15:05:36 +0100116 @Nullable
Olivier Gaillard76c231d2018-12-05 12:52:08 +0000117 public CallSession callStarted(Binder binder, int code, int workSourceUid) {
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100118 if (mDeviceState == null || mDeviceState.isCharging()) {
Olivier Gaillard86714d12018-08-01 15:05:36 +0100119 return null;
120 }
121
Olivier Gaillardb8122292018-08-01 13:49:25 +0100122 final CallSession s = obtainCallSession();
123 s.binderClass = binder.getClass();
Olivier Gaillard289ba402018-07-24 18:50:13 +0100124 s.transactionCode = code;
Olivier Gaillard9429bf52018-05-15 23:25:03 +0100125 s.exceptionThrown = false;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700126 s.cpuTimeStarted = -1;
127 s.timeStarted = -1;
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100128 if (shouldRecordDetailedData()) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100129 s.cpuTimeStarted = getThreadTimeMicro();
130 s.timeStarted = getElapsedRealtimeMicro();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700131 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800132 return s;
133 }
134
Olivier Gaillardb8122292018-08-01 13:49:25 +0100135 private CallSession obtainCallSession() {
136 CallSession s = mCallSessionsPool.poll();
137 return s == null ? new CallSession() : s;
138 }
139
Olivier Gaillard289ba402018-07-24 18:50:13 +0100140 @Override
Olivier Gaillard76c231d2018-12-05 12:52:08 +0000141 public void callEnded(@Nullable CallSession s, int parcelRequestSize,
142 int parcelReplySize, int workSourceUid) {
Olivier Gaillard289ba402018-07-24 18:50:13 +0100143 if (s == null) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100144 return;
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100145 }
146
Olivier Gaillard76c231d2018-12-05 12:52:08 +0000147 processCallEnded(s, parcelRequestSize, parcelReplySize, workSourceUid);
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100148
149 if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
150 mCallSessionsPool.add(s);
151 }
152 }
153
Olivier Gaillard76c231d2018-12-05 12:52:08 +0000154 private void processCallEnded(CallSession s,
155 int parcelRequestSize, int parcelReplySize, int workSourceUid) {
Olivier Gaillard8a760b92018-07-26 12:21:55 +0100156 // 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 Gaillard36b80ca2019-02-11 11:41:39 +0000167 final boolean screenInteractive = mTrackScreenInteractive
168 ? mDeviceState.isScreenInteractive()
169 : OVERFLOW_SCREEN_INTERACTIVE;
170 final int callingUid = mTrackDirectCallingUid
171 ? getCallingUid()
172 : OVERFLOW_DIRECT_CALLING_UID;
Olivier Gaillard8a760b92018-07-26 12:21:55 +0100173
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700174 synchronized (mLock) {
Olivier Gaillard86714d12018-08-01 15:05:36 +0100175 // This was already checked in #callStart but check again while synchronized.
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100176 if (mDeviceState == null || mDeviceState.isCharging()) {
Olivier Gaillard86714d12018-08-01 15:05:36 +0100177 return;
178 }
179
Olivier Gaillardc17d2802018-11-19 17:08:17 +0000180 final UidEntry uidEntry = getUidEntry(workSourceUid);
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100181 uidEntry.callCount++;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100182
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100183 if (recordCall) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100184 uidEntry.cpuTimeMicros += duration;
185 uidEntry.recordedCallCount++;
186
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100187 final CallStat callStat = uidEntry.getOrCreate(
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100188 callingUid, s.binderClass, s.transactionCode,
Olivier Gaillard36b80ca2019-02-11 11:41:39 +0000189 screenInteractive,
Olivier Gaillard79490612018-11-30 16:22:23 +0000190 mCallStatsCount >= mMaxBinderCallStatsCount);
191 final boolean isNewCallStat = callStat.callCount == 0;
192 if (isNewCallStat) {
193 mCallStatsCount++;
194 }
195
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100196 callStat.callCount++;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100197 callStat.recordedCallCount++;
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100198 callStat.cpuTimeMicros += duration;
199 callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
200 callStat.latencyMicros += latencyDuration;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100201 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 Gaillard34ad8af2018-08-06 15:55:03 +0100210 } 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 Gaillard88de48f2018-10-18 10:21:21 +0100214 callingUid, s.binderClass, s.transactionCode,
215 mDeviceState.isScreenInteractive());
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100216 if (callStat != null) {
217 callStat.callCount++;
218 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800219 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800220 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800221 }
222
Olivier Gaillardb8122292018-08-01 13:49:25 +0100223 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 Gaillard289ba402018-07-24 18:50:13 +0100232 @Override
233 public void callThrewException(@Nullable CallSession s, Exception exception) {
234 if (s == null) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100235 return;
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100236 }
Olivier Gaillard9429bf52018-05-15 23:25:03 +0100237 s.exceptionThrown = true;
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100238 try {
239 String className = exception.getClass().getName();
240 synchronized (mLock) {
241 if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100242 className = EXCEPTION_COUNT_OVERFLOW_NAME;
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100243 }
Olivier Gaillardb8122292018-08-01 13:49:25 +0100244 final Integer count = mExceptionCounts.get(className);
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100245 mExceptionCounts.put(className, count == null ? 1 : count + 1);
246 }
247 } catch (RuntimeException e) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100248 // Do not propagate the exception. We do not want to swallow original exception.
Olivier Gaillard86714d12018-08-01 15:05:36 +0100249 Slog.wtf(TAG, "Unexpected exception while updating mExceptionCounts");
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100250 }
Olivier Gaillard9429bf52018-05-15 23:25:03 +0100251 }
252
Olivier Gaillard1f93a772018-07-30 14:09:22 +0100253 @Nullable
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100254 private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
Olivier Gaillard1f93a772018-07-30 14:09:22 +0100255 try {
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100256 return binder.getMethod("getDefaultTransactionName", int.class);
Olivier Gaillard1f93a772018-07-30 14:09:22 +0100257 } catch (NoSuchMethodException e) {
258 // The method might not be present for stubs not generated with AIDL.
259 return null;
260 }
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100261 }
262
263 @Nullable
264 private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) {
265 if (getDefaultTransactionName == null) {
266 return null;
267 }
268
Olivier Gaillard1f93a772018-07-30 14:09:22 +0100269 try {
270 return (String) getDefaultTransactionName.invoke(null, transactionCode);
271 } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
272 throw new RuntimeException(e);
273 }
274 }
275
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100276 /**
277 * This method is expensive to call.
278 */
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100279 public ArrayList<ExportedCallStat> getExportedCallStats() {
280 // We do not collect all the data if detailed tracking is off.
281 if (!mDetailedTracking) {
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100282 return new ArrayList<>();
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100283 }
284
285 ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
286 synchronized (mLock) {
Olivier Gaillardb8122292018-08-01 13:49:25 +0100287 final int uidEntriesSize = mUidEntries.size();
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100288 for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
Olivier Gaillardb8122292018-08-01 13:49:25 +0100289 final UidEntry entry = mUidEntries.valueAt(entryIdx);
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100290 for (CallStat stat : entry.getCallStatsList()) {
291 ExportedCallStat exported = new ExportedCallStat();
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100292 exported.workSourceUid = entry.workSourceUid;
293 exported.callingUid = stat.callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100294 exported.className = stat.binderClass.getName();
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100295 exported.binderClass = stat.binderClass;
296 exported.transactionCode = stat.transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100297 exported.screenInteractive = stat.screenInteractive;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100298 exported.cpuTimeMicros = stat.cpuTimeMicros;
299 exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
300 exported.latencyMicros = stat.latencyMicros;
301 exported.maxLatencyMicros = stat.maxLatencyMicros;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100302 exported.recordedCallCount = stat.recordedCallCount;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100303 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 Gaillardf31dfb92018-07-31 12:59:20 +0100311
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 Oczeretko772e8f22018-11-21 13:00:32 +0000341 // Debug entries added to help validate the data.
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000342 if (mAddDebugEntries && mBatteryStopwatch != null) {
Olivier Gaillard28109b52018-12-14 15:14:14 +0000343 resultCallStats.add(createDebugEntry("start_time_millis", mStartElapsedTime));
344 resultCallStats.add(createDebugEntry("end_time_millis", SystemClock.elapsedRealtime()));
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000345 resultCallStats.add(
346 createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000347 }
348
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100349 return resultCallStats;
350 }
351
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000352 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 Oczeretko94501712018-12-17 18:03:45 +0000360 callStat.methodName = DEBUG_ENTRY_PREFIX + variableName;
Marcin Oczeretko8d861742018-12-06 11:13:29 +0000361 callStat.latencyMicros = value;
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000362 return callStat;
363 }
364
Olivier Gaillard6f52d152018-07-25 12:13:12 +0100365 /** @hide */
366 public ArrayMap<String, Integer> getExportedExceptionStats() {
367 synchronized (mLock) {
368 return new ArrayMap(mExceptionCounts);
369 }
370 }
371
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100372 /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000373 public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700374 synchronized (mLock) {
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000375 dumpLocked(pw, packageMap, verbose);
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700376 }
377 }
378
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000379 private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
Fyodor Kupolovca348512018-01-10 18:05:53 -0800380 long totalCallsCount = 0;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100381 long totalRecordedCallsCount = 0;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700382 long totalCpuTime = 0;
Fyodor Kupolov8aa51242018-04-23 12:44:51 -0700383 pw.print("Start time: ");
Olivier Gaillard28109b52018-12-14 15:14:14 +0000384 pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime));
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000385 pw.print("On battery time (ms): ");
386 pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100387 pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
Olivier Gaillardb8122292018-08-01 13:49:25 +0100388 final List<UidEntry> entries = new ArrayList<>();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700389
Olivier Gaillardb8122292018-08-01 13:49:25 +0100390 final int uidEntriesSize = mUidEntries.size();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700391 for (int i = 0; i < uidEntriesSize; i++) {
392 UidEntry e = mUidEntries.valueAt(i);
393 entries.add(e);
394 totalCpuTime += e.cpuTimeMicros;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100395 totalRecordedCallsCount += e.recordedCallCount;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700396 totalCallsCount += e.callCount;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800397 }
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700398
399 entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
Olivier Gaillardb8122292018-08-01 13:49:25 +0100400 final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
401 final StringBuilder sb = new StringBuilder();
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100402 pw.println("Per-UID raw data " + datasetSizeDesc
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100403 + "(package/uid, worksource, call_desc, screen_interactive, "
Olivier Gaillard86714d12018-08-01 15:05:36 +0100404 + "cpu_time_micros, max_cpu_time_micros, "
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100405 + "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 Gaillardb8122292018-08-01 13:49:25 +0100408 final List<ExportedCallStat> exportedCallStats = getExportedCallStats();
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100409 exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
410 for (ExportedCallStat e : exportedCallStats) {
Marcin Oczeretko94501712018-12-17 18:03:45 +0000411 if (e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) {
412 // Do not dump debug entries.
413 continue;
414 }
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100415 sb.setLength(0);
416 sb.append(" ")
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000417 .append(packageMap.mapUid(e.callingUid))
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100418 .append(',')
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000419 .append(packageMap.mapUid(e.workSourceUid))
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100420 .append(',').append(e.className)
421 .append('#').append(e.methodName)
Olivier Gaillard86714d12018-08-01 15:05:36 +0100422 .append(',').append(e.screenInteractive)
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100423 .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 Kupolovca348512018-01-10 18:05:53 -0800433 }
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100434 pw.println();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700435 pw.println("Per-UID Summary " + datasetSizeDesc
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100436 + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
Olivier Gaillardb8122292018-08-01 13:49:25 +0100437 final List<UidEntry> summaryEntries = verbose ? entries
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700438 : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
439 for (UidEntry entry : summaryEntries) {
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000440 String uidStr = packageMap.mapUid(entry.workSourceUid);
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100441 pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100442 entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
443 entry.recordedCallCount, entry.callCount, uidStr));
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700444 }
445 pw.println();
446 pw.println(String.format(" Summary: total_cpu_time=%d, "
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100447 + "calls_count=%d, avg_call_cpu_time=%.0f",
448 totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100449 pw.println();
450
451 pw.println("Exceptions thrown (exception_count, class_name):");
Olivier Gaillardb8122292018-08-01 13:49:25 +0100452 final List<Pair<String, Integer>> exceptionEntries = new ArrayList<>();
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100453 // We cannot use new ArrayList(Collection) constructor because MapCollections does not
454 // implement toArray method.
455 mExceptionCounts.entrySet().iterator().forEachRemaining(
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100456 (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100457 exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
458 for (Pair<String, Integer> entry : exceptionEntries) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100459 pw.println(String.format(" %6d %s", entry.second, entry.first));
460 }
461
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100462 if (mPeriodicSamplingInterval != 1) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100463 pw.println("");
464 pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100465 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800466 }
467
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700468 protected long getThreadTimeMicro() {
469 return SystemClock.currentThreadTimeMicro();
470 }
471
472 protected int getCallingUid() {
473 return Binder.getCallingUid();
474 }
475
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100476 protected long getElapsedRealtimeMicro() {
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700477 return SystemClock.elapsedRealtimeNanos() / 1000;
Olivier Gaillard121988e2018-05-15 20:49:47 +0100478 }
479
Olivier Gaillard8a3e20a2018-10-25 13:17:51 +0100480 protected boolean shouldRecordDetailedData() {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100481 return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
482 }
483
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100484 /**
485 * Sets to true to collect all the data.
486 */
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700487 public void setDetailedTracking(boolean enabled) {
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100488 synchronized (mLock) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100489 if (enabled != mDetailedTracking) {
490 mDetailedTracking = enabled;
491 reset();
492 }
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100493 }
494 }
495
Olivier Gaillard36b80ca2019-02-11 11:41:39 +0000496 /**
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 Oczeretko772e8f22018-11-21 13:00:32 +0000520 public void setAddDebugEntries(boolean addDebugEntries) {
521 mAddDebugEntries = addDebugEntries;
522 }
523
Olivier Gaillard79490612018-11-30 16:22:23 +0000524 /**
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 Gaillard1d7f6152018-07-03 13:57:58 +0100542 public void setSamplingInterval(int samplingInterval) {
Marcin Oczeretko5a082f62018-10-02 16:17:43 +0100543 if (samplingInterval <= 0) {
544 Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
545 + samplingInterval);
546 return;
547 }
548
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100549 synchronized (mLock) {
550 if (samplingInterval != mPeriodicSamplingInterval) {
551 mPeriodicSamplingInterval = samplingInterval;
552 reset();
553 }
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700554 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800555 }
556
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700557 public void reset() {
558 synchronized (mLock) {
Olivier Gaillard79490612018-11-30 16:22:23 +0000559 mCallStatsCount = 0;
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700560 mUidEntries.clear();
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100561 mExceptionCounts.clear();
Olivier Gaillard28109b52018-12-14 15:14:14 +0000562 mStartCurrentTime = System.currentTimeMillis();
563 mStartElapsedTime = SystemClock.elapsedRealtime();
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000564 if (mBatteryStopwatch != null) {
565 mBatteryStopwatch.reset();
566 }
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700567 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800568 }
569
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100570 /**
Olivier Gaillard86526902019-02-27 10:39:18 +0000571 * Aggregated data by uid/class/method to be sent through statsd.
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100572 */
573 public static class ExportedCallStat {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100574 public int callingUid;
575 public int workSourceUid;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100576 public String className;
577 public String methodName;
Olivier Gaillard6f52d152018-07-25 12:13:12 +0100578 public boolean screenInteractive;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100579 public long cpuTimeMicros;
580 public long maxCpuTimeMicros;
581 public long latencyMicros;
582 public long maxLatencyMicros;
583 public long callCount;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100584 public long recordedCallCount;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100585 public long maxRequestSizeBytes;
586 public long maxReplySizeBytes;
587 public long exceptionCount;
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100588
589 // Used internally.
590 Class<? extends Binder> binderClass;
591 int transactionCode;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100592 }
593
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700594 @VisibleForTesting
595 public static class CallStat {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100596 // 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 Gaillard86714d12018-08-01 15:05:36 +0100600 // True if the screen was interactive when the call ended.
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100601 public final boolean screenInteractive;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100602 // 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 Gaillard34ad8af2018-08-06 15:55:03 +0100605 // 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 Gaillard2c13c6f2018-07-16 16:55:58 +0100607 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 Kupolovcf0fe2d2018-05-22 18:50:04 -0700611 public long cpuTimeMicros;
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100612 public long maxCpuTimeMicros;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100613 // Total latency of all for all the recorded calls.
614 // Approximate average latency can be computed by
615 // latencyMicros * callCount / recordedCallCount
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700616 public long latencyMicros;
Olivier Gaillard58b56e32018-06-01 16:18:43 +0100617 public long maxLatencyMicros;
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100618 // The following fields are only computed if mDetailedTracking is set.
Olivier Gaillard58b56e32018-06-01 16:18:43 +0100619 public long maxRequestSizeBytes;
620 public long maxReplySizeBytes;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700621 public long exceptionCount;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800622
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100623 CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
Olivier Gaillard86714d12018-08-01 15:05:36 +0100624 boolean screenInteractive) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100625 this.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100626 this.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100627 this.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100628 this.screenInteractive = screenInteractive;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800629 }
Olivier Gaillard289ba402018-07-24 18:50:13 +0100630 }
631
632 /** Key used to store CallStat object in a Map. */
633 public static class CallStatKey {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100634 public int callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100635 public Class<? extends Binder> binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100636 public int transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100637 private boolean screenInteractive;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100638
639 @Override
Fyodor Kupolovca348512018-01-10 18:05:53 -0800640 public boolean equals(Object o) {
641 if (this == o) {
642 return true;
643 }
644
Olivier Gaillardb8122292018-08-01 13:49:25 +0100645 final CallStatKey key = (CallStatKey) o;
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100646 return callingUid == key.callingUid
647 && transactionCode == key.transactionCode
Olivier Gaillard86714d12018-08-01 15:05:36 +0100648 && screenInteractive == key.screenInteractive
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100649 && (binderClass.equals(key.binderClass));
Fyodor Kupolovca348512018-01-10 18:05:53 -0800650 }
651
652 @Override
653 public int hashCode() {
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100654 int result = binderClass.hashCode();
Olivier Gaillard289ba402018-07-24 18:50:13 +0100655 result = 31 * result + transactionCode;
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100656 result = 31 * result + callingUid;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100657 result = 31 * result + (screenInteractive ? 1231 : 1237);
Fyodor Kupolovca348512018-01-10 18:05:53 -0800658 return result;
659 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800660 }
661
Fyodor Kupolovca348512018-01-10 18:05:53 -0800662
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700663 @VisibleForTesting
664 public static class UidEntry {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100665 // 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 Gaillard2c13c6f2018-07-16 16:55:58 +0100668 // 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 Kupolovcf0fe2d2018-05-22 18:50:04 -0700672 public long callCount;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100673 // 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 Kupolovca348512018-01-10 18:05:53 -0800677
678 UidEntry(int uid) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100679 this.workSourceUid = uid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800680 }
681
682 // Aggregate time spent per each call name: call_desc -> cpu_time_micros
Olivier Gaillard289ba402018-07-24 18:50:13 +0100683 private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
684 private CallStatKey mTempKey = new CallStatKey();
Fyodor Kupolovca348512018-01-10 18:05:53 -0800685
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100686 @Nullable
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100687 CallStat get(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
Olivier Gaillard86714d12018-08-01 15:05:36 +0100688 boolean screenInteractive) {
Olivier Gaillard289ba402018-07-24 18:50:13 +0100689 // Use a global temporary key to avoid creating new objects for every lookup.
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100690 mTempKey.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100691 mTempKey.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100692 mTempKey.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100693 mTempKey.screenInteractive = screenInteractive;
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100694 return mCallStats.get(mTempKey);
695 }
696
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100697 CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
Olivier Gaillard79490612018-11-30 16:22:23 +0000698 int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100699 CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
Olivier Gaillard79490612018-11-30 16:22:23 +0000700 // Only create CallStat if it's a new entry, otherwise update existing instance.
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700701 if (mapCallStat == null) {
Olivier Gaillard79490612018-11-30 16:22:23 +0000702 if (maxCallStatsReached) {
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000703 mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER,
704 OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE);
Olivier Gaillard79490612018-11-30 16:22:23 +0000705 if (mapCallStat != null) {
706 return mapCallStat;
707 }
708
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000709 callingUid = OVERFLOW_DIRECT_CALLING_UID;
Olivier Gaillard79490612018-11-30 16:22:23 +0000710 binderClass = OVERFLOW_BINDER;
711 transactionCode = OVERFLOW_TRANSACTION_CODE;
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000712 screenInteractive = OVERFLOW_SCREEN_INTERACTIVE;
Olivier Gaillard79490612018-11-30 16:22:23 +0000713 }
714
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100715 mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
716 screenInteractive);
Olivier Gaillard289ba402018-07-24 18:50:13 +0100717 CallStatKey key = new CallStatKey();
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100718 key.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100719 key.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100720 key.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100721 key.screenInteractive = screenInteractive;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100722 mCallStats.put(key, mapCallStat);
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700723 }
724 return mapCallStat;
725 }
726
727 /**
728 * Returns list of calls sorted by CPU time
729 */
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100730 public Collection<CallStat> getCallStatsList() {
731 return mCallStats.values();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700732 }
733
Fyodor Kupolovca348512018-01-10 18:05:53 -0800734 @Override
735 public String toString() {
736 return "UidEntry{" +
Olivier Gaillard121988e2018-05-15 20:49:47 +0100737 "cpuTimeMicros=" + cpuTimeMicros +
Fyodor Kupolovca348512018-01-10 18:05:53 -0800738 ", 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 Gaillard88de48f2018-10-18 10:21:21 +0100750 return workSourceUid == uidEntry.workSourceUid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800751 }
752
753 @Override
754 public int hashCode() {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100755 return workSourceUid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800756 }
757 }
758
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700759 @VisibleForTesting
760 public SparseArray<UidEntry> getUidEntries() {
761 return mUidEntries;
762 }
763
764 @VisibleForTesting
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100765 public ArrayMap<String, Integer> getExceptionCounts() {
766 return mExceptionCounts;
767 }
768
769 @VisibleForTesting
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700770 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 Gaillardf31dfb92018-07-31 12:59:20 +0100790 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 Gaillard7a2a98b2018-07-26 13:29:16 +0100801 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800802}