Convert LogBuffer to use RingBuffer am: e0b11afd70

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

Change-Id: Ia05d255399e9cf0c24d6e0327e2ad4a88b201ebb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ned Burns
2022-05-13 22:03:10 +00:00
committed by Automerger Merge Worker
6 changed files with 55 additions and 80 deletions

View File

@@ -19,13 +19,14 @@ package com.android.systemui.log
import android.os.Trace import android.os.Trace
import android.util.Log import android.util.Log
import com.android.systemui.log.dagger.LogModule import com.android.systemui.log.dagger.LogModule
import com.android.systemui.util.collection.RingBuffer
import java.io.PrintWriter import java.io.PrintWriter
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.ArrayDeque
import java.util.Locale import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.math.max
/** /**
* A simple ring buffer of recyclable log messages * A simple ring buffer of recyclable log messages
@@ -63,29 +64,22 @@ import kotlin.concurrent.thread
* *
* Buffers are provided by [LogModule]. Instances should be created using a [LogBufferFactory]. * Buffers are provided by [LogModule]. Instances should be created using a [LogBufferFactory].
* *
* @param name The name of this buffer * @param name The name of this buffer, printed when the buffer is dumped and in some other
* @param maxLogs The maximum number of messages to keep in memory at any one time, including the * situations.
* unused pool. Must be >= [poolSize]. * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
* @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
* sequential calls to [document] that aren't immediately followed by a matching call to [push]. * the maximum, it behaves like a ring buffer.
*/ */
class LogBuffer @JvmOverloads constructor( class LogBuffer @JvmOverloads constructor(
private val name: String, private val name: String,
private val maxLogs: Int, private val maxSize: Int,
private val poolSize: Int,
private val logcatEchoTracker: LogcatEchoTracker, private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true private val systrace: Boolean = true
) { ) {
init { private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
if (maxLogs < poolSize) {
throw IllegalArgumentException("maxLogs must be greater than or equal to poolSize, " +
"but maxLogs=$maxLogs < $poolSize=poolSize")
}
}
private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque() private val echoMessageQueue: BlockingQueue<LogMessage>? =
private val echoMessageQueue: BlockingQueue<LogMessageImpl>? = if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(10) else null
if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(poolSize) else null
init { init {
if (logcatEchoTracker.logInBackgroundThread && echoMessageQueue != null) { if (logcatEchoTracker.logInBackgroundThread && echoMessageQueue != null) {
@@ -104,6 +98,9 @@ class LogBuffer @JvmOverloads constructor(
var frozen = false var frozen = false
private set private set
private val mutable
get() = !frozen && maxSize > 0
/** /**
* Logs a message to the log buffer * Logs a message to the log buffer
* *
@@ -138,34 +135,19 @@ class LogBuffer @JvmOverloads constructor(
initializer: LogMessage.() -> Unit, initializer: LogMessage.() -> Unit,
noinline printer: LogMessage.() -> String noinline printer: LogMessage.() -> String
) { ) {
if (!frozen) {
val message = obtain(tag, level, printer)
initializer(message)
push(message)
}
}
/**
* Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a
* "reason" for doing something (the thing you supply the reason to will presumably call [push]
* on that message at some point).
*/
inline fun document(
tag: String,
level: LogLevel,
initializer: LogMessage.() -> Unit,
noinline printer: LogMessage.() -> String
): LogMessage {
val message = obtain(tag, level, printer) val message = obtain(tag, level, printer)
initializer(message) initializer(message)
return message commit(message)
} }
/** /**
* Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been * You should call [log] instead of this method.
* exhausted, creates a new instance.
* *
* In general, you should call [log] or [document] instead of this method. * Obtains the next [LogMessage] from the ring buffer. If the buffer is not yet at max size,
* grows the buffer by one.
*
* After calling [obtain], the message will now be at the end of the buffer. The caller must
* store any relevant data on the message and then call [commit].
*/ */
@Synchronized @Synchronized
fun obtain( fun obtain(
@@ -173,28 +155,26 @@ class LogBuffer @JvmOverloads constructor(
level: LogLevel, level: LogLevel,
printer: (LogMessage) -> String printer: (LogMessage) -> String
): LogMessageImpl { ): LogMessageImpl {
val message = when { if (!mutable) {
frozen -> LogMessageImpl.create() return FROZEN_MESSAGE
buffer.size > maxLogs - poolSize -> buffer.removeFirst()
else -> LogMessageImpl.create()
} }
val message = buffer.advance()
message.reset(tag, level, System.currentTimeMillis(), printer) message.reset(tag, level, System.currentTimeMillis(), printer)
return message return message
} }
/** /**
* Pushes a message into buffer, possibly evicting an older message if the buffer is full. * You should call [log] instead of this method.
*
* After acquiring a message via [obtain], call this method to signal to the buffer that you
* have finished filling in its data fields. The message will be echoed to logcat if
* necessary.
*/ */
@Synchronized @Synchronized
fun push(message: LogMessage) { fun commit(message: LogMessage) {
if (frozen) { if (!mutable) {
return return
} }
if (buffer.size == maxLogs) {
Log.e(TAG, "LogBuffer $name has exceeded its pool size")
buffer.removeFirst()
}
buffer.add(message as LogMessageImpl)
// Log in the background thread only if echoMessageQueue exists and has capacity (checking // Log in the background thread only if echoMessageQueue exists and has capacity (checking
// capacity avoids the possibility of blocking this thread) // capacity avoids the possibility of blocking this thread)
if (echoMessageQueue != null && echoMessageQueue.remainingCapacity() > 0) { if (echoMessageQueue != null && echoMessageQueue.remainingCapacity() > 0) {
@@ -210,7 +190,7 @@ class LogBuffer @JvmOverloads constructor(
} }
/** Sends message to echo after determining whether to use Logcat and/or systrace. */ /** Sends message to echo after determining whether to use Logcat and/or systrace. */
private fun echoToDesiredEndpoints(message: LogMessageImpl) { private fun echoToDesiredEndpoints(message: LogMessage) {
val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) || val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) ||
logcatEchoTracker.isTagLoggable(message.tag, message.level) logcatEchoTracker.isTagLoggable(message.tag, message.level)
echo(message, toLogcat = includeInLogcat, toSystrace = systrace) echo(message, toLogcat = includeInLogcat, toSystrace = systrace)
@@ -219,19 +199,17 @@ class LogBuffer @JvmOverloads constructor(
/** Converts the entire buffer to a newline-delimited string */ /** Converts the entire buffer to a newline-delimited string */
@Synchronized @Synchronized
fun dump(pw: PrintWriter, tailLength: Int) { fun dump(pw: PrintWriter, tailLength: Int) {
val start = if (tailLength <= 0) { 0 } else { buffer.size - tailLength } val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
for ((i, message) in buffer.withIndex()) { for (i in iterationStart until buffer.size) {
if (i >= start) { dumpMessage(buffer[i], pw)
dumpMessage(message, pw)
}
} }
} }
/** /**
* "Freezes" the contents of the buffer, making them immutable until [unfreeze] is called. * "Freezes" the contents of the buffer, making it immutable until [unfreeze] is called.
* Calls to [log], [document], [obtain], and [push] will not affect the buffer and will return * Calls to [log], [obtain], and [commit] will not affect the buffer and will return dummy
* dummy values if necessary. * values if necessary.
*/ */
@Synchronized @Synchronized
fun freeze() { fun freeze() {
@@ -293,3 +271,4 @@ class LogBuffer @JvmOverloads constructor(
private const val TAG = "LogBuffer" private const val TAG = "LogBuffer"
private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val FROZEN_MESSAGE = LogMessageImpl.create()

View File

@@ -27,22 +27,21 @@ class LogBufferFactory @Inject constructor(
private val logcatEchoTracker: LogcatEchoTracker private val logcatEchoTracker: LogcatEchoTracker
) { ) {
/* limit the size of maxPoolSize for low ram (Go) devices */ /* limit the size of maxPoolSize for low ram (Go) devices */
private fun poolLimit(maxPoolSize_requested: Int): Int { private fun adjustMaxSize(requestedMaxSize: Int): Int {
if (ActivityManager.isLowRamDeviceStatic()) { return if (ActivityManager.isLowRamDeviceStatic()) {
return minOf(maxPoolSize_requested, 20) /* low ram max log size*/ minOf(requestedMaxSize, 20) /* low ram max log size*/
} else { } else {
return maxPoolSize_requested requestedMaxSize
} }
} }
@JvmOverloads @JvmOverloads
fun create( fun create(
name: String, name: String,
maxPoolSize: Int, maxSize: Int,
flexSize: Int = 10,
systrace: Boolean = true systrace: Boolean = true
): LogBuffer { ): LogBuffer {
val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker, systrace) val buffer = LogBuffer(name, adjustMaxSize(maxSize), logcatEchoTracker, systrace)
dumpManager.registerBuffer(name, buffer) dumpManager.registerBuffer(name, buffer)
return buffer return buffer
} }

View File

@@ -49,8 +49,7 @@ public class LogModule {
@SysUISingleton @SysUISingleton
@NotificationLog @NotificationLog
public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) { public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
return factory.create("NotifLog", 1000 /* maxPoolSize */, return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */);
10 /* flexSize */, false /* systrace */);
} }
/** Provides a logging buffer for all logs related to the data layer of notifications. */ /** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -74,8 +73,7 @@ public class LogModule {
@SysUISingleton @SysUISingleton
@NotificationSectionLog @NotificationSectionLog
public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) { public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) {
return factory.create("NotifSectionLog", 1000 /* maxPoolSize */, return factory.create("NotifSectionLog", 1000 /* maxSize */, false /* systrace */);
10 /* flexSize */, false /* systrace */);
} }
/** Provides a logging buffer for all logs related to the data layer of notifications. */ /** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -91,8 +89,7 @@ public class LogModule {
@SysUISingleton @SysUISingleton
@QSLog @QSLog
public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) { public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) {
return factory.create("QSLog", 500 /* maxPoolSize */, return factory.create("QSLog", 500 /* maxSize */, false /* systrace */);
10 /* flexSize */, false /* systrace */);
} }
/** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */ /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
@@ -100,8 +97,8 @@ public class LogModule {
@SysUISingleton @SysUISingleton
@BroadcastDispatcherLog @BroadcastDispatcherLog
public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) { public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) {
return factory.create("BroadcastDispatcherLog", 500 /* maxPoolSize */, return factory.create("BroadcastDispatcherLog", 500 /* maxSize */,
10 /* flexSize */, false /* systrace */); false /* systrace */);
} }
/** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */ /** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
@@ -139,8 +136,8 @@ public class LogModule {
@SysUISingleton @SysUISingleton
@QSFragmentDisableLog @QSFragmentDisableLog
public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) { public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
return factory.create("QSFragmentDisableFlagsLog", 10 /* maxPoolSize */, return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */,
10 /* flexSize */, false /* systrace */); false /* systrace */);
} }
/** /**

View File

@@ -24,7 +24,7 @@ import com.android.systemui.log.LogcatEchoTracker
* Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests. * Creates a LogBuffer that will echo everything to logcat, which is useful for debugging tests.
*/ */
fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") = fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") =
LogBuffer(name, 50, 50, LogcatEchoTrackerAlways()) LogBuffer(name, 50, LogcatEchoTrackerAlways())
/** /**
* A [LogcatEchoTracker] that always allows echoing to the logcat. * A [LogcatEchoTracker] that always allows echoing to the logcat.

View File

@@ -30,7 +30,7 @@ class LSShadeTransitionLoggerTest : SysuiTestCase() {
@Before @Before
fun setup() { fun setup() {
logger = LSShadeTransitionLogger( logger = LSShadeTransitionLogger(
LogBuffer("Test", 10, 10, mock()), LogBuffer("Test", 10, mock()),
gestureLogger, gestureLogger,
displayMetrics) displayMetrics)
} }

View File

@@ -379,7 +379,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mCommandQueue, mCommandQueue,
mCarrierConfigTracker, mCarrierConfigTracker,
new CollapsedStatusBarFragmentLogger( new CollapsedStatusBarFragmentLogger(
new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)), new LogBuffer("TEST", 1, mock(LogcatEchoTracker.class)),
new DisableFlagsLogger() new DisableFlagsLogger()
), ),
mOperatorNameViewControllerFactory, mOperatorNameViewControllerFactory,