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

This commit is contained in:
Siim Sammul
2022-04-25 19:42:18 +00:00
committed by Android (Google) Code Review
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;
// 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);
appendDropBoxProcessHeaders(process, processName, sb);
@@ -8762,6 +8764,8 @@ public class ActivityManagerService extends IActivityManager.Stub
millisSinceOldestPendingRead).append("\n");
}
}
sb.append("Dropped-Count: ").append(
rateLimitResult.droppedCountSinceRateLimitActivated()).append("\n");
sb.append("\n");
// 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. */
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.
final long now = mClock.uptimeMillis();
synchronized (mErrorClusterRecords) {
@@ -60,17 +60,33 @@ public class DropboxRateLimiter {
if (errRecord == null) {
errRecord = new ErrorRecord(now, 1);
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.setCount(1);
} else {
errRecord.incrementCount();
if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) return true;
return new RateLimitResult(false, recentlyDroppedCount(errRecord));
}
errRecord.incrementCount();
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) {
if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
@@ -87,6 +103,27 @@ public class DropboxRateLimiter {
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 {
long mStartTime;
int mCount;

View File

@@ -16,6 +16,7 @@
package com.android.server.am;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -43,34 +44,52 @@ public class DropboxRateLimiterTest {
@Test
public void testMultipleProcesses() {
// The first 5 entries should not be rate limited.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// Different processes and tags should not get rate limited either.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process2"));
assertFalse(mRateLimiter.shouldRateLimit("tag2", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process2").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag2", "process").shouldRateLimit());
// The 6th entry of the same process should be rate limited.
assertTrue(mRateLimiter.shouldRateLimit("tag", "process"));
assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
}
@Test
public void testBufferClearing() throws Exception {
// The first 5 entries should not be rate limited.
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process"));
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
// 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
// should not get rate limited anymore.
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 {