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 {assertDefined} from 'common/assert_utils'; 18import {INVALID_TIME_NS, Timestamp} from 'common/time'; 19import {TimestampConverter} from 'common/timestamp_converter'; 20import {UserNotifier} from 'common/user_notifier'; 21import {Analytics} from 'logging/analytics'; 22import {TraceSearchQueryFailed} from 'messaging/user_warnings'; 23import {CoarseVersion} from 'trace/coarse_version'; 24import { 25 CustomQueryParserResultTypeMap, 26 CustomQueryType, 27} from 'trace/custom_query'; 28import {AbsoluteEntryIndex, EntriesRange} from 'trace/index_types'; 29import {Parser} from 'trace/parser'; 30import {TraceType} from 'trace/trace_type'; 31import {QueryResult} from 'trace_processor/query_result'; 32import {TraceProcessorFactory} from 'trace_processor/trace_processor_factory'; 33 34export class ParserSearch implements Parser<QueryResult> { 35 private queryResult?: QueryResult; 36 private timestamps: Timestamp[] = []; 37 38 constructor( 39 private readonly query: string, 40 private timestampConverter: TimestampConverter, 41 ) {} 42 43 getCoarseVersion(): CoarseVersion { 44 return CoarseVersion.LATEST; 45 } 46 47 getTraceType(): TraceType { 48 return TraceType.SEARCH; 49 } 50 51 getLengthEntries(): number { 52 const queryResult = this.validateQueryResult(); 53 const numRows = queryResult.numRows(); 54 if (numRows === 0 || !this.hasTimestamps()) { 55 return 1; 56 } 57 return numRows; 58 } 59 60 getTimestamps(): Timestamp[] { 61 return this.timestamps; 62 } 63 64 async getEntry(index: AbsoluteEntryIndex): Promise<QueryResult> { 65 return this.validateQueryResult(); 66 } 67 68 customQuery<Q extends CustomQueryType>( 69 type: Q, 70 entriesRange: EntriesRange, 71 ): Promise<CustomQueryParserResultTypeMap[Q]> { 72 throw new Error('not implemented'); 73 } 74 75 getDescriptors(): string[] { 76 return [this.query]; 77 } 78 79 getRealToMonotonicTimeOffsetNs(): bigint | undefined { 80 return undefined; 81 } 82 83 getRealToBootTimeOffsetNs(): bigint | undefined { 84 return undefined; 85 } 86 87 createTimestamps(): void { 88 throw new Error('not implemented'); 89 } 90 91 async parse() { 92 const tp = await TraceProcessorFactory.getSingleInstance(); 93 try { 94 this.queryResult = await tp.query(this.query).waitAllRows(); 95 if (this.hasTimestamps() && this.queryResult.numRows() > 0) { 96 for (const it = this.queryResult.iter({}); it.valid(); it.next()) { 97 const ns = it.get('ts') as bigint; 98 if (ns === INVALID_TIME_NS) { 99 this.timestamps.push(this.timestampConverter.makeZeroTimestamp()); 100 } else { 101 this.timestamps.push( 102 this.timestampConverter.makeTimestampFromBootTimeNs(ns), 103 ); 104 } 105 } 106 } else { 107 this.timestamps.push(this.timestampConverter.makeZeroTimestamp()); 108 } 109 } catch (e) { 110 Analytics.TraceSearch.logQueryFailure(); 111 UserNotifier.add( 112 new TraceSearchQueryFailed((e as Error).message), 113 ).notify(); 114 throw e; 115 } 116 } 117 118 private hasTimestamps(): boolean { 119 return this.queryResult?.columns().includes('ts') ?? false; 120 } 121 122 private validateQueryResult(): QueryResult { 123 return assertDefined( 124 this.queryResult, 125 () => 'Attempted to retrieve query result before running search query.', 126 ); 127 } 128} 129