• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2024 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 m from 'mithril';
16import {LONG, NUM} from '../../trace_processor/query_result';
17import {getColorForSample} from '../../components/colorizer';
18import {
19  metricsFromTableOrSubquery,
20  QueryFlamegraph,
21} from '../../components/query_flamegraph';
22import {DetailsShell} from '../../widgets/details_shell';
23import {Timestamp} from '../../components/widgets/timestamp';
24import {Time, time} from '../../base/time';
25import {Flamegraph, FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph';
26import {Trace} from '../../public/trace';
27import {DatasetSliceTrack} from '../../components/tracks/dataset_slice_track';
28import {SourceDataset} from '../../trace_processor/dataset';
29
30// TODO(stevegolton): Dedupe this file with instrument_samples_profile_track.ts
31
32export function createProcessPerfSamplesProfileTrack(
33  trace: Trace,
34  uri: string,
35  upid: number,
36) {
37  return new DatasetSliceTrack({
38    trace,
39    uri,
40    dataset: new SourceDataset({
41      schema: {
42        id: NUM,
43        ts: LONG,
44        callsiteId: NUM,
45      },
46      src: `
47       SELECT
48          p.id,
49          ts,
50          callsite_id as callsiteId,
51          upid
52        FROM perf_sample p
53        JOIN thread using (utid)
54        WHERE callsite_id IS NOT NULL
55        ORDER BY ts
56      `,
57      filter: {
58        col: 'upid',
59        eq: upid,
60      },
61    }),
62    sliceName: () => 'Perf Sample',
63    colorizer: (row) => getColorForSample(row.callsiteId),
64    detailsPanel: (row) => {
65      const metrics = metricsFromTableOrSubquery(
66        `
67          (
68            select
69              id,
70              parent_id as parentId,
71              name,
72              mapping_name,
73              source_file,
74              cast(line_number AS text) as line_number,
75              self_count
76            from _callstacks_for_callsites!((
77              select p.callsite_id
78              from perf_sample p
79              join thread t using (utid)
80              where p.ts >= ${row.ts}
81                and p.ts <= ${row.ts}
82                and t.upid = ${upid}
83            ))
84          )
85        `,
86        [
87          {
88            name: 'Perf Samples',
89            unit: '',
90            columnName: 'self_count',
91          },
92        ],
93        'include perfetto module linux.perf.samples',
94        [{name: 'mapping_name', displayName: 'Mapping'}],
95        [
96          {
97            name: 'source_file',
98            displayName: 'Source File',
99            mergeAggregation: 'ONE_OR_NULL',
100          },
101          {
102            name: 'line_number',
103            displayName: 'Line Number',
104            mergeAggregation: 'ONE_OR_NULL',
105          },
106        ],
107      );
108      const serialization = {
109        schema: FLAMEGRAPH_STATE_SCHEMA,
110        state: Flamegraph.createDefaultState(metrics),
111      };
112      const flamegraph = new QueryFlamegraph(trace, metrics, serialization);
113      return {
114        render: () => renderDetailsPanel(flamegraph, Time.fromRaw(row.ts)),
115        serialization,
116      };
117    },
118  });
119}
120
121export function createThreadPerfSamplesProfileTrack(
122  trace: Trace,
123  uri: string,
124  utid: number,
125) {
126  return new DatasetSliceTrack({
127    trace,
128    uri,
129    dataset: new SourceDataset({
130      schema: {
131        id: NUM,
132        ts: LONG,
133        callsiteId: NUM,
134      },
135      src: `
136        SELECT
137          p.id,
138          ts,
139          callsite_id as callsiteId,
140          utid
141        FROM perf_sample p
142        WHERE callsite_id IS NOT NULL
143        ORDER BY ts
144      `,
145      filter: {
146        col: 'utid',
147        eq: utid,
148      },
149    }),
150    sliceName: () => 'Perf Sample',
151    colorizer: (row) => getColorForSample(row.callsiteId),
152    detailsPanel: (row) => {
153      const metrics = metricsFromTableOrSubquery(
154        `
155          (
156            select
157              id,
158              parent_id as parentId,
159              name,
160              mapping_name,
161              source_file,
162              cast(line_number AS text) as line_number,
163              self_count
164            from _callstacks_for_callsites!((
165              select p.callsite_id
166              from perf_sample p
167              where p.ts >= ${row.ts}
168                and p.ts <= ${row.ts}
169                and p.utid = ${utid}
170            ))
171          )
172        `,
173        [
174          {
175            name: 'Perf Samples',
176            unit: '',
177            columnName: 'self_count',
178          },
179        ],
180        'include perfetto module linux.perf.samples',
181        [{name: 'mapping_name', displayName: 'Mapping'}],
182        [
183          {
184            name: 'source_file',
185            displayName: 'Source File',
186            mergeAggregation: 'ONE_OR_NULL',
187          },
188          {
189            name: 'line_number',
190            displayName: 'Line Number',
191            mergeAggregation: 'ONE_OR_NULL',
192          },
193        ],
194      );
195      const serialization = {
196        schema: FLAMEGRAPH_STATE_SCHEMA,
197        state: Flamegraph.createDefaultState(metrics),
198      };
199      const flamegraph = new QueryFlamegraph(trace, metrics, serialization);
200      return {
201        render: () => renderDetailsPanel(flamegraph, Time.fromRaw(row.ts)),
202        serialization,
203      };
204    },
205  });
206}
207
208function renderDetailsPanel(flamegraph: QueryFlamegraph, ts: time) {
209  return m(
210    '.flamegraph-profile',
211    m(
212      DetailsShell,
213      {
214        fillParent: true,
215        title: m('.title', 'Perf Samples'),
216        description: [],
217        buttons: [
218          m(
219            'div.time',
220            `First timestamp: `,
221            m(Timestamp, {
222              ts,
223            }),
224          ),
225          m(
226            'div.time',
227            `Last timestamp: `,
228            m(Timestamp, {
229              ts,
230            }),
231          ),
232        ],
233      },
234      flamegraph.render(),
235    ),
236  );
237}
238