1/*
2 * Copyright (C) 2024 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 {AbstractParser} from 'parsers/perfetto/abstract_parser';
18import {LogMessage} from 'parsers/protolog/log_message';
19import {ParserProtologUtils} from 'parsers/protolog/parser_protolog_utils';
20import {TraceType} from 'trace/trace_type';
21import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
22
23class PerfettoLogMessageTableRow {
24  message = '<NO_MESSAGE>';
25  tag = '<NO_TAG>';
26  level = '<NO_LEVEL>';
27  location = '<NO_LOC>';
28  timestamp: bigint = 0n;
29
30  constructor(timestamp: bigint, tag: string, level: string, message: string) {
31    this.timestamp = timestamp ?? this.timestamp;
32    this.tag = tag ?? this.tag;
33    this.level = level ?? this.level;
34    this.message = message ?? this.message;
35  }
36}
37
38export class ParserProtolog extends AbstractParser<PropertyTreeNode> {
39  override getTraceType(): TraceType {
40    return TraceType.PROTO_LOG;
41  }
42
43  override async getEntry(index: number): Promise<PropertyTreeNode> {
44    const protologEntry = await this.queryEntry(index);
45    const logMessage: LogMessage = {
46      text: protologEntry.message,
47      tag: protologEntry.tag,
48      level: protologEntry.level,
49      at: protologEntry.location,
50      timestamp: protologEntry.timestamp,
51    };
52
53    return ParserProtologUtils.makeMessagePropertiesTree(
54      logMessage,
55      this.timestampConverter,
56      this.getRealToMonotonicTimeOffsetNs() !== undefined,
57    );
58  }
59
60  protected override getTableName(): string {
61    return 'protolog';
62  }
63
64  private async queryEntry(index: number): Promise<PerfettoLogMessageTableRow> {
65    const sql = `
66      SELECT
67        ts, tag, level, message
68      FROM
69        protolog
70      WHERE protolog.id = ${this.entryIndexToRowIdMap[index]};
71    `;
72    const result = await this.traceProcessor.query(sql).waitAllRows();
73
74    if (result.numRows() !== 1) {
75      throw new Error(
76        `Expected exactly 1 protolog message with id ${index} but got ${result.numRows()}`,
77      );
78    }
79
80    const entry = result.iter({});
81
82    return new PerfettoLogMessageTableRow(
83      entry.get('ts') as bigint,
84      entry.get('tag') as string,
85      entry.get('level') as string,
86      entry.get('message') as string,
87    );
88  }
89}
90