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