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 {RectsComputation} from 'parsers/surface_flinger/computations/rects_computation'; 27import {VisibilityPropertiesComputation} from 'parsers/surface_flinger/computations/visibility_properties_computation'; 28import {ZOrderPathsComputation} from 'parsers/surface_flinger/computations/z_order_paths_computation'; 29import {HierarchyTreeBuilderSf} from 'parsers/surface_flinger/hierarchy_tree_builder_sf'; 30import {ParserSfUtils} from 'parsers/surface_flinger/parser_surface_flinger_utils'; 31import {TamperedMessageType} from 'parsers/tampered_message_type'; 32import root from 'protos/surfaceflinger/latest/json'; 33import {perfetto} from 'protos/surfaceflinger/latest/static'; 34import { 35 CustomQueryParserResultTypeMap, 36 CustomQueryType, 37 VisitableParserCustomQuery, 38} from 'trace/custom_query'; 39import {EntriesRange} from 'trace/trace'; 40import {TraceFile} from 'trace/trace_file'; 41import {TraceType} from 'trace/trace_type'; 42import {EnumFormatter, LAYER_ID_FORMATTER} from 'trace/tree_node/formatters'; 43import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 44import {PropertiesProvider} from 'trace/tree_node/properties_provider'; 45import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder'; 46import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy'; 47 48export class ParserSurfaceFlinger extends AbstractParser<HierarchyTreeNode> { 49 private static readonly CUSTOM_FORMATTERS = new Map([ 50 ['cropLayerId', LAYER_ID_FORMATTER], 51 ['zOrderRelativeOf', LAYER_ID_FORMATTER], 52 [ 53 'hwcCompositionType', 54 new EnumFormatter(perfetto.protos.HwcCompositionType), 55 ], 56 ]); 57 58 private static readonly LayersTraceFileProto = TamperedMessageType.tamper( 59 root.lookupType('perfetto.protos.LayersTraceFileProto'), 60 ); 61 private static readonly entryField = 62 ParserSurfaceFlinger.LayersTraceFileProto.fields['entry']; 63 private static readonly layerField = assertDefined( 64 ParserSurfaceFlinger.entryField.tamperedMessageType?.fields['layers'] 65 .tamperedMessageType, 66 ).fields['layers']; 67 68 private static readonly Operations = { 69 SetFormattersLayer: new SetFormatters( 70 ParserSurfaceFlinger.layerField, 71 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 72 ), 73 TranslateIntDefLayer: new TranslateIntDef(ParserSurfaceFlinger.layerField), 74 AddDefaultsLayerEager: new AddDefaults( 75 ParserSurfaceFlinger.layerField, 76 ParserSfUtils.EAGER_PROPERTIES, 77 ), 78 AddDefaultsLayerLazy: new AddDefaults( 79 ParserSurfaceFlinger.layerField, 80 undefined, 81 ParserSfUtils.EAGER_PROPERTIES.concat(ParserSfUtils.DENYLIST_PROPERTIES), 82 ), 83 SetFormattersEntry: new SetFormatters( 84 ParserSurfaceFlinger.entryField, 85 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 86 ), 87 TranslateIntDefEntry: new TranslateIntDef(ParserSurfaceFlinger.entryField), 88 AddDefaultsEntryEager: new AddDefaults(ParserSurfaceFlinger.entryField, [ 89 'displays', 90 ]), 91 AddDefaultsEntryLazy: new AddDefaults( 92 ParserSurfaceFlinger.entryField, 93 undefined, 94 ParserSfUtils.DENYLIST_PROPERTIES, 95 ), 96 }; 97 98 private layersSnapshotProtoTransformer: FakeProtoTransformer; 99 private layerProtoTransformer: FakeProtoTransformer; 100 101 constructor( 102 traceFile: TraceFile, 103 traceProcessor: WasmEngineProxy, 104 timestampConverter: ParserTimestampConverter, 105 ) { 106 super(traceFile, traceProcessor, timestampConverter); 107 this.layersSnapshotProtoTransformer = new FakeProtoTransformer( 108 assertDefined(ParserSurfaceFlinger.entryField.tamperedMessageType), 109 ); 110 this.layerProtoTransformer = new FakeProtoTransformer( 111 assertDefined(ParserSurfaceFlinger.layerField.tamperedMessageType), 112 ); 113 } 114 115 override getTraceType(): TraceType { 116 return TraceType.SURFACE_FLINGER; 117 } 118 119 override async getEntry(index: number): Promise<HierarchyTreeNode> { 120 let snapshotProto = await Utils.queryEntry( 121 this.traceProcessor, 122 this.getTableName(), 123 this.entryIndexToRowIdMap, 124 index, 125 ); 126 snapshotProto = 127 this.layersSnapshotProtoTransformer.transform(snapshotProto); 128 129 const layerProtos = (await this.querySnapshotLayers(index)).map( 130 (layerProto) => this.layerProtoTransformer.transform(layerProto), 131 ); 132 133 return this.makeHierarchyTree(snapshotProto, layerProtos); 134 } 135 136 override async customQuery<Q extends CustomQueryType>( 137 type: Q, 138 entriesRange: EntriesRange, 139 ): Promise<CustomQueryParserResultTypeMap[Q]> { 140 return new VisitableParserCustomQuery(type) 141 .visit(CustomQueryType.VSYNCID, async () => { 142 return Utils.queryVsyncId( 143 this.traceProcessor, 144 this.getTableName(), 145 this.entryIndexToRowIdMap, 146 entriesRange, 147 ); 148 }) 149 .visit(CustomQueryType.SF_LAYERS_ID_AND_NAME, async () => { 150 const sql = ` 151 SELECT DISTINCT group_concat(value) AS id_and_name FROM ( 152 SELECT sfl.id AS id, args.key AS key, args.display_value AS value 153 FROM surfaceflinger_layer AS sfl 154 INNER JOIN args ON sfl.arg_set_id = args.arg_set_id 155 WHERE (args.key = 'id' OR args.key = 'name') 156 ORDER BY key 157 ) 158 GROUP BY id; 159 `; 160 const queryResult = await this.traceProcessor.query(sql).waitAllRows(); 161 const result: CustomQueryParserResultTypeMap[CustomQueryType.SF_LAYERS_ID_AND_NAME] = 162 []; 163 for (const it = queryResult.iter({}); it.valid(); it.next()) { 164 const idAndName = it.get('id_and_name') as string; 165 const indexDelimiter = idAndName.indexOf(','); 166 assertTrue( 167 indexDelimiter > 0, 168 () => `Unexpected value in query result: ${idAndName}`, 169 ); 170 const id = Number(idAndName.slice(0, indexDelimiter)); 171 const name = idAndName.slice(indexDelimiter + 1); 172 result.push({id, name}); 173 } 174 return result; 175 }) 176 .getResult(); 177 } 178 179 protected override getTableName(): string { 180 return 'surfaceflinger_layers_snapshot'; 181 } 182 183 private makeHierarchyTree( 184 snapshotProto: perfetto.protos.ILayersSnapshotProto, 185 layerProtos: perfetto.protos.ILayerProto[], 186 ): HierarchyTreeNode { 187 const excludesCompositionState = 188 snapshotProto?.excludesCompositionState ?? false; 189 const addExcludesCompositionState = excludesCompositionState 190 ? ParserSfUtils.OPERATIONS.AddExcludesCompositionStateTrue 191 : ParserSfUtils.OPERATIONS.AddExcludesCompositionStateFalse; 192 193 const processed = new Map<number, number>(); 194 195 const layers: PropertiesProvider[] = layerProtos.map( 196 (layer: perfetto.protos.ILayerProto) => { 197 const duplicateCount = processed.get(assertDefined(layer.id)) ?? 0; 198 processed.set(assertDefined(layer.id), duplicateCount + 1); 199 const eagerProperties = ParserSfUtils.makeEagerPropertiesTree( 200 layer, 201 duplicateCount, 202 ); 203 const lazyPropertiesStrategy = 204 ParserSfUtils.makeLayerLazyPropertiesStrategy(layer, duplicateCount); //TODO: fetch these lazily instead 205 206 const layerProps = new PropertiesProviderBuilder() 207 .setEagerProperties(eagerProperties) 208 .setLazyPropertiesStrategy(lazyPropertiesStrategy) 209 .setCommonOperations([ 210 ParserSurfaceFlinger.Operations.SetFormattersLayer, 211 ParserSurfaceFlinger.Operations.TranslateIntDefLayer, 212 ]) 213 .setEagerOperations([ 214 ParserSurfaceFlinger.Operations.AddDefaultsLayerEager, 215 ParserSfUtils.OPERATIONS.AddCompositionType, 216 ParserSfUtils.OPERATIONS.UpdateTransforms, 217 ParserSfUtils.OPERATIONS.AddVerboseFlags, 218 addExcludesCompositionState, 219 ]) 220 .setLazyOperations([ 221 ParserSurfaceFlinger.Operations.AddDefaultsLayerLazy, 222 ]) 223 .build(); 224 return layerProps; 225 }, 226 ); 227 228 const entry = new PropertiesProviderBuilder() 229 .setEagerProperties( 230 ParserSfUtils.makeEntryEagerPropertiesTree(snapshotProto), 231 ) 232 .setLazyPropertiesStrategy( 233 ParserSfUtils.makeEntryLazyPropertiesStrategy(snapshotProto), 234 ) 235 .setCommonOperations([ 236 ParserSfUtils.OPERATIONS.AddDisplayProperties, 237 ParserSurfaceFlinger.Operations.SetFormattersEntry, 238 ParserSurfaceFlinger.Operations.TranslateIntDefEntry, 239 ]) 240 .setEagerOperations([ 241 ParserSurfaceFlinger.Operations.AddDefaultsEntryEager, 242 ]) 243 .setLazyOperations([ 244 ParserSurfaceFlinger.Operations.AddDefaultsEntryLazy, 245 ParserSfUtils.OPERATIONS.AddDisplayProperties, 246 ]) 247 .build(); 248 249 return new HierarchyTreeBuilderSf() 250 .setRoot(entry) 251 .setChildren(layers) 252 .setComputations([ 253 new ZOrderPathsComputation(), 254 new VisibilityPropertiesComputation(), 255 new RectsComputation(), 256 ]) 257 .build(); 258 } 259 260 private async querySnapshotLayers( 261 index: number, 262 ): Promise<perfetto.protos.ILayerProto[]> { 263 const layerIdToBuilder = new Map<number, FakeProtoBuilder>(); 264 const getBuilder = (layerId: number) => { 265 if (!layerIdToBuilder.has(layerId)) { 266 layerIdToBuilder.set(layerId, new FakeProtoBuilder()); 267 } 268 return assertDefined(layerIdToBuilder.get(layerId)); 269 }; 270 271 const sql = ` 272 SELECT 273 sfl.snapshot_id, 274 sfl.id as layer_id, 275 args.key, 276 args.value_type, 277 args.int_value, 278 args.string_value, 279 args.real_value 280 FROM 281 surfaceflinger_layer as sfl 282 INNER JOIN args ON sfl.arg_set_id = args.arg_set_id 283 WHERE snapshot_id = ${this.entryIndexToRowIdMap[index]}; 284 `; 285 const result = await this.traceProcessor.query(sql).waitAllRows(); 286 287 for (const it = result.iter({}); it.valid(); it.next()) { 288 const builder = getBuilder(it.get('layer_id') as number); 289 builder.addArg( 290 it.get('key') as string, 291 it.get('value_type') as string, 292 it.get('int_value') as bigint | undefined, 293 it.get('real_value') as number | undefined, 294 it.get('string_value') as string | undefined, 295 ); 296 } 297 298 const layerProtos: perfetto.protos.ILayerProto[] = []; 299 layerIdToBuilder.forEach((builder) => { 300 layerProtos.push(builder.build()); 301 }); 302 303 return layerProtos; 304 } 305} 306