blob: 1cc848b8ccc63e7b8f2acdc8963f64fccfe85fd5 [file]
// Copyright (C) 2025 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 {LONG, NUM} from '../../trace_processor/query_result';
import {getColorForSample} from '../../components/colorizer';
import {
metricsFromTableOrSubquery,
QueryFlamegraph,
} from '../../components/query_flamegraph';
import {DetailsShell} from '../../widgets/details_shell';
import {Timestamp} from '../../components/widgets/timestamp';
import {Time, time} from '../../base/time';
import {Flamegraph, FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph';
import {Trace} from '../../public/trace';
import {SliceTrack} from '../../components/tracks/slice_track';
import {SourceDataset} from '../../trace_processor/dataset';
import {Stack} from '../../widgets/stack';
// TODO(stevegolton): Dedupe this file with perf_samples_profile_track.ts
export function createProcessInstrumentsSamplesProfileTrack(
trace: Trace,
uri: string,
upid: number,
) {
return SliceTrack.create({
trace,
uri,
dataset: new SourceDataset({
schema: {
id: NUM,
ts: LONG,
callsiteId: NUM,
},
src: `
SELECT
p.id,
ts,
callsite_id as callsiteId,
upid
FROM instruments_sample p
JOIN thread using (utid)
WHERE callsite_id IS NOT NULL
ORDER BY ts
`,
filter: {
col: 'upid',
eq: upid,
},
}),
sliceName: () => 'Instruments Sample',
colorizer: (row) => getColorForSample(row.callsiteId),
detailsPanel: (row) => {
const metrics = metricsFromTableOrSubquery(
`
(
select
id,
parent_id as parentId,
name,
mapping_name,
source_file || ':' || line_number as source_location,
self_count
from _callstacks_for_callsites!((
select p.callsite_id
from instruments_sample p
join thread t using (utid)
where p.ts >= ${row.ts}
and p.ts <= ${row.ts}
and t.upid = ${upid}
))
)
`,
[
{
name: 'Instruments Samples',
unit: '',
columnName: 'self_count',
},
],
'include perfetto module appleos.instruments.samples',
[{name: 'mapping_name', displayName: 'Mapping'}],
[
{
name: 'source_location',
displayName: 'Source Location',
mergeAggregation: 'ONE_OR_SUMMARY',
},
],
);
const serialization = {
schema: FLAMEGRAPH_STATE_SCHEMA,
state: Flamegraph.createDefaultState(metrics),
};
const flamegraph = new QueryFlamegraph(trace, metrics, serialization);
return {
render: () =>
renderDetailsPanel(trace, flamegraph, Time.fromRaw(row.ts)),
serialization,
};
},
});
}
export function createThreadInstrumentsSamplesProfileTrack(
trace: Trace,
uri: string,
utid: number,
) {
return SliceTrack.create({
trace,
uri,
dataset: new SourceDataset({
schema: {
id: NUM,
ts: LONG,
callsiteId: NUM,
},
src: `
SELECT
p.id,
ts,
callsite_id as callsiteId,
utid
FROM instruments_sample p
WHERE callsite_id IS NOT NULL
ORDER BY ts
`,
filter: {
col: 'utid',
eq: utid,
},
}),
sliceName: () => 'Instruments Sample',
colorizer: (row) => getColorForSample(row.callsiteId),
detailsPanel: (row) => {
const metrics = metricsFromTableOrSubquery(
`
(
select
id,
parent_id as parentId,
name,
mapping_name,
source_file || ':' || line_number as source_location,
self_count
from _callstacks_for_callsites!((
select p.callsite_id
from instruments_sample p
where p.ts >= ${row.ts}
and p.ts <= ${row.ts}
and p.utid = ${utid}
))
)
`,
[
{
name: 'Instruments Samples',
unit: '',
columnName: 'self_count',
},
],
'include perfetto module appleos.instruments.samples',
[{name: 'mapping_name', displayName: 'Mapping'}],
[
{
name: 'source_location',
displayName: 'Source Location',
mergeAggregation: 'ONE_OR_SUMMARY',
},
],
);
const serialization = {
schema: FLAMEGRAPH_STATE_SCHEMA,
state: Flamegraph.createDefaultState(metrics),
};
const flamegraph = new QueryFlamegraph(trace, metrics, serialization);
return {
render: () =>
renderDetailsPanel(trace, flamegraph, Time.fromRaw(row.ts)),
serialization,
};
},
});
}
function renderDetailsPanel(
trace: Trace,
flamegraph: QueryFlamegraph,
ts: time,
) {
return m(
'.pf-flamegraph-profile',
m(
DetailsShell,
{
fillHeight: true,
title: 'Instruments Samples',
buttons: m(Stack, {orientation: 'horizontal', spacing: 'large'}, [
m('span', [
`First timestamp: `,
m(Timestamp, {
trace,
ts,
}),
]),
m('span', [
`Last timestamp: `,
m(Timestamp, {
trace,
ts,
}),
]),
]),
},
flamegraph.render(),
),
);
}