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

Commit a095f402 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove ProtoLogTool read command" into main

parents 5bfd7ddb 6970053c
Loading
Loading
Loading
Loading
+1 −21
Original line number Diff line number Diff line
@@ -22,8 +22,7 @@ class CommandOptions(args: Array<String>) {
    companion object {
        const val TRANSFORM_CALLS_CMD = "transform-protolog-calls"
        const val GENERATE_CONFIG_CMD = "generate-viewer-config"
        const val READ_LOG_CMD = "read-log"
        private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD)
        private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD)

        // TODO: This is always the same. I don't think it's required
        private const val PROTOLOG_CLASS_PARAM = "--protolog-class"
@@ -54,9 +53,6 @@ class CommandOptions(args: Array<String>) {
                $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar>
                $VIEWER_CONFIG_PARAM <viewer.json|viewer.pb> [<input.java>]
            - creates viewer config file from given java files.

            $READ_LOG_CMD $VIEWER_CONFIG_PARAM <viewer.json|viewer.pb> <wm_log.pb>
            - translates a binary log to a readable format.
        """.trimIndent()

        private fun validateClassName(name: String): String {
@@ -252,22 +248,6 @@ class CommandOptions(args: Array<String>) {
                javaSourceArgs = validateJavaInputList(inputFiles)
                logProtofileArg = ""
            }
            READ_LOG_CMD -> {
                protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params)
                protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params)
                protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params)
                viewerConfigFileNameArg =
                    validateConfigFileName(getParam(VIEWER_CONFIG_PARAM, params))
                viewerConfigTypeArg = validateNotSpecified(VIEWER_CONFIG_TYPE_PARAM, params)
                outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
                viewerConfigFilePathArg =
                    validateNotSpecified(VIEWER_CONFIG_FILE_PATH_PARAM, params)
                legacyViewerConfigFilePathArg =
                    validateNotSpecified(LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM, params)
                legacyOutputFilePath = validateNotSpecified(LEGACY_OUTPUT_FILE_PATH, params)
                javaSourceArgs = listOf()
                logProtofileArg = validateLogInputList(inputFiles)
            }
            else -> {
                throw InvalidCommandException("Unknown command.")
            }
+0 −114
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.protolog.tool

import com.android.internal.protolog.ProtoLogFileProto
import com.android.internal.protolog.ProtoLogMessage
import com.android.internal.protolog.common.InvalidFormatStringException
import com.android.internal.protolog.common.LogDataType
import com.android.json.stream.JsonReader
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.io.PrintStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

/**
 * Implements a simple parser/viewer for binary ProtoLog logs.
 * A binary log is translated into Android "LogCat"-like text log.
 */
class LogParser(private val configParser: ViewerConfigParser) {
    companion object {
        private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
        private val magicNumber =
                ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
                        ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
    }

    private fun printTime(time: Long, offset: Long, ps: PrintStream) {
        ps.print(dateFormat.format(Date(time / 1000000 + offset)) + " ")
    }

    private fun printFormatted(
        protoLogMessage: ProtoLogMessage,
        configEntry: ViewerConfigParser.ConfigEntry,
        ps: PrintStream
    ) {
        val strParmIt = protoLogMessage.strParamsList.iterator()
        val longParamsIt = protoLogMessage.sint64ParamsList.iterator()
        val doubleParamsIt = protoLogMessage.doubleParamsList.iterator()
        val boolParamsIt = protoLogMessage.booleanParamsList.iterator()
        val args = mutableListOf<Any>()
        val format = configEntry.messageString
        val argTypes = LogDataType.parseFormatString(format)
        try {
            argTypes.forEach {
                when (it) {
                    LogDataType.BOOLEAN -> args.add(boolParamsIt.next())
                    LogDataType.LONG -> args.add(longParamsIt.next())
                    LogDataType.DOUBLE -> args.add(doubleParamsIt.next())
                    LogDataType.STRING -> args.add(strParmIt.next())
                    null -> throw NullPointerException()
                }
            }
        } catch (ex: NoSuchElementException) {
            throw InvalidFormatStringException("Invalid format string in config", ex)
        }
        if (strParmIt.hasNext() || longParamsIt.hasNext() ||
                doubleParamsIt.hasNext() || boolParamsIt.hasNext()) {
            throw RuntimeException("Invalid format string in config - no enough matchers")
        }
        val formatted = format.format(*(args.toTypedArray()))
        ps.print("${configEntry.level} ${configEntry.tag}: $formatted\n")
    }

    private fun printUnformatted(protoLogMessage: ProtoLogMessage, ps: PrintStream, tag: String) {
        ps.println("$tag: ${protoLogMessage.messageHash} - ${protoLogMessage.strParamsList}" +
                " ${protoLogMessage.sint64ParamsList} ${protoLogMessage.doubleParamsList}" +
                " ${protoLogMessage.booleanParamsList}")
    }

    fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) {
        val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput)))
        val config = configParser.parseConfig(jsonReader)
        val protoLog = ProtoLogFileProto.parseFrom(protoLogInput)

        if (protoLog.magicNumber != magicNumber) {
            throw InvalidInputException("ProtoLog file magic number is invalid.")
        }
        if (protoLog.version != Constants.VERSION) {
            throw InvalidInputException("ProtoLog file version not supported by this tool," +
                    " log version ${protoLog.version}, viewer version ${Constants.VERSION}")
        }

        protoLog.logList.forEach { log ->
            printTime(log.elapsedRealtimeNanos, protoLog.realTimeToElapsedTimeOffsetMillis, ps)
            if (log.messageHash !in config) {
                printUnformatted(log, ps, "UNKNOWN")
            } else {
                val conf = config.getValue(log.messageHash)
                try {
                    printFormatted(log, conf, ps)
                } catch (ex: Exception) {
                    printUnformatted(log, ps, "INVALID")
                }
            }
        }
    }
}
+0 −8
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import com.github.javaparser.ast.stmt.BlockStmt
import com.github.javaparser.ast.stmt.ReturnStmt
import com.github.javaparser.ast.type.ClassOrInterfaceType
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.OutputStream
@@ -457,12 +456,6 @@ object ProtoLogTool {
        return packagePath
    }

    private fun read(command: CommandOptions) {
        LogParser(ViewerConfigParser())
                .parse(FileInputStream(command.logProtofileArg),
                        FileInputStream(command.viewerConfigFileNameArg), System.out)
    }

    @JvmStatic
    fun main(args: Array<String>) {
        try {
@@ -494,7 +487,6 @@ object ProtoLogTool {
        when (command.command) {
            CommandOptions.TRANSFORM_CALLS_CMD -> processClasses(command)
            CommandOptions.GENERATE_CONFIG_CMD -> viewerConf(command)
            CommandOptions.READ_LOG_CMD -> read(command)
        }
    }

+0 −9
Original line number Diff line number Diff line
@@ -387,13 +387,4 @@ class CommandOptionsTest {
        }
        assertThat(exception).hasMessageThat().contains("required, got invalid.yaml instead")
    }

    @Test
    fun readLog() {
        val testLine = "read-log --viewer-config $TEST_VIEWER_JSON $TEST_LOG"
        val cmd = CommandOptions(testLine.split(' ').toTypedArray())
        assertEquals(CommandOptions.READ_LOG_CMD, cmd.command)
        assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigFileNameArg)
        assertEquals(TEST_LOG, cmd.logProtofileArg)
    }
}
+0 −187
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.protolog.tool

import com.android.json.stream.JsonReader
import com.android.internal.protolog.ProtoLogMessage
import com.android.internal.protolog.ProtoLogFileProto
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.mock
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.io.PrintStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

class LogParserTest {
    private val configParser: ViewerConfigParser = mock(ViewerConfigParser::class.java)
    private val parser = LogParser(configParser)
    private var config: MutableMap<Long, ViewerConfigParser.ConfigEntry> = mutableMapOf()
    private var outStream: OutputStream = ByteArrayOutputStream()
    private var printStream: PrintStream = PrintStream(outStream)
    private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)

    @Before
    fun init() {
        Mockito.`when`(configParser.parseConfig(any(JsonReader::class.java))).thenReturn(config)
    }

    private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)

    private fun getConfigDummyStream(): InputStream {
        return "".byteInputStream()
    }

    private fun buildProtoInput(logBuilder: ProtoLogFileProto.Builder): InputStream {
        logBuilder.setVersion(Constants.VERSION)
        logBuilder.magicNumber =
                ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
                        ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
        return logBuilder.build().toByteArray().inputStream()
    }

    private fun testDate(timeMS: Long): String {
        return dateFormat.format(Date(timeMS))
    }

    @Test
    fun parse() {
        config[70933285] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
                "ERROR", "WindowManager")

        val logBuilder = ProtoLogFileProto.newBuilder()
        val logMessageBuilder = ProtoLogMessage.newBuilder()
        logMessageBuilder
                .setMessageHash(70933285)
                .setElapsedRealtimeNanos(0)
                .addBooleanParams(true)
        logBuilder.addLog(logMessageBuilder.build())

        parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)

        assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: true\n",
                outStream.toString())
    }

    @Test
    fun parse_formatting() {
        config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %%" +
                " %x %s %f", "ERROR", "WindowManager")

        val logBuilder = ProtoLogFileProto.newBuilder()
        val logMessageBuilder = ProtoLogMessage.newBuilder()
        logMessageBuilder
                .setMessageHash(123)
                .setElapsedRealtimeNanos(0)
                .addBooleanParams(true)
                .addAllSint64Params(listOf(1000, 20000))
                .addDoubleParams(1000.1)
                .addStrParams("test")
        logBuilder.addLog(logMessageBuilder.build())

        parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)

        assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: " +
                "true 1000 % 4e20 test 1000.100000\n",
                outStream.toString())
    }

    @Test
    fun parse_invalidParamsTooMany() {
        config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %%",
                "ERROR", "WindowManager")

        val logBuilder = ProtoLogFileProto.newBuilder()
        val logMessageBuilder = ProtoLogMessage.newBuilder()
        logMessageBuilder
                .setMessageHash(123)
                .setElapsedRealtimeNanos(0)
                .addBooleanParams(true)
                .addAllSint64Params(listOf(1000, 20000, 300000))
                .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1))
                .addStrParams("test")
        logBuilder.addLog(logMessageBuilder.build())

        parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)

        assertEquals("${testDate(0)} INVALID: 123 - [test] [1000, 20000, 300000] " +
                "[0.1, 1.0E-5, 1000.1] [true]\n", outStream.toString())
    }

    @Test
    fun parse_invalidParamsNotEnough() {
        config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %%" +
                " %x %s %f", "ERROR", "WindowManager")

        val logBuilder = ProtoLogFileProto.newBuilder()
        val logMessageBuilder = ProtoLogMessage.newBuilder()
        logMessageBuilder
                .setMessageHash(123)
                .setElapsedRealtimeNanos(0)
                .addBooleanParams(true)
                .addStrParams("test")
        logBuilder.addLog(logMessageBuilder.build())

        parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)

        assertEquals("${testDate(0)} INVALID: 123 - [test] [] [] [true]\n",
                outStream.toString())
    }

    @Test(expected = InvalidInputException::class)
    fun parse_invalidMagicNumber() {
        val logBuilder = ProtoLogFileProto.newBuilder()
        logBuilder.setVersion(Constants.VERSION)
        logBuilder.magicNumber = 0
        val stream = logBuilder.build().toByteArray().inputStream()

        parser.parse(stream, getConfigDummyStream(), printStream)
    }

    @Test(expected = InvalidInputException::class)
    fun parse_invalidVersion() {
        val logBuilder = ProtoLogFileProto.newBuilder()
        logBuilder.setVersion("invalid")
        logBuilder.magicNumber =
                ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
                        ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
        val stream = logBuilder.build().toByteArray().inputStream()

        parser.parse(stream, getConfigDummyStream(), printStream)
    }

    @Test
    fun parse_noConfig() {
        val logBuilder = ProtoLogFileProto.newBuilder()
        val logMessageBuilder = ProtoLogMessage.newBuilder()
        logMessageBuilder
                .setMessageHash(70933285)
                .setElapsedRealtimeNanos(0)
                .addBooleanParams(true)
        logBuilder.addLog(logMessageBuilder.build())

        parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)

        assertEquals("${testDate(0)} UNKNOWN: 70933285 - [] [] [] [true]\n",
                outStream.toString())
    }
}