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 {Timestamp} from 'common/time'; 18import {AbsoluteFrameIndex} from './index_types'; 19import {Trace} from './trace'; 20import {TraceEntryTypeMap, TraceType} from './trace_type'; 21 22export class Traces { 23 private traces = new Set<Trace<{}>>(); 24 25 addTrace(trace: Trace<{}>) { 26 this.traces.add(trace); 27 } 28 29 getTrace<T extends TraceType>( 30 type: T, 31 ): Trace<TraceEntryTypeMap[T]> | undefined { 32 let longestTraceWithMatchingType: Trace<{}> | undefined; 33 this.traces.forEach((trace) => { 34 if (trace.type !== type) { 35 return; 36 } 37 38 // If multiple traces match the target type, return the longest one. 39 // Assuming that covering this scenario is good enough (hopefully): 40 // - Two traces have the same type because one is a dump (e.g. SF trace + dump) 41 // - A viewer needs to access another trace (e.g. IME viewers accesses SF trace) 42 if ( 43 !longestTraceWithMatchingType || 44 trace.lengthEntries > longestTraceWithMatchingType.lengthEntries 45 ) { 46 longestTraceWithMatchingType = trace; 47 } 48 }); 49 return longestTraceWithMatchingType as 50 | Trace<TraceEntryTypeMap[T]> 51 | undefined; 52 } 53 54 getTraces<T extends TraceType>(type: T): Array<Trace<TraceEntryTypeMap[T]>> { 55 return Array.from(this.traces).filter( 56 (trace) => trace.type === type, 57 ) as Array<Trace<TraceEntryTypeMap[T]>>; 58 } 59 60 deleteTrace(trace: Trace<{}>) { 61 this.traces.delete(trace); 62 } 63 64 deleteTracesByType(type: TraceType) { 65 this.traces.forEach((trace) => { 66 if (trace.type !== type) { 67 return; 68 } 69 this.traces.delete(trace); 70 }); 71 } 72 73 hasTrace(trace: Trace<{}>) { 74 return this.traces.has(trace); 75 } 76 77 sliceTime(start?: Timestamp, end?: Timestamp): Traces { 78 const slice = new Traces(); 79 this.traces.forEach((trace) => { 80 slice.addTrace(trace.sliceTime(start, end)); 81 }); 82 return slice; 83 } 84 85 sliceFrames(start?: AbsoluteFrameIndex, end?: AbsoluteFrameIndex): Traces { 86 const slice = new Traces(); 87 this.traces.forEach((trace) => { 88 slice.addTrace(trace.sliceFrames(start, end)); 89 }); 90 return slice; 91 } 92 93 forEachTrace(callback: (trace: Trace<{}>, type: TraceType) => void): void { 94 this.traces.forEach((trace, type) => { 95 callback(trace, trace.type); 96 }); 97 } 98 99 mapTrace<T>(callback: (trace: Trace<{}>, type: TraceType) => T): T[] { 100 const result: T[] = []; 101 this.forEachTrace((trace, type) => { 102 result.push(callback(trace, type)); 103 }); 104 return result; 105 } 106 107 forEachFrame( 108 callback: (traces: Traces, index: AbsoluteFrameIndex) => void, 109 ): void { 110 let startFrameIndex: AbsoluteFrameIndex = Number.MAX_VALUE; 111 let endFrameIndex: AbsoluteFrameIndex = Number.MIN_VALUE; 112 113 this.traces.forEach((trace) => { 114 const framesRange = trace.getFramesRange(); 115 if (framesRange && framesRange.start < framesRange.end) { 116 startFrameIndex = Math.min(startFrameIndex, framesRange.start); 117 endFrameIndex = Math.max(endFrameIndex, framesRange.end); 118 } 119 }); 120 121 for (let i = startFrameIndex; i < endFrameIndex; ++i) { 122 callback(this.sliceFrames(i, i + 1), i); 123 } 124 } 125 126 mapFrame<T>(callback: (traces: Traces, index: AbsoluteFrameIndex) => T): T[] { 127 const result: T[] = []; 128 this.forEachFrame((traces, index) => { 129 result.push(callback(traces, index)); 130 }); 131 return result; 132 } 133 134 getSize(): number { 135 return this.traces.size; 136 } 137 138 [Symbol.iterator]() { 139 return this.traces.values(); 140 } 141} 142