blob: 30dfe3cd7d77d73e1ad456af30eda6c3338d7538 [file] [log] [blame]
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import m from 'mithril';
import {v4 as uuidv4} from 'uuid';
import {Disposable} from '../../base/disposable';
import {Actions} from '../../common/actions';
import {SCROLLING_TRACK_GROUP} from '../../common/state';
import {globals} from '../../frontend/globals';
import {
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
import {TrackButton} from '../../frontend/track_panel';
import {PrimaryTrackSortKey, TrackContext} from '../../public';
import {EngineProxy} from '../../trace_processor/engine';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
CustomSqlTableSliceTrack,
} from '../custom_sql_table_slices';
import {DEBUG_SLICE_TRACK_URI} from '.';
import {ARG_PREFIX} from './add_debug_track_menu';
import {DebugSliceDetailsTab} from './details_tab';
// Names of the columns of the underlying view to be used as ts / dur / name.
export interface SliceColumns {
ts: string;
dur: string;
name: string;
}
export interface DebugTrackV2Config {
sqlTableName: string;
columns: SliceColumns;
}
interface DebugTrackV2Types extends NamedSliceTrackTypes {
config: DebugTrackV2Config;
}
export class DebugTrackV2 extends CustomSqlTableSliceTrack<DebugTrackV2Types> {
constructor(engine: EngineProxy, trackKey: string) {
super({
engine,
trackKey,
});
}
onCreate(ctx: TrackContext): void {
// TODO(stevegolton): Validate params before type asserting.
// TODO(stevegolton): Avoid just pushing this config up for some base
// class to use. Be more explicit.
this.config = ctx.params as DebugTrackV2Config;
}
getSqlDataSource(): CustomSqlTableDefConfig {
return {
sqlTableName: this.config.sqlTableName,
};
}
getDetailsPanel(): CustomSqlDetailsPanelConfig {
return {
kind: DebugSliceDetailsTab.kind,
config: {
sqlTableName: this.config.sqlTableName,
title: 'Debug Slice',
},
};
}
async onInit(): Promise<Disposable> {
return super.onInit();
}
getTrackShellButtons(): m.Children {
return m(TrackButton, {
action: () => {
globals.dispatch(Actions.removeTracks({trackKeys: [this.trackKey]}));
},
i: 'close',
tooltip: 'Close',
showButton: true,
});
}
}
let debugTrackCount = 0;
export interface SqlDataSource {
// SQL source selecting the necessary data.
sqlSource: string;
// Optional: Rename columns from the query result.
// If omitted, original column names from the query are used instead.
// The caller is responsible for ensuring that the number of items in this
// list matches the number of columns returned by sqlSource.
columns?: string[];
}
export async function addDebugSliceTrack(
engine: EngineProxy,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
argColumns: string[]) {
// To prepare displaying the provided data as a track, materialize it and
// compute depths.
const debugTrackId = ++debugTrackCount;
const sqlTableName = `__debug_slice_${debugTrackId}`;
// If the view has clashing names (e.g. "name" coming from joining two
// different tables, we will see names like "name_1", "name_2", but they won't
// be addressable from the SQL. So we explicitly name them through a list of
// columns passed to CTE.
const dataColumns =
data.columns !== undefined ? `(${data.columns.join(', ')})` : '';
// TODO(altimin): Support removing this table when the track is closed.
const dur = sliceColumns.dur === '0' ? 0 : sliceColumns.dur;
await engine.query(`
create table ${sqlTableName} as
with data${dataColumns} as (
${data.sqlSource}
),
prepared_data as (
select
row_number() over () as id,
${sliceColumns.ts} as ts,
ifnull(cast(${dur} as int), -1) as dur,
printf('%s', ${sliceColumns.name}) as name
${argColumns.length > 0 ? ',' : ''}
${argColumns.map((c) => `${c} as ${ARG_PREFIX}${c}`).join(',\n')}
from data
)
select
*
from prepared_data
order by ts;`);
const trackKey = uuidv4();
globals.dispatchMultiple([
Actions.addTrack({
key: trackKey,
name: trackName.trim() || `Debug Track ${debugTrackId}`,
uri: DEBUG_SLICE_TRACK_URI,
trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
trackGroup: SCROLLING_TRACK_GROUP,
params: {
sqlTableName,
columns: sliceColumns,
},
}),
Actions.toggleTrackPinned({trackKey}),
]);
}