| /* |
| * Copyright (C) 2022 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.server.wm.traces.common.parser.events |
| |
| import com.android.server.wm.traces.common.CrossPlatform |
| import com.android.server.wm.traces.common.Timestamp |
| import com.android.server.wm.traces.common.events.CujEvent |
| import com.android.server.wm.traces.common.events.Event |
| import com.android.server.wm.traces.common.events.EventLog |
| import com.android.server.wm.traces.common.events.EventLog.Companion.MAGIC_NUMBER |
| import com.android.server.wm.traces.common.events.FocusEvent |
| import com.android.server.wm.traces.common.parser.AbstractParser |
| |
| operator fun <T> List<T>.component6(): T = get(5) |
| |
| class EventLogParser : AbstractParser<Array<String>, EventLog>() { |
| override val traceName: String = "Event Log" |
| |
| override fun doDecodeByteArray(bytes: ByteArray): Array<String> { |
| val logsString = bytes.decodeToString() |
| return logsString |
| .split("\n") |
| .dropWhile { |
| it.contains(MAGIC_NUMBER) || it.contains("beginning of events") || it.isBlank() |
| } |
| .dropLastWhile { it.isBlank() } |
| .toTypedArray() |
| } |
| |
| override fun doParse(input: Array<String>): EventLog { |
| val events = |
| input.map { log -> |
| val (metaData, eventData) = log.split(":", limit = 2).map { it.trim() } |
| val (rawTimestamp, uid, pid, tid, priority, tag) = metaData.split("\\s+".toRegex()) |
| |
| val timestamp = |
| CrossPlatform.timestamp.from(unixNanos = rawTimestamp.replace(".", "").toLong()) |
| parseEvent(timestamp, pid.toInt(), uid, tid.toInt(), tag, eventData) |
| } |
| |
| return EventLog(events.toTypedArray()) |
| } |
| |
| private fun parseEvent( |
| timestamp: Timestamp, |
| pid: Int, |
| uid: String, |
| tid: Int, |
| tag: String, |
| eventData: String |
| ): Event { |
| eventData.split(",") |
| |
| return when (tag) { |
| INPUT_FOCUS_TAG -> { |
| FocusEvent(timestamp, pid, uid, tid, parseDataArray(eventData)) |
| } |
| JANK_CUJ_BEGIN_TAG -> { |
| CujEvent(timestamp, pid, uid, tid, tag, eventData) |
| } |
| JANK_CUJ_END_TAG -> { |
| CujEvent(timestamp, pid, uid, tid, tag, eventData) |
| } |
| JANK_CUJ_CANCEL_TAG -> { |
| CujEvent(timestamp, pid, uid, tid, tag, eventData) |
| } |
| else -> { |
| Event(timestamp, pid, uid, tid, tag) |
| } |
| } |
| } |
| |
| private fun parseDataArray(data: String): Array<String> { |
| require(data.first() == '[') |
| require(data.last() == ']') |
| return data.drop(1).dropLast(1).split(",").toTypedArray() |
| } |
| |
| fun parse(bytes: ByteArray, from: Timestamp, to: Timestamp): EventLog { |
| require(from.unixNanos < to.unixNanos) { "'to' needs to be greater than 'from'" } |
| require(from.hasUnixTimestamp && to.hasUnixTimestamp) { "Missing required timestamp type" } |
| return doParse( |
| this.doDecodeByteArray(bytes) |
| .dropWhile { getTimestampFromRawEntry(it).unixNanos < from.unixNanos } |
| .dropLastWhile { getTimestampFromRawEntry(it).unixNanos > to.unixNanos } |
| .toTypedArray() |
| ) |
| } |
| |
| private fun getTimestampFromRawEntry(entry: String): Timestamp { |
| val (metaData, _) = entry.split(":", limit = 2).map { it.trim() } |
| val (rawTimestamp, _, _, _, _, _) = metaData.split("\\s+".toRegex()) |
| return CrossPlatform.timestamp.from(unixNanos = rawTimestamp.replace(".", "").toLong()) |
| } |
| |
| companion object { |
| const val EVENT_LOG_INPUT_FOCUS_TAG = 62001 |
| |
| const val WM_JANK_CUJ_EVENTS_BEGIN_REQUEST = 37001 |
| const val WM_JANK_CUJ_EVENTS_END_REQUEST = 37002 |
| const val WM_JANK_CUJ_EVENTS_CANCEL_REQUEST = 37003 |
| |
| const val INPUT_FOCUS_TAG = "input_focus" |
| const val JANK_CUJ_BEGIN_TAG = "jank_cuj_events_begin_request" |
| const val JANK_CUJ_END_TAG = "jank_cuj_events_end_request" |
| const val JANK_CUJ_CANCEL_TAG = "jank_cuj_events_cancel_request" |
| } |
| } |