Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 893f2116 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Merge changes I48a0acc7,I57fbba0a into tm-qpr-dev am: 4a685521 am:...

Merge "Merge changes I48a0acc7,I57fbba0a into tm-qpr-dev am: 4a685521 am: d6cb1eaa" into udc-dev am: 2791471b

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



Change-Id: I2ba3958d4588ff818e8d850ce7d1063c7ea96203
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 2478f55f 2791471b
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -59,6 +59,17 @@ data class TableChange(
        int = value
        int = value
    }
    }


    /** Updates this to store the same value as [change]. */
    fun updateTo(change: TableChange) {
        reset(change.timestamp, change.columnPrefix, change.columnName)
        when (change.type) {
            DataType.STRING -> set(change.str)
            DataType.INT -> set(change.int)
            DataType.BOOLEAN -> set(change.bool)
            DataType.EMPTY -> {}
        }
    }

    /** Returns true if this object has a change. */
    /** Returns true if this object has a change. */
    fun hasData(): Boolean {
    fun hasData(): Boolean {
        return columnName.isNotBlank() && type != DataType.EMPTY
        return columnName.isNotBlank() && type != DataType.EMPTY
+36 −1
Original line number Original line Diff line number Diff line
@@ -16,13 +16,13 @@


package com.android.systemui.log.table
package com.android.systemui.log.table


import android.os.Trace
import com.android.systemui.Dumpable
import com.android.systemui.Dumpable
import com.android.systemui.plugins.util.RingBuffer
import com.android.systemui.plugins.util.RingBuffer
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Locale
import kotlinx.coroutines.flow.Flow


/**
/**
 * A logger that logs changes in table format.
 * A logger that logs changes in table format.
@@ -82,6 +82,19 @@ class TableLogBuffer(


    private val buffer = RingBuffer(maxSize) { TableChange() }
    private val buffer = RingBuffer(maxSize) { TableChange() }


    // Stores the most recently evicted value for each column name (indexed on column name).
    //
    // Why it's necessary: Because we use a RingBuffer of a fixed size, it's possible that a column
    // that's logged infrequently will eventually get pushed out by a different column that's
    // logged more frequently. Then, that infrequently-logged column isn't present in the RingBuffer
    // at all and we have no logs that the column ever existed. This is a problem because the
    // column's information is still relevant, valid, and may be critical to debugging issues.
    //
    // Fix: When a change is being evicted from the RingBuffer, we store it in this map (based on
    // its [TableChange.getName()]. This ensures that we always have at least one value for every
    // column ever logged. See b/272016422 for more details.
    private val lastEvictedValues = mutableMapOf<String, TableChange>()

    // A [TableRowLogger] object, re-used each time [logDiffs] is called.
    // A [TableRowLogger] object, re-used each time [logDiffs] is called.
    // (Re-used to avoid object allocation.)
    // (Re-used to avoid object allocation.)
    private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
    private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
@@ -138,18 +151,24 @@ class TableLogBuffer(
    // timestamps.)
    // timestamps.)


    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
        Trace.beginSection("TableLogBuffer#logChange(string)")
        val change = obtain(timestamp, prefix, columnName)
        val change = obtain(timestamp, prefix, columnName)
        change.set(value)
        change.set(value)
        Trace.endSection()
    }
    }


    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
        Trace.beginSection("TableLogBuffer#logChange(boolean)")
        val change = obtain(timestamp, prefix, columnName)
        val change = obtain(timestamp, prefix, columnName)
        change.set(value)
        change.set(value)
        Trace.endSection()
    }
    }


    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
        Trace.beginSection("TableLogBuffer#logChange(int)")
        val change = obtain(timestamp, prefix, columnName)
        val change = obtain(timestamp, prefix, columnName)
        change.set(value)
        change.set(value)
        Trace.endSection()
    }
    }


    // TODO(b/259454430): Add additional change types here.
    // TODO(b/259454430): Add additional change types here.
@@ -158,6 +177,9 @@ class TableLogBuffer(
    private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
    private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
        verifyValidName(prefix, columnName)
        verifyValidName(prefix, columnName)
        val tableChange = buffer.advance()
        val tableChange = buffer.advance()
        if (tableChange.hasData()) {
            saveEvictedValue(tableChange)
        }
        tableChange.reset(timestamp, prefix, columnName)
        tableChange.reset(timestamp, prefix, columnName)
        return tableChange
        return tableChange
    }
    }
@@ -173,10 +195,23 @@ class TableLogBuffer(
        }
        }
    }
    }


    private fun saveEvictedValue(change: TableChange) {
        Trace.beginSection("TableLogBuffer#saveEvictedValue")
        val name = change.getName()
        val previouslyEvicted =
            lastEvictedValues[name] ?: TableChange().also { lastEvictedValues[name] = it }
        // For recycling purposes, update the existing object in the map with the new information
        // instead of creating a new object.
        previouslyEvicted.updateTo(change)
        Trace.endSection()
    }

    @Synchronized
    @Synchronized
    override fun dump(pw: PrintWriter, args: Array<out String>) {
    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println(HEADER_PREFIX + name)
        pw.println(HEADER_PREFIX + name)
        pw.println("version $VERSION")
        pw.println("version $VERSION")

        lastEvictedValues.values.sortedBy { it.timestamp }.forEach { it.dump(pw) }
        for (i in 0 until buffer.size) {
        for (i in 0 until buffer.size) {
            buffer[i].dump(pw)
            buffer[i].dump(pw)
        }
        }
+88 −0
Original line number Original line Diff line number Diff line
@@ -121,4 +121,92 @@ class TableChangeTest : SysuiTestCase() {
        assertThat(underTest.getName()).doesNotContain("original")
        assertThat(underTest.getName()).doesNotContain("original")
        assertThat(underTest.getVal()).isEqualTo("8900")
        assertThat(underTest.getVal()).isEqualTo("8900")
    }
    }

    @Test
    fun updateTo_emptyToString_isString() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        new.set("newString")
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isTrue()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("newString")
    }

    @Test
    fun updateTo_intToEmpty_isEmpty() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
        underTest.set(42)

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isFalse()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("null")
    }

    @Test
    fun updateTo_stringToBool_isBool() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
        underTest.set("oldString")

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        new.set(true)
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isTrue()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("true")
    }

    @Test
    fun updateTo_intToString_isString() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
        underTest.set(43)

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        new.set("newString")
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isTrue()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("newString")
    }

    @Test
    fun updateTo_boolToInt_isInt() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
        underTest.set(false)

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        new.set(44)
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isTrue()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("44")
    }

    @Test
    fun updateTo_boolToNewBool_isNewBool() {
        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
        underTest.set(false)

        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
        new.set(true)
        underTest.updateTo(new)

        assertThat(underTest.hasData()).isTrue()
        assertThat(underTest.getName()).contains("newPrefix")
        assertThat(underTest.getName()).contains("newName")
        assertThat(underTest.getVal()).isEqualTo("true")
    }
}
}
+226 −1
Original line number Original line Diff line number Diff line
@@ -435,11 +435,236 @@ class TableLogBufferTest : SysuiTestCase() {


        assertThat(dumpedString).doesNotContain("testString[0]")
        assertThat(dumpedString).doesNotContain("testString[0]")
        assertThat(dumpedString).doesNotContain("testString[1]")
        assertThat(dumpedString).doesNotContain("testString[1]")
        assertThat(dumpedString).doesNotContain("testString[2]")
        // The buffer should contain [MAX_SIZE + 1] entries since we also save the most recently
        // evicted value.
        assertThat(dumpedString).contains("testString[2]")
        assertThat(dumpedString).contains("testString[3]")
        assertThat(dumpedString).contains("testString[3]")
        assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
        assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
    }
    }


    @Test
    fun columnEvicted_lastKnownColumnValueInDump() {
        systemClock.setCurrentTimeMillis(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvicted", value = "evictedValue")

        // Exactly fill the buffer so that "willBeEvicted" is evicted
        for (i in 0 until MAX_SIZE) {
            systemClock.advanceTime(100L)
            val dumpString = "fillString[$i]"
            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
        }

        val dumpedString = dumpChanges()

        // Expect that we'll have both the evicted column entry...
        val evictedColumnLog =
            TABLE_LOG_DATE_FORMAT.format(100L) +
                SEPARATOR +
                "willBeEvicted" +
                SEPARATOR +
                "evictedValue"
        assertThat(dumpedString).contains(evictedColumnLog)

        // ... *and* all of the fillingColumn entries.
        val firstFillingColumnLog =
            TABLE_LOG_DATE_FORMAT.format(200L) +
                SEPARATOR +
                "fillingColumn" +
                SEPARATOR +
                "fillString[0]"
        val lastFillingColumnLog =
            TABLE_LOG_DATE_FORMAT.format(1100L) +
                SEPARATOR +
                "fillingColumn" +
                SEPARATOR +
                "fillString[9]"
        assertThat(dumpedString).contains(firstFillingColumnLog)
        assertThat(dumpedString).contains(lastFillingColumnLog)
    }

    @Test
    fun multipleColumnsEvicted_allColumnsInDump() {
        systemClock.setCurrentTimeMillis(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedString", value = "evictedValue")
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedInt", value = 45)
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedBool", value = true)

        // Exactly fill the buffer so that all the above columns will be evicted
        for (i in 0 until MAX_SIZE) {
            systemClock.advanceTime(100L)
            val dumpString = "fillString[$i]"
            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
        }

        val dumpedString = dumpChanges()

        // Expect that we'll have all the evicted column entries...
        val evictedColumnLogString =
            TABLE_LOG_DATE_FORMAT.format(100L) +
                SEPARATOR +
                "willBeEvictedString" +
                SEPARATOR +
                "evictedValue"
        val evictedColumnLogInt =
            TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + "willBeEvictedInt" + SEPARATOR + "45"
        val evictedColumnLogBool =
            TABLE_LOG_DATE_FORMAT.format(300L) +
                SEPARATOR +
                "willBeEvictedBool" +
                SEPARATOR +
                "true"
        assertThat(dumpedString).contains(evictedColumnLogString)
        assertThat(dumpedString).contains(evictedColumnLogInt)
        assertThat(dumpedString).contains(evictedColumnLogBool)

        // ... *and* all of the fillingColumn entries.
        val firstFillingColumnLog =
            TABLE_LOG_DATE_FORMAT.format(400) +
                SEPARATOR +
                "fillingColumn" +
                SEPARATOR +
                "fillString[0]"
        val lastFillingColumnLog =
            TABLE_LOG_DATE_FORMAT.format(1300) +
                SEPARATOR +
                "fillingColumn" +
                SEPARATOR +
                "fillString[9]"
        assertThat(dumpedString).contains(firstFillingColumnLog)
        assertThat(dumpedString).contains(lastFillingColumnLog)
    }

    @Test
    fun multipleColumnsEvicted_differentPrefixSameName_allColumnsInDump() {
        systemClock.setCurrentTimeMillis(100L)
        underTest.logChange(prefix = "prefix1", columnName = "sameName", value = "value1")
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "prefix2", columnName = "sameName", value = "value2")
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "prefix3", columnName = "sameName", value = "value3")

        // Exactly fill the buffer so that all the above columns will be evicted
        for (i in 0 until MAX_SIZE) {
            systemClock.advanceTime(100L)
            val dumpString = "fillString[$i]"
            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
        }

        val dumpedString = dumpChanges()

        // Expect that we'll have all the evicted column entries
        val evictedColumn1 =
            TABLE_LOG_DATE_FORMAT.format(100L) +
                SEPARATOR +
                "prefix1.sameName" +
                SEPARATOR +
                "value1"
        val evictedColumn2 =
            TABLE_LOG_DATE_FORMAT.format(200L) +
                SEPARATOR +
                "prefix2.sameName" +
                SEPARATOR +
                "value2"
        val evictedColumn3 =
            TABLE_LOG_DATE_FORMAT.format(300L) +
                SEPARATOR +
                "prefix3.sameName" +
                SEPARATOR +
                "value3"
        assertThat(dumpedString).contains(evictedColumn1)
        assertThat(dumpedString).contains(evictedColumn2)
        assertThat(dumpedString).contains(evictedColumn3)
    }

    @Test
    fun multipleColumnsEvicted_dumpSortedByTimestamp() {
        systemClock.setCurrentTimeMillis(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedFirst", value = "evictedValue")
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedSecond", value = 45)
        systemClock.advanceTime(100L)
        underTest.logChange(prefix = "", columnName = "willBeEvictedThird", value = true)

        // Exactly fill the buffer with so that all the above columns will be evicted
        for (i in 0 until MAX_SIZE) {
            systemClock.advanceTime(100L)
            val dumpString = "fillString[$i]"
            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
        }

        val dumpedString = dumpChanges()

        // Expect that we'll have all the evicted column entries in timestamp order
        val firstEvictedLog =
            TABLE_LOG_DATE_FORMAT.format(100L) +
                SEPARATOR +
                "willBeEvictedFirst" +
                SEPARATOR +
                "evictedValue"
        val secondEvictedLog =
            TABLE_LOG_DATE_FORMAT.format(200L) +
                SEPARATOR +
                "willBeEvictedSecond" +
                SEPARATOR +
                "45"
        val thirdEvictedLog =
            TABLE_LOG_DATE_FORMAT.format(300L) +
                SEPARATOR +
                "willBeEvictedThird" +
                SEPARATOR +
                "true"
        assertThat(dumpedString).contains(firstEvictedLog)
        val stringAfterFirst = dumpedString.substringAfter(firstEvictedLog)
        assertThat(stringAfterFirst).contains(secondEvictedLog)
        val stringAfterSecond = stringAfterFirst.substringAfter(secondEvictedLog)
        assertThat(stringAfterSecond).contains(thirdEvictedLog)
    }

    @Test
    fun sameColumnEvictedMultipleTimes_onlyLastEvictionInDump() {
        systemClock.setCurrentTimeMillis(0L)

        for (i in 1 until 4) {
            systemClock.advanceTime(100L)
            val dumpString = "evicted[$i]"
            underTest.logChange(prefix = "", columnName = "evictedColumn", value = dumpString)
        }

        // Exactly fill the buffer so that all the entries for "evictedColumn" will be evicted.
        for (i in 0 until MAX_SIZE) {
            systemClock.advanceTime(100L)
            val dumpString = "fillString[$i]"
            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
        }

        val dumpedString = dumpChanges()

        // Expect that we only have the most recent evicted column entry
        val evictedColumnLog1 =
            TABLE_LOG_DATE_FORMAT.format(100L) +
                SEPARATOR +
                "evictedColumn" +
                SEPARATOR +
                "evicted[1]"
        val evictedColumnLog2 =
            TABLE_LOG_DATE_FORMAT.format(200L) +
                SEPARATOR +
                "evictedColumn" +
                SEPARATOR +
                "evicted[2]"
        val evictedColumnLog3 =
            TABLE_LOG_DATE_FORMAT.format(300L) +
                SEPARATOR +
                "evictedColumn" +
                SEPARATOR +
                "evicted[3]"
        assertThat(dumpedString).doesNotContain(evictedColumnLog1)
        assertThat(dumpedString).doesNotContain(evictedColumnLog2)
        assertThat(dumpedString).contains(evictedColumnLog3)
    }

    private fun dumpChanges(): String {
    private fun dumpChanges(): String {
        underTest.dump(PrintWriter(outputWriter), arrayOf())
        underTest.dump(PrintWriter(outputWriter), arrayOf())
        return outputWriter.toString()
        return outputWriter.toString()