1 /* <lambda>null2 * Copyright (C) 2023 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 android.tools.common.parsers.events 18 19 import android.tools.common.Timestamp 20 import android.tools.common.Timestamps 21 import android.tools.common.parsers.AbstractParser 22 import android.tools.common.traces.events.CujEvent 23 import android.tools.common.traces.events.Event 24 import android.tools.common.traces.events.EventLog 25 import android.tools.common.traces.events.EventLog.Companion.MAGIC_NUMBER 26 import android.tools.common.traces.events.FocusEvent 27 import kotlin.js.JsExport 28 29 operator fun <T> List<T>.component6(): T = get(5) 30 31 @JsExport 32 class EventLogParser : AbstractParser<Array<String>, EventLog>() { 33 override val traceName: String = "Event Log" 34 35 override fun doDecodeByteArray(bytes: ByteArray): Array<String> { 36 val logsString = bytes.decodeToString() 37 return logsString 38 .split("\n") 39 .dropWhile { 40 it.contains(MAGIC_NUMBER) || it.contains("beginning of events") || it.isBlank() 41 } 42 .dropLastWhile { it.isBlank() } 43 .toTypedArray() 44 } 45 46 override fun doParse(input: Array<String>): EventLog { 47 val events = 48 input.map { log -> 49 val (metaData, eventData) = log.split(":", limit = 2).map { it.trim() } 50 val (rawTimestamp, uid, pid, tid, priority, tag) = 51 metaData.split(" ").filter { it.isNotEmpty() } 52 53 val timestamp = Timestamps.from(unixNanos = rawTimestamp.replace(".", "").toLong()) 54 parseEvent(timestamp, pid.toInt(), uid, tid.toInt(), tag, eventData) 55 } 56 57 return EventLog(events.toTypedArray()) 58 } 59 60 private fun parseEvent( 61 timestamp: Timestamp, 62 pid: Int, 63 uid: String, 64 tid: Int, 65 tag: String, 66 eventData: String 67 ): Event { 68 return when (tag) { 69 INPUT_FOCUS_TAG -> { 70 FocusEvent.from(timestamp, pid, uid, tid, parseDataArray(eventData)) 71 } 72 JANK_CUJ_BEGIN_TAG -> { 73 CujEvent.fromData(pid, uid, tid, tag, eventData) 74 } 75 JANK_CUJ_END_TAG -> { 76 CujEvent.fromData(pid, uid, tid, tag, eventData) 77 } 78 JANK_CUJ_CANCEL_TAG -> { 79 CujEvent.fromData(pid, uid, tid, tag, eventData) 80 } 81 else -> { 82 Event(timestamp, pid, uid, tid, tag) 83 } 84 } 85 } 86 87 private fun parseDataArray(data: String): Array<String> { 88 require(data.first() == '[') 89 require(data.last() == ']') 90 return data.drop(1).dropLast(1).split(",").toTypedArray() 91 } 92 93 fun parseSlice(bytes: ByteArray, from: Timestamp, to: Timestamp): EventLog { 94 require(from.unixNanos < to.unixNanos) { "'to' needs to be greater than 'from'" } 95 require(from.hasUnixTimestamp && to.hasUnixTimestamp) { "Missing required timestamp type" } 96 return doParse( 97 this.doDecodeByteArray(bytes) 98 .dropWhile { getTimestampFromRawEntry(it).unixNanos < from.unixNanos } 99 .dropLastWhile { getTimestampFromRawEntry(it).unixNanos > to.unixNanos } 100 .toTypedArray() 101 ) 102 } 103 104 private fun getTimestampFromRawEntry(entry: String): Timestamp { 105 val (metaData, _) = entry.split(":", limit = 2).map { it.trim() } 106 val (rawTimestamp, _, _, _, _, _) = metaData.split(" ").filter { it.isNotEmpty() } 107 return Timestamps.from(unixNanos = rawTimestamp.replace(".", "").toLong()) 108 } 109 110 companion object { 111 const val EVENT_LOG_INPUT_FOCUS_TAG = 62001 112 113 const val WM_JANK_CUJ_EVENTS_BEGIN_REQUEST = 37001 114 const val WM_JANK_CUJ_EVENTS_END_REQUEST = 37002 115 const val WM_JANK_CUJ_EVENTS_CANCEL_REQUEST = 37003 116 117 const val INPUT_FOCUS_TAG = "input_focus" 118 const val JANK_CUJ_BEGIN_TAG = "jank_cuj_events_begin_request" 119 const val JANK_CUJ_END_TAG = "jank_cuj_events_end_request" 120 const val JANK_CUJ_CANCEL_TAG = "jank_cuj_events_cancel_request" 121 } 122 } 123