Merge "Count the number of dropped events due to rate limiting and attach that number to the header of the dropbox entry." into tm-dev am: 48ecaf9d88

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17619726

Change-Id: I7f717fa0bfdc6f18cf0b624c2ccc9d1b5ecdf5ef
Ignore-AOSP-First: this is an automerge
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Siim Sammul
2022-04-25 19:55:20 +00:00
committed by Automerger Merge Worker
3 changed files with 82 additions and 22 deletions

View File

@@ -8713,7 +8713,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
// Check if we should rate limit and abort early if needed. // Check if we should rate limit and abort early if needed.
if (mDropboxRateLimiter.shouldRateLimit(eventType, processName)) return; final DropboxRateLimiter.RateLimitResult rateLimitResult =
mDropboxRateLimiter.shouldRateLimit(eventType, processName);
if (rateLimitResult.shouldRateLimit()) return;
final StringBuilder sb = new StringBuilder(1024); final StringBuilder sb = new StringBuilder(1024);
appendDropBoxProcessHeaders(process, processName, sb); appendDropBoxProcessHeaders(process, processName, sb);
@@ -8762,6 +8764,8 @@ public class ActivityManagerService extends IActivityManager.Stub
millisSinceOldestPendingRead).append("\n"); millisSinceOldestPendingRead).append("\n");
} }
} }
sb.append("Dropped-Count: ").append(
rateLimitResult.droppedCountSinceRateLimitActivated()).append("\n");
sb.append("\n"); sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O // Do the rest in a worker thread to avoid blocking the caller on I/O

View File

@@ -49,7 +49,7 @@ public class DropboxRateLimiter {
} }
/** Determines whether dropbox entries of a specific tag and process should be rate limited. */ /** Determines whether dropbox entries of a specific tag and process should be rate limited. */
public boolean shouldRateLimit(String eventType, String processName) { public RateLimitResult shouldRateLimit(String eventType, String processName) {
// Rate-limit how often we're willing to do the heavy lifting to collect and record logs. // Rate-limit how often we're willing to do the heavy lifting to collect and record logs.
final long now = mClock.uptimeMillis(); final long now = mClock.uptimeMillis();
synchronized (mErrorClusterRecords) { synchronized (mErrorClusterRecords) {
@@ -60,17 +60,33 @@ public class DropboxRateLimiter {
if (errRecord == null) { if (errRecord == null) {
errRecord = new ErrorRecord(now, 1); errRecord = new ErrorRecord(now, 1);
mErrorClusterRecords.put(errorKey(eventType, processName), errRecord); mErrorClusterRecords.put(errorKey(eventType, processName), errRecord);
} else if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) { return new RateLimitResult(false, 0);
}
if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
errRecord.setStartTime(now); errRecord.setStartTime(now);
errRecord.setCount(1); errRecord.setCount(1);
} else { return new RateLimitResult(false, recentlyDroppedCount(errRecord));
}
errRecord.incrementCount(); errRecord.incrementCount();
if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) return true; if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) {
return new RateLimitResult(true, recentlyDroppedCount(errRecord));
} }
} }
return false; return new RateLimitResult(false, 0);
} }
/**
* Returns the number of entries of a certain type and process that have recenlty been
* dropped. Resets every RATE_LIMIT_BUFFER_DURATION if events are still actively created or
* RATE_LIMIT_BUFFER_EXPIRY if not. */
private int recentlyDroppedCount(ErrorRecord errRecord) {
if (errRecord == null || errRecord.getCount() < RATE_LIMIT_ALLOWED_ENTRIES) return 0;
return errRecord.getCount() - RATE_LIMIT_ALLOWED_ENTRIES;
}
private void maybeRemoveExpiredRecords(long now) { private void maybeRemoveExpiredRecords(long now) {
if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return; if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
@@ -87,6 +103,27 @@ public class DropboxRateLimiter {
return eventType + processName; return eventType + processName;
} }
/** Holds information on whether we should rate limit and how many events have been dropped. */
public class RateLimitResult {
boolean mShouldRateLimit;
int mDroppedCountSinceRateLimitActivated;
public RateLimitResult(boolean shouldRateLimit, int droppedCountSinceRateLimitActivated) {
mShouldRateLimit = shouldRateLimit;
mDroppedCountSinceRateLimitActivated = droppedCountSinceRateLimitActivated;
}
/** Whether to rate limit. */
public boolean shouldRateLimit() {
return mShouldRateLimit;
}
/** The number of dropped events since rate limit was activated. */
public int droppedCountSinceRateLimitActivated() {
return mDroppedCountSinceRateLimitActivated;
}
}
private class ErrorRecord { private class ErrorRecord {
long mStartTime; long mStartTime;
int mCount; int mCount;

View File

@@ -16,6 +16,7 @@
package com.android.server.am; package com.android.server.am;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -43,34 +44,52 @@ public class DropboxRateLimiterTest {
@Test @Test
public void testMultipleProcesses() { public void testMultipleProcesses() {
// The first 5 entries should not be rate limited. // The first 5 entries should not be rate limited.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// Different processes and tags should not get rate limited either. // Different processes and tags should not get rate limited either.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process2")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process2").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag2", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag2", "process").shouldRateLimit());
// The 6th entry of the same process should be rate limited. // The 6th entry of the same process should be rate limited.
assertTrue(mRateLimiter.shouldRateLimit("tag", "process")); assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
} }
@Test @Test
public void testBufferClearing() throws Exception { public void testBufferClearing() throws Exception {
// The first 5 entries should not be rate limited. // The first 5 entries should not be rate limited.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// The 6th entry of the same process should be rate limited. // The 6th entry of the same process should be rate limited.
assertTrue(mRateLimiter.shouldRateLimit("tag", "process")); assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// After 11 seconds there should be nothing left in the buffer and the same type of entry // After 11 seconds there should be nothing left in the buffer and the same type of entry
// should not get rate limited anymore. // should not get rate limited anymore.
mClock.setOffsetMillis(11000); mClock.setOffsetMillis(11000);
assertFalse(mRateLimiter.shouldRateLimit("tag", "process")); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
}
@Test
public void testRecentlyDroppedCount() throws Exception {
assertEquals(0,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(0,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(0,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(0,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(0,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(1,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
assertEquals(2,
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
} }
private static class TestClock implements DropboxRateLimiter.Clock { private static class TestClock implements DropboxRateLimiter.Clock {