blob: d8a8e1de1138d365b6c7bf31dc9c9d5764b125c3 [file]
// 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 m from 'mithril';
import {colorForThread} from '../../components/colorizer';
import {SliceTrack, ColorVariant} from '../../components/tracks/slice_track';
import {LONG, NUM} from '../../trace_processor/query_result';
import type {Trace} from '../../public/trace';
import type {ThreadMap} from '../dev.perfetto.Thread/threads';
import {SourceDataset} from '../../trace_processor/dataset';
import {RECT_PATTERN_HATCHED} from '../../base/renderer';
import {SchedSliceDetailsPanel} from './sched_details_tab';
const MARGIN_TOP = 3;
const RECT_HEIGHT = 24;
const CPU_SLICE_SCHEMA = {
id: NUM,
ts: LONG,
dur: LONG,
utid: NUM,
pid: LONG,
priority: NUM,
depth: NUM,
ucpu: NUM,
} as const;
type CpuSliceRow = typeof CPU_SLICE_SCHEMA;
export function createCpuSliceTrack(
trace: Trace,
uri: string,
tableName: string,
ucpu: number,
threads: ThreadMap,
): SliceTrack<CpuSliceRow> {
return SliceTrack.create({
trace,
uri,
rootTableName: 'sched_slice',
dataset: () =>
new SourceDataset({
src: tableName,
schema: CPU_SLICE_SCHEMA,
filter: {
col: 'ucpu',
eq: ucpu,
},
}),
sliceLayout: {
padding: MARGIN_TOP,
sliceHeight: RECT_HEIGHT,
},
colorizer(row) {
const threadInfo = threads.get(row.utid);
return colorForThread(threadInfo);
},
sliceName(row) {
const threadInfo = threads.get(row.utid);
if (!threadInfo) {
return `[utid:${row.utid}]`;
}
if (threadInfo.pid !== undefined && threadInfo.pid !== 0n) {
let procName = threadInfo.procName ?? '';
if (procName.startsWith('/')) {
procName = procName.substring(procName.lastIndexOf('/') + 1);
}
return `${procName} [${threadInfo.pid}]`;
}
return `${threadInfo.threadName} [${threadInfo.tid}]`;
},
sliceSubtitle(row) {
const threadInfo = threads.get(row.utid);
const isRealtime = row.priority < 100;
if (!threadInfo) {
return isRealtime ? '(RT)' : '';
}
if (threadInfo.pid !== undefined && threadInfo.pid !== 0n) {
const suffix = isRealtime ? ' (RT)' : '';
return `${threadInfo.threadName} [${threadInfo.tid}]${suffix}`;
}
return isRealtime ? '(RT)' : '';
},
slicePattern(row) {
return row.priority < 100 ? RECT_PATTERN_HATCHED : 0;
},
onUpdatedSlices(slices) {
const timeline = trace.timeline;
const hoveredUtid = timeline.hoveredUtid;
const hoveredPid = timeline.hoveredPid;
const isHovering = hoveredUtid !== undefined;
const n = slices.length;
const variants = new Array<ColorVariant>(n);
for (let i = 0; i < n; ++i) {
const row = slices[i].row;
const isThreadHovered = hoveredUtid === row.utid;
const isProcessHovered = hoveredPid === row.pid;
if (isHovering && !isThreadHovered) {
variants[i] = isProcessHovered
? ColorVariant.VARIANT
: ColorVariant.DISABLED;
} else {
variants[i] = ColorVariant.BASE;
}
}
return variants;
},
onSliceOver({slice}) {
const threadInfo = threads.get(slice.row.utid);
trace.timeline.hoveredUtid = slice.row.utid;
trace.timeline.hoveredPid = threadInfo?.pid;
},
onSliceOut() {
trace.timeline.hoveredUtid = undefined;
trace.timeline.hoveredPid = undefined;
},
tooltip(slice) {
const threadInfo = threads.get(slice.row.utid);
if (!threadInfo) {
return undefined;
}
const tidText = `T: ${threadInfo.threadName} [${threadInfo.tid}]`;
const countDiv =
slice.count > 1 && m('div', `and ${slice.count - 1} other events`);
if (threadInfo.pid !== undefined) {
const pidText = `P: ${threadInfo.procName} [${threadInfo.pid}]`;
return m('.tooltip', [m('div', pidText), m('div', tidText), countDiv]);
}
return m('.tooltip', tidText, countDiv);
},
detailsPanel() {
return new SchedSliceDetailsPanel(trace, threads);
},
});
}