1 /* <lambda>null2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.protolog.tool 18 19 import com.android.json.stream.JsonReader 20 import com.android.server.protolog.common.InvalidFormatStringException 21 import com.android.server.protolog.common.LogDataType 22 import com.android.server.protolog.ProtoLogMessage 23 import com.android.server.protolog.ProtoLogFileProto 24 import java.io.BufferedReader 25 import java.io.InputStream 26 import java.io.InputStreamReader 27 import java.io.PrintStream 28 import java.lang.Exception 29 import java.text.SimpleDateFormat 30 import java.util.Date 31 import java.util.Locale 32 33 /** 34 * Implements a simple parser/viewer for binary ProtoLog logs. 35 * A binary log is translated into Android "LogCat"-like text log. 36 */ 37 class LogParser(private val configParser: ViewerConfigParser) { 38 companion object { 39 private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) 40 private val magicNumber = 41 ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or 42 ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong() 43 } 44 45 private fun printTime(time: Long, offset: Long, ps: PrintStream) { 46 ps.print(dateFormat.format(Date(time / 1000000 + offset)) + " ") 47 } 48 49 private fun printFormatted( 50 protoLogMessage: ProtoLogMessage, 51 configEntry: ViewerConfigParser.ConfigEntry, 52 ps: PrintStream 53 ) { 54 val strParmIt = protoLogMessage.strParamsList.iterator() 55 val longParamsIt = protoLogMessage.sint64ParamsList.iterator() 56 val doubleParamsIt = protoLogMessage.doubleParamsList.iterator() 57 val boolParamsIt = protoLogMessage.booleanParamsList.iterator() 58 val args = mutableListOf<Any>() 59 val format = configEntry.messageString 60 val argTypes = LogDataType.parseFormatString(format) 61 try { 62 argTypes.forEach { 63 when (it) { 64 LogDataType.BOOLEAN -> args.add(boolParamsIt.next()) 65 LogDataType.LONG -> args.add(longParamsIt.next()) 66 LogDataType.DOUBLE -> args.add(doubleParamsIt.next()) 67 LogDataType.STRING -> args.add(strParmIt.next()) 68 null -> throw NullPointerException() 69 } 70 } 71 } catch (ex: NoSuchElementException) { 72 throw InvalidFormatStringException("Invalid format string in config", ex) 73 } 74 if (strParmIt.hasNext() || longParamsIt.hasNext() || 75 doubleParamsIt.hasNext() || boolParamsIt.hasNext()) { 76 throw RuntimeException("Invalid format string in config - no enough matchers") 77 } 78 val formatted = format.format(*(args.toTypedArray())) 79 ps.print("${configEntry.level} ${configEntry.tag}: $formatted\n") 80 } 81 82 private fun printUnformatted(protoLogMessage: ProtoLogMessage, ps: PrintStream, tag: String) { 83 ps.println("$tag: ${protoLogMessage.messageHash} - ${protoLogMessage.strParamsList}" + 84 " ${protoLogMessage.sint64ParamsList} ${protoLogMessage.doubleParamsList}" + 85 " ${protoLogMessage.booleanParamsList}") 86 } 87 88 fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) { 89 val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput))) 90 val config = configParser.parseConfig(jsonReader) 91 val protoLog = ProtoLogFileProto.parseFrom(protoLogInput) 92 93 if (protoLog.magicNumber != magicNumber) { 94 throw InvalidInputException("ProtoLog file magic number is invalid.") 95 } 96 if (protoLog.version != Constants.VERSION) { 97 throw InvalidInputException("ProtoLog file version not supported by this tool," + 98 " log version ${protoLog.version}, viewer version ${Constants.VERSION}") 99 } 100 101 protoLog.logList.forEach { log -> 102 printTime(log.elapsedRealtimeNanos, protoLog.realTimeToElapsedTimeOffsetMillis, ps) 103 if (log.messageHash !in config) { 104 printUnformatted(log, ps, "UNKNOWN") 105 } else { 106 val conf = config.getValue(log.messageHash) 107 try { 108 printFormatted(log, conf, ps) 109 } catch (ex: Exception) { 110 printUnformatted(log, ps, "INVALID") 111 } 112 } 113 } 114 } 115 } 116