blob: 0fb2728aed7320b1a5641752162ec5c076b74751 [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 Gaillard45b68652019-03-01 13:37:47 +000057 public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500;
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,
Olivier Gaillard1c56b4d2019-03-07 16:18:39 +0000215 screenInteractive);
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()));
Olivier Gaillard5d339dd2019-03-08 11:25:05 +0000347 resultCallStats.add(createDebugEntry("sampling_interval", mPeriodicSamplingInterval));
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000348 }
349
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100350 return resultCallStats;
351 }
352
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000353 private ExportedCallStat createDebugEntry(String variableName, long value) {
354 final int uid = Process.myUid();
355 final ExportedCallStat callStat = new ExportedCallStat();
356 callStat.className = "";
357 callStat.workSourceUid = uid;
358 callStat.callingUid = uid;
359 callStat.recordedCallCount = 1;
360 callStat.callCount = 1;
Marcin Oczeretko94501712018-12-17 18:03:45 +0000361 callStat.methodName = DEBUG_ENTRY_PREFIX + variableName;
Marcin Oczeretko8d861742018-12-06 11:13:29 +0000362 callStat.latencyMicros = value;
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000363 return callStat;
364 }
365
Olivier Gaillard6f52d152018-07-25 12:13:12 +0100366 /** @hide */
367 public ArrayMap<String, Integer> getExportedExceptionStats() {
368 synchronized (mLock) {
369 return new ArrayMap(mExceptionCounts);
370 }
371 }
372
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100373 /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000374 public void dump(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700375 synchronized (mLock) {
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000376 dumpLocked(pw, packageMap, verbose);
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700377 }
378 }
379
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000380 private void dumpLocked(PrintWriter pw, AppIdToPackageMap packageMap, boolean verbose) {
Fyodor Kupolovca348512018-01-10 18:05:53 -0800381 long totalCallsCount = 0;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100382 long totalRecordedCallsCount = 0;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700383 long totalCpuTime = 0;
Fyodor Kupolov8aa51242018-04-23 12:44:51 -0700384 pw.print("Start time: ");
Olivier Gaillard28109b52018-12-14 15:14:14 +0000385 pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartCurrentTime));
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000386 pw.print("On battery time (ms): ");
387 pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100388 pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
Olivier Gaillardb8122292018-08-01 13:49:25 +0100389 final List<UidEntry> entries = new ArrayList<>();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700390
Olivier Gaillardb8122292018-08-01 13:49:25 +0100391 final int uidEntriesSize = mUidEntries.size();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700392 for (int i = 0; i < uidEntriesSize; i++) {
393 UidEntry e = mUidEntries.valueAt(i);
394 entries.add(e);
395 totalCpuTime += e.cpuTimeMicros;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100396 totalRecordedCallsCount += e.recordedCallCount;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700397 totalCallsCount += e.callCount;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800398 }
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700399
400 entries.sort(Comparator.<UidEntry>comparingDouble(value -> value.cpuTimeMicros).reversed());
Olivier Gaillardb8122292018-08-01 13:49:25 +0100401 final String datasetSizeDesc = verbose ? "" : "(top 90% by cpu time) ";
402 final StringBuilder sb = new StringBuilder();
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100403 pw.println("Per-UID raw data " + datasetSizeDesc
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100404 + "(package/uid, worksource, call_desc, screen_interactive, "
Olivier Gaillard86714d12018-08-01 15:05:36 +0100405 + "cpu_time_micros, max_cpu_time_micros, "
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100406 + "latency_time_micros, max_latency_time_micros, exception_count, "
407 + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
408 + "call_count):");
Olivier Gaillardb8122292018-08-01 13:49:25 +0100409 final List<ExportedCallStat> exportedCallStats = getExportedCallStats();
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100410 exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
411 for (ExportedCallStat e : exportedCallStats) {
Marcin Oczeretko94501712018-12-17 18:03:45 +0000412 if (e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) {
413 // Do not dump debug entries.
414 continue;
415 }
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100416 sb.setLength(0);
417 sb.append(" ")
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000418 .append(packageMap.mapUid(e.callingUid))
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100419 .append(',')
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000420 .append(packageMap.mapUid(e.workSourceUid))
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100421 .append(',').append(e.className)
422 .append('#').append(e.methodName)
Olivier Gaillard86714d12018-08-01 15:05:36 +0100423 .append(',').append(e.screenInteractive)
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100424 .append(',').append(e.cpuTimeMicros)
425 .append(',').append(e.maxCpuTimeMicros)
426 .append(',').append(e.latencyMicros)
427 .append(',').append(e.maxLatencyMicros)
428 .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
429 .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
430 .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
431 .append(',').append(e.recordedCallCount)
432 .append(',').append(e.callCount);
433 pw.println(sb);
Fyodor Kupolovca348512018-01-10 18:05:53 -0800434 }
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100435 pw.println();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700436 pw.println("Per-UID Summary " + datasetSizeDesc
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100437 + "(cpu_time, % of total cpu_time, recorded_call_count, call_count, package/uid):");
Olivier Gaillardb8122292018-08-01 13:49:25 +0100438 final List<UidEntry> summaryEntries = verbose ? entries
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700439 : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
440 for (UidEntry entry : summaryEntries) {
Marcin Oczeretkoc4c45a82018-12-06 15:09:49 +0000441 String uidStr = packageMap.mapUid(entry.workSourceUid);
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100442 pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100443 entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
444 entry.recordedCallCount, entry.callCount, uidStr));
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700445 }
446 pw.println();
447 pw.println(String.format(" Summary: total_cpu_time=%d, "
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100448 + "calls_count=%d, avg_call_cpu_time=%.0f",
449 totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100450 pw.println();
451
452 pw.println("Exceptions thrown (exception_count, class_name):");
Olivier Gaillardb8122292018-08-01 13:49:25 +0100453 final List<Pair<String, Integer>> exceptionEntries = new ArrayList<>();
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100454 // We cannot use new ArrayList(Collection) constructor because MapCollections does not
455 // implement toArray method.
456 mExceptionCounts.entrySet().iterator().forEachRemaining(
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100457 (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100458 exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
459 for (Pair<String, Integer> entry : exceptionEntries) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100460 pw.println(String.format(" %6d %s", entry.second, entry.first));
461 }
462
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100463 if (mPeriodicSamplingInterval != 1) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100464 pw.println("");
465 pw.println("/!\\ Displayed data is sampled. See sampling interval at the top.");
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100466 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800467 }
468
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700469 protected long getThreadTimeMicro() {
470 return SystemClock.currentThreadTimeMicro();
471 }
472
473 protected int getCallingUid() {
474 return Binder.getCallingUid();
475 }
476
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100477 protected long getElapsedRealtimeMicro() {
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700478 return SystemClock.elapsedRealtimeNanos() / 1000;
Olivier Gaillard121988e2018-05-15 20:49:47 +0100479 }
480
Olivier Gaillard8a3e20a2018-10-25 13:17:51 +0100481 protected boolean shouldRecordDetailedData() {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100482 return mRandom.nextInt() % mPeriodicSamplingInterval == 0;
483 }
484
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100485 /**
486 * Sets to true to collect all the data.
487 */
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700488 public void setDetailedTracking(boolean enabled) {
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100489 synchronized (mLock) {
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100490 if (enabled != mDetailedTracking) {
491 mDetailedTracking = enabled;
492 reset();
493 }
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100494 }
495 }
496
Olivier Gaillard36b80ca2019-02-11 11:41:39 +0000497 /**
498 * Whether to track the screen state.
499 */
500 public void setTrackScreenInteractive(boolean enabled) {
501 synchronized (mLock) {
502 if (enabled != mTrackScreenInteractive) {
503 mTrackScreenInteractive = enabled;
504 reset();
505 }
506 }
507 }
508
509 /**
510 * Whether to track direct caller uid.
511 */
512 public void setTrackDirectCallerUid(boolean enabled) {
513 synchronized (mLock) {
514 if (enabled != mTrackDirectCallingUid) {
515 mTrackDirectCallingUid = enabled;
516 reset();
517 }
518 }
519 }
520
Marcin Oczeretko772e8f22018-11-21 13:00:32 +0000521 public void setAddDebugEntries(boolean addDebugEntries) {
522 mAddDebugEntries = addDebugEntries;
523 }
524
Olivier Gaillard79490612018-11-30 16:22:23 +0000525 /**
526 * Sets the maximum number of items to track.
527 */
528 public void setMaxBinderCallStats(int maxKeys) {
529 if (maxKeys <= 0) {
530 Slog.w(TAG, "Ignored invalid max value (value must be positive): "
531 + maxKeys);
532 return;
533 }
534
535 synchronized (mLock) {
536 if (maxKeys != mMaxBinderCallStatsCount) {
537 mMaxBinderCallStatsCount = maxKeys;
538 reset();
539 }
540 }
541 }
542
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100543 public void setSamplingInterval(int samplingInterval) {
Marcin Oczeretko5a082f62018-10-02 16:17:43 +0100544 if (samplingInterval <= 0) {
545 Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
546 + samplingInterval);
547 return;
548 }
549
Olivier Gaillard1d7f6152018-07-03 13:57:58 +0100550 synchronized (mLock) {
551 if (samplingInterval != mPeriodicSamplingInterval) {
552 mPeriodicSamplingInterval = samplingInterval;
553 reset();
554 }
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700555 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800556 }
557
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700558 public void reset() {
559 synchronized (mLock) {
Olivier Gaillard79490612018-11-30 16:22:23 +0000560 mCallStatsCount = 0;
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700561 mUidEntries.clear();
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100562 mExceptionCounts.clear();
Olivier Gaillard28109b52018-12-14 15:14:14 +0000563 mStartCurrentTime = System.currentTimeMillis();
564 mStartElapsedTime = SystemClock.elapsedRealtime();
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000565 if (mBatteryStopwatch != null) {
566 mBatteryStopwatch.reset();
567 }
Fyodor Kupolov3f3af612018-04-18 17:26:43 -0700568 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800569 }
570
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100571 /**
Olivier Gaillard86526902019-02-27 10:39:18 +0000572 * Aggregated data by uid/class/method to be sent through statsd.
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100573 */
574 public static class ExportedCallStat {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100575 public int callingUid;
576 public int workSourceUid;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100577 public String className;
578 public String methodName;
Olivier Gaillard6f52d152018-07-25 12:13:12 +0100579 public boolean screenInteractive;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100580 public long cpuTimeMicros;
581 public long maxCpuTimeMicros;
582 public long latencyMicros;
583 public long maxLatencyMicros;
584 public long callCount;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100585 public long recordedCallCount;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100586 public long maxRequestSizeBytes;
587 public long maxReplySizeBytes;
588 public long exceptionCount;
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100589
590 // Used internally.
591 Class<? extends Binder> binderClass;
592 int transactionCode;
Olivier Gaillard00bfb1b2018-07-10 11:25:09 +0100593 }
594
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700595 @VisibleForTesting
596 public static class CallStat {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100597 // The UID who executed the transaction (i.e. Binder#getCallingUid).
598 public final int callingUid;
599 public final Class<? extends Binder> binderClass;
600 public final int transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100601 // True if the screen was interactive when the call ended.
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100602 public final boolean screenInteractive;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100603 // Number of calls for which we collected data for. We do not record data for all the calls
604 // when sampling is on.
605 public long recordedCallCount;
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100606 // Roughly the real number of total calls. We only track only track the API call count once
607 // at least one non-sampled count happened.
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100608 public long callCount;
609 // Total CPU of all for all the recorded calls.
610 // Approximate total CPU usage can be computed by
611 // cpuTimeMicros * callCount / recordedCallCount
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700612 public long cpuTimeMicros;
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100613 public long maxCpuTimeMicros;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100614 // Total latency of all for all the recorded calls.
615 // Approximate average latency can be computed by
616 // latencyMicros * callCount / recordedCallCount
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700617 public long latencyMicros;
Olivier Gaillard58b56e32018-06-01 16:18:43 +0100618 public long maxLatencyMicros;
Olivier Gaillard11965ed2018-06-04 14:14:04 +0100619 // The following fields are only computed if mDetailedTracking is set.
Olivier Gaillard58b56e32018-06-01 16:18:43 +0100620 public long maxRequestSizeBytes;
621 public long maxReplySizeBytes;
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700622 public long exceptionCount;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800623
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100624 CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
Olivier Gaillard86714d12018-08-01 15:05:36 +0100625 boolean screenInteractive) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100626 this.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100627 this.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100628 this.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100629 this.screenInteractive = screenInteractive;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800630 }
Olivier Gaillard289ba402018-07-24 18:50:13 +0100631 }
632
633 /** Key used to store CallStat object in a Map. */
634 public static class CallStatKey {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100635 public int callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100636 public Class<? extends Binder> binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100637 public int transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100638 private boolean screenInteractive;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100639
640 @Override
Fyodor Kupolovca348512018-01-10 18:05:53 -0800641 public boolean equals(Object o) {
642 if (this == o) {
643 return true;
644 }
645
Olivier Gaillardb8122292018-08-01 13:49:25 +0100646 final CallStatKey key = (CallStatKey) o;
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100647 return callingUid == key.callingUid
648 && transactionCode == key.transactionCode
Olivier Gaillard86714d12018-08-01 15:05:36 +0100649 && screenInteractive == key.screenInteractive
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100650 && (binderClass.equals(key.binderClass));
Fyodor Kupolovca348512018-01-10 18:05:53 -0800651 }
652
653 @Override
654 public int hashCode() {
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100655 int result = binderClass.hashCode();
Olivier Gaillard289ba402018-07-24 18:50:13 +0100656 result = 31 * result + transactionCode;
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100657 result = 31 * result + callingUid;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100658 result = 31 * result + (screenInteractive ? 1231 : 1237);
Fyodor Kupolovca348512018-01-10 18:05:53 -0800659 return result;
660 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800661 }
662
Fyodor Kupolovca348512018-01-10 18:05:53 -0800663
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700664 @VisibleForTesting
665 public static class UidEntry {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100666 // The UID who is responsible for the binder transaction. If the bluetooth process execute a
667 // transaction on behalf of app foo, the workSourceUid will be the uid of app foo.
668 public int workSourceUid;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100669 // Number of calls for which we collected data for. We do not record data for all the calls
670 // when sampling is on.
671 public long recordedCallCount;
672 // Real number of total calls.
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700673 public long callCount;
Olivier Gaillard2c13c6f2018-07-16 16:55:58 +0100674 // Total CPU of all for all the recorded calls.
675 // Approximate total CPU usage can be computed by
676 // cpuTimeMicros * callCount / recordedCallCount
677 public long cpuTimeMicros;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800678
679 UidEntry(int uid) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100680 this.workSourceUid = uid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800681 }
682
683 // Aggregate time spent per each call name: call_desc -> cpu_time_micros
Olivier Gaillard289ba402018-07-24 18:50:13 +0100684 private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
685 private CallStatKey mTempKey = new CallStatKey();
Fyodor Kupolovca348512018-01-10 18:05:53 -0800686
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100687 @Nullable
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100688 CallStat get(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
Olivier Gaillard86714d12018-08-01 15:05:36 +0100689 boolean screenInteractive) {
Olivier Gaillard289ba402018-07-24 18:50:13 +0100690 // Use a global temporary key to avoid creating new objects for every lookup.
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100691 mTempKey.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100692 mTempKey.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100693 mTempKey.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100694 mTempKey.screenInteractive = screenInteractive;
Olivier Gaillard34ad8af2018-08-06 15:55:03 +0100695 return mCallStats.get(mTempKey);
696 }
697
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100698 CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
Olivier Gaillard79490612018-11-30 16:22:23 +0000699 int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100700 CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
Olivier Gaillard79490612018-11-30 16:22:23 +0000701 // Only create CallStat if it's a new entry, otherwise update existing instance.
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700702 if (mapCallStat == null) {
Olivier Gaillard79490612018-11-30 16:22:23 +0000703 if (maxCallStatsReached) {
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000704 mapCallStat = get(OVERFLOW_DIRECT_CALLING_UID, OVERFLOW_BINDER,
705 OVERFLOW_TRANSACTION_CODE, OVERFLOW_SCREEN_INTERACTIVE);
Olivier Gaillard79490612018-11-30 16:22:23 +0000706 if (mapCallStat != null) {
707 return mapCallStat;
708 }
709
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000710 callingUid = OVERFLOW_DIRECT_CALLING_UID;
Olivier Gaillard79490612018-11-30 16:22:23 +0000711 binderClass = OVERFLOW_BINDER;
712 transactionCode = OVERFLOW_TRANSACTION_CODE;
Olivier Gaillarda3686dd2018-12-07 11:28:07 +0000713 screenInteractive = OVERFLOW_SCREEN_INTERACTIVE;
Olivier Gaillard79490612018-11-30 16:22:23 +0000714 }
715
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100716 mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
717 screenInteractive);
Olivier Gaillard289ba402018-07-24 18:50:13 +0100718 CallStatKey key = new CallStatKey();
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100719 key.callingUid = callingUid;
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100720 key.binderClass = binderClass;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100721 key.transactionCode = transactionCode;
Olivier Gaillard86714d12018-08-01 15:05:36 +0100722 key.screenInteractive = screenInteractive;
Olivier Gaillard289ba402018-07-24 18:50:13 +0100723 mCallStats.put(key, mapCallStat);
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700724 }
725 return mapCallStat;
726 }
727
728 /**
729 * Returns list of calls sorted by CPU time
730 */
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100731 public Collection<CallStat> getCallStatsList() {
732 return mCallStats.values();
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700733 }
734
Fyodor Kupolovca348512018-01-10 18:05:53 -0800735 @Override
736 public String toString() {
737 return "UidEntry{" +
Olivier Gaillard121988e2018-05-15 20:49:47 +0100738 "cpuTimeMicros=" + cpuTimeMicros +
Fyodor Kupolovca348512018-01-10 18:05:53 -0800739 ", callCount=" + callCount +
740 ", mCallStats=" + mCallStats +
741 '}';
742 }
743
744 @Override
745 public boolean equals(Object o) {
746 if (this == o) {
747 return true;
748 }
749
750 UidEntry uidEntry = (UidEntry) o;
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100751 return workSourceUid == uidEntry.workSourceUid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800752 }
753
754 @Override
755 public int hashCode() {
Olivier Gaillard88de48f2018-10-18 10:21:21 +0100756 return workSourceUid;
Fyodor Kupolovca348512018-01-10 18:05:53 -0800757 }
758 }
759
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700760 @VisibleForTesting
761 public SparseArray<UidEntry> getUidEntries() {
762 return mUidEntries;
763 }
764
765 @VisibleForTesting
Olivier Gaillardf82d2e732018-06-07 11:45:35 +0100766 public ArrayMap<String, Integer> getExceptionCounts() {
767 return mExceptionCounts;
768 }
769
770 @VisibleForTesting
Fyodor Kupolovcf0fe2d2018-05-22 18:50:04 -0700771 public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble,
772 double percentile) {
773 List<T> sortedList = new ArrayList<>(list);
774 sortedList.sort(Comparator.comparingDouble(toDouble).reversed());
775 double total = 0;
776 for (T item : list) {
777 total += toDouble.applyAsDouble(item);
778 }
779 List<T> result = new ArrayList<>();
780 double runningSum = 0;
781 for (T item : sortedList) {
782 if (runningSum > percentile * total) {
783 break;
784 }
785 result.add(item);
786 runningSum += toDouble.applyAsDouble(item);
787 }
788 return result;
789 }
790
Olivier Gaillardf31dfb92018-07-31 12:59:20 +0100791 private static int compareByCpuDesc(
792 ExportedCallStat a, ExportedCallStat b) {
793 return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
794 }
795
796 private static int compareByBinderClassAndCode(
797 ExportedCallStat a, ExportedCallStat b) {
798 int result = a.className.compareTo(b.className);
799 return result != 0
800 ? result
801 : Integer.compare(a.transactionCode, b.transactionCode);
Olivier Gaillard7a2a98b2018-07-26 13:29:16 +0100802 }
Fyodor Kupolovca348512018-01-10 18:05:53 -0800803}