xref: /aosp_15_r20/development/tools/winscope/src/parsers/perfetto/abstract_parser.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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 {assertDefined, assertTrue} from 'common/assert_utils';
18import {INVALID_TIME_NS, Timestamp} from 'common/time';
19import {ParserTimestampConverter} from 'common/timestamp_converter';
20import {CoarseVersion} from 'trace/coarse_version';
21import {
22  CustomQueryParamTypeMap,
23  CustomQueryParserResultTypeMap,
24  CustomQueryType,
25} from 'trace/custom_query';
26import {AbsoluteEntryIndex, EntriesRange} from 'trace/index_types';
27import {Parser} from 'trace/parser';
28import {TraceFile} from 'trace/trace_file';
29import {TRACE_INFO} from 'trace/trace_info';
30import {TraceType} from 'trace/trace_type';
31import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy';
32
33export abstract class AbstractParser<T> implements Parser<T> {
34  protected traceProcessor: WasmEngineProxy;
35  protected realToBootTimeOffsetNs?: bigint;
36  protected timestampConverter: ParserTimestampConverter;
37  protected entryIndexToRowIdMap: number[] = [];
38
39  private lengthEntries = 0;
40  private traceFile: TraceFile;
41  private bootTimeTimestampsNs: Array<bigint> = [];
42  private timestamps: Timestamp[] | undefined;
43
44  constructor(
45    traceFile: TraceFile,
46    traceProcessor: WasmEngineProxy,
47    timestampConverter: ParserTimestampConverter,
48  ) {
49    this.traceFile = traceFile;
50    this.traceProcessor = traceProcessor;
51    this.timestampConverter = timestampConverter;
52  }
53
54  async parse() {
55    const module = this.getStdLibModuleName();
56    if (module) {
57      await this.traceProcessor.query(`INCLUDE PERFETTO MODULE ${module};`);
58    }
59
60    this.entryIndexToRowIdMap = await this.buildEntryIndexToRowIdMap();
61    const rowBootTimeTimestampsNs = await this.queryRowBootTimeTimestamps();
62    this.bootTimeTimestampsNs = this.entryIndexToRowIdMap.map(
63      (rowId) => rowBootTimeTimestampsNs[rowId],
64    );
65    this.lengthEntries = this.bootTimeTimestampsNs.length;
66    assertTrue(
67      this.lengthEntries > 0,
68      () =>
69        `Perfetto trace has no ${TRACE_INFO[this.getTraceType()].name} entries`,
70    );
71
72    let lastNonZeroTimestamp: bigint | undefined;
73    for (let i = this.bootTimeTimestampsNs.length - 1; i >= 0; i--) {
74      if (this.bootTimeTimestampsNs[i] !== 0n) {
75        lastNonZeroTimestamp = this.bootTimeTimestampsNs[i];
76        break;
77      }
78    }
79    this.realToBootTimeOffsetNs = await this.queryRealToBootTimeOffset(
80      assertDefined(lastNonZeroTimestamp),
81    );
82  }
83
84  createTimestamps() {
85    this.timestamps = this.bootTimeTimestampsNs.map((ns) => {
86      if (ns === INVALID_TIME_NS) {
87        return this.timestampConverter.makeZeroTimestamp();
88      }
89      return this.timestampConverter.makeTimestampFromBootTimeNs(ns);
90    });
91  }
92
93  getLengthEntries(): number {
94    return this.lengthEntries;
95  }
96
97  getTimestamps(): Timestamp[] | undefined {
98    return this.timestamps;
99  }
100
101  getCoarseVersion(): CoarseVersion {
102    return CoarseVersion.LATEST;
103  }
104
105  customQuery<Q extends CustomQueryType>(
106    type: Q,
107    entriesRange: EntriesRange,
108    param?: CustomQueryParamTypeMap[Q],
109  ): Promise<CustomQueryParserResultTypeMap[Q]> {
110    throw new Error('Not implemented');
111  }
112
113  getDescriptors(): string[] {
114    return [this.traceFile.getDescriptor()];
115  }
116
117  getRealToMonotonicTimeOffsetNs(): bigint | undefined {
118    return undefined;
119  }
120
121  getRealToBootTimeOffsetNs(): bigint | undefined {
122    return this.realToBootTimeOffsetNs;
123  }
124
125  protected async buildEntryIndexToRowIdMap(): Promise<AbsoluteEntryIndex[]> {
126    const sqlRowIdAndTimestamp = `
127     SELECT DISTINCT tbl.id AS id, tbl.ts
128     FROM ${this.getTableName()} AS tbl
129     ORDER BY tbl.ts;
130   `;
131    const result = await this.traceProcessor
132      .query(sqlRowIdAndTimestamp)
133      .waitAllRows();
134    const entryIndexToRowId: AbsoluteEntryIndex[] = [];
135    for (const it = result.iter({}); it.valid(); it.next()) {
136      const rowId = Number(it.get('id') as bigint);
137      entryIndexToRowId.push(rowId);
138    }
139    return entryIndexToRowId;
140  }
141
142  async queryRowBootTimeTimestamps(): Promise<Array<bigint>> {
143    const sql = `SELECT ts FROM ${this.getTableName()} ORDER BY id;`;
144    const result = await this.traceProcessor.query(sql).waitAllRows();
145    const timestamps: Array<bigint> = [];
146    for (const it = result.iter({}); it.valid(); it.next()) {
147      timestamps.push(it.get('ts') as bigint);
148    }
149    return timestamps;
150  }
151
152  // Query the real-to-boot time offset at the specified time
153  // (timestamp parameter).
154  // The timestamp parameter must be a non-zero timestamp queried/provided by TP,
155  // otherwise the TO_REALTIME() SQL function might return invalid values.
156  private async queryRealToBootTimeOffset(bootTimeNs: bigint): Promise<bigint> {
157    const sql = `
158      SELECT TO_REALTIME(${bootTimeNs}) as realtime;
159    `;
160
161    const result = await this.traceProcessor.query(sql).waitAllRows();
162    assertTrue(
163      result.numRows() === 1,
164      () => 'Failed to query realtime timestamp',
165    );
166
167    const real = result.iter({}).get('realtime') as bigint;
168    return real - bootTimeNs;
169  }
170
171  protected getStdLibModuleName(): string | undefined {
172    return undefined;
173  }
174
175  protected abstract getTableName(): string;
176  abstract getEntry(index: AbsoluteEntryIndex): Promise<T>;
177  abstract getTraceType(): TraceType;
178}
179