Merge "Include binder stats in battery stats" am: 83d1954afe
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1708610 Change-Id: I17856dbb12a1c1cff7b4d6df740a016876b0bb1a
This commit is contained in:
@@ -40,6 +40,7 @@ import android.net.Uri;
|
|||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.os.BatteryStats;
|
import android.os.BatteryStats;
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBatteryPropertiesRegistrar;
|
import android.os.IBatteryPropertiesRegistrar;
|
||||||
@@ -71,6 +72,7 @@ import android.telephony.SignalStrength;
|
|||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.IntArray;
|
import android.util.IntArray;
|
||||||
import android.util.KeyValueListParser;
|
import android.util.KeyValueListParser;
|
||||||
@@ -122,6 +124,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -6130,10 +6133,11 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
* Records timing data related to an incoming Binder call in order to attribute
|
* Records timing data related to an incoming Binder call in order to attribute
|
||||||
* the power consumption to the calling app.
|
* the power consumption to the calling app.
|
||||||
*/
|
*/
|
||||||
public void noteBinderCallStats(int workSourceUid,
|
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
|
||||||
Collection<BinderCallsStats.CallStat> callStats) {
|
Collection<BinderCallsStats.CallStat> callStats) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(callStats);
|
getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
|
||||||
|
callStats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6579,6 +6583,65 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulates stats for a specific binder transaction.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
protected static class BinderCallStats {
|
||||||
|
static final Comparator<BinderCallStats> COMPARATOR =
|
||||||
|
Comparator.comparing(BinderCallStats::getClassName)
|
||||||
|
.thenComparing(BinderCallStats::getMethodName);
|
||||||
|
|
||||||
|
public Class<? extends Binder> binderClass;
|
||||||
|
public int transactionCode;
|
||||||
|
public String methodName;
|
||||||
|
|
||||||
|
public long callCount;
|
||||||
|
public long recordedCallCount;
|
||||||
|
public long recordedCpuTimeMicros;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return binderClass.hashCode() * 31 + transactionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof BinderCallStats)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BinderCallStats bcsk = (BinderCallStats) obj;
|
||||||
|
return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return binderClass.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethodName() {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
public void ensureMethodName(BinderTransactionNameResolver resolver) {
|
||||||
|
if (methodName == null) {
|
||||||
|
methodName = resolver.getMethodName(binderClass, transactionCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BinderCallStats{"
|
||||||
|
+ binderClass
|
||||||
|
+ " transaction=" + transactionCode
|
||||||
|
+ " callCount=" + callCount
|
||||||
|
+ " recordedCallCount=" + recordedCallCount
|
||||||
|
+ " recorderCpuTimeMicros=" + recordedCpuTimeMicros
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The statistics associated with a particular uid.
|
* The statistics associated with a particular uid.
|
||||||
*/
|
*/
|
||||||
@@ -6752,6 +6815,16 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
*/
|
*/
|
||||||
final SparseArray<Pid> mPids = new SparseArray<>();
|
final SparseArray<Pid> mPids = new SparseArray<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand total of system server binder calls made by this uid.
|
||||||
|
*/
|
||||||
|
private long mBinderCallCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed information about system server binder calls made by this uid.
|
||||||
|
*/
|
||||||
|
private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
|
||||||
|
|
||||||
public Uid(BatteryStatsImpl bsi, int uid) {
|
public Uid(BatteryStatsImpl bsi, int uid) {
|
||||||
mBsi = bsi;
|
mBsi = bsi;
|
||||||
mUid = uid;
|
mUid = uid;
|
||||||
@@ -6860,6 +6933,14 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
return nullIfAllZeros(mProcStateScreenOffTimeMs[procState], which);
|
return nullIfAllZeros(mProcStateScreenOffTimeMs[procState], which);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getBinderCallCount() {
|
||||||
|
return mBinderCallCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArraySet<BinderCallStats> getBinderCallStats() {
|
||||||
|
return mBinderCallStats;
|
||||||
|
}
|
||||||
|
|
||||||
public void addIsolatedUid(int isolatedUid) {
|
public void addIsolatedUid(int isolatedUid) {
|
||||||
if (mChildUids == null) {
|
if (mChildUids == null) {
|
||||||
mChildUids = new IntArray();
|
mChildUids = new IntArray();
|
||||||
@@ -7948,6 +8029,9 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
}
|
}
|
||||||
mPackageStats.clear();
|
mPackageStats.clear();
|
||||||
|
|
||||||
|
mBinderCallCount = 0;
|
||||||
|
mBinderCallStats.clear();
|
||||||
|
|
||||||
mLastStepUserTime = mLastStepSystemTime = 0;
|
mLastStepUserTime = mLastStepSystemTime = 0;
|
||||||
mCurStepUserTime = mCurStepSystemTime = 0;
|
mCurStepUserTime = mCurStepSystemTime = 0;
|
||||||
|
|
||||||
@@ -8703,15 +8787,38 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reusable object used as a key to lookup values in mBinderCallStats
|
||||||
|
private static BinderCallStats sTempBinderCallStats = new BinderCallStats();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notes incoming binder call stats associated with this work source UID.
|
* Notes incoming binder call stats associated with this work source UID.
|
||||||
*/
|
*/
|
||||||
public void noteBinderCallStatsLocked(Collection<BinderCallsStats.CallStat> callStats) {
|
public void noteBinderCallStatsLocked(long incrementalCallCount,
|
||||||
|
Collection<BinderCallsStats.CallStat> callStats) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], callStats = ["
|
Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], "
|
||||||
|
+ " incrementalCallCount: " + incrementalCallCount + " callStats = ["
|
||||||
+ new ArrayList<>(callStats) + "]");
|
+ new ArrayList<>(callStats) + "]");
|
||||||
}
|
}
|
||||||
// TODO(dplotnikov): finish the implementation by actually remembering the stats
|
mBinderCallCount += incrementalCallCount;
|
||||||
|
for (BinderCallsStats.CallStat stat : callStats) {
|
||||||
|
BinderCallStats bcs;
|
||||||
|
sTempBinderCallStats.binderClass = stat.binderClass;
|
||||||
|
sTempBinderCallStats.transactionCode = stat.transactionCode;
|
||||||
|
int index = mBinderCallStats.indexOf(sTempBinderCallStats);
|
||||||
|
if (index >= 0) {
|
||||||
|
bcs = mBinderCallStats.valueAt(index);
|
||||||
|
} else {
|
||||||
|
bcs = new BinderCallStats();
|
||||||
|
bcs.binderClass = stat.binderClass;
|
||||||
|
bcs.transactionCode = stat.transactionCode;
|
||||||
|
mBinderCallStats.add(bcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bcs.callCount += stat.incrementalCallCount;
|
||||||
|
bcs.recordedCallCount = stat.recordedCallCount;
|
||||||
|
bcs.recordedCpuTimeMicros = stat.cpuTimeMicros;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13235,6 +13342,45 @@ public class BatteryStatsImpl extends BatteryStats {
|
|||||||
pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
|
pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
|
||||||
pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
|
pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
|
||||||
}
|
}
|
||||||
|
pw.println("Per UID system service calls:");
|
||||||
|
BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
int u = mUidStats.keyAt(i);
|
||||||
|
Uid uid = mUidStats.get(u);
|
||||||
|
long binderCallCount = uid.getBinderCallCount();
|
||||||
|
if (binderCallCount != 0) {
|
||||||
|
pw.print(" ");
|
||||||
|
pw.print(u);
|
||||||
|
pw.print(" system service calls: ");
|
||||||
|
pw.print(binderCallCount);
|
||||||
|
ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
|
||||||
|
if (!binderCallStats.isEmpty()) {
|
||||||
|
pw.println(", including");
|
||||||
|
BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
|
||||||
|
binderCallStats.toArray(bcss);
|
||||||
|
for (BinderCallStats bcs : bcss) {
|
||||||
|
bcs.ensureMethodName(nameResolver);
|
||||||
|
}
|
||||||
|
Arrays.sort(bcss, BinderCallStats.COMPARATOR);
|
||||||
|
for (BinderCallStats callStats : bcss) {
|
||||||
|
pw.print(" ");
|
||||||
|
pw.print(callStats.getClassName());
|
||||||
|
pw.print('#');
|
||||||
|
pw.print(callStats.getMethodName());
|
||||||
|
pw.print(" calls: ");
|
||||||
|
pw.print(callStats.callCount);
|
||||||
|
if (callStats.recordedCallCount != 0) {
|
||||||
|
pw.print(" time: ");
|
||||||
|
pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
|
||||||
|
/ callStats.recordedCallCount / 1000);
|
||||||
|
}
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pw.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pw.println("Per UID CPU active time in ms:");
|
pw.println("Per UID CPU active time in ms:");
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
int u = mUidStats.keyAt(i);
|
int u = mUidStats.keyAt(i);
|
||||||
|
|||||||
@@ -36,13 +36,10 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.internal.os.BinderInternal.CallSession;
|
import com.android.internal.os.BinderInternal.CallSession;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
@@ -118,8 +115,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i));
|
UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i));
|
||||||
if (uidEntry != null) {
|
if (uidEntry != null) {
|
||||||
|
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
|
||||||
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
|
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
|
||||||
uidEntry.getCallStatsList());
|
uidEntry.incrementalCallCount, callStats.values());
|
||||||
|
uidEntry.incrementalCallCount = 0;
|
||||||
|
for (int j = callStats.size() - 1; j >= 0; j--) {
|
||||||
|
callStats.valueAt(j).incrementalCallCount = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mSendUidsToObserver.clear();
|
mSendUidsToObserver.clear();
|
||||||
@@ -248,6 +250,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
|
|
||||||
final UidEntry uidEntry = getUidEntry(workSourceUid);
|
final UidEntry uidEntry = getUidEntry(workSourceUid);
|
||||||
uidEntry.callCount++;
|
uidEntry.callCount++;
|
||||||
|
uidEntry.incrementalCallCount++;
|
||||||
|
|
||||||
if (recordCall) {
|
if (recordCall) {
|
||||||
uidEntry.cpuTimeMicros += duration;
|
uidEntry.cpuTimeMicros += duration;
|
||||||
@@ -263,6 +266,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
callStat.callCount++;
|
callStat.callCount++;
|
||||||
|
callStat.incrementalCallCount++;
|
||||||
callStat.recordedCallCount++;
|
callStat.recordedCallCount++;
|
||||||
callStat.cpuTimeMicros += duration;
|
callStat.cpuTimeMicros += duration;
|
||||||
callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
|
callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
|
||||||
@@ -284,6 +288,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
screenInteractive);
|
screenInteractive);
|
||||||
if (callStat != null) {
|
if (callStat != null) {
|
||||||
callStat.callCount++;
|
callStat.callCount++;
|
||||||
|
callStat.incrementalCallCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) {
|
if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) {
|
||||||
@@ -322,30 +327,6 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
|
|
||||||
try {
|
|
||||||
return binder.getMethod("getDefaultTransactionName", int.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
// The method might not be present for stubs not generated with AIDL.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static String resolveTransactionCode(Method getDefaultTransactionName,
|
|
||||||
int transactionCode) {
|
|
||||||
String resolvedCode = null;
|
|
||||||
if (getDefaultTransactionName != null) {
|
|
||||||
try {
|
|
||||||
resolvedCode = (String) getDefaultTransactionName.invoke(null, transactionCode);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resolvedCode == null ? String.valueOf(transactionCode) : resolvedCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is expensive to call.
|
* This method is expensive to call.
|
||||||
*/
|
*/
|
||||||
@@ -384,28 +365,23 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
|
|
||||||
// Resolve codes outside of the lock since it can be slow.
|
// Resolve codes outside of the lock since it can be slow.
|
||||||
ExportedCallStat previous = null;
|
ExportedCallStat previous = null;
|
||||||
// Cache the previous method/transaction code.
|
|
||||||
Method getDefaultTransactionName = null;
|
|
||||||
String previousMethodName = null;
|
String previousMethodName = null;
|
||||||
resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
|
resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
|
||||||
|
BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
|
||||||
for (ExportedCallStat exported : resultCallStats) {
|
for (ExportedCallStat exported : resultCallStats) {
|
||||||
final boolean isClassDifferent = previous == null
|
final boolean isClassDifferent = previous == null
|
||||||
|| !previous.className.equals(exported.className);
|
|| !previous.className.equals(exported.className);
|
||||||
if (isClassDifferent) {
|
|
||||||
getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean isCodeDifferent = previous == null
|
final boolean isCodeDifferent = previous == null
|
||||||
|| previous.transactionCode != exported.transactionCode;
|
|| previous.transactionCode != exported.transactionCode;
|
||||||
final String methodName;
|
final String methodName;
|
||||||
if (isClassDifferent || isCodeDifferent) {
|
if (isClassDifferent || isCodeDifferent) {
|
||||||
methodName = resolveTransactionCode(
|
methodName = resolver.getMethodName(exported.binderClass, exported.transactionCode);
|
||||||
getDefaultTransactionName, exported.transactionCode);
|
|
||||||
} else {
|
} else {
|
||||||
methodName = previousMethodName;
|
methodName = previousMethodName;
|
||||||
}
|
}
|
||||||
previousMethodName = methodName;
|
previousMethodName = methodName;
|
||||||
exported.methodName = methodName;
|
exported.methodName = methodName;
|
||||||
|
previous = exported;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug entries added to help validate the data.
|
// Debug entries added to help validate the data.
|
||||||
@@ -703,8 +679,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
public long maxRequestSizeBytes;
|
public long maxRequestSizeBytes;
|
||||||
public long maxReplySizeBytes;
|
public long maxReplySizeBytes;
|
||||||
public long exceptionCount;
|
public long exceptionCount;
|
||||||
|
// Call count since reset
|
||||||
|
public long incrementalCallCount;
|
||||||
|
|
||||||
CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
|
public CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
|
||||||
boolean screenInteractive) {
|
boolean screenInteractive) {
|
||||||
this.callingUid = callingUid;
|
this.callingUid = callingUid;
|
||||||
this.binderClass = binderClass;
|
this.binderClass = binderClass;
|
||||||
@@ -714,12 +692,14 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
// This is expensive, but CallStat.toString() is only used for debugging.
|
||||||
|
String methodName = new BinderTransactionNameResolver().getMethodName(binderClass,
|
||||||
|
transactionCode);
|
||||||
return "CallStat{"
|
return "CallStat{"
|
||||||
+ "callingUid=" + callingUid
|
+ "callingUid=" + callingUid
|
||||||
+ ", transaction=" + binderClass.getSimpleName()
|
+ ", transaction=" + binderClass.getSimpleName() + '.' + methodName
|
||||||
+ '.' + resolveTransactionCode(
|
|
||||||
getDefaultTransactionNameMethod(binderClass), transactionCode)
|
|
||||||
+ ", callCount=" + callCount
|
+ ", callCount=" + callCount
|
||||||
|
+ ", incrementalCallCount=" + incrementalCallCount
|
||||||
+ ", recordedCallCount=" + recordedCallCount
|
+ ", recordedCallCount=" + recordedCallCount
|
||||||
+ ", cpuTimeMicros=" + cpuTimeMicros
|
+ ", cpuTimeMicros=" + cpuTimeMicros
|
||||||
+ ", latencyMicros=" + latencyMicros
|
+ ", latencyMicros=" + latencyMicros
|
||||||
@@ -772,13 +752,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
|
|||||||
// Approximate total CPU usage can be computed by
|
// Approximate total CPU usage can be computed by
|
||||||
// cpuTimeMicros * callCount / recordedCallCount
|
// cpuTimeMicros * callCount / recordedCallCount
|
||||||
public long cpuTimeMicros;
|
public long cpuTimeMicros;
|
||||||
|
// Call count that gets reset after delivery to BatteryStats
|
||||||
|
public long incrementalCallCount;
|
||||||
|
|
||||||
UidEntry(int uid) {
|
UidEntry(int uid) {
|
||||||
this.workSourceUid = uid;
|
this.workSourceUid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggregate time spent per each call name: call_desc -> cpu_time_micros
|
// Aggregate time spent per each call name: call_desc -> cpu_time_micros
|
||||||
private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
|
private ArrayMap<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
|
||||||
private CallStatKey mTempKey = new CallStatKey();
|
private CallStatKey mTempKey = new CallStatKey();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -141,7 +141,8 @@ public class BinderInternal {
|
|||||||
/**
|
/**
|
||||||
* Notes incoming binder call stats associated with this work source UID.
|
* Notes incoming binder call stats associated with this work source UID.
|
||||||
*/
|
*/
|
||||||
void noteCallStats(int workSourceUid, Collection<BinderCallsStats.CallStat> callStats);
|
void noteCallStats(int workSourceUid, long incrementalCallCount,
|
||||||
|
Collection<BinderCallsStats.CallStat> callStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.os;
|
||||||
|
|
||||||
|
import android.os.Binder;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a binder class and transaction code to the default transaction name. Since this
|
||||||
|
* resolution is class-based as opposed to instance-based, any custom implementation of
|
||||||
|
* {@link Binder#getTransactionName} will be ignored.
|
||||||
|
*
|
||||||
|
* The class is NOT thread safe
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
|
public class BinderTransactionNameResolver {
|
||||||
|
private static final Method NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the default transaction method name, which is just the transaction code.
|
||||||
|
* Used when the binder does not define a static "getDefaultTransactionName" method.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static String noDefaultTransactionName(int transactionCode) {
|
||||||
|
return String.valueOf(transactionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
NO_GET_DEFAULT_TRANSACTION_NAME_METHOD = BinderTransactionNameResolver.class.getMethod(
|
||||||
|
"noDefaultTransactionName", int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final HashMap<Class<? extends Binder>, Method>
|
||||||
|
mGetDefaultTransactionNameMethods = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a binder class name and transaction code, returns the corresponding method name.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public String getMethodName(Class<? extends Binder> binderClass, int transactionCode) {
|
||||||
|
Method method = mGetDefaultTransactionNameMethods.get(binderClass);
|
||||||
|
if (method == null) {
|
||||||
|
try {
|
||||||
|
method = binderClass.getMethod("getDefaultTransactionName", int.class);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||||
|
}
|
||||||
|
if (method.getReturnType() != String.class
|
||||||
|
|| !Modifier.isStatic(method.getModifiers())) {
|
||||||
|
method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
|
||||||
|
}
|
||||||
|
mGetDefaultTransactionNameMethods.put(binderClass, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (String) method.invoke(null, transactionCode);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.internal.os;
|
||||||
|
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test cases for android.os.BatteryStats, system server Binder call stats.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class BatteryStatsBinderCallStatsTest extends TestCase {
|
||||||
|
|
||||||
|
private static final int TRANSACTION_CODE = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test BatteryStatsImpl.Uid.noteBinderCallStats.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNoteBinderCallStats() throws Exception {
|
||||||
|
final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
|
||||||
|
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
|
||||||
|
|
||||||
|
int callingUid = Process.FIRST_APPLICATION_UID + 1;
|
||||||
|
int workSourceUid = Process.FIRST_APPLICATION_UID + 1;
|
||||||
|
|
||||||
|
Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
|
||||||
|
BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
|
||||||
|
MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
|
||||||
|
stat1.incrementalCallCount = 21;
|
||||||
|
stat1.recordedCallCount = 5;
|
||||||
|
stat1.cpuTimeMicros = 1000;
|
||||||
|
callStats.add(stat1);
|
||||||
|
|
||||||
|
bi.noteBinderCallStats(workSourceUid, 42, callStats);
|
||||||
|
|
||||||
|
callStats.clear();
|
||||||
|
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
|
||||||
|
MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
|
||||||
|
stat2.incrementalCallCount = 9;
|
||||||
|
stat2.recordedCallCount = 8;
|
||||||
|
stat2.cpuTimeMicros = 500;
|
||||||
|
callStats.add(stat2);
|
||||||
|
|
||||||
|
bi.noteBinderCallStats(workSourceUid, 8, callStats);
|
||||||
|
|
||||||
|
BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
|
||||||
|
assertEquals(42 + 8, uid.getBinderCallCount());
|
||||||
|
|
||||||
|
BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
|
||||||
|
ArraySet<BatteryStatsImpl.BinderCallStats> stats = uid.getBinderCallStats();
|
||||||
|
assertEquals(1, stats.size());
|
||||||
|
BatteryStatsImpl.BinderCallStats value = stats.valueAt(0);
|
||||||
|
value.ensureMethodName(resolver);
|
||||||
|
assertEquals("testMethod", value.getMethodName());
|
||||||
|
assertEquals(21 + 9, value.callCount);
|
||||||
|
assertEquals(8, value.recordedCallCount);
|
||||||
|
assertEquals(500, value.recordedCpuTimeMicros);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MockBinder extends Binder {
|
||||||
|
public static String getDefaultTransactionName(int txCode) {
|
||||||
|
return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import org.junit.runners.Suite;
|
|||||||
@Suite.SuiteClasses({
|
@Suite.SuiteClasses({
|
||||||
BatteryStatsCpuTimesTest.class,
|
BatteryStatsCpuTimesTest.class,
|
||||||
BatteryStatsBackgroundStatsTest.class,
|
BatteryStatsBackgroundStatsTest.class,
|
||||||
|
BatteryStatsBinderCallStatsTest.class,
|
||||||
BatteryStatsCounterTest.class,
|
BatteryStatsCounterTest.class,
|
||||||
BatteryStatsDualTimerTest.class,
|
BatteryStatsDualTimerTest.class,
|
||||||
BatteryStatsDurationTimerTest.class,
|
BatteryStatsDurationTimerTest.class,
|
||||||
|
|||||||
@@ -767,7 +767,9 @@ public class BinderCallsStatsTest {
|
|||||||
bcs.setTrackScreenInteractive(false);
|
bcs.setTrackScreenInteractive(false);
|
||||||
|
|
||||||
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
|
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
|
||||||
bcs.setCallStatsObserver((workSourceUid, callStats) -> callStatsList.addAll(callStats));
|
bcs.setCallStatsObserver(
|
||||||
|
(workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
|
||||||
|
callStats));
|
||||||
|
|
||||||
Binder binder = new Binder();
|
Binder binder = new Binder();
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,6 @@ public abstract class BatteryStatsInternal {
|
|||||||
/**
|
/**
|
||||||
* Informs battery stats of binder stats for the given work source UID.
|
* Informs battery stats of binder stats for the given work source UID.
|
||||||
*/
|
*/
|
||||||
public abstract void noteBinderCallStats(int workSourceUid,
|
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
|
||||||
Collection<BinderCallsStats.CallStat> callStats);
|
Collection<BinderCallsStats.CallStat> callStats);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,9 +308,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noteBinderCallStats(int workSourceUid,
|
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
|
||||||
Collection<BinderCallsStats.CallStat> callStats) {
|
Collection<BinderCallsStats.CallStat> callStats) {
|
||||||
mStats.noteBinderCallStats(workSourceUid, callStats);
|
mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user