• 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 {
16  createQueryCounterTrack,
17  SqlDataSource,
18} from '../../components/tracks/query_counter_track';
19import {PerfettoPlugin} from '../../public/plugin';
20import {Trace} from '../../public/trace';
21import {COUNTER_TRACK_KIND, SLICE_TRACK_KIND} from '../../public/track_kinds';
22import {TrackNode} from '../../public/workspace';
23import {NUM, NUM_NULL, STR} from '../../trace_processor/query_result';
24import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups';
25import StandardGroupsPlugin from '../dev.perfetto.StandardGroups';
26import TraceProcessorTrackPlugin from '../dev.perfetto.TraceProcessorTrack';
27import {TraceProcessorCounterTrack} from '../dev.perfetto.TraceProcessorTrack/trace_processor_counter_track';
28import {createTraceProcessorSliceTrack} from '../dev.perfetto.TraceProcessorTrack/trace_processor_slice_track';
29
30async function registerAllocsTrack(
31  ctx: Trace,
32  uri: string,
33  dataSource: SqlDataSource,
34) {
35  const track = await createQueryCounterTrack({
36    trace: ctx,
37    uri,
38    data: dataSource,
39  });
40  ctx.tracks.registerTrack({
41    uri,
42    title: `dmabuf allocs`,
43    track: track,
44  });
45}
46
47export default class implements PerfettoPlugin {
48  static readonly id = 'dev.perfetto.AndroidDmabuf';
49  static readonly dependencies = [
50    ProcessThreadGroupsPlugin,
51    StandardGroupsPlugin,
52    TraceProcessorTrackPlugin,
53  ];
54
55  async onTraceLoad(ctx: Trace): Promise<void> {
56    const e = ctx.engine;
57    await e.query(`INCLUDE PERFETTO MODULE android.memory.dmabuf`);
58    await e.query(`
59      CREATE PERFETTO TABLE _android_memory_cumulative_dmabuf AS
60      SELECT
61        upid, utid, ts,
62        SUM(buf_size) OVER(PARTITION BY COALESCE(upid, utid) ORDER BY ts) AS value
63      FROM android_dmabuf_allocs;`);
64
65    const pids = await e.query(
66      `SELECT DISTINCT upid, IIF(upid IS NULL, utid, NULL) AS utid FROM _android_memory_cumulative_dmabuf`,
67    );
68    const it = pids.iter({upid: NUM_NULL, utid: NUM_NULL});
69    for (; it.valid(); it.next()) {
70      if (it.upid != null) {
71        const uri = `/android_process_dmabuf_upid_${it.upid}`;
72        const config: SqlDataSource = {
73          sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
74                 WHERE upid = ${it.upid}`,
75        };
76        await registerAllocsTrack(ctx, uri, config);
77        ctx.plugins
78          .getPlugin(ProcessThreadGroupsPlugin)
79          .getGroupForProcess(it.upid)
80          ?.addChildInOrder(new TrackNode({uri, title: 'dmabuf allocs'}));
81      } else if (it.utid != null) {
82        const uri = `/android_process_dmabuf_utid_${it.utid}`;
83        const config: SqlDataSource = {
84          sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
85                 WHERE utid = ${it.utid}`,
86        };
87        await registerAllocsTrack(ctx, uri, config);
88        ctx.plugins
89          .getPlugin(ProcessThreadGroupsPlugin)
90          .getGroupForThread(it.utid)
91          ?.addChildInOrder(new TrackNode({uri, title: 'dmabuf allocs'}));
92      }
93    }
94    const memoryGroupFn = () => {
95      return ctx.plugins
96        .getPlugin(StandardGroupsPlugin)
97        .getOrCreateStandardGroup(ctx.workspace, 'MEMORY');
98    };
99    const node = await addGlobalCounter(ctx, memoryGroupFn);
100    await addGlobalAllocs(ctx, () => {
101      return node ?? memoryGroupFn();
102    });
103  }
104}
105
106async function addGlobalCounter(ctx: Trace, parent: () => TrackNode) {
107  const track = await ctx.engine.query(`
108    select id, name
109    from track
110    where type = 'android_dma_heap'
111  `);
112  const it = track.maybeFirstRow({id: NUM, name: STR});
113  if (!it) {
114    return undefined;
115  }
116  const {id, name: title} = it;
117  const uri = `/android_dmabuf_counter`;
118  ctx.tracks.registerTrack({
119    uri,
120    title,
121    tags: {
122      kind: COUNTER_TRACK_KIND,
123      trackIds: [id],
124    },
125    track: new TraceProcessorCounterTrack(ctx, uri, {}, id, title),
126  });
127  const node = new TrackNode({
128    uri,
129    title,
130  });
131  parent().addChildInOrder(node);
132  return node;
133}
134
135async function addGlobalAllocs(ctx: Trace, parent: () => TrackNode) {
136  const track = await ctx.engine.query(`
137    select name, group_concat(id) as trackIds
138    from track
139    where type = 'android_dma_allocations'
140    group by name
141  `);
142  const it = track.maybeFirstRow({trackIds: STR, name: STR});
143  if (!it) {
144    return undefined;
145  }
146  const {trackIds, name: title} = it;
147  const uri = `/android_dmabuf_allocs`;
148  const ids = trackIds.split(',').map((x) => Number(x));
149  ctx.tracks.registerTrack({
150    uri,
151    title,
152    tags: {
153      kind: SLICE_TRACK_KIND,
154      trackIds: ids,
155    },
156    track: createTraceProcessorSliceTrack({trace: ctx, uri, trackIds: ids}),
157  });
158  const node = new TrackNode({
159    uri,
160    title,
161  });
162  parent().addChildInOrder(node);
163}
164