diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt index 25effec42f1ac..db446c39767a2 100644 --- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt @@ -19,13 +19,14 @@ package com.android.systemui.log import android.os.Trace import android.util.Log import com.android.systemui.log.dagger.LogModule +import com.android.systemui.util.collection.RingBuffer import java.io.PrintWriter import java.text.SimpleDateFormat -import java.util.ArrayDeque import java.util.Locale import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.BlockingQueue import kotlin.concurrent.thread +import kotlin.math.max /** * 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]. * - * @param name The name of this buffer - * @param maxLogs The maximum number of messages to keep in memory at any one time, including the - * unused pool. Must be >= [poolSize]. - * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to - * sequential calls to [document] that aren't immediately followed by a matching call to [push]. + * @param name The name of this buffer, printed when the buffer is dumped and in some other + * situations. + * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start + * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches + * the maximum, it behaves like a ring buffer. */ class LogBuffer @JvmOverloads constructor( private val name: String, - private val maxLogs: Int, - private val poolSize: Int, + private val maxSize: Int, private val logcatEchoTracker: LogcatEchoTracker, private val systrace: Boolean = true ) { - init { - if (maxLogs < poolSize) { - throw IllegalArgumentException("maxLogs must be greater than or equal to poolSize, " + - "but maxLogs=$maxLogs < $poolSize=poolSize") - } - } + private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() } - private val buffer: ArrayDeque = ArrayDeque() - private val echoMessageQueue: BlockingQueue? = - if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(poolSize) else null + private val echoMessageQueue: BlockingQueue? = + if (logcatEchoTracker.logInBackgroundThread) ArrayBlockingQueue(10) else null init { if (logcatEchoTracker.logInBackgroundThread && echoMessageQueue != null) { @@ -104,6 +98,9 @@ class LogBuffer @JvmOverloads constructor( var frozen = false private set + private val mutable + get() = !frozen && maxSize > 0 + /** * Logs a message to the log buffer * @@ -138,34 +135,19 @@ class LogBuffer @JvmOverloads constructor( initializer: LogMessage.() -> Unit, 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) initializer(message) - return message + commit(message) } /** - * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been - * exhausted, creates a new instance. + * You should call [log] instead of this method. * - * 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 fun obtain( @@ -173,28 +155,26 @@ class LogBuffer @JvmOverloads constructor( level: LogLevel, printer: (LogMessage) -> String ): LogMessageImpl { - val message = when { - frozen -> LogMessageImpl.create() - buffer.size > maxLogs - poolSize -> buffer.removeFirst() - else -> LogMessageImpl.create() + if (!mutable) { + return FROZEN_MESSAGE } + val message = buffer.advance() message.reset(tag, level, System.currentTimeMillis(), printer) 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 - fun push(message: LogMessage) { - if (frozen) { + fun commit(message: LogMessage) { + if (!mutable) { 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 // capacity avoids the possibility of blocking this thread) 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. */ - private fun echoToDesiredEndpoints(message: LogMessageImpl) { + private fun echoToDesiredEndpoints(message: LogMessage) { val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) || logcatEchoTracker.isTagLoggable(message.tag, message.level) echo(message, toLogcat = includeInLogcat, toSystrace = systrace) @@ -219,19 +199,17 @@ class LogBuffer @JvmOverloads constructor( /** Converts the entire buffer to a newline-delimited string */ @Synchronized 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()) { - if (i >= start) { - dumpMessage(message, pw) - } + for (i in iterationStart until buffer.size) { + dumpMessage(buffer[i], pw) } } /** - * "Freezes" the contents of the buffer, making them immutable until [unfreeze] is called. - * Calls to [log], [document], [obtain], and [push] will not affect the buffer and will return - * dummy values if necessary. + * "Freezes" the contents of the buffer, making it immutable until [unfreeze] is called. + * Calls to [log], [obtain], and [commit] will not affect the buffer and will return dummy + * values if necessary. */ @Synchronized fun freeze() { @@ -293,3 +271,4 @@ class LogBuffer @JvmOverloads constructor( private const val TAG = "LogBuffer" private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) +private val FROZEN_MESSAGE = LogMessageImpl.create() \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt index cbfca25e0c603..5651399cb891e 100644 --- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt @@ -27,22 +27,21 @@ class LogBufferFactory @Inject constructor( private val logcatEchoTracker: LogcatEchoTracker ) { /* limit the size of maxPoolSize for low ram (Go) devices */ - private fun poolLimit(maxPoolSize_requested: Int): Int { - if (ActivityManager.isLowRamDeviceStatic()) { - return minOf(maxPoolSize_requested, 20) /* low ram max log size*/ + private fun adjustMaxSize(requestedMaxSize: Int): Int { + return if (ActivityManager.isLowRamDeviceStatic()) { + minOf(requestedMaxSize, 20) /* low ram max log size*/ } else { - return maxPoolSize_requested + requestedMaxSize } } @JvmOverloads fun create( name: String, - maxPoolSize: Int, - flexSize: Int = 10, + maxSize: Int, systrace: Boolean = true ): LogBuffer { - val buffer = LogBuffer(name, poolLimit(maxPoolSize), flexSize, logcatEchoTracker, systrace) + val buffer = LogBuffer(name, adjustMaxSize(maxSize), logcatEchoTracker, systrace) dumpManager.registerBuffer(name, buffer) return buffer } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index f72f1bb474681..19f2faf48e1b2 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -49,8 +49,7 @@ public class LogModule { @SysUISingleton @NotificationLog public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) { - return factory.create("NotifLog", 1000 /* maxPoolSize */, - 10 /* flexSize */, false /* systrace */); + return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for all logs related to the data layer of notifications. */ @@ -74,8 +73,7 @@ public class LogModule { @SysUISingleton @NotificationSectionLog public static LogBuffer provideNotificationSectionLogBuffer(LogBufferFactory factory) { - return factory.create("NotifSectionLog", 1000 /* maxPoolSize */, - 10 /* flexSize */, false /* systrace */); + return factory.create("NotifSectionLog", 1000 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for all logs related to the data layer of notifications. */ @@ -91,8 +89,7 @@ public class LogModule { @SysUISingleton @QSLog public static LogBuffer provideQuickSettingsLogBuffer(LogBufferFactory factory) { - return factory.create("QSLog", 500 /* maxPoolSize */, - 10 /* flexSize */, false /* systrace */); + return factory.create("QSLog", 500 /* maxSize */, false /* systrace */); } /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */ @@ -100,8 +97,8 @@ public class LogModule { @SysUISingleton @BroadcastDispatcherLog public static LogBuffer provideBroadcastDispatcherLogBuffer(LogBufferFactory factory) { - return factory.create("BroadcastDispatcherLog", 500 /* maxPoolSize */, - 10 /* flexSize */, false /* systrace */); + return factory.create("BroadcastDispatcherLog", 500 /* maxSize */, + false /* systrace */); } /** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */ @@ -139,8 +136,8 @@ public class LogModule { @SysUISingleton @QSFragmentDisableLog public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) { - return factory.create("QSFragmentDisableFlagsLog", 10 /* maxPoolSize */, - 10 /* flexSize */, false /* systrace */); + return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */, + false /* systrace */); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt index 0720bdb065567..bd029a727ee31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt @@ -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. */ fun logcatLogBuffer(name: String = "EchoToLogcatLogBuffer") = - LogBuffer(name, 50, 50, LogcatEchoTrackerAlways()) + LogBuffer(name, 50, LogcatEchoTrackerAlways()) /** * A [LogcatEchoTracker] that always allows echoing to the logcat. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt index 6971c63ed6d49..8cb530c355bdc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt @@ -30,7 +30,7 @@ class LSShadeTransitionLoggerTest : SysuiTestCase() { @Before fun setup() { logger = LSShadeTransitionLogger( - LogBuffer("Test", 10, 10, mock()), + LogBuffer("Test", 10, mock()), gestureLogger, displayMetrics) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 98397fb01b6ba..6abc687f0ebbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -379,7 +379,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mCommandQueue, mCarrierConfigTracker, new CollapsedStatusBarFragmentLogger( - new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)), + new LogBuffer("TEST", 1, mock(LogcatEchoTracker.class)), new DisableFlagsLogger() ), mOperatorNameViewControllerFactory,