xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.HeapProfile/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2021 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 {HEAP_PROFILE_TRACK_KIND} from '../../public/track_kinds';
16import {Trace} from '../../public/trace';
17import {PerfettoPlugin} from '../../public/plugin';
18import {LONG, NUM, STR} from '../../trace_processor/query_result';
19import {HeapProfileTrack} from './heap_profile_track';
20import {TrackNode} from '../../public/workspace';
21import {createPerfettoTable} from '../../trace_processor/sql_utils';
22import ProcessThreadGroupsPlugin from '../dev.perfetto.ProcessThreadGroups';
23
24function getUriForTrack(upid: number): string {
25  return `/process_${upid}/heap_profile`;
26}
27
28export default class implements PerfettoPlugin {
29  static readonly id = 'dev.perfetto.HeapProfile';
30  static readonly dependencies = [ProcessThreadGroupsPlugin];
31
32  async onTraceLoad(ctx: Trace): Promise<void> {
33    const it = await ctx.engine.query(`
34      select value from stats
35      where name = 'heap_graph_non_finalized_graph'
36    `);
37    const incomplete = it.firstRow({value: NUM}).value > 0;
38
39    const result = await ctx.engine.query(`
40      select distinct upid from heap_profile_allocation
41      union
42      select distinct upid from heap_graph_object
43    `);
44    for (const it = result.iter({upid: NUM}); it.valid(); it.next()) {
45      const upid = it.upid;
46      const uri = getUriForTrack(upid);
47      const title = 'Heap Profile';
48      const tableName = `_heap_profile_${upid}`;
49
50      createPerfettoTable(
51        ctx.engine,
52        tableName,
53        `
54          with
55            heaps as (select group_concat(distinct heap_name) h from heap_profile_allocation where upid = ${upid}),
56            allocation_tses as (select distinct ts from heap_profile_allocation where upid = ${upid}),
57            graph_tses as (select distinct graph_sample_ts from heap_graph_object where upid = ${upid})
58          select
59            *,
60            0 AS dur,
61            0 AS depth
62          from (
63            select
64              (
65                select a.id
66                from heap_profile_allocation a
67                where a.ts = t.ts
68                order by a.id
69                limit 1
70              ) as id,
71              ts,
72              'heap_profile:' || (select h from heaps) AS type
73            from allocation_tses t
74            union all
75            select
76              (
77                select o.id
78                from heap_graph_object o
79                where o.graph_sample_ts = g.graph_sample_ts
80                order by o.id
81                limit 1
82              ) as id,
83              graph_sample_ts AS ts,
84              'graph' AS type
85            from graph_tses g
86          )
87        `,
88      );
89
90      ctx.tracks.registerTrack({
91        uri,
92        title,
93        tags: {
94          kind: HEAP_PROFILE_TRACK_KIND,
95          upid,
96        },
97        track: new HeapProfileTrack(ctx, uri, tableName, upid, incomplete),
98      });
99      const group = ctx.plugins
100        .getPlugin(ProcessThreadGroupsPlugin)
101        .getGroupForProcess(upid);
102      const track = new TrackNode({uri, title, sortOrder: -30});
103      group?.addChildInOrder(track);
104    }
105
106    ctx.onTraceReady.addListener(async () => {
107      await selectFirstHeapProfile(ctx);
108    });
109  }
110}
111
112async function selectFirstHeapProfile(ctx: Trace) {
113  const query = `
114    select * from (
115      select
116        min(ts) AS ts,
117        'heap_profile:' || group_concat(distinct heap_name) AS type,
118        upid
119      from heap_profile_allocation
120      group by upid
121      union
122      select distinct graph_sample_ts as ts, 'graph' as type, upid
123      from heap_graph_object
124    )
125    order by ts
126    limit 1
127  `;
128  const profile = await ctx.engine.query(query);
129  if (profile.numRows() !== 1) return;
130  const row = profile.firstRow({ts: LONG, type: STR, upid: NUM});
131  const upid = row.upid;
132
133  ctx.selection.selectTrackEvent(getUriForTrack(upid), 0);
134}
135