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