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 {DENYLIST_PROPERTIES} from 'parsers/surface_flinger/denylist_properties'; 24import {EAGER_PROPERTIES} from 'parsers/surface_flinger/eager_properties'; 25import {EntryHierarchyTreeFactory} from 'parsers/surface_flinger/entry_hierarchy_tree_factory'; 26import {TamperedMessageType} from 'parsers/tampered_message_type'; 27import root from 'protos/surfaceflinger/udc/json'; 28import {android} from 'protos/surfaceflinger/udc/static'; 29import { 30 CustomQueryParserResultTypeMap, 31 CustomQueryType, 32 VisitableParserCustomQuery, 33} from 'trace/custom_query'; 34import {EntriesRange} from 'trace/trace'; 35import {TraceType} from 'trace/trace_type'; 36import {EnumFormatter, LAYER_ID_FORMATTER} from 'trace/tree_node/formatters'; 37import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 38 39class ParserSurfaceFlinger extends AbstractParser<HierarchyTreeNode> { 40 private static readonly MAGIC_NUMBER = [ 41 0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45, 42 ]; // .LYRTRACE 43 private static readonly CUSTOM_FORMATTERS = new Map([ 44 ['cropLayerId', LAYER_ID_FORMATTER], 45 ['zOrderRelativeOf', LAYER_ID_FORMATTER], 46 [ 47 'hwcCompositionType', 48 new EnumFormatter(android.surfaceflinger.HwcCompositionType), 49 ], 50 ]); 51 52 private static readonly LayersTraceFileProto = TamperedMessageType.tamper( 53 root.lookupType('android.surfaceflinger.LayersTraceFileProto'), 54 ); 55 private static readonly entryField = 56 ParserSurfaceFlinger.LayersTraceFileProto.fields['entry']; 57 private static readonly layerField = assertDefined( 58 ParserSurfaceFlinger.entryField.tamperedMessageType?.fields['layers'] 59 .tamperedMessageType, 60 ).fields['layers']; 61 62 static readonly Operations = { 63 SetFormattersLayer: new SetFormatters( 64 ParserSurfaceFlinger.layerField, 65 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 66 ), 67 TranslateIntDefLayer: new TranslateIntDef(ParserSurfaceFlinger.layerField), 68 AddDefaultsLayerEager: new AddDefaults( 69 ParserSurfaceFlinger.layerField, 70 EAGER_PROPERTIES, 71 ), 72 AddDefaultsLayerLazy: new AddDefaults( 73 ParserSurfaceFlinger.layerField, 74 undefined, 75 EAGER_PROPERTIES.concat(DENYLIST_PROPERTIES), 76 ), 77 SetFormattersEntry: new SetFormatters( 78 ParserSurfaceFlinger.entryField, 79 ParserSurfaceFlinger.CUSTOM_FORMATTERS, 80 ), 81 TranslateIntDefEntry: new TranslateIntDef(ParserSurfaceFlinger.entryField), 82 AddDefaultsEntryEager: new AddDefaults(ParserSurfaceFlinger.entryField, [ 83 'displays', 84 ]), 85 AddDefaultsEntryLazy: new AddDefaults( 86 ParserSurfaceFlinger.entryField, 87 undefined, 88 DENYLIST_PROPERTIES, 89 ), 90 }; 91 92 private readonly factory = new EntryHierarchyTreeFactory(); 93 private realToMonotonicTimeOffsetNs: bigint | undefined; 94 private isDump = false; 95 96 override getTraceType(): TraceType { 97 return TraceType.SURFACE_FLINGER; 98 } 99 100 override getMagicNumber(): number[] { 101 return ParserSurfaceFlinger.MAGIC_NUMBER; 102 } 103 104 override getRealToBootTimeOffsetNs(): bigint | undefined { 105 return undefined; 106 } 107 108 override getRealToMonotonicTimeOffsetNs(): bigint | undefined { 109 return this.realToMonotonicTimeOffsetNs; 110 } 111 112 override decodeTrace( 113 buffer: Uint8Array, 114 ): android.surfaceflinger.ILayersTraceProto[] { 115 const decoded = ParserSurfaceFlinger.LayersTraceFileProto.decode( 116 buffer, 117 ) as android.surfaceflinger.ILayersTraceFileProto; 118 const timeOffset = BigInt( 119 decoded.realToElapsedTimeOffsetNanos?.toString() ?? '0', 120 ); 121 this.realToMonotonicTimeOffsetNs = 122 timeOffset !== 0n ? timeOffset : undefined; 123 this.isDump = 124 decoded.entry?.length === 1 && 125 !Object.prototype.hasOwnProperty.call( 126 decoded.entry[0], 127 'elapsedRealtimeNanos', 128 ); 129 return decoded.entry ?? []; 130 } 131 132 protected override getTimestamp( 133 entry: android.surfaceflinger.ILayersTraceProto, 134 ): Timestamp { 135 if (this.isDump) { 136 return this.timestampConverter.makeZeroTimestamp(); 137 } 138 return this.timestampConverter.makeTimestampFromMonotonicNs( 139 BigInt(assertDefined(entry.elapsedRealtimeNanos).toString()), 140 ); 141 } 142 143 override processDecodedEntry( 144 index: number, 145 entry: android.surfaceflinger.ILayersTraceProto, 146 ): HierarchyTreeNode { 147 return this.factory.makeEntryHierarchyTree( 148 entry, 149 assertDefined(entry.layers?.layers), 150 ParserSurfaceFlinger, 151 ); 152 } 153 154 override customQuery<Q extends CustomQueryType>( 155 type: Q, 156 entriesRange: EntriesRange, 157 ): Promise<CustomQueryParserResultTypeMap[Q]> { 158 return new VisitableParserCustomQuery(type) 159 .visit(CustomQueryType.VSYNCID, () => { 160 const result = this.decodedEntries 161 .slice(entriesRange.start, entriesRange.end) 162 .map((entry) => { 163 return BigInt(entry.vsyncId.toString()); // convert Long to bigint 164 }); 165 return Promise.resolve(result); 166 }) 167 .visit(CustomQueryType.SF_LAYERS_ID_AND_NAME, () => { 168 const result: Array<{id: number; name: string}> = []; 169 this.decodedEntries 170 .slice(entriesRange.start, entriesRange.end) 171 .forEach((entry: android.surfaceflinger.ILayersTraceProto) => { 172 entry.layers?.layers?.forEach( 173 (layer: android.surfaceflinger.ILayerProto) => { 174 result.push({ 175 id: assertDefined(layer.id), 176 name: assertDefined(layer.name), 177 }); 178 }, 179 ); 180 }); 181 return Promise.resolve(result); 182 }) 183 .getResult(); 184 } 185} 186 187export {ParserSurfaceFlinger}; 188