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 */ 16 17import {assertDefined, assertTrue} from 'common/assert_utils'; 18import {ParserTimestampConverter} from 'common/timestamp_converter'; 19import {AddDefaults} from 'parsers/operations/add_defaults'; 20import {SetFormatters} from 'parsers/operations/set_formatters'; 21import {TranslateIntDef} from 'parsers/operations/translate_intdef'; 22import {AbstractParser} from 'parsers/perfetto/abstract_parser'; 23import {FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder'; 24import {FakeProtoTransformer} from 'parsers/perfetto/fake_proto_transformer'; 25import {Utils} from 'parsers/perfetto/utils'; 26import {DENYLIST_PROPERTIES} from 'parsers/surface_flinger/denylist_properties'; 27import {EAGER_PROPERTIES} from 'parsers/surface_flinger/eager_properties'; 28import {EntryHierarchyTreeFactory} from 'parsers/surface_flinger/entry_hierarchy_tree_factory'; 29import {TamperedMessageType} from 'parsers/tampered_message_type'; 30import root from 'protos/surfaceflinger/latest/json'; 31import {perfetto} from 'protos/surfaceflinger/latest/static'; 32import { 33 CustomQueryParserResultTypeMap, 34 CustomQueryType, 35 VisitableParserCustomQuery, 36} from 'trace/custom_query'; 37import {EntriesRange} from 'trace/trace'; 38import {TraceFile} from 'trace/trace_file'; 39import {TraceType} from 'trace/trace_type'; 40import {EnumFormatter, LAYER_ID_FORMATTER} from 'trace/tree_node/formatters'; 41import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 42import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy'; 43 44export class ParserSurfaceFlinger extends AbstractParser<HierarchyTreeNode> { 45 private static readonly CUSTOM_FORMATTERS = new Map([ 46 ['cropLayerId', LAYER_ID_FORMATTER], 47 ['zOrderRelativeOf', LAYER_ID_FORMATTER], 48 [ 49 'hwcCompositionType', 50 new EnumFormatter(perfetto.protos.HwcCompositionType), 51 ], 52 ]); 53 54 private static readonly LayersTraceFileProto = TamperedMessageType.tamper( 55 root.lookupType('perfetto.protos.LayersTraceFileProto'), 56 ); 57 private static readonly entryField = 58 ParserSurfaceFlinger.LayersTraceFileProto.fields['entry']; 59 private static readonly layerField = assertDefined( 60 ParserSurfaceFlinger.entryField.tamperedMessageType?.fields['layers'] 61 .tamperedMessageType, 62 ).fields['layers']; 63 64 static readonly Operations = { 65 SetFormattersLayer: new SetFormatters( 66 ParserSurfaceFlinger.layerField, 67 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 68 ), 69 TranslateIntDefLayer: new TranslateIntDef(ParserSurfaceFlinger.layerField), 70 AddDefaultsLayerEager: new AddDefaults( 71 ParserSurfaceFlinger.layerField, 72 EAGER_PROPERTIES, 73 ), 74 AddDefaultsLayerLazy: new AddDefaults( 75 ParserSurfaceFlinger.layerField, 76 undefined, 77 EAGER_PROPERTIES.concat(DENYLIST_PROPERTIES), 78 ), 79 SetFormattersEntry: new SetFormatters( 80 ParserSurfaceFlinger.entryField, 81 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 82 ), 83 TranslateIntDefEntry: new TranslateIntDef(ParserSurfaceFlinger.entryField), 84 AddDefaultsEntryEager: new AddDefaults(ParserSurfaceFlinger.entryField, [ 85 'displays', 86 ]), 87 AddDefaultsEntryLazy: new AddDefaults( 88 ParserSurfaceFlinger.entryField, 89 undefined, 90 DENYLIST_PROPERTIES, 91 ), 92 }; 93 94 private readonly factory = new EntryHierarchyTreeFactory(); 95 private layersSnapshotProtoTransformer: FakeProtoTransformer; 96 private layerProtoTransformer: FakeProtoTransformer; 97 98 constructor( 99 traceFile: TraceFile, 100 traceProcessor: WasmEngineProxy, 101 timestampConverter: ParserTimestampConverter, 102 ) { 103 super(traceFile, traceProcessor, timestampConverter); 104 this.layersSnapshotProtoTransformer = new FakeProtoTransformer( 105 assertDefined(ParserSurfaceFlinger.entryField.tamperedMessageType), 106 ); 107 this.layerProtoTransformer = new FakeProtoTransformer( 108 assertDefined(ParserSurfaceFlinger.layerField.tamperedMessageType), 109 ); 110 } 111 112 override getTraceType(): TraceType { 113 return TraceType.SURFACE_FLINGER; 114 } 115 116 override async getEntry(index: number): Promise<HierarchyTreeNode> { 117 let snapshotProto = await Utils.queryEntry( 118 this.traceProcessor, 119 this.getTableName(), 120 this.entryIndexToRowIdMap, 121 index, 122 ); 123 snapshotProto = 124 this.layersSnapshotProtoTransformer.transform(snapshotProto); 125 126 const layerProtos = (await this.querySnapshotLayers(index)).map( 127 (layerProto) => this.layerProtoTransformer.transform(layerProto), 128 ); 129 130 return this.factory.makeEntryHierarchyTree( 131 snapshotProto, 132 layerProtos, 133 ParserSurfaceFlinger, 134 ); 135 } 136 137 override async customQuery<Q extends CustomQueryType>( 138 type: Q, 139 entriesRange: EntriesRange, 140 ): Promise<CustomQueryParserResultTypeMap[Q]> { 141 return new VisitableParserCustomQuery(type) 142 .visit(CustomQueryType.VSYNCID, async () => { 143 return Utils.queryVsyncId( 144 this.traceProcessor, 145 this.getTableName(), 146 this.entryIndexToRowIdMap, 147 entriesRange, 148 ); 149 }) 150 .visit(CustomQueryType.SF_LAYERS_ID_AND_NAME, async () => { 151 const sql = ` 152 SELECT DISTINCT group_concat(value) AS id_and_name FROM ( 153 SELECT sfl.id AS id, args.key AS key, args.display_value AS value 154 FROM surfaceflinger_layer AS sfl 155 INNER JOIN args ON sfl.arg_set_id = args.arg_set_id 156 WHERE (args.key = 'id' OR args.key = 'name') 157 ORDER BY key 158 ) 159 GROUP BY id; 160 `; 161 const queryResult = await this.traceProcessor.query(sql).waitAllRows(); 162 const result: CustomQueryParserResultTypeMap[CustomQueryType.SF_LAYERS_ID_AND_NAME] = 163 []; 164 for (const it = queryResult.iter({}); it.valid(); it.next()) { 165 const idAndName = it.get('id_and_name') as string; 166 const indexDelimiter = idAndName.indexOf(','); 167 assertTrue( 168 indexDelimiter > 0, 169 () => `Unexpected value in query result: ${idAndName}`, 170 ); 171 const id = Number(idAndName.slice(0, indexDelimiter)); 172 const name = idAndName.slice(indexDelimiter + 1); 173 result.push({id, name}); 174 } 175 return result; 176 }) 177 .getResult(); 178 } 179 180 protected override getTableName(): string { 181 return 'surfaceflinger_layers_snapshot'; 182 } 183 184 private async querySnapshotLayers( 185 index: number, 186 ): Promise<perfetto.protos.ILayerProto[]> { 187 const layerIdToBuilder = new Map<number, FakeProtoBuilder>(); 188 const getBuilder = (layerId: number) => { 189 if (!layerIdToBuilder.has(layerId)) { 190 layerIdToBuilder.set(layerId, new FakeProtoBuilder()); 191 } 192 return assertDefined(layerIdToBuilder.get(layerId)); 193 }; 194 195 const sql = ` 196 SELECT 197 sfl.snapshot_id, 198 sfl.id as layer_id, 199 args.key, 200 args.value_type, 201 args.int_value, 202 args.string_value, 203 args.real_value 204 FROM 205 surfaceflinger_layer as sfl 206 INNER JOIN args ON sfl.arg_set_id = args.arg_set_id 207 WHERE snapshot_id = ${this.entryIndexToRowIdMap[index]}; 208 `; 209 const result = await this.traceProcessor.query(sql).waitAllRows(); 210 211 for (const it = result.iter({}); it.valid(); it.next()) { 212 const builder = getBuilder(it.get('layer_id') as number); 213 builder.addArg( 214 it.get('key') as string, 215 it.get('value_type') as string, 216 it.get('int_value') as bigint | undefined, 217 it.get('real_value') as number | undefined, 218 it.get('string_value') as string | undefined, 219 ); 220 } 221 222 const layerProtos: perfetto.protos.ILayerProto[] = []; 223 layerIdToBuilder.forEach((builder) => { 224 layerProtos.push(builder.build()); 225 }); 226 227 return layerProtos; 228 } 229} 230