blob: 7b3fe117fcef3a6d29738dd79ad5511ad77a664e [file] [log] [blame]
// Copyright (C) 2024 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 {uuidv4} from '../base/uuid';
import {Actions, DeferredAction} from '../common/actions';
import {SCROLLING_TRACK_GROUP} from '../common/state';
import {globals} from './globals';
import {Engine, PrimaryTrackSortKey} from '../public';
import {DebugTrackV2Config} from '../core_plugins/debug/slice_track';
export const ARG_PREFIX = 'arg_';
export const DEBUG_SLICE_TRACK_URI = 'perfetto.DebugSlices';
export const DEBUG_COUNTER_TRACK_URI = 'perfetto.DebugCounter';
// Names of the columns of the underlying view to be used as
// ts / dur / name / pivot.
export interface SliceColumns {
ts: string;
dur: string;
name: string;
pivot?: string;
}
export interface DebugTrackV2CreateConfig {
pinned?: boolean; // default true
closeable?: boolean; // default 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[];
}
// Creates actions to add a debug track. The actions must be dispatched to
// have an effect. Use this variant if you want to create many tracks at
// once or want to tweak the actions once produced. Otherwise, use
// addDebugSliceTrack().
export async function createDebugSliceTrackActions(
_engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
argColumns: string[],
config?: DebugTrackV2CreateConfig,
): Promise<DeferredAction<{}>[]> {
const debugTrackId = ++debugTrackCount;
const closeable = config?.closeable ?? true;
const trackKey = uuidv4();
const trackConfig: DebugTrackV2Config = {
data,
columns: sliceColumns,
argColumns,
};
const actions: DeferredAction<{}>[] = [
Actions.addTrack({
key: trackKey,
name: trackName.trim() || `Debug Track ${debugTrackId}`,
uri: DEBUG_SLICE_TRACK_URI,
trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
trackGroup: SCROLLING_TRACK_GROUP,
params: trackConfig,
closeable,
}),
];
if (config?.pinned ?? true) {
actions.push(Actions.toggleTrackPinned({trackKey}));
}
return actions;
}
export async function addPivotDebugSliceTracks(
engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
argColumns: string[],
config?: DebugTrackV2CreateConfig,
) {
if (sliceColumns.pivot) {
// Get distinct values to group by
const pivotValues = await engine.query(`
with all_vals as (${data.sqlSource})
select DISTINCT ${sliceColumns.pivot} from all_vals;`);
const iter = pivotValues.iter({});
for (; iter.valid(); iter.next()) {
const pivotDataSource: SqlDataSource = {
sqlSource: `select * from
(${data.sqlSource})
where ${sliceColumns.pivot} = '${iter.get(sliceColumns.pivot)}'`,
};
const actions = await createDebugSliceTrackActions(
engine,
pivotDataSource,
`${trackName.trim() || 'Pivot Track'}: ${iter.get(sliceColumns.pivot)}`,
sliceColumns,
argColumns,
config,
);
globals.dispatchMultiple(actions);
}
}
}
// Adds a debug track immediately. Use createDebugSliceTrackActions() if you
// want to create many tracks at once.
export async function addDebugSliceTrack(
engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
argColumns: string[],
config?: DebugTrackV2CreateConfig,
) {
const actions = await createDebugSliceTrackActions(
engine,
data,
trackName,
sliceColumns,
argColumns,
config,
);
globals.dispatchMultiple(actions);
}
// Names of the columns of the underlying view to be used as ts / dur / name.
export interface CounterColumns {
ts: string;
value: string;
}
export interface CounterDebugTrackConfig {
data: SqlDataSource;
columns: CounterColumns;
}
export interface CounterDebugTrackCreateConfig {
pinned?: boolean; // default true
closeable?: boolean; // default true
}
// Creates actions to add a debug track. The actions must be dispatched to
// have an effect. Use this variant if you want to create many tracks at
// once or want to tweak the actions once produced. Otherwise, use
// addDebugCounterTrack().
export async function createDebugCounterTrackActions(
data: SqlDataSource,
trackName: string,
columns: CounterColumns,
config?: CounterDebugTrackCreateConfig,
) {
// To prepare displaying the provided data as a track, materialize it and
// compute depths.
const debugTrackId = ++debugTrackCount;
const closeable = config?.closeable ?? true;
const params: CounterDebugTrackConfig = {
data,
columns,
};
const trackKey = uuidv4();
const actions: DeferredAction<{}>[] = [
Actions.addTrack({
key: trackKey,
uri: DEBUG_COUNTER_TRACK_URI,
name: trackName.trim() || `Debug Track ${debugTrackId}`,
trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
trackGroup: SCROLLING_TRACK_GROUP,
params,
closeable,
}),
];
if (config?.pinned ?? true) {
actions.push(Actions.toggleTrackPinned({trackKey}));
}
return actions;
}
// Adds a debug track immediately. Use createDebugCounterTrackActions() if you
// want to create many tracks at once.
export async function addDebugCounterTrack(
data: SqlDataSource,
trackName: string,
columns: CounterColumns,
config?: CounterDebugTrackCreateConfig,
) {
const actions = await createDebugCounterTrackActions(
data,
trackName,
columns,
config,
);
globals.dispatchMultiple(actions);
}