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 {createAggregationToTabAdaptor} from '../../components/aggregation_adapter'; 16import { 17 metricsFromTableOrSubquery, 18 QueryFlamegraph, 19} from '../../components/query_flamegraph'; 20import {PerfettoPlugin} from '../../public/plugin'; 21import {AreaSelection, areaSelectionsEqual} from '../../public/selection'; 22import {Trace} from '../../public/trace'; 23import {SLICE_TRACK_KIND} from '../../public/track_kinds'; 24import {Flamegraph} from '../../widgets/flamegraph'; 25import {CounterSelectionAggregator} from './counter_selection_aggregator'; 26import {PivotTableTab} from './pivot_table_tab'; 27import {SliceSelectionAggregator} from './slice_selection_aggregator'; 28 29/** 30 * This plugin adds the generic aggregations for slice tracks and counter 31 * tracks. 32 */ 33export default class implements PerfettoPlugin { 34 static readonly id = 'dev.perfetto.GenericAggregations'; 35 36 async onTraceLoad(ctx: Trace): Promise<void> { 37 ctx.selection.registerAreaSelectionTab( 38 createAggregationToTabAdaptor(ctx, new CounterSelectionAggregator()), 39 ); 40 41 ctx.selection.registerAreaSelectionTab( 42 createAggregationToTabAdaptor(ctx, new SliceSelectionAggregator()), 43 ); 44 45 ctx.selection.registerAreaSelectionTab(new PivotTableTab(ctx)); 46 ctx.selection.registerAreaSelectionTab(createSliceFlameGraphPanel(ctx)); 47 } 48} 49 50function createSliceFlameGraphPanel(trace: Trace) { 51 let previousSelection: AreaSelection | undefined; 52 let sliceFlamegraph: QueryFlamegraph | undefined; 53 return { 54 id: 'slice_flamegraph_selection', 55 name: 'Slice Flamegraph', 56 render(selection: AreaSelection) { 57 const selectionChanged = 58 previousSelection === undefined || 59 !areaSelectionsEqual(previousSelection, selection); 60 previousSelection = selection; 61 if (selectionChanged) { 62 sliceFlamegraph = computeSliceFlamegraph(trace, selection); 63 } 64 65 if (sliceFlamegraph === undefined) { 66 return undefined; 67 } 68 69 return {isLoading: false, content: sliceFlamegraph.render()}; 70 }, 71 }; 72} 73 74function computeSliceFlamegraph(trace: Trace, currentSelection: AreaSelection) { 75 const trackIds = []; 76 for (const trackInfo of currentSelection.tracks) { 77 if (trackInfo?.tags?.kind !== SLICE_TRACK_KIND) { 78 continue; 79 } 80 if (trackInfo.tags?.trackIds === undefined) { 81 continue; 82 } 83 trackIds.push(...trackInfo.tags.trackIds); 84 } 85 if (trackIds.length === 0) { 86 return undefined; 87 } 88 const metrics = metricsFromTableOrSubquery( 89 ` 90 ( 91 select * 92 from _viz_slice_ancestor_agg!(( 93 select s.id, s.dur 94 from slice s 95 left join slice t on t.parent_id = s.id 96 where s.ts >= ${currentSelection.start} 97 and s.ts <= ${currentSelection.end} 98 and s.track_id in (${trackIds.join(',')}) 99 and t.id is null 100 )) 101 ) 102 `, 103 [ 104 { 105 name: 'Duration', 106 unit: 'ns', 107 columnName: 'self_dur', 108 }, 109 { 110 name: 'Samples', 111 unit: '', 112 columnName: 'self_count', 113 }, 114 ], 115 'include perfetto module viz.slices;', 116 ); 117 return new QueryFlamegraph(trace, metrics, { 118 state: Flamegraph.createDefaultState(metrics), 119 }); 120} 121