1/* 2 * 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 */ 16import {assertDefined} from 'common/assert_utils'; 17import {AbstractParser} from 'parsers/perfetto/abstract_parser'; 18import {FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder'; 19import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils'; 20import {perfetto} from 'protos/transitions/latest/static'; 21import {TraceType} from 'trace/trace_type'; 22import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 23 24export class ParserTransitions extends AbstractParser<PropertyTreeNode> { 25 private handlerIdToName: {[id: number]: string} | undefined = undefined; 26 27 override getTraceType(): TraceType { 28 return TraceType.TRANSITION; 29 } 30 31 override async getEntry(index: number): Promise<PropertyTreeNode> { 32 const transitionProto = await this.queryEntry(index); 33 if (this.handlerIdToName === undefined) { 34 const handlers = await this.queryHandlers(); 35 this.handlerIdToName = {}; 36 handlers.forEach( 37 (it) => (assertDefined(this.handlerIdToName)[it.id] = it.name), 38 ); 39 } 40 return this.makePropertiesTree(transitionProto); 41 } 42 43 protected override getTableName(): string { 44 return 'window_manager_shell_transitions'; 45 } 46 47 private async queryEntry( 48 index: number, 49 ): Promise<perfetto.protos.ShellTransition> { 50 const protoBuilder = new FakeProtoBuilder(); 51 52 const sql = ` 53 SELECT 54 transitions.transition_id, 55 args.key, 56 args.value_type, 57 args.int_value, 58 args.string_value, 59 args.real_value 60 FROM 61 window_manager_shell_transitions as transitions 62 INNER JOIN args ON transitions.arg_set_id = args.arg_set_id 63 WHERE transitions.id = ${this.entryIndexToRowIdMap[index]}; 64 `; 65 const result = await this.traceProcessor.query(sql).waitAllRows(); 66 67 for (const it = result.iter({}); it.valid(); it.next()) { 68 protoBuilder.addArg( 69 it.get('key') as string, 70 it.get('value_type') as string, 71 it.get('int_value') as bigint | undefined, 72 it.get('real_value') as number | undefined, 73 it.get('string_value') as string | undefined, 74 ); 75 } 76 77 return protoBuilder.build(); 78 } 79 80 private makePropertiesTree( 81 transitionProto: perfetto.protos.ShellTransition, 82 ): PropertyTreeNode { 83 this.validatePerfettoTransition(transitionProto); 84 85 const perfettoTransitionInfo = { 86 entry: transitionProto, 87 realToBootTimeOffsetNs: undefined, 88 handlerMapping: this.handlerIdToName, 89 timestampConverter: this.timestampConverter, 90 }; 91 92 const shellEntryTree = ParserTransitionsUtils.makeShellPropertiesTree( 93 perfettoTransitionInfo, 94 [ 95 'createTimeNs', 96 'sendTimeNs', 97 'wmAbortTimeNs', 98 'finishTimeNs', 99 'startTransactionId', 100 'finishTransactionId', 101 'type', 102 'targets', 103 'flags', 104 'startingWindowRemoveTimeNs', 105 ], 106 ); 107 const wmEntryTree = ParserTransitionsUtils.makeWmPropertiesTree( 108 perfettoTransitionInfo, 109 [ 110 'dispatchTimeNs', 111 'mergeTimeNs', 112 'mergeRequestTimeNs', 113 'shellAbortTimeNs', 114 'handler', 115 'mergeTarget', 116 ], 117 ); 118 119 return ParserTransitionsUtils.makeTransitionPropertiesTree( 120 shellEntryTree, 121 wmEntryTree, 122 ); 123 } 124 125 private async queryHandlers(): Promise<TransitionHandler[]> { 126 const sql = 127 'SELECT handler_id, handler_name FROM window_manager_shell_transition_handlers;'; 128 const result = await this.traceProcessor.query(sql).waitAllRows(); 129 130 const handlers: TransitionHandler[] = []; 131 for (const it = result.iter({}); it.valid(); it.next()) { 132 handlers.push({ 133 id: it.get('handler_id') as number, 134 name: it.get('handler_name') as string, 135 }); 136 } 137 138 return handlers; 139 } 140 141 private validatePerfettoTransition( 142 transition: perfetto.protos.IShellTransition, 143 ) { 144 if (transition.id === 0) { 145 throw new Error('Entry need a non null id'); 146 } 147 if ( 148 !transition.createTimeNs && 149 !transition.sendTimeNs && 150 !transition.wmAbortTimeNs && 151 !transition.finishTimeNs && 152 !transition.dispatchTimeNs && 153 !transition.mergeRequestTimeNs && 154 !transition.mergeTimeNs && 155 !transition.shellAbortTimeNs 156 ) { 157 throw new Error('Requires at least one non-null timestamp'); 158 } 159 } 160} 161 162interface TransitionHandler { 163 id: number; 164 name: string; 165} 166