Store the collected binder latency samples in buckets to form a
histogram. Add the ability to generate histograms with varying bucket
counts and sizes.
Bug: 179999191
Test: unit tests
Change-Id: I58d3cbdd9ae43c8da121ec41d994b077e29bb371
Merged-In: I58d3cbdd9ae43c8da121ec41d994b077e29bb371
(cherry picked from commit dcd3dc69a1)
This commit is contained in:
91
core/java/com/android/internal/os/BinderLatencyBuckets.java
Normal file
91
core/java/com/android/internal/os/BinderLatencyBuckets.java
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the bucket thresholds (with a custom logarithmic scale) for a histogram to store
|
||||||
|
* latency samples in.
|
||||||
|
*/
|
||||||
|
public class BinderLatencyBuckets {
|
||||||
|
private static final String TAG = "BinderLatencyBuckets";
|
||||||
|
private ArrayList<Integer> mBuckets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bucketCount the number of buckets the histogram should have
|
||||||
|
* @param firstBucketSize the size of the first bucket (used to avoid excessive small buckets)
|
||||||
|
* @param scaleFactor the rate in which each consecutive bucket increases (before rounding)
|
||||||
|
*/
|
||||||
|
public BinderLatencyBuckets(int bucketCount, int firstBucketSize, float scaleFactor) {
|
||||||
|
mBuckets = new ArrayList<>(bucketCount - 1);
|
||||||
|
mBuckets.add(firstBucketSize);
|
||||||
|
|
||||||
|
// Last value and the target are disjoint as we never want to create buckets smaller than 1.
|
||||||
|
double lastTarget = firstBucketSize;
|
||||||
|
int lastValue = firstBucketSize;
|
||||||
|
|
||||||
|
// First bucket is already created and the last bucket is anything greater than the final
|
||||||
|
// bucket in the list, so create 'bucketCount' - 2 buckets.
|
||||||
|
for (int i = 1; i < bucketCount - 1; i++) {
|
||||||
|
// Increase the target bucket limit value by the scale factor.
|
||||||
|
double nextTarget = lastTarget * scaleFactor;
|
||||||
|
|
||||||
|
if (nextTarget > Integer.MAX_VALUE || lastValue == Integer.MAX_VALUE) {
|
||||||
|
// Do not throw an exception here as this should not affect binder calls.
|
||||||
|
Slog.w(TAG, "Attempted to create a bucket larger than maxint");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int) nextTarget > lastValue) {
|
||||||
|
// Convert the target bucket limit value to an integer.
|
||||||
|
mBuckets.add((int) nextTarget);
|
||||||
|
lastValue = (int) nextTarget;
|
||||||
|
} else {
|
||||||
|
// Avoid creating redundant buckets, so bucket size should be 1 at a minimum.
|
||||||
|
mBuckets.add(lastValue + 1);
|
||||||
|
lastValue = lastValue + 1;
|
||||||
|
}
|
||||||
|
lastTarget = nextTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the bucket index to insert the provided sample in. */
|
||||||
|
public int sampleToBucket(int sample) {
|
||||||
|
if (sample > mBuckets.get(mBuckets.size() - 1)) {
|
||||||
|
return mBuckets.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search returns the element index if it is contained in the list - in this case the
|
||||||
|
// correct bucket is the index after as we use [minValue, maxValue) for bucket boundaries.
|
||||||
|
// Otherwise, it returns (-(insertion point) - 1), where insertion point is the point where
|
||||||
|
// to insert the element so that the array remains sorted - in this case the bucket index
|
||||||
|
// is the insertion point.
|
||||||
|
int searchResult = Collections.binarySearch(mBuckets, sample);
|
||||||
|
return searchResult < 0 ? -(1 + searchResult) : searchResult + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public ArrayList<Integer> getBuckets() {
|
||||||
|
return mBuckets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.os.BinderInternal.CallSession;
|
import com.android.internal.os.BinderInternal.CallSession;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/** Collects statistics about Binder call latency per calling API and method. */
|
/** Collects statistics about Binder call latency per calling API and method. */
|
||||||
@@ -34,18 +33,25 @@ public class BinderLatencyObserver {
|
|||||||
private static final String TAG = "BinderLatencyObserver";
|
private static final String TAG = "BinderLatencyObserver";
|
||||||
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;
|
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;
|
||||||
|
|
||||||
// This is not the final data structure - we will eventually store latency histograms instead of
|
// Histogram buckets parameters.
|
||||||
// raw samples as it is much more memory / disk space efficient.
|
public static final int BUCKET_COUNT_DEFAULT = 100;
|
||||||
// TODO(b/179999191): change this to store the histogram.
|
public static final int FIRST_BUCKET_SIZE_DEFAULT = 5;
|
||||||
// TODO(b/179999191): pre allocate the array size so we would not have to resize this.
|
public static final float BUCKET_SCALE_FACTOR_DEFAULT = 1.125f;
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final ArrayMap<LatencyDims, ArrayList<Long>> mLatencySamples = new ArrayMap<>();
|
private final ArrayMap<LatencyDims, int[]> mLatencyHistograms = new ArrayMap<>();
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
// Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
|
// Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
|
||||||
// of 100 requests.
|
// of 100 requests.
|
||||||
private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
|
private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
|
||||||
|
|
||||||
|
private int mBucketCount = BUCKET_COUNT_DEFAULT;
|
||||||
|
private int mFirstBucketSize = FIRST_BUCKET_SIZE_DEFAULT;
|
||||||
|
private float mBucketScaleFactor = BUCKET_SCALE_FACTOR_DEFAULT;
|
||||||
|
|
||||||
private final Random mRandom;
|
private final Random mRandom;
|
||||||
|
private BinderLatencyBuckets mLatencyBuckets;
|
||||||
|
|
||||||
/** Injector for {@link BinderLatencyObserver}. */
|
/** Injector for {@link BinderLatencyObserver}. */
|
||||||
public static class Injector {
|
public static class Injector {
|
||||||
@@ -56,6 +62,8 @@ public class BinderLatencyObserver {
|
|||||||
|
|
||||||
public BinderLatencyObserver(Injector injector) {
|
public BinderLatencyObserver(Injector injector) {
|
||||||
mRandom = injector.getRandomGenerator();
|
mRandom = injector.getRandomGenerator();
|
||||||
|
mLatencyBuckets = new BinderLatencyBuckets(
|
||||||
|
mBucketCount, mFirstBucketSize, mBucketScaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Should be called when a Binder call completes, will store latency data. */
|
/** Should be called when a Binder call completes, will store latency data. */
|
||||||
@@ -67,12 +75,21 @@ public class BinderLatencyObserver {
|
|||||||
LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode);
|
LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode);
|
||||||
long callDuration = getElapsedRealtimeMicro() - s.timeStarted;
|
long callDuration = getElapsedRealtimeMicro() - s.timeStarted;
|
||||||
|
|
||||||
|
// Find the bucket this sample should go to.
|
||||||
|
int bucketIdx = mLatencyBuckets.sampleToBucket(
|
||||||
|
callDuration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) callDuration);
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (!mLatencySamples.containsKey(dims)) {
|
int[] buckets = mLatencyHistograms.get(dims);
|
||||||
mLatencySamples.put(dims, new ArrayList<Long>());
|
if (buckets == null) {
|
||||||
|
buckets = new int[mBucketCount];
|
||||||
|
mLatencyHistograms.put(dims, buckets);
|
||||||
}
|
}
|
||||||
|
|
||||||
mLatencySamples.get(dims).add(callDuration);
|
// Increment the correct bucket.
|
||||||
|
if (buckets[bucketIdx] < Integer.MAX_VALUE) {
|
||||||
|
buckets[bucketIdx] += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +117,26 @@ public class BinderLatencyObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates the histogram buckets parameters. */
|
||||||
|
public void setHistogramBucketsParams(
|
||||||
|
int bucketCount, int firstBucketSize, float bucketScaleFactor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (bucketCount != mBucketCount || firstBucketSize != mFirstBucketSize
|
||||||
|
|| bucketScaleFactor != mBucketScaleFactor) {
|
||||||
|
mBucketCount = bucketCount;
|
||||||
|
mFirstBucketSize = firstBucketSize;
|
||||||
|
mBucketScaleFactor = bucketScaleFactor;
|
||||||
|
mLatencyBuckets = new BinderLatencyBuckets(
|
||||||
|
mBucketCount, mFirstBucketSize, mBucketScaleFactor);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Resets the sample collection. */
|
/** Resets the sample collection. */
|
||||||
public void reset() {
|
public void reset() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mLatencySamples.clear();
|
mLatencyHistograms.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +184,7 @@ public class BinderLatencyObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ArrayMap<LatencyDims, ArrayList<Long>> getLatencySamples() {
|
public ArrayMap<LatencyDims, int[]> getLatencyHistograms() {
|
||||||
return mLatencySamples;
|
return mLatencyHistograms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -934,7 +934,7 @@ public class BinderCallsStatsTest {
|
|||||||
bcs.elapsedTime += 20;
|
bcs.elapsedTime += 20;
|
||||||
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
|
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
|
||||||
|
|
||||||
assertEquals(1, bcs.getLatencyObserver().getLatencySamples().size());
|
assertEquals(1, bcs.getLatencyObserver().getLatencyHistograms().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -948,7 +948,7 @@ public class BinderCallsStatsTest {
|
|||||||
bcs.elapsedTime += 20;
|
bcs.elapsedTime += 20;
|
||||||
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
|
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
|
||||||
|
|
||||||
assertEquals(0, bcs.getLatencyObserver().getLatencySamples().size());
|
assertEquals(0, bcs.getLatencyObserver().getLatencyHistograms().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestHandler extends Handler {
|
private static class TestHandler extends Handler {
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import android.platform.test.annotations.Presubmit;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@Presubmit
|
||||||
|
public class BinderLatencyBucketsTest {
|
||||||
|
@Test
|
||||||
|
public void testBucketThresholds() {
|
||||||
|
BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f);
|
||||||
|
assertThat(latencyBuckets.getBuckets())
|
||||||
|
.containsExactly(2, 3, 4, 6, 8, 12, 18, 26, 39)
|
||||||
|
.inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSampleAssignment() {
|
||||||
|
BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f);
|
||||||
|
assertEquals(0, latencyBuckets.sampleToBucket(0));
|
||||||
|
assertEquals(0, latencyBuckets.sampleToBucket(1));
|
||||||
|
assertEquals(1, latencyBuckets.sampleToBucket(2));
|
||||||
|
assertEquals(2, latencyBuckets.sampleToBucket(3));
|
||||||
|
assertEquals(3, latencyBuckets.sampleToBucket(4));
|
||||||
|
assertEquals(5, latencyBuckets.sampleToBucket(9));
|
||||||
|
assertEquals(6, latencyBuckets.sampleToBucket(13));
|
||||||
|
assertEquals(7, latencyBuckets.sampleToBucket(25));
|
||||||
|
assertEquals(9, latencyBuckets.sampleToBucket(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaxIntBuckets() {
|
||||||
|
BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(5, Integer.MAX_VALUE / 2, 2);
|
||||||
|
assertThat(latencyBuckets.getBuckets())
|
||||||
|
.containsExactly(Integer.MAX_VALUE / 2, Integer.MAX_VALUE - 1)
|
||||||
|
.inOrder();
|
||||||
|
|
||||||
|
assertEquals(0, latencyBuckets.sampleToBucket(0));
|
||||||
|
assertEquals(0, latencyBuckets.sampleToBucket(Integer.MAX_VALUE / 2 - 1));
|
||||||
|
assertEquals(1, latencyBuckets.sampleToBucket(Integer.MAX_VALUE - 2));
|
||||||
|
assertEquals(2, latencyBuckets.sampleToBucket(Integer.MAX_VALUE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,6 @@ import com.android.internal.os.BinderLatencyObserver.LatencyDims;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
@@ -44,6 +43,7 @@ public class BinderLatencyObserverTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testLatencyCollectionWithMultipleClasses() {
|
public void testLatencyCollectionWithMultipleClasses() {
|
||||||
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
||||||
|
blo.setHistogramBucketsParams(5, 5, 1.125f);
|
||||||
|
|
||||||
Binder binder = new Binder();
|
Binder binder = new Binder();
|
||||||
CallSession callSession = new CallSession();
|
CallSession callSession = new CallSession();
|
||||||
@@ -51,20 +51,24 @@ public class BinderLatencyObserverTest {
|
|||||||
callSession.transactionCode = 1;
|
callSession.transactionCode = 1;
|
||||||
blo.callEnded(callSession);
|
blo.callEnded(callSession);
|
||||||
blo.callEnded(callSession);
|
blo.callEnded(callSession);
|
||||||
|
blo.callEnded(callSession);
|
||||||
callSession.transactionCode = 2;
|
callSession.transactionCode = 2;
|
||||||
blo.callEnded(callSession);
|
blo.callEnded(callSession);
|
||||||
|
blo.callEnded(callSession);
|
||||||
|
|
||||||
ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples();
|
ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
|
||||||
assertEquals(2, latencySamples.keySet().size());
|
assertEquals(2, latencyHistograms.keySet().size());
|
||||||
assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 1)))
|
assertThat(latencyHistograms.get(new LatencyDims(binder.getClass(), 1)))
|
||||||
.containsExactlyElementsIn(Arrays.asList(1L, 2L));
|
.asList().containsExactly(2, 0, 1, 0, 0).inOrder();
|
||||||
assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 2))).containsExactly(3L);
|
assertThat(latencyHistograms.get(new LatencyDims(binder.getClass(), 2)))
|
||||||
|
.asList().containsExactly(0, 0, 0, 0, 2).inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSampling() {
|
public void testSampling() {
|
||||||
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
||||||
blo.setSamplingInterval(2);
|
blo.setSamplingInterval(2);
|
||||||
|
blo.setHistogramBucketsParams(5, 5, 1.125f);
|
||||||
|
|
||||||
Binder binder = new Binder();
|
Binder binder = new Binder();
|
||||||
CallSession callSession = new CallSession();
|
CallSession callSession = new CallSession();
|
||||||
@@ -74,17 +78,58 @@ public class BinderLatencyObserverTest {
|
|||||||
callSession.transactionCode = 2;
|
callSession.transactionCode = 2;
|
||||||
blo.callEnded(callSession);
|
blo.callEnded(callSession);
|
||||||
|
|
||||||
ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples();
|
ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
|
||||||
assertEquals(1, latencySamples.size());
|
assertEquals(1, latencyHistograms.size());
|
||||||
LatencyDims dims = latencySamples.keySet().iterator().next();
|
LatencyDims dims = latencyHistograms.keySet().iterator().next();
|
||||||
assertEquals(binder.getClass(), dims.getBinderClass());
|
assertEquals(binder.getClass(), dims.getBinderClass());
|
||||||
assertEquals(1, dims.getTransactionCode());
|
assertEquals(1, dims.getTransactionCode());
|
||||||
ArrayList<Long> values = latencySamples.get(dims);
|
assertThat(latencyHistograms.get(dims)).asList().containsExactly(1, 0, 0, 0, 0).inOrder();
|
||||||
assertThat(values).containsExactly(1L);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTooCallLengthOverflow() {
|
||||||
|
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
||||||
|
blo.setElapsedTime(2L + (long) Integer.MAX_VALUE);
|
||||||
|
blo.setHistogramBucketsParams(5, 5, 1.125f);
|
||||||
|
|
||||||
|
Binder binder = new Binder();
|
||||||
|
CallSession callSession = new CallSession();
|
||||||
|
callSession.binderClass = binder.getClass();
|
||||||
|
callSession.transactionCode = 1;
|
||||||
|
blo.callEnded(callSession);
|
||||||
|
|
||||||
|
// The long call should be capped to maxint (to not overflow) and placed in the last bucket.
|
||||||
|
assertThat(blo.getLatencyHistograms()
|
||||||
|
.get(new LatencyDims(binder.getClass(), 1)))
|
||||||
|
.asList().containsExactly(0, 0, 0, 0, 1)
|
||||||
|
.inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHistogramBucketOverflow() {
|
||||||
|
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
|
||||||
|
blo.setHistogramBucketsParams(3, 5, 1.125f);
|
||||||
|
|
||||||
|
Binder binder = new Binder();
|
||||||
|
CallSession callSession = new CallSession();
|
||||||
|
callSession.binderClass = binder.getClass();
|
||||||
|
callSession.transactionCode = 1;
|
||||||
|
blo.callEnded(callSession);
|
||||||
|
|
||||||
|
LatencyDims dims = new LatencyDims(binder.getClass(), 1);
|
||||||
|
// Fill the buckets with maxint.
|
||||||
|
Arrays.fill(blo.getLatencyHistograms().get(dims), Integer.MAX_VALUE);
|
||||||
|
assertThat(blo.getLatencyHistograms().get(dims))
|
||||||
|
.asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
|
// Try to add another sample.
|
||||||
|
blo.callEnded(callSession);
|
||||||
|
// Make sure the buckets don't overflow.
|
||||||
|
assertThat(blo.getLatencyHistograms().get(dims))
|
||||||
|
.asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestBinderLatencyObserver extends BinderLatencyObserver {
|
public static class TestBinderLatencyObserver extends BinderLatencyObserver {
|
||||||
private long mElapsedTimeCallCount = 0;
|
private long mElapsedTime = 0;
|
||||||
|
|
||||||
TestBinderLatencyObserver() {
|
TestBinderLatencyObserver() {
|
||||||
// Make random generator not random.
|
// Make random generator not random.
|
||||||
@@ -104,7 +149,12 @@ public class BinderLatencyObserverTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected long getElapsedRealtimeMicro() {
|
protected long getElapsedRealtimeMicro() {
|
||||||
return ++mElapsedTimeCallCount;
|
mElapsedTime += 2;
|
||||||
|
return mElapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElapsedTime(long time) {
|
||||||
|
mElapsedTime = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,6 +138,12 @@ public class BinderCallsStatsService extends Binder {
|
|||||||
private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
|
private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
|
||||||
private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
|
private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
|
||||||
"latency_observer_sampling_interval";
|
"latency_observer_sampling_interval";
|
||||||
|
private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
|
||||||
|
"latency_histogram_bucket_count";
|
||||||
|
private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
|
||||||
|
"latency_histogram_first_bucket_size";
|
||||||
|
private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
|
||||||
|
"latency_histogram_bucket_scale_factor";
|
||||||
|
|
||||||
private boolean mEnabled;
|
private boolean mEnabled;
|
||||||
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
|
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
|
||||||
@@ -198,9 +204,20 @@ public class BinderCallsStatsService extends Binder {
|
|||||||
mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
|
mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
|
||||||
BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
|
BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
|
||||||
// Binder latency observer settings.
|
// Binder latency observer settings.
|
||||||
mBinderCallsStats.getLatencyObserver().setSamplingInterval(mParser.getInt(
|
BinderLatencyObserver binderLatencyObserver = mBinderCallsStats.getLatencyObserver();
|
||||||
|
binderLatencyObserver.setSamplingInterval(mParser.getInt(
|
||||||
SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
|
SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
|
||||||
BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
|
BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
|
||||||
|
binderLatencyObserver.setHistogramBucketsParams(
|
||||||
|
mParser.getInt(
|
||||||
|
SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
|
||||||
|
BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
|
||||||
|
mParser.getInt(
|
||||||
|
SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
|
||||||
|
BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
|
||||||
|
mParser.getFloat(
|
||||||
|
SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
|
||||||
|
BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
|
||||||
|
|
||||||
|
|
||||||
final boolean enabled =
|
final boolean enabled =
|
||||||
|
|||||||
Reference in New Issue
Block a user