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 hasTrace(trace: Trace<{}>) { 65 return this.traces.has(trace); 66 } 67 68 sliceTime(start?: Timestamp, end?: Timestamp): Traces { 69 const slice = new Traces(); 70 this.traces.forEach((trace) => { 71 slice.addTrace(trace.sliceTime(start, end)); 72 }); 73 return slice; 74 } 75 76 sliceFrames(start?: AbsoluteFrameIndex, end?: AbsoluteFrameIndex): Traces { 77 const slice = new Traces(); 78 this.traces.forEach((trace) => { 79 slice.addTrace(trace.sliceFrames(start, end)); 80 }); 81 return slice; 82 } 83 84 forEachTrace(callback: (trace: Trace<{}>, type: TraceType) => void): void { 85 this.traces.forEach((trace, type) => { 86 callback(trace, trace.type); 87 }); 88 } 89 90 mapTrace<T>(callback: (trace: Trace<{}>, type: TraceType) => T): T[] { 91 const result: T[] = []; 92 this.forEachTrace((trace, type) => { 93 result.push(callback(trace, type)); 94 }); 95 return result; 96 } 97 98 forEachFrame( 99 callback: (traces: Traces, index: AbsoluteFrameIndex) => void, 100 ): void { 101 let startFrameIndex: AbsoluteFrameIndex = Number.MAX_VALUE; 102 let endFrameIndex: AbsoluteFrameIndex = Number.MIN_VALUE; 103 104 this.traces.forEach((trace) => { 105 const framesRange = trace.getFramesRange(); 106 if (framesRange && framesRange.start < framesRange.end) { 107 startFrameIndex = Math.min(startFrameIndex, framesRange.start); 108 endFrameIndex = Math.max(endFrameIndex, framesRange.end); 109 } 110 }); 111 112 for (let i = startFrameIndex; i < endFrameIndex; ++i) { 113 callback(this.sliceFrames(i, i + 1), i); 114 } 115 } 116 117 mapFrame<T>(callback: (traces: Traces, index: AbsoluteFrameIndex) => T): T[] { 118 const result: T[] = []; 119 this.forEachFrame((traces, index) => { 120 result.push(callback(traces, index)); 121 }); 122 return result; 123 } 124 125 getSize(): number { 126 return this.traces.size; 127 } 128 129 [Symbol.iterator]() { 130 return this.traces.values(); 131 } 132} 133