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 {BigintMath as BIMath} from '../../base/bigint_math'; 16import {clamp} from '../../base/math_utils'; 17import { 18 NAMED_ROW, 19 NamedSliceTrack, 20} from '../../components/tracks/named_slice_track'; 21import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../components/tracks/slice_layout'; 22import {TrackEventDetails} from '../../public/selection'; 23import {Trace} from '../../public/trace'; 24import {Slice} from '../../public/track'; 25import {SourceDataset, Dataset} from '../../trace_processor/dataset'; 26import { 27 LONG, 28 LONG_NULL, 29 NUM, 30 NUM_NULL, 31 STR, 32} from '../../trace_processor/query_result'; 33 34export const THREAD_SLICE_ROW = { 35 // Base columns (tsq, ts, dur, id, depth). 36 ...NAMED_ROW, 37 38 // Thread-specific columns. 39 threadDur: LONG_NULL, 40}; 41export type ThreadSliceRow = typeof THREAD_SLICE_ROW; 42 43export class AsyncSliceTrack extends NamedSliceTrack<Slice, ThreadSliceRow> { 44 constructor( 45 trace: Trace, 46 uri: string, 47 maxDepth: number, 48 private readonly trackIds: number[], 49 ) { 50 super(trace, uri); 51 this.sliceLayout = { 52 ...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS, 53 depthGuess: maxDepth, 54 }; 55 } 56 57 getRowSpec(): ThreadSliceRow { 58 return THREAD_SLICE_ROW; 59 } 60 61 rowToSlice(row: ThreadSliceRow): Slice { 62 const namedSlice = this.rowToSliceBase(row); 63 64 if (row.dur > 0n && row.threadDur !== null) { 65 const fillRatio = clamp(BIMath.ratio(row.threadDur, row.dur), 0, 1); 66 return {...namedSlice, fillRatio}; 67 } else { 68 return namedSlice; 69 } 70 } 71 72 getSqlSource(): string { 73 // If we only have one track ID we can avoid the overhead of 74 // experimental_slice_layout, and just go straight to the slice table. 75 if (this.trackIds.length === 1) { 76 return ` 77 select 78 ts, 79 dur, 80 id, 81 depth, 82 ifnull(name, '[null]') as name, 83 thread_dur as threadDur 84 from slice 85 where track_id = ${this.trackIds[0]} 86 `; 87 } else { 88 return ` 89 select 90 id, 91 ts, 92 dur, 93 layout_depth as depth, 94 ifnull(name, '[null]') as name, 95 thread_dur as threadDur 96 from experimental_slice_layout 97 where filter_track_ids = '${this.trackIds.join(',')}' 98 `; 99 } 100 } 101 102 onUpdatedSlices(slices: Slice[]) { 103 for (const slice of slices) { 104 slice.isHighlighted = slice === this.hoveredSlice; 105 } 106 } 107 108 async getSelectionDetails( 109 id: number, 110 ): Promise<TrackEventDetails | undefined> { 111 const baseDetails = await super.getSelectionDetails(id); 112 if (!baseDetails) return undefined; 113 return { 114 ...baseDetails, 115 tableName: 'slice', 116 }; 117 } 118 119 override getDataset(): Dataset { 120 return new SourceDataset({ 121 src: `slice`, 122 filter: { 123 col: 'track_id', 124 in: this.trackIds, 125 }, 126 schema: { 127 id: NUM, 128 name: STR, 129 ts: LONG, 130 dur: LONG, 131 parent_id: NUM_NULL, 132 }, 133 }); 134 } 135} 136