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