1/* 2 * Copyright (C) 2022 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} from 'common/assert_utils'; 18import {Timestamp} from 'common/time'; 19import {AbstractParser} from 'parsers/legacy/abstract_parser'; 20import {AddDefaults} from 'parsers/operations/add_defaults'; 21import {SetFormatters} from 'parsers/operations/set_formatters'; 22import {TranslateIntDef} from 'parsers/operations/translate_intdef'; 23import {RectsComputation} from 'parsers/surface_flinger/computations/rects_computation'; 24import {VisibilityPropertiesComputation} from 'parsers/surface_flinger/computations/visibility_properties_computation'; 25import {ZOrderPathsComputation} from 'parsers/surface_flinger/computations/z_order_paths_computation'; 26import {HierarchyTreeBuilderSf} from 'parsers/surface_flinger/hierarchy_tree_builder_sf'; 27import {ParserSfUtils} from 'parsers/surface_flinger/parser_surface_flinger_utils'; 28import {TamperedMessageType} from 'parsers/tampered_message_type'; 29import root from 'protos/surfaceflinger/udc/json'; 30import {android} from 'protos/surfaceflinger/udc/static'; 31import { 32 CustomQueryParserResultTypeMap, 33 CustomQueryType, 34 VisitableParserCustomQuery, 35} from 'trace/custom_query'; 36import {EntriesRange} from 'trace/trace'; 37import {TraceType} from 'trace/trace_type'; 38import {EnumFormatter, LAYER_ID_FORMATTER} from 'trace/tree_node/formatters'; 39import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 40import {PropertiesProvider} from 'trace/tree_node/properties_provider'; 41import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder'; 42 43class ParserSurfaceFlinger extends AbstractParser<HierarchyTreeNode> { 44 private static readonly MAGIC_NUMBER = [ 45 0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45, 46 ]; // .LYRTRACE 47 private static readonly CUSTOM_FORMATTERS = new Map([ 48 ['cropLayerId', LAYER_ID_FORMATTER], 49 ['zOrderRelativeOf', LAYER_ID_FORMATTER], 50 [ 51 'hwcCompositionType', 52 new EnumFormatter(android.surfaceflinger.HwcCompositionType), 53 ], 54 ]); 55 56 private static readonly LayersTraceFileProto = TamperedMessageType.tamper( 57 root.lookupType('android.surfaceflinger.LayersTraceFileProto'), 58 ); 59 private static readonly entryField = 60 ParserSurfaceFlinger.LayersTraceFileProto.fields['entry']; 61 private static readonly layerField = assertDefined( 62 ParserSurfaceFlinger.entryField.tamperedMessageType?.fields['layers'] 63 .tamperedMessageType, 64 ).fields['layers']; 65 66 private static readonly Operations = { 67 SetFormattersLayer: new SetFormatters( 68 ParserSurfaceFlinger.layerField, 69 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 70 ), 71 TranslateIntDefLayer: new TranslateIntDef(ParserSurfaceFlinger.layerField), 72 AddDefaultsLayerEager: new AddDefaults( 73 ParserSurfaceFlinger.layerField, 74 ParserSfUtils.EAGER_PROPERTIES, 75 ), 76 AddDefaultsLayerLazy: new AddDefaults( 77 ParserSurfaceFlinger.layerField, 78 undefined, 79 ParserSfUtils.EAGER_PROPERTIES.concat(ParserSfUtils.DENYLIST_PROPERTIES), 80 ), 81 SetFormattersEntry: new SetFormatters( 82 ParserSurfaceFlinger.entryField, 83 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 84 ), 85 TranslateIntDefEntry: new TranslateIntDef(ParserSurfaceFlinger.entryField), 86 AddDefaultsEntryEager: new AddDefaults(ParserSurfaceFlinger.entryField, [ 87 'displays', 88 ]), 89 AddDefaultsEntryLazy: new AddDefaults( 90 ParserSurfaceFlinger.entryField, 91 undefined, 92 ParserSfUtils.DENYLIST_PROPERTIES, 93 ), 94 }; 95 96 private realToMonotonicTimeOffsetNs: bigint | undefined; 97 private isDump = false; 98 99 override getTraceType(): TraceType { 100 return TraceType.SURFACE_FLINGER; 101 } 102 103 override getMagicNumber(): number[] { 104 return ParserSurfaceFlinger.MAGIC_NUMBER; 105 } 106 107 override getRealToBootTimeOffsetNs(): bigint | undefined { 108 return undefined; 109 } 110 111 override getRealToMonotonicTimeOffsetNs(): bigint | undefined { 112 return this.realToMonotonicTimeOffsetNs; 113 } 114 115 override decodeTrace( 116 buffer: Uint8Array, 117 ): android.surfaceflinger.ILayersTraceProto[] { 118 const decoded = ParserSurfaceFlinger.LayersTraceFileProto.decode( 119 buffer, 120 ) as android.surfaceflinger.ILayersTraceFileProto; 121 const timeOffset = BigInt( 122 decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0', 123 ); 124 this.realToMonotonicTimeOffsetNs = 125 timeOffset !== 0n ? timeOffset : undefined; 126 this.isDump = 127 decoded.entry?.length === 1 && 128 !Object.prototype.hasOwnProperty.call( 129 decoded.entry[0], 130 'elapsedRealtimeNanos', 131 ); 132 return decoded.entry ?? []; 133 } 134 135 protected override getTimestamp( 136 entry: android.surfaceflinger.ILayersTraceProto, 137 ): Timestamp { 138 if (this.isDump) { 139 return this.timestampConverter.makeZeroTimestamp(); 140 } 141 return this.timestampConverter.makeTimestampFromMonotonicNs( 142 BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()), 143 ); 144 } 145 146 override processDecodedEntry( 147 index: number, 148 entry: android.surfaceflinger.ILayersTraceProto, 149 ): HierarchyTreeNode { 150 return this.makeHierarchyTree(entry); 151 } 152 153 override customQuery<Q extends CustomQueryType>( 154 type: Q, 155 entriesRange: EntriesRange, 156 ): Promise<CustomQueryParserResultTypeMap[Q]> { 157 return new VisitableParserCustomQuery(type) 158 .visit(CustomQueryType.VSYNCID, () => { 159 const result = this.decodedEntries 160 .slice(entriesRange.start, entriesRange.end) 161 .map((entry) => { 162 return BigInt(entry.vsyncId.toString()); // convert Long to bigint 163 }); 164 return Promise.resolve(result); 165 }) 166 .visit(CustomQueryType.SF_LAYERS_ID_AND_NAME, () => { 167 const result: Array<{id: number; name: string}> = []; 168 this.decodedEntries 169 .slice(entriesRange.start, entriesRange.end) 170 .forEach((entry: android.surfaceflinger.ILayersTraceProto) => { 171 entry.layers?.layers?.forEach( 172 (layer: android.surfaceflinger.ILayerProto) => { 173 result.push({ 174 id: assertDefined(layer.id), 175 name: assertDefined(layer.name), 176 }); 177 }, 178 ); 179 }); 180 return Promise.resolve(result); 181 }) 182 .getResult(); 183 } 184 185 private makeHierarchyTree( 186 entryProto: android.surfaceflinger.ILayersTraceProto, 187 ): HierarchyTreeNode { 188 const excludesCompositionState = 189 entryProto?.excludesCompositionState ?? false; 190 const addExcludesCompositionState = excludesCompositionState 191 ? ParserSfUtils.OPERATIONS.AddExcludesCompositionStateTrue 192 : ParserSfUtils.OPERATIONS.AddExcludesCompositionStateFalse; 193 194 const processed = new Map<number, number>(); 195 196 const layers: PropertiesProvider[] = assertDefined( 197 entryProto.layers?.layers, 198 ).map((layer: android.surfaceflinger.ILayerProto) => { 199 const duplicateCount = processed.get(assertDefined(layer.id)) ?? 0; 200 processed.set(assertDefined(layer.id), duplicateCount + 1); 201 const eagerProperties = ParserSfUtils.makeEagerPropertiesTree( 202 layer, 203 duplicateCount, 204 ); 205 const lazyPropertiesStrategy = 206 ParserSfUtils.makeLayerLazyPropertiesStrategy(layer, duplicateCount); 207 208 const layerProps = new PropertiesProviderBuilder() 209 .setEagerProperties(eagerProperties) 210 .setLazyPropertiesStrategy(lazyPropertiesStrategy) 211 .setCommonOperations([ 212 ParserSurfaceFlinger.Operations.SetFormattersLayer, 213 ParserSurfaceFlinger.Operations.TranslateIntDefLayer, 214 ]) 215 .setEagerOperations([ 216 ParserSurfaceFlinger.Operations.AddDefaultsLayerEager, 217 ParserSfUtils.OPERATIONS.AddCompositionType, 218 ParserSfUtils.OPERATIONS.UpdateTransforms, 219 ParserSfUtils.OPERATIONS.AddVerboseFlags, 220 addExcludesCompositionState, 221 ]) 222 .setLazyOperations([ 223 ParserSurfaceFlinger.Operations.AddDefaultsLayerLazy, 224 ]) 225 .build(); 226 return layerProps; 227 }); 228 229 const entry = new PropertiesProviderBuilder() 230 .setEagerProperties( 231 ParserSfUtils.makeEntryEagerPropertiesTree(entryProto), 232 ) 233 .setLazyPropertiesStrategy( 234 ParserSfUtils.makeEntryLazyPropertiesStrategy(entryProto), 235 ) 236 .setCommonOperations([ 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 261export {ParserSurfaceFlinger}; 262