• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {ArrayUtils} from 'common/array_utils';
18import {Parser} from 'trace/parser';
19import {Timestamp, TimestampType} from 'trace/timestamp';
20import {TraceFile} from 'trace/trace_file';
21import {TraceType} from 'trace/trace_type';
22
23abstract class AbstractParser<T extends object = object> implements Parser<T> {
24  protected traceFile: TraceFile;
25  protected decodedEntries: any[] = [];
26  private timestamps: Map<TimestampType, Timestamp[]> = new Map<TimestampType, Timestamp[]>();
27
28  protected constructor(trace: TraceFile) {
29    this.traceFile = trace;
30  }
31
32  async parse() {
33    const traceBuffer = new Uint8Array(await this.traceFile.file.arrayBuffer());
34
35    const magicNumber = this.getMagicNumber();
36    if (magicNumber !== undefined) {
37      const bufferContainsMagicNumber = ArrayUtils.equal(
38        magicNumber,
39        traceBuffer.slice(0, magicNumber.length)
40      );
41      if (!bufferContainsMagicNumber) {
42        throw TypeError("buffer doesn't contain expected magic number");
43      }
44    }
45
46    this.decodedEntries = this.decodeTrace(traceBuffer).map((it) => this.addDefaultProtoFields(it));
47
48    for (const type of [TimestampType.ELAPSED, TimestampType.REAL]) {
49      const timestamps: Timestamp[] = [];
50      let areTimestampsValid = true;
51
52      for (const entry of this.decodedEntries) {
53        const timestamp = this.getTimestamp(type, entry);
54        if (timestamp === undefined) {
55          areTimestampsValid = false;
56          break;
57        }
58        timestamps.push(timestamp);
59      }
60
61      if (areTimestampsValid) {
62        this.timestamps.set(type, timestamps);
63      }
64    }
65  }
66
67  abstract getTraceType(): TraceType;
68
69  getDescriptors(): string[] {
70    return [this.traceFile.getDescriptor()];
71  }
72
73  getLengthEntries(): number {
74    return this.decodedEntries.length;
75  }
76
77  getTimestamps(type: TimestampType): undefined | Timestamp[] {
78    return this.timestamps.get(type);
79  }
80
81  getEntry(index: number, timestampType: TimestampType): Promise<T> {
82    const entry = this.processDecodedEntry(index, timestampType, this.decodedEntries[index]);
83    return Promise.resolve(entry);
84  }
85
86  // Add default values to the proto objects.
87  private addDefaultProtoFields(protoObj: any): any {
88    if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
89      return protoObj;
90    }
91
92    for (const fieldName in protoObj.$type.fields) {
93      if (Object.prototype.hasOwnProperty.call(protoObj.$type.fields, fieldName)) {
94        const fieldProperties = protoObj.$type.fields[fieldName];
95        const field = protoObj[fieldName];
96
97        if (Array.isArray(field)) {
98          field.forEach((item, _) => {
99            this.addDefaultProtoFields(item);
100          });
101          continue;
102        }
103
104        if (!field) {
105          protoObj[fieldName] = fieldProperties.defaultValue;
106        }
107
108        if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
109          protoObj[fieldName] =
110            fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
111          continue;
112        }
113        this.addDefaultProtoFields(protoObj[fieldName]);
114      }
115    }
116
117    return protoObj;
118  }
119
120  protected abstract getMagicNumber(): undefined | number[];
121  protected abstract decodeTrace(trace: Uint8Array): any[];
122  protected abstract getTimestamp(type: TimestampType, decodedEntry: any): undefined | Timestamp;
123  protected abstract processDecodedEntry(
124    index: number,
125    timestampType: TimestampType,
126    decodedEntry: any
127  ): any;
128}
129
130export {AbstractParser};
131