• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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