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