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 {UserNotifier} from 'common/user_notifier'; 19import {MissingVsyncId} from 'messaging/user_warnings'; 20import {AbsoluteEntryIndex, EntriesRange} from 'trace/trace'; 21import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy'; 22import {FakeProto, FakeProtoBuilder} from './fake_proto_builder'; 23 24export class Utils { 25 static async queryEntry( 26 traceProcessor: WasmEngineProxy, 27 tableName: string, 28 entryIndexToRowIdMap: number[], 29 entryIndex: AbsoluteEntryIndex, 30 ): Promise<FakeProto> { 31 const rowId = entryIndexToRowIdMap[entryIndex]; 32 const sql = ` 33 SELECT 34 tbl.id, 35 args.key, 36 args.value_type, 37 args.int_value, 38 args.string_value, 39 args.real_value 40 FROM ${tableName} AS tbl 41 INNER JOIN args ON tbl.arg_set_id = args.arg_set_id 42 WHERE tbl.id = ${rowId}; 43 `; 44 const result = await traceProcessor.query(sql).waitAllRows(); 45 46 const builder = new FakeProtoBuilder(); 47 for (const it = result.iter({}); it.valid(); it.next()) { 48 builder.addArg( 49 it.get('key') as string, 50 it.get('value_type') as string, 51 it.get('int_value') as bigint | undefined, 52 it.get('real_value') as number | undefined, 53 it.get('string_value') as string | undefined, 54 ); 55 } 56 return builder.build(); 57 } 58 59 static async queryVsyncId( 60 traceProcessor: WasmEngineProxy, 61 tableName: string, 62 entryIndexToRowIdMap: number[], 63 entriesRange: EntriesRange, 64 createVsyncIdQuery: ( 65 tableName: string, 66 minRowId: number, 67 maxRowId: number, 68 ) => string = Utils.createDefaultVsyncIdQuery, 69 ): Promise<Array<bigint>> { 70 let minRowId = Number.MAX_VALUE; 71 let maxRowId = Number.MIN_VALUE; 72 for ( 73 let entryIndex = entriesRange.start; 74 entryIndex < entriesRange.end; 75 ++entryIndex 76 ) { 77 const rowId = entryIndexToRowIdMap[entryIndex]; 78 minRowId = Math.min(minRowId, rowId); 79 maxRowId = Math.max(maxRowId, rowId); 80 } 81 const numEntries = maxRowId - minRowId + 1; 82 83 const sql = createVsyncIdQuery(tableName, minRowId, maxRowId); 84 const result = await traceProcessor.query(sql).waitAllRows(); 85 86 const vsyncIdOrderedByRow: Array<bigint> = []; 87 let curRowId = BigInt(minRowId); 88 for (const it = result.iter({}); it.valid(); it.next()) { 89 const id = assertDefined(it.get('id') as bigint | undefined); 90 while (curRowId < id) { 91 // Handle missing table rows that don't have a vsync_id 92 vsyncIdOrderedByRow.push(-1n); 93 curRowId++; 94 } 95 assertTrue( 96 curRowId === id, 97 () => 'query for vsyncId contains duplicate rows with the same id', 98 ); 99 const value = it.get('int_value') as bigint | undefined; 100 const valueType = it.get('value_type') as string; 101 assertTrue( 102 valueType === 'uint' || valueType === 'int', 103 () => 'expected vsync_id to have integer type', 104 ); 105 vsyncIdOrderedByRow.push(value ?? -1n); 106 curRowId++; 107 } 108 while (curRowId <= maxRowId) { 109 // Handle missing table rows at the end of the trace 110 vsyncIdOrderedByRow.push(-1n); 111 curRowId++; 112 } 113 114 if (vsyncIdOrderedByRow.length !== numEntries) { 115 UserNotifier.add(new MissingVsyncId(tableName)); 116 } 117 118 const vsyncIdOrderedByEntry: Array<bigint> = []; 119 for ( 120 let entryIndex = entriesRange.start; 121 entryIndex < entriesRange.end; 122 ++entryIndex 123 ) { 124 const rowId = entryIndexToRowIdMap[entryIndex]; 125 const vsyncId = vsyncIdOrderedByRow[rowId - minRowId]; 126 vsyncIdOrderedByEntry.push(vsyncId); 127 } 128 129 return vsyncIdOrderedByEntry; 130 } 131 132 // Creates a sql query for the vsync_id of the table rows that have 133 // an id in the range [minRowId, maxRowId]. The query may be created in a way 134 // where rows that don't have a vsync_id can be omitted from the query result. 135 private static createDefaultVsyncIdQuery( 136 tableName: string, 137 minRowId: number, 138 maxRowId: number, 139 ): string { 140 return ` 141 SELECT 142 tbl.id AS id, 143 args.key, 144 args.value_type, 145 args.int_value 146 FROM ${tableName} AS tbl 147 INNER JOIN args ON tbl.arg_set_id = args.arg_set_id 148 WHERE 149 tbl.id BETWEEN ${minRowId} AND ${maxRowId} 150 AND args.key = 'vsync_id' 151 ORDER BY tbl.id; 152 `; 153 } 154} 155