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 {FrameMap} from 'trace/frame_map'; 18import {FrameMapBuilder} from 'trace/frame_map_builder'; 19import {AbsoluteEntryIndex, AbsoluteFrameIndex, EntriesRange} from 'trace/index_types'; 20import {Parser} from 'trace/parser'; 21import {ParserMock} from 'trace/parser_mock'; 22import {Timestamp, TimestampType} from 'trace/timestamp'; 23import {Trace} from 'trace/trace'; 24import {TraceType} from 'trace/trace_type'; 25 26export class TraceBuilder<T> { 27 private type = TraceType.SURFACE_FLINGER; 28 private parser?: Parser<T>; 29 private entries?: T[]; 30 private timestamps?: Timestamp[]; 31 private timestampType = TimestampType.REAL; 32 private frameMap?: FrameMap; 33 private frameMapBuilder?: FrameMapBuilder; 34 private descriptors: string[] = []; 35 36 setType(type: TraceType): TraceBuilder<T> { 37 this.type = type; 38 return this; 39 } 40 41 setParser(parser: Parser<T>): TraceBuilder<T> { 42 this.parser = parser; 43 return this; 44 } 45 46 setEntries(entries: T[]): TraceBuilder<T> { 47 this.entries = entries; 48 return this; 49 } 50 51 setTimestamps(timestamps: Timestamp[]): TraceBuilder<T> { 52 this.timestamps = timestamps; 53 return this; 54 } 55 56 setTimestampType(type: TimestampType): TraceBuilder<T> { 57 this.timestampType = type; 58 return this; 59 } 60 61 setFrameMap(frameMap?: FrameMap): TraceBuilder<T> { 62 this.frameMap = frameMap; 63 return this; 64 } 65 66 setFrame(entry: AbsoluteEntryIndex, frame: AbsoluteFrameIndex) { 67 if (!this.entries) { 68 throw new Error(`Can't set frames before specifying the entries`); 69 } 70 if (!this.frameMapBuilder) { 71 this.frameMapBuilder = new FrameMapBuilder(this.entries.length, 1000); 72 } 73 this.frameMapBuilder.setFrames(entry, {start: frame, end: frame + 1}); 74 return this; 75 } 76 77 setDescriptors(descriptors: string[]): TraceBuilder<T> { 78 this.descriptors = descriptors; 79 return this; 80 } 81 82 build(): Trace<T> { 83 if (!this.parser) { 84 this.parser = this.createParser(); 85 } 86 87 const entriesRange: EntriesRange = { 88 start: 0, 89 end: this.parser.getLengthEntries(), 90 }; 91 const trace = Trace.newInitializedTrace<T>( 92 this.type, 93 this.parser, 94 this.descriptors, 95 this.timestampType, 96 entriesRange 97 ); 98 99 const frameMap = this.getFrameMap(); 100 if (frameMap) { 101 trace.setFrameInfo(frameMap, frameMap.getFullTraceFramesRange()); 102 } 103 104 return trace; 105 } 106 107 private createParser(): Parser<T> { 108 if (!this.timestamps && !this.entries) { 109 throw new Error(`Either the timestamps or the entries should be specified`); 110 } 111 112 if (!this.timestamps) { 113 this.timestamps = this.createTimestamps(this.entries as T[]); 114 } 115 116 if (!this.entries) { 117 this.entries = this.createEntries(this.timestamps); 118 } 119 120 if (this.entries.length !== this.timestamps.length) { 121 throw new Error('Entries and timestamps arrays must have the same length'); 122 } 123 124 return new ParserMock(this.timestamps, this.entries); 125 } 126 127 private createTimestamps(entries: T[]): Timestamp[] { 128 const timestamps = new Array<Timestamp>(); 129 for (let i = 0; i < entries.length; ++i) { 130 timestamps[i] = new Timestamp(TimestampType.REAL, BigInt(i)); 131 } 132 return timestamps; 133 } 134 135 private createEntries(timestamps: Timestamp[]): T[] { 136 const entries = new Array<T>(); 137 for (let i = 0; i < timestamps.length; ++i) { 138 entries.push(`entry-${i}` as unknown as T); 139 } 140 return entries; 141 } 142 143 private getFrameMap(): FrameMap | undefined { 144 if (this.frameMap && this.frameMapBuilder) { 145 throw new Error( 146 `Cannot set a full frame map as well as individual entry's frames. Pick one of the two options.` 147 ); 148 } 149 if (this.frameMap) { 150 return this.frameMap; 151 } 152 if (this.frameMapBuilder) { 153 return this.frameMapBuilder.build(); 154 } 155 return undefined; 156 } 157} 158