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.parsers.events
18 
19 import android.tools.Timestamp
20 import android.tools.Timestamps
21 import android.tools.parsers.AbstractParser
22 import android.tools.traces.events.CujEvent
23 import android.tools.traces.events.Event
24 import android.tools.traces.events.EventLog
25 import android.tools.traces.events.EventLog.Companion.MAGIC_NUMBER
26 import android.tools.traces.events.FocusEvent
27 
28 operator fun <T> List<T>.component6(): T = get(5)
29 
30 class EventLogParser : AbstractParser<Collection<String>, EventLog>() {
31     override val traceName: String = "Event Log"
32 
33     override fun doDecodeByteArray(bytes: ByteArray): Collection<String> {
34         val logsString = bytes.decodeToString()
35         return logsString
36             .split("\n")
37             .dropWhile {
38                 it.contains(MAGIC_NUMBER) || it.contains("beginning of events") || it.isBlank()
39             }
40             .dropLastWhile { it.isBlank() }
41     }
42 
43     override fun doParse(input: Collection<String>): EventLog {
44         val events =
45             input
46                 .map { log ->
47                     val (metaData, eventData) = log.split(":", limit = 2).map { it.trim() }
48                     val (rawTimestamp, uid, pid, tid, priority, tag) =
49                         metaData.split(" ").filter { it.isNotEmpty() }
50 
51                     val timestamp =
52                         Timestamps.from(unixNanos = rawTimestamp.replace(".", "").toLong())
53                     parseEvent(timestamp, pid.toInt(), uid, tid.toInt(), tag, eventData)
54                 }
55                 .sortedBy { it.timestamp.unixNanos }
56 
57         return EventLog(events)
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, parseData(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 parseData(data: String): Collection<String> {
88         require(data.first() == '[')
89         require(data.last() == ']')
90         return data.drop(1).dropLast(1).split(",")
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                 .sortedBy { getTimestampFromRawEntry(it).unixNanos }
99                 .dropWhile { getTimestampFromRawEntry(it).unixNanos < from.unixNanos }
100                 .dropLastWhile { getTimestampFromRawEntry(it).unixNanos > to.unixNanos }
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