1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {duration, Time, time} from '../../base/time'; 16import {exists} from '../../base/utils'; 17import {Engine} from '../../trace_processor/engine'; 18import { 19 LONG, 20 LONG_NULL, 21 NUM, 22 NUM_NULL, 23 STR, 24 STR_NULL, 25} from '../../trace_processor/query_result'; 26import { 27 constraintsToQuerySuffix, 28 SQLConstraints, 29} from '../../trace_processor/sql_utils'; 30import { 31 asArgSetId, 32 asSliceSqlId, 33 asUpid, 34 asUtid, 35 SliceSqlId, 36 Upid, 37 Utid, 38} from './core_types'; 39import {Arg, getArgs} from './args'; 40import {getThreadInfo, ThreadInfo} from './thread'; 41import {getProcessInfo, ProcessInfo} from './process'; 42 43// Basic information about a slice. 44export interface SliceDetails { 45 id: SliceSqlId; 46 name: string; 47 ts: time; 48 absTime?: string; 49 dur: duration; 50 parentId?: SliceSqlId; 51 trackId: number; 52 depth: number; 53 thread?: ThreadInfo; 54 process?: ProcessInfo; 55 threadTs?: time; 56 threadDur?: duration; 57 category?: string; 58 args?: Arg[]; 59} 60 61async function getUtidAndUpid( 62 engine: Engine, 63 sqlTrackId: number, 64): Promise<{utid?: Utid; upid?: Upid}> { 65 const columnInfo = ( 66 await engine.query(` 67 WITH 68 leafTrackTable AS (SELECT type FROM track WHERE id = ${sqlTrackId}), 69 cols AS ( 70 SELECT name 71 FROM pragma_table_info((SELECT type FROM leafTrackTable)) 72 ) 73 SELECT 74 type as leafTrackTable, 75 'upid' in cols AS hasUpid, 76 'utid' in cols AS hasUtid 77 FROM leafTrackTable 78 `) 79 ).firstRow({hasUpid: NUM, hasUtid: NUM, leafTrackTable: STR}); 80 const hasUpid = columnInfo.hasUpid !== 0; 81 const hasUtid = columnInfo.hasUtid !== 0; 82 83 const result: {utid?: Utid; upid?: Upid} = {}; 84 85 if (hasUtid) { 86 const utid = ( 87 await engine.query(` 88 SELECT utid 89 FROM ${columnInfo.leafTrackTable} 90 WHERE id = ${sqlTrackId}; 91 `) 92 ).firstRow({ 93 utid: NUM, 94 }).utid; 95 result.utid = asUtid(utid); 96 } else if (hasUpid) { 97 const upid = ( 98 await engine.query(` 99 SELECT upid 100 FROM ${columnInfo.leafTrackTable} 101 WHERE id = ${sqlTrackId}; 102 `) 103 ).firstRow({ 104 upid: NUM, 105 }).upid; 106 result.upid = asUpid(upid); 107 } 108 return result; 109} 110 111export async function getSliceFromConstraints( 112 engine: Engine, 113 constraints: SQLConstraints, 114): Promise<SliceDetails[]> { 115 const query = await engine.query(` 116 SELECT 117 id, 118 name, 119 ts, 120 dur, 121 track_id as trackId, 122 depth, 123 parent_id as parentId, 124 thread_dur as threadDur, 125 thread_ts as threadTs, 126 category, 127 arg_set_id as argSetId, 128 ABS_TIME_STR(ts) as absTime 129 FROM slice 130 ${constraintsToQuerySuffix(constraints)}`); 131 const it = query.iter({ 132 id: NUM, 133 name: STR, 134 ts: LONG, 135 dur: LONG, 136 trackId: NUM, 137 depth: NUM, 138 parentId: NUM_NULL, 139 threadDur: LONG_NULL, 140 threadTs: LONG_NULL, 141 category: STR_NULL, 142 argSetId: NUM, 143 absTime: STR_NULL, 144 }); 145 146 const result: SliceDetails[] = []; 147 for (; it.valid(); it.next()) { 148 const {utid, upid} = await getUtidAndUpid(engine, it.trackId); 149 150 const thread: ThreadInfo | undefined = 151 utid === undefined ? undefined : await getThreadInfo(engine, utid); 152 const process: ProcessInfo | undefined = 153 thread !== undefined 154 ? thread.process 155 : upid === undefined 156 ? undefined 157 : await getProcessInfo(engine, upid); 158 159 result.push({ 160 id: asSliceSqlId(it.id), 161 name: it.name, 162 ts: Time.fromRaw(it.ts), 163 dur: it.dur, 164 trackId: it.trackId, 165 depth: it.depth, 166 parentId: asSliceSqlId(it.parentId ?? undefined), 167 thread, 168 process, 169 threadDur: it.threadDur ?? undefined, 170 threadTs: exists(it.threadTs) ? Time.fromRaw(it.threadTs) : undefined, 171 category: it.category ?? undefined, 172 args: await getArgs(engine, asArgSetId(it.argSetId)), 173 absTime: it.absTime ?? undefined, 174 }); 175 } 176 return result; 177} 178 179export async function getSlice( 180 engine: Engine, 181 id: SliceSqlId, 182): Promise<SliceDetails | undefined> { 183 const result = await getSliceFromConstraints(engine, { 184 filters: [`id=${id}`], 185 }); 186 if (result.length > 1) { 187 throw new Error(`slice table has more than one row with id ${id}`); 188 } 189 if (result.length === 0) { 190 return undefined; 191 } 192 return result[0]; 193} 194 195// A slice tree node, combining the information about the given slice with 196// information about its descendants. 197export interface SliceTreeNode extends SliceDetails { 198 children: SliceTreeNode[]; 199 parent?: SliceTreeNode; 200} 201 202// Get all descendants for a given slice in a tree form. 203export async function getDescendantSliceTree( 204 engine: Engine, 205 id: SliceSqlId, 206): Promise<SliceTreeNode | undefined> { 207 const slice = await getSlice(engine, id); 208 if (slice === undefined) { 209 return undefined; 210 } 211 const descendants = await getSliceFromConstraints(engine, { 212 filters: [ 213 `track_id=${slice.trackId}`, 214 `depth >= ${slice.depth}`, 215 `ts >= ${slice.ts}`, 216 // TODO(altimin): consider making `dur` undefined here instead of -1. 217 slice.dur >= 0 ? `ts <= (${slice.ts} + ${slice.dur})` : undefined, 218 ], 219 orderBy: ['ts', 'depth'], 220 }); 221 const slices: {[key: SliceSqlId]: SliceTreeNode} = Object.fromEntries( 222 descendants.map((slice) => [ 223 slice.id, 224 { 225 children: [], 226 ...slice, 227 }, 228 ]), 229 ); 230 for (const [_, slice] of Object.entries(slices)) { 231 if (slice.parentId !== undefined) { 232 const parent = slices[slice.parentId]; 233 slice.parent = parent; 234 parent.children.push(slice); 235 } 236 } 237 return slices[id]; 238} 239