• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 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.traces.parsers.perfetto
18 
19 import android.tools.Timestamp
20 import android.tools.Timestamps
21 import android.tools.parsers.AbstractTraceParser
22 import android.tools.traces.wm.ShellTransitionData
23 import android.tools.traces.wm.Transition
24 import android.tools.traces.wm.TransitionChange
25 import android.tools.traces.wm.TransitionType
26 import android.tools.traces.wm.TransitionsTrace
27 import android.tools.traces.wm.WmTransitionData
28 
29 class TransitionsTraceParser :
30     AbstractTraceParser<TraceProcessorSession, Transition, Transition, TransitionsTrace>() {
31 
32     override val traceName = "Transitions Trace"
33 
34     override fun createTrace(entries: Collection<Transition>): TransitionsTrace {
35         return TransitionsTrace(entries)
36     }
37 
38     override fun doDecodeByteArray(bytes: ByteArray): TraceProcessorSession {
39         error("This parser can only read from perfetto trace processor")
40     }
41 
42     override fun shouldParseEntry(entry: Transition) = true
43 
44     override fun getEntries(input: TraceProcessorSession): List<Transition> {
45         val transitions = ArrayList<Transition>()
46 
47         val handlerMapping = mutableMapOf<Int, String>()
48         input.query(getSqlQueryHandlerMappings()) {
49             it.forEach { mapping ->
50                 handlerMapping[(mapping["handler_id"] as Long).toInt()] =
51                     mapping["handler_name"] as String
52             }
53         }
54 
55         input.query(getSqlQueryTransitions()) { transitionsRows ->
56             val transitionRowsGrouped =
57                 transitionsRows.groupBy {
58                     it["transition_entry_id"]
59                         ?: error("transition_entry_id column should not be null")
60                 }
61 
62             transitionRowsGrouped.values.forEach { transitionRows ->
63                 transitions.add(buildTransition(input, transitionRows, handlerMapping))
64             }
65         }
66 
67         return transitions
68     }
69 
70     override fun getTimestamp(entry: Transition): Timestamp = entry.timestamp
71 
72     override fun onBeforeParse(input: TraceProcessorSession) {}
73 
74     override fun doParseEntry(entry: Transition) = entry
75 
76     companion object {
77         private fun getSqlQueryHandlerMappings() =
78             "SELECT handler_id, handler_name FROM window_manager_shell_transition_handlers;"
79 
80         private fun getSqlQueryTransitions() =
81             """
82                SELECT transitions.id AS transition_entry_id, args.key, args.display_value AS value, args.value_type
83                FROM window_manager_shell_transitions AS transitions
84                INNER JOIN args ON transitions.arg_set_id = args.arg_set_id;
85             """
86                 .trimIndent()
87 
88         private fun buildTransition(
89             input: TraceProcessorSession,
90             transitionRows: List<Row>,
91             handlerMapping: Map<Int, String>,
92         ): Transition {
93             val args = Args.build(transitionRows)
94             return Transition(
95                 id = args.getChild("id")?.getInt() ?: error("Missing transition id"),
96                 wmData =
97                     WmTransitionData(
98                         createTime = args.getChild("create_time_ns")?.getLong()?.toTimestamp(input),
99                         sendTime = args.getChild("send_time_ns")?.getLong()?.toTimestamp(input),
100                         abortTime =
101                             args.getChild("wm_abort_time_ns")?.getLong()?.toTimestamp(input),
102                         finishTime = args.getChild("finish_time_ns")?.getLong()?.toTimestamp(input),
103                         startingWindowRemoveTime =
104                             args
105                                 .getChild("starting_window_remove_time_ns")
106                                 ?.getLong()
107                                 ?.toTimestamp(input),
108                         startTransactionId = args.getChild("start_transaction_id")?.getLong(),
109                         finishTransactionId = args.getChild("finish_transaction_id")?.getLong(),
110                         type = args.getChild("type")?.getInt()?.toTransitionType(),
111                         changes =
112                             args
113                                 .getChildren("targets")
114                                 ?.map {
115                                     TransitionChange(
116                                         it.getChild("mode")?.getInt()?.toTransitionType()
117                                             ?: error("Missing mode (${it.getChild("mode")})"),
118                                         it.getChild("layer_id")?.getInt()
119                                             ?: error("Missing layer id ${it.getChild("layer_id")}"),
120                                         it.getChild("window_id")?.getInt()
121                                             ?: error(
122                                                 "Missing window id ${it.getChild("window_id")}"
123                                             ),
124                                     )
125                                 }
126                                 ?.ifEmpty { null },
127                     ),
128                 shellData =
129                     ShellTransitionData(
130                         dispatchTime =
131                             args.getChild("dispatch_time_ns")?.getLong()?.toTimestamp(input),
132                         mergeRequestTime =
133                             args.getChild("merge_request_time_ns")?.getLong()?.toTimestamp(input),
134                         mergeTime = args.getChild("merge_time_ns")?.getLong()?.toTimestamp(input),
135                         abortTime =
136                             args.getChild("shell_abort_time_ns")?.getLong()?.toTimestamp(input),
137                         handler = args.getChild("handler")?.getInt()?.let { handlerMapping[it] },
138                         mergeTarget = args.getChild("merge_target")?.getInt(),
139                     ),
140             )
141         }
142 
143         private fun Long.toTimestamp(input: TraceProcessorSession): Timestamp? {
144             if (this == 0L) {
145                 return null
146             }
147 
148             val ts = this
149             return input.query(
150                 "SELECT TO_REALTIME($ts) as real_ts, TO_MONOTONIC($ts) as monotonic_ts"
151             ) {
152                 require(it.size == 1)
153                 Timestamps.from(
154                     unixNanos = it[0]["real_ts"] as Long,
155                     systemUptimeNanos = it[0]["monotonic_ts"] as Long,
156                     elapsedNanos = this,
157                 )
158             }
159         }
160 
161         private fun Int.toTransitionType() = TransitionType.fromInt(this)
162     }
163 }
164