• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {FunctionUtils} from 'common/function_utils';
18import {MakeTimestampStrategyType} from 'common/time/time';
19import {Trace, TraceEntry} from 'trace/trace';
20import {
21  ColumnType,
22  QueryResult,
23  RowIteratorBase,
24} from 'trace_processor/query_result';
25import {
26  AbstractLogViewerPresenter,
27  NotifyLogViewCallbackType,
28} from 'viewers/common/abstract_log_viewer_presenter';
29import {LogPresenter} from 'viewers/common/log_presenter';
30import {
31  LogEntry,
32  LogField,
33  LogFieldValue,
34  LogHeader,
35} from 'viewers/common/ui_data_log';
36import {SearchResult} from './ui_data';
37
38export class SearchResultPresenter extends AbstractLogViewerPresenter<
39  SearchResult,
40  QueryResult
41> {
42  protected override logPresenter = new LogPresenter<LogEntry>();
43  constructor(
44    trace: Trace<QueryResult>,
45    notifyViewCallback: NotifyLogViewCallbackType<SearchResult>,
46    private readonly makeTimestampStrategy: MakeTimestampStrategyType,
47    private readonly queryResult?: QueryResult,
48  ) {
49    super(trace, notifyViewCallback, new SearchResult([], []));
50  }
51
52  onDestroy() {
53    // until presenter is garbage collected it may still receive events
54    // so we must make sure it can no longer affect ui data
55    this.notifyViewChanged = FunctionUtils.DO_NOTHING;
56  }
57
58  protected override makeHeaders(): LogHeader[] {
59    return (
60      this.queryResult?.columns().map((colName) => {
61        return new LogHeader({name: colName, cssClass: 'search-result'});
62      }) ?? []
63    );
64  }
65
66  protected override async makeUiDataEntries(
67    headers: LogHeader[],
68  ): Promise<LogEntry[]> {
69    if (!this.queryResult || this.trace.lengthEntries === 0) {
70      return [];
71    }
72    const entry = this.trace.getEntry(0);
73    const hasTimestamps = !this.trace.isDumpWithoutTimestamp();
74    const entries: LogEntry[] = [];
75    let i = 0;
76    for (const it = this.queryResult.iter({}); it.valid(); it.next()) {
77      entries.push(
78        this.makeLogEntry(
79          headers,
80          it,
81          i,
82          hasTimestamps ? this.trace.getEntry(i) : entry,
83        ),
84      );
85      i++;
86    }
87    return entries;
88  }
89
90  private makeLogEntry(
91    headers: LogHeader[],
92    it: RowIteratorBase,
93    i: number,
94    traceEntry: TraceEntry<QueryResult>,
95  ): LogEntry {
96    const fields: LogField[] = [];
97    for (const header of headers) {
98      const value = it.get(header.spec.name);
99      let fieldValue = this.tryMakeEntryTsFieldValue(header, i);
100      if (fieldValue === undefined) {
101        fieldValue = this.tryMakeTsFieldValue(headers, it, header, value);
102      }
103      if (fieldValue === undefined) {
104        fieldValue = this.convertToLogFieldValue(value);
105      }
106      fields.push({
107        spec: header.spec,
108        value: fieldValue,
109      });
110    }
111    return {
112      traceEntry,
113      fields,
114      propertiesTree: undefined,
115    };
116  }
117
118  private tryMakeEntryTsFieldValue(
119    header: LogHeader,
120    entryIndex: number,
121  ): LogFieldValue | undefined {
122    if (header.spec.name === 'ts') {
123      const entry = this.trace.getEntry(entryIndex);
124      if (entry.hasValidTimestamp()) {
125        return entry.getTimestamp();
126      }
127    }
128    return undefined;
129  }
130
131  private tryMakeTsFieldValue(
132    headers: LogHeader[],
133    it: RowIteratorBase,
134    header: LogHeader,
135    value: ColumnType | null,
136  ): LogFieldValue | undefined {
137    if (
138      header.spec.name === 'value' &&
139      typeof value === 'string' &&
140      Number(value)
141    ) {
142      const propertyHeader = headers.find((h) => h.spec.name === 'property');
143      if (propertyHeader) {
144        const property = it.get(propertyHeader.spec.name);
145        if (typeof property === 'string' && property.endsWith('time_ns')) {
146          return this.makeTimestampStrategy(BigInt(value));
147        }
148      }
149    }
150    return undefined;
151  }
152
153  private convertToLogFieldValue(value: ColumnType | null): LogFieldValue {
154    let displayValue: LogFieldValue;
155    if (value === null) {
156      displayValue = 'NULL';
157    } else if (typeof value === 'bigint') {
158      displayValue = Number(value);
159    } else if (value instanceof Uint8Array) {
160      displayValue = '[]';
161    } else {
162      displayValue = value;
163    }
164    return displayValue;
165  }
166}
167