xref: /aosp_15_r20/development/tools/winscope/src/trace/trace.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker/*
2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project
3*90c8c64dSAndroid Build Coastguard Worker *
4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker *
8*90c8c64dSAndroid Build Coastguard Worker *      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker *
10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker */
16*90c8c64dSAndroid Build Coastguard Worker
17*90c8c64dSAndroid Build Coastguard Workerimport {ArrayUtils} from 'common/array_utils';
18*90c8c64dSAndroid Build Coastguard Workerimport {assertDefined} from 'common/assert_utils';
19*90c8c64dSAndroid Build Coastguard Workerimport {INVALID_TIME_NS, Timestamp} from 'common/time';
20*90c8c64dSAndroid Build Coastguard Workerimport {TimestampUtils} from 'common/timestamp_utils';
21*90c8c64dSAndroid Build Coastguard Workerimport {TracesParserInput} from 'parsers/input/perfetto/traces_parser_input';
22*90c8c64dSAndroid Build Coastguard Workerimport {AbstractParser as AbstractPerfettoParser} from 'parsers/perfetto/abstract_parser';
23*90c8c64dSAndroid Build Coastguard Workerimport {
24*90c8c64dSAndroid Build Coastguard Worker  CustomQueryParamTypeMap,
25*90c8c64dSAndroid Build Coastguard Worker  CustomQueryParserResultTypeMap,
26*90c8c64dSAndroid Build Coastguard Worker  CustomQueryResultTypeMap,
27*90c8c64dSAndroid Build Coastguard Worker  CustomQueryType,
28*90c8c64dSAndroid Build Coastguard Worker  ProcessParserResult,
29*90c8c64dSAndroid Build Coastguard Worker} from './custom_query';
30*90c8c64dSAndroid Build Coastguard Workerimport {FrameMap} from './frame_map';
31*90c8c64dSAndroid Build Coastguard Workerimport {
32*90c8c64dSAndroid Build Coastguard Worker  AbsoluteEntryIndex,
33*90c8c64dSAndroid Build Coastguard Worker  AbsoluteFrameIndex,
34*90c8c64dSAndroid Build Coastguard Worker  EntriesRange,
35*90c8c64dSAndroid Build Coastguard Worker  FramesRange,
36*90c8c64dSAndroid Build Coastguard Worker  RelativeEntryIndex,
37*90c8c64dSAndroid Build Coastguard Worker} from './index_types';
38*90c8c64dSAndroid Build Coastguard Workerimport {Parser} from './parser';
39*90c8c64dSAndroid Build Coastguard Workerimport {TRACE_INFO} from './trace_info';
40*90c8c64dSAndroid Build Coastguard Workerimport {TraceType} from './trace_type';
41*90c8c64dSAndroid Build Coastguard Worker
42*90c8c64dSAndroid Build Coastguard Workerexport {
43*90c8c64dSAndroid Build Coastguard Worker  AbsoluteEntryIndex,
44*90c8c64dSAndroid Build Coastguard Worker  AbsoluteFrameIndex,
45*90c8c64dSAndroid Build Coastguard Worker  EntriesRange,
46*90c8c64dSAndroid Build Coastguard Worker  FramesRange,
47*90c8c64dSAndroid Build Coastguard Worker  RelativeEntryIndex,
48*90c8c64dSAndroid Build Coastguard Worker} from './index_types';
49*90c8c64dSAndroid Build Coastguard Worker
50*90c8c64dSAndroid Build Coastguard Workerexport abstract class TraceEntry<T> {
51*90c8c64dSAndroid Build Coastguard Worker  constructor(
52*90c8c64dSAndroid Build Coastguard Worker    protected readonly fullTrace: Trace<T>,
53*90c8c64dSAndroid Build Coastguard Worker    protected readonly parser: Parser<T>,
54*90c8c64dSAndroid Build Coastguard Worker    protected readonly index: AbsoluteEntryIndex,
55*90c8c64dSAndroid Build Coastguard Worker    protected readonly timestamp: Timestamp,
56*90c8c64dSAndroid Build Coastguard Worker    protected readonly framesRange: FramesRange | undefined,
57*90c8c64dSAndroid Build Coastguard Worker  ) {}
58*90c8c64dSAndroid Build Coastguard Worker
59*90c8c64dSAndroid Build Coastguard Worker  getFullTrace(): Trace<T> {
60*90c8c64dSAndroid Build Coastguard Worker    return this.fullTrace;
61*90c8c64dSAndroid Build Coastguard Worker  }
62*90c8c64dSAndroid Build Coastguard Worker
63*90c8c64dSAndroid Build Coastguard Worker  getIndex(): AbsoluteEntryIndex {
64*90c8c64dSAndroid Build Coastguard Worker    return this.index;
65*90c8c64dSAndroid Build Coastguard Worker  }
66*90c8c64dSAndroid Build Coastguard Worker
67*90c8c64dSAndroid Build Coastguard Worker  getTimestamp(): Timestamp {
68*90c8c64dSAndroid Build Coastguard Worker    return this.timestamp;
69*90c8c64dSAndroid Build Coastguard Worker  }
70*90c8c64dSAndroid Build Coastguard Worker
71*90c8c64dSAndroid Build Coastguard Worker  hasValidTimestamp() {
72*90c8c64dSAndroid Build Coastguard Worker    return this.timestamp.getValueNs() !== INVALID_TIME_NS;
73*90c8c64dSAndroid Build Coastguard Worker  }
74*90c8c64dSAndroid Build Coastguard Worker
75*90c8c64dSAndroid Build Coastguard Worker  getFramesRange(): FramesRange | undefined {
76*90c8c64dSAndroid Build Coastguard Worker    if (!this.fullTrace.hasFrameInfo()) {
77*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
78*90c8c64dSAndroid Build Coastguard Worker        `Trace ${
79*90c8c64dSAndroid Build Coastguard Worker          TRACE_INFO[this.fullTrace.type].name
80*90c8c64dSAndroid Build Coastguard Worker        } can't be accessed in frame domain (no frame info available)`,
81*90c8c64dSAndroid Build Coastguard Worker      );
82*90c8c64dSAndroid Build Coastguard Worker    }
83*90c8c64dSAndroid Build Coastguard Worker    return this.framesRange;
84*90c8c64dSAndroid Build Coastguard Worker  }
85*90c8c64dSAndroid Build Coastguard Worker
86*90c8c64dSAndroid Build Coastguard Worker  abstract getValue(): any;
87*90c8c64dSAndroid Build Coastguard Worker}
88*90c8c64dSAndroid Build Coastguard Worker
89*90c8c64dSAndroid Build Coastguard Workerexport class TraceEntryLazy<T> extends TraceEntry<T> {
90*90c8c64dSAndroid Build Coastguard Worker  constructor(
91*90c8c64dSAndroid Build Coastguard Worker    fullTrace: Trace<T>,
92*90c8c64dSAndroid Build Coastguard Worker    parser: Parser<T>,
93*90c8c64dSAndroid Build Coastguard Worker    index: AbsoluteEntryIndex,
94*90c8c64dSAndroid Build Coastguard Worker    timestamp: Timestamp,
95*90c8c64dSAndroid Build Coastguard Worker    framesRange: FramesRange | undefined,
96*90c8c64dSAndroid Build Coastguard Worker  ) {
97*90c8c64dSAndroid Build Coastguard Worker    super(fullTrace, parser, index, timestamp, framesRange);
98*90c8c64dSAndroid Build Coastguard Worker  }
99*90c8c64dSAndroid Build Coastguard Worker
100*90c8c64dSAndroid Build Coastguard Worker  override async getValue(): Promise<T> {
101*90c8c64dSAndroid Build Coastguard Worker    try {
102*90c8c64dSAndroid Build Coastguard Worker      return await this.parser.getEntry(this.index);
103*90c8c64dSAndroid Build Coastguard Worker    } catch (e) {
104*90c8c64dSAndroid Build Coastguard Worker      this.fullTrace.setCorruptedState(
105*90c8c64dSAndroid Build Coastguard Worker        true,
106*90c8c64dSAndroid Build Coastguard Worker        `Cannot parse entry at index ${this.index}`,
107*90c8c64dSAndroid Build Coastguard Worker      );
108*90c8c64dSAndroid Build Coastguard Worker      throw e;
109*90c8c64dSAndroid Build Coastguard Worker    }
110*90c8c64dSAndroid Build Coastguard Worker  }
111*90c8c64dSAndroid Build Coastguard Worker}
112*90c8c64dSAndroid Build Coastguard Worker
113*90c8c64dSAndroid Build Coastguard Workerexport class TraceEntryEager<T, U> extends TraceEntry<T> {
114*90c8c64dSAndroid Build Coastguard Worker  private readonly value: U;
115*90c8c64dSAndroid Build Coastguard Worker
116*90c8c64dSAndroid Build Coastguard Worker  constructor(
117*90c8c64dSAndroid Build Coastguard Worker    fullTrace: Trace<T>,
118*90c8c64dSAndroid Build Coastguard Worker    parser: Parser<T>,
119*90c8c64dSAndroid Build Coastguard Worker    index: AbsoluteEntryIndex,
120*90c8c64dSAndroid Build Coastguard Worker    timestamp: Timestamp,
121*90c8c64dSAndroid Build Coastguard Worker    framesRange: FramesRange | undefined,
122*90c8c64dSAndroid Build Coastguard Worker    value: U,
123*90c8c64dSAndroid Build Coastguard Worker  ) {
124*90c8c64dSAndroid Build Coastguard Worker    super(fullTrace, parser, index, timestamp, framesRange);
125*90c8c64dSAndroid Build Coastguard Worker    this.value = value;
126*90c8c64dSAndroid Build Coastguard Worker  }
127*90c8c64dSAndroid Build Coastguard Worker
128*90c8c64dSAndroid Build Coastguard Worker  override getValue(): U {
129*90c8c64dSAndroid Build Coastguard Worker    return this.value;
130*90c8c64dSAndroid Build Coastguard Worker  }
131*90c8c64dSAndroid Build Coastguard Worker}
132*90c8c64dSAndroid Build Coastguard Worker
133*90c8c64dSAndroid Build Coastguard Workerexport class Trace<T> {
134*90c8c64dSAndroid Build Coastguard Worker  readonly type: TraceType;
135*90c8c64dSAndroid Build Coastguard Worker  readonly lengthEntries: number;
136*90c8c64dSAndroid Build Coastguard Worker
137*90c8c64dSAndroid Build Coastguard Worker  private readonly parser: Parser<T>;
138*90c8c64dSAndroid Build Coastguard Worker  private readonly descriptors: string[];
139*90c8c64dSAndroid Build Coastguard Worker  private readonly fullTrace: Trace<T>;
140*90c8c64dSAndroid Build Coastguard Worker  private readonly entriesRange: EntriesRange;
141*90c8c64dSAndroid Build Coastguard Worker  private frameMap?: FrameMap;
142*90c8c64dSAndroid Build Coastguard Worker  private framesRange?: FramesRange;
143*90c8c64dSAndroid Build Coastguard Worker  private corruptedState = false;
144*90c8c64dSAndroid Build Coastguard Worker  private corruptedReason: string | undefined;
145*90c8c64dSAndroid Build Coastguard Worker
146*90c8c64dSAndroid Build Coastguard Worker  static fromParser<T>(parser: Parser<T>): Trace<T> {
147*90c8c64dSAndroid Build Coastguard Worker    return new Trace(
148*90c8c64dSAndroid Build Coastguard Worker      parser.getTraceType(),
149*90c8c64dSAndroid Build Coastguard Worker      parser,
150*90c8c64dSAndroid Build Coastguard Worker      parser.getDescriptors(),
151*90c8c64dSAndroid Build Coastguard Worker      undefined,
152*90c8c64dSAndroid Build Coastguard Worker      undefined,
153*90c8c64dSAndroid Build Coastguard Worker    );
154*90c8c64dSAndroid Build Coastguard Worker  }
155*90c8c64dSAndroid Build Coastguard Worker
156*90c8c64dSAndroid Build Coastguard Worker  constructor(
157*90c8c64dSAndroid Build Coastguard Worker    type: TraceType,
158*90c8c64dSAndroid Build Coastguard Worker    parser: Parser<T>,
159*90c8c64dSAndroid Build Coastguard Worker    descriptors: string[],
160*90c8c64dSAndroid Build Coastguard Worker    fullTrace: Trace<T> | undefined,
161*90c8c64dSAndroid Build Coastguard Worker    entriesRange: EntriesRange | undefined,
162*90c8c64dSAndroid Build Coastguard Worker  ) {
163*90c8c64dSAndroid Build Coastguard Worker    this.type = type;
164*90c8c64dSAndroid Build Coastguard Worker    this.parser = parser;
165*90c8c64dSAndroid Build Coastguard Worker    this.descriptors = descriptors;
166*90c8c64dSAndroid Build Coastguard Worker    this.fullTrace = fullTrace ?? this;
167*90c8c64dSAndroid Build Coastguard Worker    this.entriesRange = entriesRange ?? {
168*90c8c64dSAndroid Build Coastguard Worker      start: 0,
169*90c8c64dSAndroid Build Coastguard Worker      end: parser.getLengthEntries(),
170*90c8c64dSAndroid Build Coastguard Worker    };
171*90c8c64dSAndroid Build Coastguard Worker    this.lengthEntries = this.entriesRange.end - this.entriesRange.start;
172*90c8c64dSAndroid Build Coastguard Worker  }
173*90c8c64dSAndroid Build Coastguard Worker
174*90c8c64dSAndroid Build Coastguard Worker  getDescriptors(): string[] {
175*90c8c64dSAndroid Build Coastguard Worker    return this.parser.getDescriptors();
176*90c8c64dSAndroid Build Coastguard Worker  }
177*90c8c64dSAndroid Build Coastguard Worker
178*90c8c64dSAndroid Build Coastguard Worker  getParser(): Parser<T> {
179*90c8c64dSAndroid Build Coastguard Worker    return this.parser;
180*90c8c64dSAndroid Build Coastguard Worker  }
181*90c8c64dSAndroid Build Coastguard Worker
182*90c8c64dSAndroid Build Coastguard Worker  canSearch(): boolean {
183*90c8c64dSAndroid Build Coastguard Worker    return [AbstractPerfettoParser, TracesParserInput].some(
184*90c8c64dSAndroid Build Coastguard Worker      (ParserType) => this.parser instanceof ParserType,
185*90c8c64dSAndroid Build Coastguard Worker    );
186*90c8c64dSAndroid Build Coastguard Worker  }
187*90c8c64dSAndroid Build Coastguard Worker
188*90c8c64dSAndroid Build Coastguard Worker  setFrameInfo(frameMap: FrameMap, framesRange: FramesRange | undefined) {
189*90c8c64dSAndroid Build Coastguard Worker    if (frameMap.lengthEntries !== this.fullTrace.lengthEntries) {
190*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
191*90c8c64dSAndroid Build Coastguard Worker        `Attempted to set a frame map for ${
192*90c8c64dSAndroid Build Coastguard Worker          TRACE_INFO[this.type].name
193*90c8c64dSAndroid Build Coastguard Worker        } trace with incompatible number of entries`,
194*90c8c64dSAndroid Build Coastguard Worker      );
195*90c8c64dSAndroid Build Coastguard Worker    }
196*90c8c64dSAndroid Build Coastguard Worker    this.frameMap = frameMap;
197*90c8c64dSAndroid Build Coastguard Worker    this.framesRange = framesRange;
198*90c8c64dSAndroid Build Coastguard Worker  }
199*90c8c64dSAndroid Build Coastguard Worker
200*90c8c64dSAndroid Build Coastguard Worker  hasFrameInfo(): boolean {
201*90c8c64dSAndroid Build Coastguard Worker    return this.frameMap !== undefined;
202*90c8c64dSAndroid Build Coastguard Worker  }
203*90c8c64dSAndroid Build Coastguard Worker
204*90c8c64dSAndroid Build Coastguard Worker  getEntry(index: RelativeEntryIndex): TraceEntryLazy<T> {
205*90c8c64dSAndroid Build Coastguard Worker    return this.getEntryInternal(index, (index, timestamp, frames) => {
206*90c8c64dSAndroid Build Coastguard Worker      return new TraceEntryLazy<T>(
207*90c8c64dSAndroid Build Coastguard Worker        this.fullTrace,
208*90c8c64dSAndroid Build Coastguard Worker        this.parser,
209*90c8c64dSAndroid Build Coastguard Worker        index,
210*90c8c64dSAndroid Build Coastguard Worker        timestamp,
211*90c8c64dSAndroid Build Coastguard Worker        frames,
212*90c8c64dSAndroid Build Coastguard Worker      );
213*90c8c64dSAndroid Build Coastguard Worker    });
214*90c8c64dSAndroid Build Coastguard Worker  }
215*90c8c64dSAndroid Build Coastguard Worker
216*90c8c64dSAndroid Build Coastguard Worker  async customQuery<Q extends CustomQueryType>(
217*90c8c64dSAndroid Build Coastguard Worker    type: Q,
218*90c8c64dSAndroid Build Coastguard Worker    param?: CustomQueryParamTypeMap[Q],
219*90c8c64dSAndroid Build Coastguard Worker  ): Promise<CustomQueryResultTypeMap<T>[Q]> {
220*90c8c64dSAndroid Build Coastguard Worker    const makeTraceEntry = <U>(
221*90c8c64dSAndroid Build Coastguard Worker      index: RelativeEntryIndex,
222*90c8c64dSAndroid Build Coastguard Worker      value: U,
223*90c8c64dSAndroid Build Coastguard Worker    ): TraceEntryEager<T, U> => {
224*90c8c64dSAndroid Build Coastguard Worker      return this.getEntryInternal(index, (index, timestamp, frames) => {
225*90c8c64dSAndroid Build Coastguard Worker        return new TraceEntryEager<T, U>(
226*90c8c64dSAndroid Build Coastguard Worker          this.fullTrace,
227*90c8c64dSAndroid Build Coastguard Worker          this.parser,
228*90c8c64dSAndroid Build Coastguard Worker          index,
229*90c8c64dSAndroid Build Coastguard Worker          timestamp,
230*90c8c64dSAndroid Build Coastguard Worker          frames,
231*90c8c64dSAndroid Build Coastguard Worker          value,
232*90c8c64dSAndroid Build Coastguard Worker        );
233*90c8c64dSAndroid Build Coastguard Worker      });
234*90c8c64dSAndroid Build Coastguard Worker    };
235*90c8c64dSAndroid Build Coastguard Worker
236*90c8c64dSAndroid Build Coastguard Worker    const processParserResult = ProcessParserResult[type] as (
237*90c8c64dSAndroid Build Coastguard Worker      parserResult: CustomQueryParserResultTypeMap[Q],
238*90c8c64dSAndroid Build Coastguard Worker      make: typeof makeTraceEntry,
239*90c8c64dSAndroid Build Coastguard Worker    ) => CustomQueryResultTypeMap<T>[Q];
240*90c8c64dSAndroid Build Coastguard Worker
241*90c8c64dSAndroid Build Coastguard Worker    const parserResult = (await this.parser.customQuery(
242*90c8c64dSAndroid Build Coastguard Worker      type,
243*90c8c64dSAndroid Build Coastguard Worker      this.entriesRange,
244*90c8c64dSAndroid Build Coastguard Worker      param,
245*90c8c64dSAndroid Build Coastguard Worker    )) as CustomQueryParserResultTypeMap[Q];
246*90c8c64dSAndroid Build Coastguard Worker    const finalResult = processParserResult(parserResult, makeTraceEntry);
247*90c8c64dSAndroid Build Coastguard Worker    return Promise.resolve(finalResult);
248*90c8c64dSAndroid Build Coastguard Worker  }
249*90c8c64dSAndroid Build Coastguard Worker
250*90c8c64dSAndroid Build Coastguard Worker  getFrame(frame: AbsoluteFrameIndex): Trace<T> {
251*90c8c64dSAndroid Build Coastguard Worker    this.checkTraceCanBeAccessedInFrameDomain();
252*90c8c64dSAndroid Build Coastguard Worker    const entries = this.frameMap!.getEntriesRange({
253*90c8c64dSAndroid Build Coastguard Worker      start: frame,
254*90c8c64dSAndroid Build Coastguard Worker      end: frame + 1,
255*90c8c64dSAndroid Build Coastguard Worker    });
256*90c8c64dSAndroid Build Coastguard Worker    return this.createSlice(entries, {start: frame, end: frame + 1});
257*90c8c64dSAndroid Build Coastguard Worker  }
258*90c8c64dSAndroid Build Coastguard Worker
259*90c8c64dSAndroid Build Coastguard Worker  findClosestEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
260*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries === 0) {
261*90c8c64dSAndroid Build Coastguard Worker      return undefined;
262*90c8c64dSAndroid Build Coastguard Worker    }
263*90c8c64dSAndroid Build Coastguard Worker
264*90c8c64dSAndroid Build Coastguard Worker    const entry = this.clampEntryToSliceBounds(
265*90c8c64dSAndroid Build Coastguard Worker      ArrayUtils.binarySearchFirstGreaterOrEqual(
266*90c8c64dSAndroid Build Coastguard Worker        this.getFullTraceTimestamps(),
267*90c8c64dSAndroid Build Coastguard Worker        time,
268*90c8c64dSAndroid Build Coastguard Worker      ),
269*90c8c64dSAndroid Build Coastguard Worker    );
270*90c8c64dSAndroid Build Coastguard Worker    if (entry === undefined || entry === this.entriesRange.end) {
271*90c8c64dSAndroid Build Coastguard Worker      return this.getEntry(this.lengthEntries - 1);
272*90c8c64dSAndroid Build Coastguard Worker    }
273*90c8c64dSAndroid Build Coastguard Worker
274*90c8c64dSAndroid Build Coastguard Worker    if (entry === this.entriesRange.start) {
275*90c8c64dSAndroid Build Coastguard Worker      return this.getEntry(0);
276*90c8c64dSAndroid Build Coastguard Worker    }
277*90c8c64dSAndroid Build Coastguard Worker
278*90c8c64dSAndroid Build Coastguard Worker    const abs = (time: bigint) => (time < 0 ? -time : time);
279*90c8c64dSAndroid Build Coastguard Worker    const timeDiff = abs(
280*90c8c64dSAndroid Build Coastguard Worker      this.getFullTraceTimestamps()[entry].getValueNs() - time.getValueNs(),
281*90c8c64dSAndroid Build Coastguard Worker    );
282*90c8c64dSAndroid Build Coastguard Worker    const prevEntry = entry - 1;
283*90c8c64dSAndroid Build Coastguard Worker    const prevTimeDiff = abs(
284*90c8c64dSAndroid Build Coastguard Worker      this.getFullTraceTimestamps()[prevEntry].getValueNs() - time.getValueNs(),
285*90c8c64dSAndroid Build Coastguard Worker    );
286*90c8c64dSAndroid Build Coastguard Worker    if (prevTimeDiff < timeDiff) {
287*90c8c64dSAndroid Build Coastguard Worker      return this.getEntry(prevEntry - this.entriesRange.start);
288*90c8c64dSAndroid Build Coastguard Worker    }
289*90c8c64dSAndroid Build Coastguard Worker    return this.getEntry(entry - this.entriesRange.start);
290*90c8c64dSAndroid Build Coastguard Worker  }
291*90c8c64dSAndroid Build Coastguard Worker
292*90c8c64dSAndroid Build Coastguard Worker  findFirstGreaterOrEqualEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
293*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries === 0) {
294*90c8c64dSAndroid Build Coastguard Worker      return undefined;
295*90c8c64dSAndroid Build Coastguard Worker    }
296*90c8c64dSAndroid Build Coastguard Worker
297*90c8c64dSAndroid Build Coastguard Worker    const pos = this.clampEntryToSliceBounds(
298*90c8c64dSAndroid Build Coastguard Worker      ArrayUtils.binarySearchFirstGreaterOrEqual(
299*90c8c64dSAndroid Build Coastguard Worker        this.getFullTraceTimestamps(),
300*90c8c64dSAndroid Build Coastguard Worker        time,
301*90c8c64dSAndroid Build Coastguard Worker      ),
302*90c8c64dSAndroid Build Coastguard Worker    );
303*90c8c64dSAndroid Build Coastguard Worker    if (pos === undefined || pos === this.entriesRange.end) {
304*90c8c64dSAndroid Build Coastguard Worker      return undefined;
305*90c8c64dSAndroid Build Coastguard Worker    }
306*90c8c64dSAndroid Build Coastguard Worker
307*90c8c64dSAndroid Build Coastguard Worker    const entry = this.getEntry(pos - this.entriesRange.start);
308*90c8c64dSAndroid Build Coastguard Worker    if (entry.getTimestamp().getValueNs() < time.getValueNs()) {
309*90c8c64dSAndroid Build Coastguard Worker      return undefined;
310*90c8c64dSAndroid Build Coastguard Worker    }
311*90c8c64dSAndroid Build Coastguard Worker
312*90c8c64dSAndroid Build Coastguard Worker    return entry;
313*90c8c64dSAndroid Build Coastguard Worker  }
314*90c8c64dSAndroid Build Coastguard Worker
315*90c8c64dSAndroid Build Coastguard Worker  findFirstGreaterEntry(time: Timestamp): TraceEntryLazy<T> | undefined {
316*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries === 0) {
317*90c8c64dSAndroid Build Coastguard Worker      return undefined;
318*90c8c64dSAndroid Build Coastguard Worker    }
319*90c8c64dSAndroid Build Coastguard Worker
320*90c8c64dSAndroid Build Coastguard Worker    const pos = this.clampEntryToSliceBounds(
321*90c8c64dSAndroid Build Coastguard Worker      ArrayUtils.binarySearchFirstGreater(this.getFullTraceTimestamps(), time),
322*90c8c64dSAndroid Build Coastguard Worker    );
323*90c8c64dSAndroid Build Coastguard Worker    if (pos === undefined || pos === this.entriesRange.end) {
324*90c8c64dSAndroid Build Coastguard Worker      return undefined;
325*90c8c64dSAndroid Build Coastguard Worker    }
326*90c8c64dSAndroid Build Coastguard Worker
327*90c8c64dSAndroid Build Coastguard Worker    const entry = this.getEntry(pos - this.entriesRange.start);
328*90c8c64dSAndroid Build Coastguard Worker    if (entry.getTimestamp().getValueNs() <= time.getValueNs()) {
329*90c8c64dSAndroid Build Coastguard Worker      return undefined;
330*90c8c64dSAndroid Build Coastguard Worker    }
331*90c8c64dSAndroid Build Coastguard Worker
332*90c8c64dSAndroid Build Coastguard Worker    return entry;
333*90c8c64dSAndroid Build Coastguard Worker  }
334*90c8c64dSAndroid Build Coastguard Worker
335*90c8c64dSAndroid Build Coastguard Worker  findLastLowerOrEqualEntry(
336*90c8c64dSAndroid Build Coastguard Worker    timestamp: Timestamp,
337*90c8c64dSAndroid Build Coastguard Worker  ): TraceEntryLazy<T> | undefined {
338*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries === 0) {
339*90c8c64dSAndroid Build Coastguard Worker      return undefined;
340*90c8c64dSAndroid Build Coastguard Worker    }
341*90c8c64dSAndroid Build Coastguard Worker    const firstGreater = this.findFirstGreaterEntry(timestamp);
342*90c8c64dSAndroid Build Coastguard Worker    if (!firstGreater) {
343*90c8c64dSAndroid Build Coastguard Worker      return this.getEntry(this.lengthEntries - 1);
344*90c8c64dSAndroid Build Coastguard Worker    }
345*90c8c64dSAndroid Build Coastguard Worker    if (firstGreater.getIndex() === this.entriesRange.start) {
346*90c8c64dSAndroid Build Coastguard Worker      return undefined;
347*90c8c64dSAndroid Build Coastguard Worker    }
348*90c8c64dSAndroid Build Coastguard Worker    return this.getEntry(firstGreater.getIndex() - this.entriesRange.start - 1);
349*90c8c64dSAndroid Build Coastguard Worker  }
350*90c8c64dSAndroid Build Coastguard Worker
351*90c8c64dSAndroid Build Coastguard Worker  findLastLowerEntry(timestamp: Timestamp): TraceEntryLazy<T> | undefined {
352*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries === 0) {
353*90c8c64dSAndroid Build Coastguard Worker      return undefined;
354*90c8c64dSAndroid Build Coastguard Worker    }
355*90c8c64dSAndroid Build Coastguard Worker    const firstGreaterOrEqual = this.findFirstGreaterOrEqualEntry(timestamp);
356*90c8c64dSAndroid Build Coastguard Worker    if (!firstGreaterOrEqual) {
357*90c8c64dSAndroid Build Coastguard Worker      return this.getEntry(this.lengthEntries - 1);
358*90c8c64dSAndroid Build Coastguard Worker    }
359*90c8c64dSAndroid Build Coastguard Worker    if (firstGreaterOrEqual.getIndex() === this.entriesRange.start) {
360*90c8c64dSAndroid Build Coastguard Worker      return undefined;
361*90c8c64dSAndroid Build Coastguard Worker    }
362*90c8c64dSAndroid Build Coastguard Worker    return this.getEntry(
363*90c8c64dSAndroid Build Coastguard Worker      firstGreaterOrEqual.getIndex() - this.entriesRange.start - 1,
364*90c8c64dSAndroid Build Coastguard Worker    );
365*90c8c64dSAndroid Build Coastguard Worker  }
366*90c8c64dSAndroid Build Coastguard Worker
367*90c8c64dSAndroid Build Coastguard Worker  sliceEntries(start?: RelativeEntryIndex, end?: RelativeEntryIndex): Trace<T> {
368*90c8c64dSAndroid Build Coastguard Worker    const startEntry =
369*90c8c64dSAndroid Build Coastguard Worker      this.clampEntryToSliceBounds(this.convertToAbsoluteEntryIndex(start)) ??
370*90c8c64dSAndroid Build Coastguard Worker      this.entriesRange.start;
371*90c8c64dSAndroid Build Coastguard Worker    const endEntry =
372*90c8c64dSAndroid Build Coastguard Worker      this.clampEntryToSliceBounds(this.convertToAbsoluteEntryIndex(end)) ??
373*90c8c64dSAndroid Build Coastguard Worker      this.entriesRange.end;
374*90c8c64dSAndroid Build Coastguard Worker    const entries: EntriesRange = {
375*90c8c64dSAndroid Build Coastguard Worker      start: startEntry,
376*90c8c64dSAndroid Build Coastguard Worker      end: endEntry,
377*90c8c64dSAndroid Build Coastguard Worker    };
378*90c8c64dSAndroid Build Coastguard Worker    const frames = this.frameMap?.getFramesRange(entries);
379*90c8c64dSAndroid Build Coastguard Worker    return this.createSlice(entries, frames);
380*90c8c64dSAndroid Build Coastguard Worker  }
381*90c8c64dSAndroid Build Coastguard Worker
382*90c8c64dSAndroid Build Coastguard Worker  sliceTime(start?: Timestamp, end?: Timestamp): Trace<T> {
383*90c8c64dSAndroid Build Coastguard Worker    const startEntry =
384*90c8c64dSAndroid Build Coastguard Worker      start === undefined
385*90c8c64dSAndroid Build Coastguard Worker        ? this.entriesRange.start
386*90c8c64dSAndroid Build Coastguard Worker        : this.clampEntryToSliceBounds(
387*90c8c64dSAndroid Build Coastguard Worker            ArrayUtils.binarySearchFirstGreaterOrEqual(
388*90c8c64dSAndroid Build Coastguard Worker              this.getFullTraceTimestamps(),
389*90c8c64dSAndroid Build Coastguard Worker              start,
390*90c8c64dSAndroid Build Coastguard Worker            ),
391*90c8c64dSAndroid Build Coastguard Worker          ) ?? this.entriesRange.end;
392*90c8c64dSAndroid Build Coastguard Worker    const endEntry =
393*90c8c64dSAndroid Build Coastguard Worker      end === undefined
394*90c8c64dSAndroid Build Coastguard Worker        ? this.entriesRange.end
395*90c8c64dSAndroid Build Coastguard Worker        : this.clampEntryToSliceBounds(
396*90c8c64dSAndroid Build Coastguard Worker            ArrayUtils.binarySearchFirstGreaterOrEqual(
397*90c8c64dSAndroid Build Coastguard Worker              this.getFullTraceTimestamps(),
398*90c8c64dSAndroid Build Coastguard Worker              end,
399*90c8c64dSAndroid Build Coastguard Worker            ),
400*90c8c64dSAndroid Build Coastguard Worker          ) ?? this.entriesRange.end;
401*90c8c64dSAndroid Build Coastguard Worker    const entries: EntriesRange = {
402*90c8c64dSAndroid Build Coastguard Worker      start: startEntry,
403*90c8c64dSAndroid Build Coastguard Worker      end: endEntry,
404*90c8c64dSAndroid Build Coastguard Worker    };
405*90c8c64dSAndroid Build Coastguard Worker    const frames = this.frameMap?.getFramesRange(entries);
406*90c8c64dSAndroid Build Coastguard Worker    return this.createSlice(entries, frames);
407*90c8c64dSAndroid Build Coastguard Worker  }
408*90c8c64dSAndroid Build Coastguard Worker
409*90c8c64dSAndroid Build Coastguard Worker  sliceFrames(start?: AbsoluteFrameIndex, end?: AbsoluteFrameIndex): Trace<T> {
410*90c8c64dSAndroid Build Coastguard Worker    this.checkTraceCanBeAccessedInFrameDomain();
411*90c8c64dSAndroid Build Coastguard Worker    if (!this.framesRange) {
412*90c8c64dSAndroid Build Coastguard Worker      return this.createSlice(undefined, undefined);
413*90c8c64dSAndroid Build Coastguard Worker    }
414*90c8c64dSAndroid Build Coastguard Worker    const frames: FramesRange = {
415*90c8c64dSAndroid Build Coastguard Worker      start: this.clampFrameToSliceBounds(start) ?? this.framesRange.start,
416*90c8c64dSAndroid Build Coastguard Worker      end: this.clampFrameToSliceBounds(end) ?? this.framesRange.end,
417*90c8c64dSAndroid Build Coastguard Worker    };
418*90c8c64dSAndroid Build Coastguard Worker    const entries = this.frameMap!.getEntriesRange(frames);
419*90c8c64dSAndroid Build Coastguard Worker    return this.createSlice(entries, frames);
420*90c8c64dSAndroid Build Coastguard Worker  }
421*90c8c64dSAndroid Build Coastguard Worker
422*90c8c64dSAndroid Build Coastguard Worker  forEachEntry(
423*90c8c64dSAndroid Build Coastguard Worker    callback: (pos: TraceEntryLazy<T>, index: RelativeEntryIndex) => void,
424*90c8c64dSAndroid Build Coastguard Worker  ) {
425*90c8c64dSAndroid Build Coastguard Worker    for (let index = 0; index < this.lengthEntries; ++index) {
426*90c8c64dSAndroid Build Coastguard Worker      callback(this.getEntry(index), index);
427*90c8c64dSAndroid Build Coastguard Worker    }
428*90c8c64dSAndroid Build Coastguard Worker  }
429*90c8c64dSAndroid Build Coastguard Worker
430*90c8c64dSAndroid Build Coastguard Worker  mapEntry<U>(
431*90c8c64dSAndroid Build Coastguard Worker    callback: (entry: TraceEntryLazy<T>, index: RelativeEntryIndex) => U,
432*90c8c64dSAndroid Build Coastguard Worker  ): U[] {
433*90c8c64dSAndroid Build Coastguard Worker    const result: U[] = [];
434*90c8c64dSAndroid Build Coastguard Worker    this.forEachEntry((entry, index) => {
435*90c8c64dSAndroid Build Coastguard Worker      result.push(callback(entry, index));
436*90c8c64dSAndroid Build Coastguard Worker    });
437*90c8c64dSAndroid Build Coastguard Worker    return result;
438*90c8c64dSAndroid Build Coastguard Worker  }
439*90c8c64dSAndroid Build Coastguard Worker
440*90c8c64dSAndroid Build Coastguard Worker  forEachTimestamp(
441*90c8c64dSAndroid Build Coastguard Worker    callback: (timestamp: Timestamp, index: RelativeEntryIndex) => void,
442*90c8c64dSAndroid Build Coastguard Worker  ) {
443*90c8c64dSAndroid Build Coastguard Worker    const timestamps = this.getFullTraceTimestamps();
444*90c8c64dSAndroid Build Coastguard Worker    for (let index = 0; index < this.lengthEntries; ++index) {
445*90c8c64dSAndroid Build Coastguard Worker      callback(timestamps[this.entriesRange.start + index], index);
446*90c8c64dSAndroid Build Coastguard Worker    }
447*90c8c64dSAndroid Build Coastguard Worker  }
448*90c8c64dSAndroid Build Coastguard Worker
449*90c8c64dSAndroid Build Coastguard Worker  forEachFrame(callback: (frame: Trace<T>, index: AbsoluteFrameIndex) => void) {
450*90c8c64dSAndroid Build Coastguard Worker    this.checkTraceCanBeAccessedInFrameDomain();
451*90c8c64dSAndroid Build Coastguard Worker    if (!this.framesRange) {
452*90c8c64dSAndroid Build Coastguard Worker      return;
453*90c8c64dSAndroid Build Coastguard Worker    }
454*90c8c64dSAndroid Build Coastguard Worker    for (
455*90c8c64dSAndroid Build Coastguard Worker      let frame = this.framesRange.start;
456*90c8c64dSAndroid Build Coastguard Worker      frame < this.framesRange.end;
457*90c8c64dSAndroid Build Coastguard Worker      ++frame
458*90c8c64dSAndroid Build Coastguard Worker    ) {
459*90c8c64dSAndroid Build Coastguard Worker      callback(this.getFrame(frame), frame);
460*90c8c64dSAndroid Build Coastguard Worker    }
461*90c8c64dSAndroid Build Coastguard Worker  }
462*90c8c64dSAndroid Build Coastguard Worker
463*90c8c64dSAndroid Build Coastguard Worker  mapFrame<U>(
464*90c8c64dSAndroid Build Coastguard Worker    callback: (frame: Trace<T>, index: AbsoluteFrameIndex) => U,
465*90c8c64dSAndroid Build Coastguard Worker  ): U[] {
466*90c8c64dSAndroid Build Coastguard Worker    const result: U[] = [];
467*90c8c64dSAndroid Build Coastguard Worker    this.forEachFrame((traces, index) => {
468*90c8c64dSAndroid Build Coastguard Worker      result.push(callback(traces, index));
469*90c8c64dSAndroid Build Coastguard Worker    });
470*90c8c64dSAndroid Build Coastguard Worker    return result;
471*90c8c64dSAndroid Build Coastguard Worker  }
472*90c8c64dSAndroid Build Coastguard Worker
473*90c8c64dSAndroid Build Coastguard Worker  getFramesRange(): FramesRange | undefined {
474*90c8c64dSAndroid Build Coastguard Worker    this.checkTraceCanBeAccessedInFrameDomain();
475*90c8c64dSAndroid Build Coastguard Worker    return this.framesRange;
476*90c8c64dSAndroid Build Coastguard Worker  }
477*90c8c64dSAndroid Build Coastguard Worker
478*90c8c64dSAndroid Build Coastguard Worker  isDump(): boolean {
479*90c8c64dSAndroid Build Coastguard Worker    return this.lengthEntries === 1;
480*90c8c64dSAndroid Build Coastguard Worker  }
481*90c8c64dSAndroid Build Coastguard Worker
482*90c8c64dSAndroid Build Coastguard Worker  isDumpWithoutTimestamp(): boolean {
483*90c8c64dSAndroid Build Coastguard Worker    return this.isDump() && !this.getEntry(0).hasValidTimestamp();
484*90c8c64dSAndroid Build Coastguard Worker  }
485*90c8c64dSAndroid Build Coastguard Worker
486*90c8c64dSAndroid Build Coastguard Worker  isCorrupted(): boolean {
487*90c8c64dSAndroid Build Coastguard Worker    return this.corruptedState;
488*90c8c64dSAndroid Build Coastguard Worker  }
489*90c8c64dSAndroid Build Coastguard Worker
490*90c8c64dSAndroid Build Coastguard Worker  getCorruptedReason(): string | undefined {
491*90c8c64dSAndroid Build Coastguard Worker    return this.corruptedReason;
492*90c8c64dSAndroid Build Coastguard Worker  }
493*90c8c64dSAndroid Build Coastguard Worker
494*90c8c64dSAndroid Build Coastguard Worker  setCorruptedState(value: boolean, reason?: string) {
495*90c8c64dSAndroid Build Coastguard Worker    this.corruptedState = value;
496*90c8c64dSAndroid Build Coastguard Worker    this.corruptedReason = reason;
497*90c8c64dSAndroid Build Coastguard Worker  }
498*90c8c64dSAndroid Build Coastguard Worker
499*90c8c64dSAndroid Build Coastguard Worker  spansMultipleDates(): boolean {
500*90c8c64dSAndroid Build Coastguard Worker    if (this.lengthEntries > 0) {
501*90c8c64dSAndroid Build Coastguard Worker      let firstTs: string | undefined;
502*90c8c64dSAndroid Build Coastguard Worker      let i = 0;
503*90c8c64dSAndroid Build Coastguard Worker      while (firstTs === undefined && i < this.lengthEntries) {
504*90c8c64dSAndroid Build Coastguard Worker        const entry = this.getEntry(i);
505*90c8c64dSAndroid Build Coastguard Worker        if (entry.hasValidTimestamp()) {
506*90c8c64dSAndroid Build Coastguard Worker          firstTs = entry.getTimestamp().format();
507*90c8c64dSAndroid Build Coastguard Worker          break;
508*90c8c64dSAndroid Build Coastguard Worker        }
509*90c8c64dSAndroid Build Coastguard Worker        i++;
510*90c8c64dSAndroid Build Coastguard Worker      }
511*90c8c64dSAndroid Build Coastguard Worker      const firstDate = TimestampUtils.extractDateFromHumanTimestamp(
512*90c8c64dSAndroid Build Coastguard Worker        assertDefined(firstTs),
513*90c8c64dSAndroid Build Coastguard Worker      );
514*90c8c64dSAndroid Build Coastguard Worker      if (firstDate) {
515*90c8c64dSAndroid Build Coastguard Worker        const lastDate = TimestampUtils.extractDateFromHumanTimestamp(
516*90c8c64dSAndroid Build Coastguard Worker          this.getEntry(this.lengthEntries - 1)
517*90c8c64dSAndroid Build Coastguard Worker            .getTimestamp()
518*90c8c64dSAndroid Build Coastguard Worker            .format(),
519*90c8c64dSAndroid Build Coastguard Worker        );
520*90c8c64dSAndroid Build Coastguard Worker        return firstDate !== lastDate;
521*90c8c64dSAndroid Build Coastguard Worker      }
522*90c8c64dSAndroid Build Coastguard Worker    }
523*90c8c64dSAndroid Build Coastguard Worker    return false;
524*90c8c64dSAndroid Build Coastguard Worker  }
525*90c8c64dSAndroid Build Coastguard Worker
526*90c8c64dSAndroid Build Coastguard Worker  private getEntryInternal<
527*90c8c64dSAndroid Build Coastguard Worker    EntryType extends TraceEntryLazy<T> | TraceEntryEager<T, any>,
528*90c8c64dSAndroid Build Coastguard Worker  >(
529*90c8c64dSAndroid Build Coastguard Worker    index: RelativeEntryIndex,
530*90c8c64dSAndroid Build Coastguard Worker    makeEntry: (
531*90c8c64dSAndroid Build Coastguard Worker      absoluteIndex: AbsoluteEntryIndex,
532*90c8c64dSAndroid Build Coastguard Worker      timestamp: Timestamp,
533*90c8c64dSAndroid Build Coastguard Worker      frames: FramesRange | undefined,
534*90c8c64dSAndroid Build Coastguard Worker    ) => EntryType,
535*90c8c64dSAndroid Build Coastguard Worker  ): EntryType {
536*90c8c64dSAndroid Build Coastguard Worker    const absoluteIndex = this.convertToAbsoluteEntryIndex(
537*90c8c64dSAndroid Build Coastguard Worker      index,
538*90c8c64dSAndroid Build Coastguard Worker    ) as AbsoluteEntryIndex;
539*90c8c64dSAndroid Build Coastguard Worker    if (
540*90c8c64dSAndroid Build Coastguard Worker      absoluteIndex < this.entriesRange.start ||
541*90c8c64dSAndroid Build Coastguard Worker      absoluteIndex >= this.entriesRange.end
542*90c8c64dSAndroid Build Coastguard Worker    ) {
543*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
544*90c8c64dSAndroid Build Coastguard Worker        `${
545*90c8c64dSAndroid Build Coastguard Worker          TRACE_INFO[this.type].name
546*90c8c64dSAndroid Build Coastguard Worker        } trace entry's index out of bounds. Input relative index: ${index}. Slice length: ${
547*90c8c64dSAndroid Build Coastguard Worker          this.lengthEntries
548*90c8c64dSAndroid Build Coastguard Worker        }.`,
549*90c8c64dSAndroid Build Coastguard Worker      );
550*90c8c64dSAndroid Build Coastguard Worker    }
551*90c8c64dSAndroid Build Coastguard Worker    const timestamp = this.getFullTraceTimestamps()[absoluteIndex];
552*90c8c64dSAndroid Build Coastguard Worker    const frames = this.clampFramesRangeToSliceBounds(
553*90c8c64dSAndroid Build Coastguard Worker      this.frameMap?.getFramesRange({
554*90c8c64dSAndroid Build Coastguard Worker        start: absoluteIndex,
555*90c8c64dSAndroid Build Coastguard Worker        end: absoluteIndex + 1,
556*90c8c64dSAndroid Build Coastguard Worker      }),
557*90c8c64dSAndroid Build Coastguard Worker    );
558*90c8c64dSAndroid Build Coastguard Worker    return makeEntry(absoluteIndex, timestamp, frames);
559*90c8c64dSAndroid Build Coastguard Worker  }
560*90c8c64dSAndroid Build Coastguard Worker
561*90c8c64dSAndroid Build Coastguard Worker  private getFullTraceTimestamps(): Timestamp[] {
562*90c8c64dSAndroid Build Coastguard Worker    const timestamps = this.parser.getTimestamps();
563*90c8c64dSAndroid Build Coastguard Worker    if (!timestamps) {
564*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
565*90c8c64dSAndroid Build Coastguard Worker        `Timestamps expected to be available for this ${
566*90c8c64dSAndroid Build Coastguard Worker          TRACE_INFO[this.type].name
567*90c8c64dSAndroid Build Coastguard Worker        } trace.`,
568*90c8c64dSAndroid Build Coastguard Worker      );
569*90c8c64dSAndroid Build Coastguard Worker    }
570*90c8c64dSAndroid Build Coastguard Worker    return timestamps;
571*90c8c64dSAndroid Build Coastguard Worker  }
572*90c8c64dSAndroid Build Coastguard Worker
573*90c8c64dSAndroid Build Coastguard Worker  private convertToAbsoluteEntryIndex(
574*90c8c64dSAndroid Build Coastguard Worker    index: RelativeEntryIndex | undefined,
575*90c8c64dSAndroid Build Coastguard Worker  ): AbsoluteEntryIndex | undefined {
576*90c8c64dSAndroid Build Coastguard Worker    if (index === undefined) {
577*90c8c64dSAndroid Build Coastguard Worker      return undefined;
578*90c8c64dSAndroid Build Coastguard Worker    }
579*90c8c64dSAndroid Build Coastguard Worker    if (index < 0) {
580*90c8c64dSAndroid Build Coastguard Worker      return this.entriesRange.end + index;
581*90c8c64dSAndroid Build Coastguard Worker    }
582*90c8c64dSAndroid Build Coastguard Worker    return this.entriesRange.start + index;
583*90c8c64dSAndroid Build Coastguard Worker  }
584*90c8c64dSAndroid Build Coastguard Worker
585*90c8c64dSAndroid Build Coastguard Worker  private createSlice(
586*90c8c64dSAndroid Build Coastguard Worker    entries: EntriesRange | undefined,
587*90c8c64dSAndroid Build Coastguard Worker    frames: FramesRange | undefined,
588*90c8c64dSAndroid Build Coastguard Worker  ): Trace<T> {
589*90c8c64dSAndroid Build Coastguard Worker    entries = this.clampEntriesRangeToSliceBounds(entries);
590*90c8c64dSAndroid Build Coastguard Worker    frames = this.clampFramesRangeToSliceBounds(frames);
591*90c8c64dSAndroid Build Coastguard Worker
592*90c8c64dSAndroid Build Coastguard Worker    if (entries === undefined || entries.start >= entries.end) {
593*90c8c64dSAndroid Build Coastguard Worker      entries = {
594*90c8c64dSAndroid Build Coastguard Worker        start: this.entriesRange.end,
595*90c8c64dSAndroid Build Coastguard Worker        end: this.entriesRange.end,
596*90c8c64dSAndroid Build Coastguard Worker      };
597*90c8c64dSAndroid Build Coastguard Worker    }
598*90c8c64dSAndroid Build Coastguard Worker
599*90c8c64dSAndroid Build Coastguard Worker    const slice = new Trace<T>(
600*90c8c64dSAndroid Build Coastguard Worker      this.type,
601*90c8c64dSAndroid Build Coastguard Worker      this.parser,
602*90c8c64dSAndroid Build Coastguard Worker      this.descriptors,
603*90c8c64dSAndroid Build Coastguard Worker      this.fullTrace,
604*90c8c64dSAndroid Build Coastguard Worker      entries,
605*90c8c64dSAndroid Build Coastguard Worker    );
606*90c8c64dSAndroid Build Coastguard Worker
607*90c8c64dSAndroid Build Coastguard Worker    if (this.frameMap) {
608*90c8c64dSAndroid Build Coastguard Worker      slice.setFrameInfo(this.frameMap, frames);
609*90c8c64dSAndroid Build Coastguard Worker    }
610*90c8c64dSAndroid Build Coastguard Worker
611*90c8c64dSAndroid Build Coastguard Worker    return slice;
612*90c8c64dSAndroid Build Coastguard Worker  }
613*90c8c64dSAndroid Build Coastguard Worker
614*90c8c64dSAndroid Build Coastguard Worker  private clampEntriesRangeToSliceBounds(
615*90c8c64dSAndroid Build Coastguard Worker    entries: EntriesRange | undefined,
616*90c8c64dSAndroid Build Coastguard Worker  ): EntriesRange | undefined {
617*90c8c64dSAndroid Build Coastguard Worker    if (entries === undefined) {
618*90c8c64dSAndroid Build Coastguard Worker      return undefined;
619*90c8c64dSAndroid Build Coastguard Worker    }
620*90c8c64dSAndroid Build Coastguard Worker    return {
621*90c8c64dSAndroid Build Coastguard Worker      start: this.clampEntryToSliceBounds(entries.start) as AbsoluteEntryIndex,
622*90c8c64dSAndroid Build Coastguard Worker      end: this.clampEntryToSliceBounds(entries.end) as AbsoluteEntryIndex,
623*90c8c64dSAndroid Build Coastguard Worker    };
624*90c8c64dSAndroid Build Coastguard Worker  }
625*90c8c64dSAndroid Build Coastguard Worker
626*90c8c64dSAndroid Build Coastguard Worker  private clampFramesRangeToSliceBounds(
627*90c8c64dSAndroid Build Coastguard Worker    frames: FramesRange | undefined,
628*90c8c64dSAndroid Build Coastguard Worker  ): FramesRange | undefined {
629*90c8c64dSAndroid Build Coastguard Worker    if (frames === undefined) {
630*90c8c64dSAndroid Build Coastguard Worker      return undefined;
631*90c8c64dSAndroid Build Coastguard Worker    }
632*90c8c64dSAndroid Build Coastguard Worker    return {
633*90c8c64dSAndroid Build Coastguard Worker      start: this.clampFrameToSliceBounds(frames.start) as AbsoluteFrameIndex,
634*90c8c64dSAndroid Build Coastguard Worker      end: this.clampFrameToSliceBounds(frames.end) as AbsoluteFrameIndex,
635*90c8c64dSAndroid Build Coastguard Worker    };
636*90c8c64dSAndroid Build Coastguard Worker  }
637*90c8c64dSAndroid Build Coastguard Worker
638*90c8c64dSAndroid Build Coastguard Worker  private clampEntryToSliceBounds(
639*90c8c64dSAndroid Build Coastguard Worker    entry: AbsoluteEntryIndex | undefined,
640*90c8c64dSAndroid Build Coastguard Worker  ): AbsoluteEntryIndex | undefined {
641*90c8c64dSAndroid Build Coastguard Worker    if (entry === undefined) {
642*90c8c64dSAndroid Build Coastguard Worker      return undefined;
643*90c8c64dSAndroid Build Coastguard Worker    }
644*90c8c64dSAndroid Build Coastguard Worker    return Math.min(
645*90c8c64dSAndroid Build Coastguard Worker      Math.max(entry, this.entriesRange.start),
646*90c8c64dSAndroid Build Coastguard Worker      this.entriesRange.end,
647*90c8c64dSAndroid Build Coastguard Worker    );
648*90c8c64dSAndroid Build Coastguard Worker  }
649*90c8c64dSAndroid Build Coastguard Worker
650*90c8c64dSAndroid Build Coastguard Worker  private clampFrameToSliceBounds(
651*90c8c64dSAndroid Build Coastguard Worker    frame: AbsoluteFrameIndex | undefined,
652*90c8c64dSAndroid Build Coastguard Worker  ): AbsoluteFrameIndex | undefined {
653*90c8c64dSAndroid Build Coastguard Worker    if (!this.framesRange || frame === undefined) {
654*90c8c64dSAndroid Build Coastguard Worker      return undefined;
655*90c8c64dSAndroid Build Coastguard Worker    }
656*90c8c64dSAndroid Build Coastguard Worker
657*90c8c64dSAndroid Build Coastguard Worker    if (frame < 0) {
658*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
659*90c8c64dSAndroid Build Coastguard Worker        `Absolute frame index cannot be negative. Found '${frame}'`,
660*90c8c64dSAndroid Build Coastguard Worker      );
661*90c8c64dSAndroid Build Coastguard Worker    }
662*90c8c64dSAndroid Build Coastguard Worker
663*90c8c64dSAndroid Build Coastguard Worker    return Math.min(
664*90c8c64dSAndroid Build Coastguard Worker      Math.max(frame, this.framesRange.start),
665*90c8c64dSAndroid Build Coastguard Worker      this.framesRange.end,
666*90c8c64dSAndroid Build Coastguard Worker    );
667*90c8c64dSAndroid Build Coastguard Worker  }
668*90c8c64dSAndroid Build Coastguard Worker
669*90c8c64dSAndroid Build Coastguard Worker  private checkTraceCanBeAccessedInFrameDomain() {
670*90c8c64dSAndroid Build Coastguard Worker    if (!this.frameMap) {
671*90c8c64dSAndroid Build Coastguard Worker      throw new Error(
672*90c8c64dSAndroid Build Coastguard Worker        `Trace ${
673*90c8c64dSAndroid Build Coastguard Worker          TRACE_INFO[this.type].name
674*90c8c64dSAndroid Build Coastguard Worker        } can't be accessed in frame domain (no frame mapping available)`,
675*90c8c64dSAndroid Build Coastguard Worker      );
676*90c8c64dSAndroid Build Coastguard Worker    }
677*90c8c64dSAndroid Build Coastguard Worker  }
678*90c8c64dSAndroid Build Coastguard Worker}
679