1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project 2*6dbdd20aSAndroid Build Coastguard Worker// 3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*6dbdd20aSAndroid Build Coastguard Worker// you may not use size file except in compliance with the License. 5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*6dbdd20aSAndroid Build Coastguard Worker// 7*6dbdd20aSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*6dbdd20aSAndroid Build Coastguard Worker// 9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License. 14*6dbdd20aSAndroid Build Coastguard Worker 15*6dbdd20aSAndroid Build Coastguard Workerimport m from 'mithril'; 16*6dbdd20aSAndroid Build Coastguard Workerimport {AggregationPanel} from './aggregation_panel'; 17*6dbdd20aSAndroid Build Coastguard Workerimport {isEmptyData} from '../public/aggregation'; 18*6dbdd20aSAndroid Build Coastguard Workerimport {DetailsShell} from '../widgets/details_shell'; 19*6dbdd20aSAndroid Build Coastguard Workerimport {Button, ButtonBar} from '../widgets/button'; 20*6dbdd20aSAndroid Build Coastguard Workerimport {raf} from '../core/raf_scheduler'; 21*6dbdd20aSAndroid Build Coastguard Workerimport {EmptyState} from '../widgets/empty_state'; 22*6dbdd20aSAndroid Build Coastguard Workerimport {FlowEventsAreaSelectedPanel} from './flow_events_panel'; 23*6dbdd20aSAndroid Build Coastguard Workerimport {PivotTable} from './pivot_table'; 24*6dbdd20aSAndroid Build Coastguard Workerimport {AreaSelection} from '../public/selection'; 25*6dbdd20aSAndroid Build Coastguard Workerimport {Monitor} from '../base/monitor'; 26*6dbdd20aSAndroid Build Coastguard Workerimport { 27*6dbdd20aSAndroid Build Coastguard Worker CPU_PROFILE_TRACK_KIND, 28*6dbdd20aSAndroid Build Coastguard Worker PERF_SAMPLES_PROFILE_TRACK_KIND, 29*6dbdd20aSAndroid Build Coastguard Worker SLICE_TRACK_KIND, 30*6dbdd20aSAndroid Build Coastguard Worker} from '../public/track_kinds'; 31*6dbdd20aSAndroid Build Coastguard Workerimport { 32*6dbdd20aSAndroid Build Coastguard Worker QueryFlamegraph, 33*6dbdd20aSAndroid Build Coastguard Worker metricsFromTableOrSubquery, 34*6dbdd20aSAndroid Build Coastguard Worker} from '../components/query_flamegraph'; 35*6dbdd20aSAndroid Build Coastguard Workerimport {DisposableStack} from '../base/disposable_stack'; 36*6dbdd20aSAndroid Build Coastguard Workerimport {assertExists} from '../base/logging'; 37*6dbdd20aSAndroid Build Coastguard Workerimport {TraceImpl} from '../core/trace_impl'; 38*6dbdd20aSAndroid Build Coastguard Workerimport {Trace} from '../public/trace'; 39*6dbdd20aSAndroid Build Coastguard Workerimport {Flamegraph} from '../widgets/flamegraph'; 40*6dbdd20aSAndroid Build Coastguard Worker 41*6dbdd20aSAndroid Build Coastguard Workerinterface View { 42*6dbdd20aSAndroid Build Coastguard Worker key: string; 43*6dbdd20aSAndroid Build Coastguard Worker name: string; 44*6dbdd20aSAndroid Build Coastguard Worker content: m.Children; 45*6dbdd20aSAndroid Build Coastguard Worker} 46*6dbdd20aSAndroid Build Coastguard Worker 47*6dbdd20aSAndroid Build Coastguard Workerexport type AreaDetailsPanelAttrs = {trace: TraceImpl}; 48*6dbdd20aSAndroid Build Coastguard Worker 49*6dbdd20aSAndroid Build Coastguard Workerclass AreaDetailsPanel implements m.ClassComponent<AreaDetailsPanelAttrs> { 50*6dbdd20aSAndroid Build Coastguard Worker private trace: TraceImpl; 51*6dbdd20aSAndroid Build Coastguard Worker private monitor: Monitor; 52*6dbdd20aSAndroid Build Coastguard Worker private currentTab: string | undefined = undefined; 53*6dbdd20aSAndroid Build Coastguard Worker private cpuProfileFlamegraph?: QueryFlamegraph; 54*6dbdd20aSAndroid Build Coastguard Worker private perfSampleFlamegraph?: QueryFlamegraph; 55*6dbdd20aSAndroid Build Coastguard Worker private sliceFlamegraph?: QueryFlamegraph; 56*6dbdd20aSAndroid Build Coastguard Worker 57*6dbdd20aSAndroid Build Coastguard Worker constructor({attrs}: m.CVnode<AreaDetailsPanelAttrs>) { 58*6dbdd20aSAndroid Build Coastguard Worker this.trace = attrs.trace; 59*6dbdd20aSAndroid Build Coastguard Worker this.monitor = new Monitor([() => this.trace.selection.selection]); 60*6dbdd20aSAndroid Build Coastguard Worker } 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Worker private getCurrentView(): string | undefined { 63*6dbdd20aSAndroid Build Coastguard Worker const types = this.getViews().map(({key}) => key); 64*6dbdd20aSAndroid Build Coastguard Worker 65*6dbdd20aSAndroid Build Coastguard Worker if (types.length === 0) { 66*6dbdd20aSAndroid Build Coastguard Worker return undefined; 67*6dbdd20aSAndroid Build Coastguard Worker } 68*6dbdd20aSAndroid Build Coastguard Worker 69*6dbdd20aSAndroid Build Coastguard Worker if (this.currentTab === undefined) { 70*6dbdd20aSAndroid Build Coastguard Worker return types[0]; 71*6dbdd20aSAndroid Build Coastguard Worker } 72*6dbdd20aSAndroid Build Coastguard Worker 73*6dbdd20aSAndroid Build Coastguard Worker if (!types.includes(this.currentTab)) { 74*6dbdd20aSAndroid Build Coastguard Worker return types[0]; 75*6dbdd20aSAndroid Build Coastguard Worker } 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Worker return this.currentTab; 78*6dbdd20aSAndroid Build Coastguard Worker } 79*6dbdd20aSAndroid Build Coastguard Worker 80*6dbdd20aSAndroid Build Coastguard Worker private getViews(): View[] { 81*6dbdd20aSAndroid Build Coastguard Worker const views: View[] = []; 82*6dbdd20aSAndroid Build Coastguard Worker 83*6dbdd20aSAndroid Build Coastguard Worker for (const aggregator of this.trace.selection.aggregation.aggregators) { 84*6dbdd20aSAndroid Build Coastguard Worker const aggregatorId = aggregator.id; 85*6dbdd20aSAndroid Build Coastguard Worker const value = 86*6dbdd20aSAndroid Build Coastguard Worker this.trace.selection.aggregation.getAggregatedData(aggregatorId); 87*6dbdd20aSAndroid Build Coastguard Worker if (value !== undefined && !isEmptyData(value)) { 88*6dbdd20aSAndroid Build Coastguard Worker views.push({ 89*6dbdd20aSAndroid Build Coastguard Worker key: value.tabName, 90*6dbdd20aSAndroid Build Coastguard Worker name: value.tabName, 91*6dbdd20aSAndroid Build Coastguard Worker content: m(AggregationPanel, { 92*6dbdd20aSAndroid Build Coastguard Worker aggregatorId, 93*6dbdd20aSAndroid Build Coastguard Worker data: value, 94*6dbdd20aSAndroid Build Coastguard Worker trace: this.trace, 95*6dbdd20aSAndroid Build Coastguard Worker }), 96*6dbdd20aSAndroid Build Coastguard Worker }); 97*6dbdd20aSAndroid Build Coastguard Worker } 98*6dbdd20aSAndroid Build Coastguard Worker } 99*6dbdd20aSAndroid Build Coastguard Worker 100*6dbdd20aSAndroid Build Coastguard Worker const pivotTableState = this.trace.pivotTable.state; 101*6dbdd20aSAndroid Build Coastguard Worker const tree = pivotTableState.queryResult?.tree; 102*6dbdd20aSAndroid Build Coastguard Worker if ( 103*6dbdd20aSAndroid Build Coastguard Worker pivotTableState.selectionArea != undefined && 104*6dbdd20aSAndroid Build Coastguard Worker (tree === undefined || tree.children.size > 0 || tree?.rows.length > 0) 105*6dbdd20aSAndroid Build Coastguard Worker ) { 106*6dbdd20aSAndroid Build Coastguard Worker views.push({ 107*6dbdd20aSAndroid Build Coastguard Worker key: 'pivot_table', 108*6dbdd20aSAndroid Build Coastguard Worker name: 'Pivot Table', 109*6dbdd20aSAndroid Build Coastguard Worker content: m(PivotTable, { 110*6dbdd20aSAndroid Build Coastguard Worker trace: this.trace, 111*6dbdd20aSAndroid Build Coastguard Worker selectionArea: pivotTableState.selectionArea, 112*6dbdd20aSAndroid Build Coastguard Worker }), 113*6dbdd20aSAndroid Build Coastguard Worker }); 114*6dbdd20aSAndroid Build Coastguard Worker } 115*6dbdd20aSAndroid Build Coastguard Worker 116*6dbdd20aSAndroid Build Coastguard Worker this.addFlamegraphView(this.trace, this.monitor.ifStateChanged(), views); 117*6dbdd20aSAndroid Build Coastguard Worker 118*6dbdd20aSAndroid Build Coastguard Worker // Add this after all aggregation panels, to make it appear after 'Slices' 119*6dbdd20aSAndroid Build Coastguard Worker if (this.trace.flows.selectedFlows.length > 0) { 120*6dbdd20aSAndroid Build Coastguard Worker views.push({ 121*6dbdd20aSAndroid Build Coastguard Worker key: 'selected_flows', 122*6dbdd20aSAndroid Build Coastguard Worker name: 'Flow Events', 123*6dbdd20aSAndroid Build Coastguard Worker content: m(FlowEventsAreaSelectedPanel, {trace: this.trace}), 124*6dbdd20aSAndroid Build Coastguard Worker }); 125*6dbdd20aSAndroid Build Coastguard Worker } 126*6dbdd20aSAndroid Build Coastguard Worker 127*6dbdd20aSAndroid Build Coastguard Worker return views; 128*6dbdd20aSAndroid Build Coastguard Worker } 129*6dbdd20aSAndroid Build Coastguard Worker 130*6dbdd20aSAndroid Build Coastguard Worker view(): m.Children { 131*6dbdd20aSAndroid Build Coastguard Worker const views = this.getViews(); 132*6dbdd20aSAndroid Build Coastguard Worker const currentViewKey = this.getCurrentView(); 133*6dbdd20aSAndroid Build Coastguard Worker 134*6dbdd20aSAndroid Build Coastguard Worker const aggregationButtons = views.map(({key, name}) => { 135*6dbdd20aSAndroid Build Coastguard Worker return m(Button, { 136*6dbdd20aSAndroid Build Coastguard Worker onclick: () => { 137*6dbdd20aSAndroid Build Coastguard Worker this.currentTab = key; 138*6dbdd20aSAndroid Build Coastguard Worker raf.scheduleFullRedraw(); 139*6dbdd20aSAndroid Build Coastguard Worker }, 140*6dbdd20aSAndroid Build Coastguard Worker key, 141*6dbdd20aSAndroid Build Coastguard Worker label: name, 142*6dbdd20aSAndroid Build Coastguard Worker active: currentViewKey === key, 143*6dbdd20aSAndroid Build Coastguard Worker }); 144*6dbdd20aSAndroid Build Coastguard Worker }); 145*6dbdd20aSAndroid Build Coastguard Worker 146*6dbdd20aSAndroid Build Coastguard Worker if (currentViewKey === undefined) { 147*6dbdd20aSAndroid Build Coastguard Worker return this.renderEmptyState(); 148*6dbdd20aSAndroid Build Coastguard Worker } 149*6dbdd20aSAndroid Build Coastguard Worker 150*6dbdd20aSAndroid Build Coastguard Worker const content = views.find(({key}) => key === currentViewKey)?.content; 151*6dbdd20aSAndroid Build Coastguard Worker if (content === undefined) { 152*6dbdd20aSAndroid Build Coastguard Worker return this.renderEmptyState(); 153*6dbdd20aSAndroid Build Coastguard Worker } 154*6dbdd20aSAndroid Build Coastguard Worker 155*6dbdd20aSAndroid Build Coastguard Worker return m( 156*6dbdd20aSAndroid Build Coastguard Worker DetailsShell, 157*6dbdd20aSAndroid Build Coastguard Worker { 158*6dbdd20aSAndroid Build Coastguard Worker title: 'Area Selection', 159*6dbdd20aSAndroid Build Coastguard Worker description: m(ButtonBar, aggregationButtons), 160*6dbdd20aSAndroid Build Coastguard Worker }, 161*6dbdd20aSAndroid Build Coastguard Worker content, 162*6dbdd20aSAndroid Build Coastguard Worker ); 163*6dbdd20aSAndroid Build Coastguard Worker } 164*6dbdd20aSAndroid Build Coastguard Worker 165*6dbdd20aSAndroid Build Coastguard Worker private renderEmptyState(): m.Children { 166*6dbdd20aSAndroid Build Coastguard Worker return m( 167*6dbdd20aSAndroid Build Coastguard Worker EmptyState, 168*6dbdd20aSAndroid Build Coastguard Worker { 169*6dbdd20aSAndroid Build Coastguard Worker className: 'pf-noselection', 170*6dbdd20aSAndroid Build Coastguard Worker title: 'Unsupported area selection', 171*6dbdd20aSAndroid Build Coastguard Worker }, 172*6dbdd20aSAndroid Build Coastguard Worker 'No details available for this area selection', 173*6dbdd20aSAndroid Build Coastguard Worker ); 174*6dbdd20aSAndroid Build Coastguard Worker } 175*6dbdd20aSAndroid Build Coastguard Worker 176*6dbdd20aSAndroid Build Coastguard Worker private addFlamegraphView(trace: Trace, isChanged: boolean, views: View[]) { 177*6dbdd20aSAndroid Build Coastguard Worker this.cpuProfileFlamegraph = this.computeCpuProfileFlamegraph( 178*6dbdd20aSAndroid Build Coastguard Worker trace, 179*6dbdd20aSAndroid Build Coastguard Worker isChanged, 180*6dbdd20aSAndroid Build Coastguard Worker ); 181*6dbdd20aSAndroid Build Coastguard Worker if (this.cpuProfileFlamegraph !== undefined) { 182*6dbdd20aSAndroid Build Coastguard Worker views.push({ 183*6dbdd20aSAndroid Build Coastguard Worker key: 'cpu_profile_flamegraph_selection', 184*6dbdd20aSAndroid Build Coastguard Worker name: 'CPU Profile Sample Flamegraph', 185*6dbdd20aSAndroid Build Coastguard Worker content: this.cpuProfileFlamegraph.render(), 186*6dbdd20aSAndroid Build Coastguard Worker }); 187*6dbdd20aSAndroid Build Coastguard Worker } 188*6dbdd20aSAndroid Build Coastguard Worker this.perfSampleFlamegraph = this.computePerfSampleFlamegraph( 189*6dbdd20aSAndroid Build Coastguard Worker trace, 190*6dbdd20aSAndroid Build Coastguard Worker isChanged, 191*6dbdd20aSAndroid Build Coastguard Worker ); 192*6dbdd20aSAndroid Build Coastguard Worker if (this.perfSampleFlamegraph !== undefined) { 193*6dbdd20aSAndroid Build Coastguard Worker views.push({ 194*6dbdd20aSAndroid Build Coastguard Worker key: 'perf_sample_flamegraph_selection', 195*6dbdd20aSAndroid Build Coastguard Worker name: 'Perf Sample Flamegraph', 196*6dbdd20aSAndroid Build Coastguard Worker content: this.perfSampleFlamegraph.render(), 197*6dbdd20aSAndroid Build Coastguard Worker }); 198*6dbdd20aSAndroid Build Coastguard Worker } 199*6dbdd20aSAndroid Build Coastguard Worker this.sliceFlamegraph = this.computeSliceFlamegraph(trace, isChanged); 200*6dbdd20aSAndroid Build Coastguard Worker if (this.sliceFlamegraph !== undefined) { 201*6dbdd20aSAndroid Build Coastguard Worker views.push({ 202*6dbdd20aSAndroid Build Coastguard Worker key: 'slice_flamegraph_selection', 203*6dbdd20aSAndroid Build Coastguard Worker name: 'Slice Flamegraph', 204*6dbdd20aSAndroid Build Coastguard Worker content: this.sliceFlamegraph.render(), 205*6dbdd20aSAndroid Build Coastguard Worker }); 206*6dbdd20aSAndroid Build Coastguard Worker } 207*6dbdd20aSAndroid Build Coastguard Worker } 208*6dbdd20aSAndroid Build Coastguard Worker 209*6dbdd20aSAndroid Build Coastguard Worker private computeCpuProfileFlamegraph(trace: Trace, isChanged: boolean) { 210*6dbdd20aSAndroid Build Coastguard Worker const currentSelection = trace.selection.selection; 211*6dbdd20aSAndroid Build Coastguard Worker if (currentSelection.kind !== 'area') { 212*6dbdd20aSAndroid Build Coastguard Worker return undefined; 213*6dbdd20aSAndroid Build Coastguard Worker } 214*6dbdd20aSAndroid Build Coastguard Worker if (!isChanged) { 215*6dbdd20aSAndroid Build Coastguard Worker // If the selection has not changed, just return a copy of the last seen 216*6dbdd20aSAndroid Build Coastguard Worker // attrs. 217*6dbdd20aSAndroid Build Coastguard Worker return this.cpuProfileFlamegraph; 218*6dbdd20aSAndroid Build Coastguard Worker } 219*6dbdd20aSAndroid Build Coastguard Worker const utids = []; 220*6dbdd20aSAndroid Build Coastguard Worker for (const trackInfo of currentSelection.tracks) { 221*6dbdd20aSAndroid Build Coastguard Worker if (trackInfo?.tags?.kind === CPU_PROFILE_TRACK_KIND) { 222*6dbdd20aSAndroid Build Coastguard Worker utids.push(trackInfo.tags?.utid); 223*6dbdd20aSAndroid Build Coastguard Worker } 224*6dbdd20aSAndroid Build Coastguard Worker } 225*6dbdd20aSAndroid Build Coastguard Worker if (utids.length === 0) { 226*6dbdd20aSAndroid Build Coastguard Worker return undefined; 227*6dbdd20aSAndroid Build Coastguard Worker } 228*6dbdd20aSAndroid Build Coastguard Worker const metrics = metricsFromTableOrSubquery( 229*6dbdd20aSAndroid Build Coastguard Worker ` 230*6dbdd20aSAndroid Build Coastguard Worker ( 231*6dbdd20aSAndroid Build Coastguard Worker select 232*6dbdd20aSAndroid Build Coastguard Worker id, 233*6dbdd20aSAndroid Build Coastguard Worker parent_id as parentId, 234*6dbdd20aSAndroid Build Coastguard Worker name, 235*6dbdd20aSAndroid Build Coastguard Worker mapping_name, 236*6dbdd20aSAndroid Build Coastguard Worker source_file, 237*6dbdd20aSAndroid Build Coastguard Worker cast(line_number AS text) as line_number, 238*6dbdd20aSAndroid Build Coastguard Worker self_count 239*6dbdd20aSAndroid Build Coastguard Worker from _callstacks_for_callsites!(( 240*6dbdd20aSAndroid Build Coastguard Worker select p.callsite_id 241*6dbdd20aSAndroid Build Coastguard Worker from cpu_profile_stack_sample p 242*6dbdd20aSAndroid Build Coastguard Worker where p.ts >= ${currentSelection.start} 243*6dbdd20aSAndroid Build Coastguard Worker and p.ts <= ${currentSelection.end} 244*6dbdd20aSAndroid Build Coastguard Worker and p.utid in (${utids.join(',')}) 245*6dbdd20aSAndroid Build Coastguard Worker )) 246*6dbdd20aSAndroid Build Coastguard Worker ) 247*6dbdd20aSAndroid Build Coastguard Worker `, 248*6dbdd20aSAndroid Build Coastguard Worker [ 249*6dbdd20aSAndroid Build Coastguard Worker { 250*6dbdd20aSAndroid Build Coastguard Worker name: 'CPU Profile Samples', 251*6dbdd20aSAndroid Build Coastguard Worker unit: '', 252*6dbdd20aSAndroid Build Coastguard Worker columnName: 'self_count', 253*6dbdd20aSAndroid Build Coastguard Worker }, 254*6dbdd20aSAndroid Build Coastguard Worker ], 255*6dbdd20aSAndroid Build Coastguard Worker 'include perfetto module callstacks.stack_profile', 256*6dbdd20aSAndroid Build Coastguard Worker [{name: 'mapping_name', displayName: 'Mapping'}], 257*6dbdd20aSAndroid Build Coastguard Worker [ 258*6dbdd20aSAndroid Build Coastguard Worker { 259*6dbdd20aSAndroid Build Coastguard Worker name: 'source_file', 260*6dbdd20aSAndroid Build Coastguard Worker displayName: 'Source File', 261*6dbdd20aSAndroid Build Coastguard Worker mergeAggregation: 'ONE_OR_NULL', 262*6dbdd20aSAndroid Build Coastguard Worker }, 263*6dbdd20aSAndroid Build Coastguard Worker { 264*6dbdd20aSAndroid Build Coastguard Worker name: 'line_number', 265*6dbdd20aSAndroid Build Coastguard Worker displayName: 'Line Number', 266*6dbdd20aSAndroid Build Coastguard Worker mergeAggregation: 'ONE_OR_NULL', 267*6dbdd20aSAndroid Build Coastguard Worker }, 268*6dbdd20aSAndroid Build Coastguard Worker ], 269*6dbdd20aSAndroid Build Coastguard Worker ); 270*6dbdd20aSAndroid Build Coastguard Worker return new QueryFlamegraph(trace, metrics, { 271*6dbdd20aSAndroid Build Coastguard Worker state: Flamegraph.createDefaultState(metrics), 272*6dbdd20aSAndroid Build Coastguard Worker }); 273*6dbdd20aSAndroid Build Coastguard Worker } 274*6dbdd20aSAndroid Build Coastguard Worker 275*6dbdd20aSAndroid Build Coastguard Worker private computePerfSampleFlamegraph(trace: Trace, isChanged: boolean) { 276*6dbdd20aSAndroid Build Coastguard Worker const currentSelection = trace.selection.selection; 277*6dbdd20aSAndroid Build Coastguard Worker if (currentSelection.kind !== 'area') { 278*6dbdd20aSAndroid Build Coastguard Worker return undefined; 279*6dbdd20aSAndroid Build Coastguard Worker } 280*6dbdd20aSAndroid Build Coastguard Worker if (!isChanged) { 281*6dbdd20aSAndroid Build Coastguard Worker // If the selection has not changed, just return a copy of the last seen 282*6dbdd20aSAndroid Build Coastguard Worker // attrs. 283*6dbdd20aSAndroid Build Coastguard Worker return this.perfSampleFlamegraph; 284*6dbdd20aSAndroid Build Coastguard Worker } 285*6dbdd20aSAndroid Build Coastguard Worker const upids = getUpidsFromPerfSampleAreaSelection(currentSelection); 286*6dbdd20aSAndroid Build Coastguard Worker const utids = getUtidsFromPerfSampleAreaSelection(currentSelection); 287*6dbdd20aSAndroid Build Coastguard Worker if (utids.length === 0 && upids.length === 0) { 288*6dbdd20aSAndroid Build Coastguard Worker return undefined; 289*6dbdd20aSAndroid Build Coastguard Worker } 290*6dbdd20aSAndroid Build Coastguard Worker const metrics = metricsFromTableOrSubquery( 291*6dbdd20aSAndroid Build Coastguard Worker ` 292*6dbdd20aSAndroid Build Coastguard Worker ( 293*6dbdd20aSAndroid Build Coastguard Worker select id, parent_id as parentId, name, self_count 294*6dbdd20aSAndroid Build Coastguard Worker from _callstacks_for_callsites!(( 295*6dbdd20aSAndroid Build Coastguard Worker select p.callsite_id 296*6dbdd20aSAndroid Build Coastguard Worker from perf_sample p 297*6dbdd20aSAndroid Build Coastguard Worker join thread t using (utid) 298*6dbdd20aSAndroid Build Coastguard Worker where p.ts >= ${currentSelection.start} 299*6dbdd20aSAndroid Build Coastguard Worker and p.ts <= ${currentSelection.end} 300*6dbdd20aSAndroid Build Coastguard Worker and ( 301*6dbdd20aSAndroid Build Coastguard Worker p.utid in (${utids.join(',')}) 302*6dbdd20aSAndroid Build Coastguard Worker or t.upid in (${upids.join(',')}) 303*6dbdd20aSAndroid Build Coastguard Worker ) 304*6dbdd20aSAndroid Build Coastguard Worker )) 305*6dbdd20aSAndroid Build Coastguard Worker ) 306*6dbdd20aSAndroid Build Coastguard Worker `, 307*6dbdd20aSAndroid Build Coastguard Worker [ 308*6dbdd20aSAndroid Build Coastguard Worker { 309*6dbdd20aSAndroid Build Coastguard Worker name: 'Perf Samples', 310*6dbdd20aSAndroid Build Coastguard Worker unit: '', 311*6dbdd20aSAndroid Build Coastguard Worker columnName: 'self_count', 312*6dbdd20aSAndroid Build Coastguard Worker }, 313*6dbdd20aSAndroid Build Coastguard Worker ], 314*6dbdd20aSAndroid Build Coastguard Worker 'include perfetto module linux.perf.samples', 315*6dbdd20aSAndroid Build Coastguard Worker ); 316*6dbdd20aSAndroid Build Coastguard Worker return new QueryFlamegraph(trace, metrics, { 317*6dbdd20aSAndroid Build Coastguard Worker state: Flamegraph.createDefaultState(metrics), 318*6dbdd20aSAndroid Build Coastguard Worker }); 319*6dbdd20aSAndroid Build Coastguard Worker } 320*6dbdd20aSAndroid Build Coastguard Worker 321*6dbdd20aSAndroid Build Coastguard Worker private computeSliceFlamegraph(trace: Trace, isChanged: boolean) { 322*6dbdd20aSAndroid Build Coastguard Worker const currentSelection = trace.selection.selection; 323*6dbdd20aSAndroid Build Coastguard Worker if (currentSelection.kind !== 'area') { 324*6dbdd20aSAndroid Build Coastguard Worker return undefined; 325*6dbdd20aSAndroid Build Coastguard Worker } 326*6dbdd20aSAndroid Build Coastguard Worker if (!isChanged) { 327*6dbdd20aSAndroid Build Coastguard Worker // If the selection has not changed, just return a copy of the last seen 328*6dbdd20aSAndroid Build Coastguard Worker // attrs. 329*6dbdd20aSAndroid Build Coastguard Worker return this.sliceFlamegraph; 330*6dbdd20aSAndroid Build Coastguard Worker } 331*6dbdd20aSAndroid Build Coastguard Worker const trackIds = []; 332*6dbdd20aSAndroid Build Coastguard Worker for (const trackInfo of currentSelection.tracks) { 333*6dbdd20aSAndroid Build Coastguard Worker if (trackInfo?.tags?.kind !== SLICE_TRACK_KIND) { 334*6dbdd20aSAndroid Build Coastguard Worker continue; 335*6dbdd20aSAndroid Build Coastguard Worker } 336*6dbdd20aSAndroid Build Coastguard Worker if (trackInfo.tags?.trackIds === undefined) { 337*6dbdd20aSAndroid Build Coastguard Worker continue; 338*6dbdd20aSAndroid Build Coastguard Worker } 339*6dbdd20aSAndroid Build Coastguard Worker trackIds.push(...trackInfo.tags.trackIds); 340*6dbdd20aSAndroid Build Coastguard Worker } 341*6dbdd20aSAndroid Build Coastguard Worker if (trackIds.length === 0) { 342*6dbdd20aSAndroid Build Coastguard Worker return undefined; 343*6dbdd20aSAndroid Build Coastguard Worker } 344*6dbdd20aSAndroid Build Coastguard Worker const metrics = metricsFromTableOrSubquery( 345*6dbdd20aSAndroid Build Coastguard Worker ` 346*6dbdd20aSAndroid Build Coastguard Worker ( 347*6dbdd20aSAndroid Build Coastguard Worker select * 348*6dbdd20aSAndroid Build Coastguard Worker from _viz_slice_ancestor_agg!(( 349*6dbdd20aSAndroid Build Coastguard Worker select s.id, s.dur 350*6dbdd20aSAndroid Build Coastguard Worker from slice s 351*6dbdd20aSAndroid Build Coastguard Worker left join slice t on t.parent_id = s.id 352*6dbdd20aSAndroid Build Coastguard Worker where s.ts >= ${currentSelection.start} 353*6dbdd20aSAndroid Build Coastguard Worker and s.ts <= ${currentSelection.end} 354*6dbdd20aSAndroid Build Coastguard Worker and s.track_id in (${trackIds.join(',')}) 355*6dbdd20aSAndroid Build Coastguard Worker and t.id is null 356*6dbdd20aSAndroid Build Coastguard Worker )) 357*6dbdd20aSAndroid Build Coastguard Worker ) 358*6dbdd20aSAndroid Build Coastguard Worker `, 359*6dbdd20aSAndroid Build Coastguard Worker [ 360*6dbdd20aSAndroid Build Coastguard Worker { 361*6dbdd20aSAndroid Build Coastguard Worker name: 'Duration', 362*6dbdd20aSAndroid Build Coastguard Worker unit: 'ns', 363*6dbdd20aSAndroid Build Coastguard Worker columnName: 'self_dur', 364*6dbdd20aSAndroid Build Coastguard Worker }, 365*6dbdd20aSAndroid Build Coastguard Worker { 366*6dbdd20aSAndroid Build Coastguard Worker name: 'Samples', 367*6dbdd20aSAndroid Build Coastguard Worker unit: '', 368*6dbdd20aSAndroid Build Coastguard Worker columnName: 'self_count', 369*6dbdd20aSAndroid Build Coastguard Worker }, 370*6dbdd20aSAndroid Build Coastguard Worker ], 371*6dbdd20aSAndroid Build Coastguard Worker 'include perfetto module viz.slices;', 372*6dbdd20aSAndroid Build Coastguard Worker ); 373*6dbdd20aSAndroid Build Coastguard Worker return new QueryFlamegraph(trace, metrics, { 374*6dbdd20aSAndroid Build Coastguard Worker state: Flamegraph.createDefaultState(metrics), 375*6dbdd20aSAndroid Build Coastguard Worker }); 376*6dbdd20aSAndroid Build Coastguard Worker } 377*6dbdd20aSAndroid Build Coastguard Worker} 378*6dbdd20aSAndroid Build Coastguard Worker 379*6dbdd20aSAndroid Build Coastguard Workerexport class AggregationsTabs implements Disposable { 380*6dbdd20aSAndroid Build Coastguard Worker private trash = new DisposableStack(); 381*6dbdd20aSAndroid Build Coastguard Worker 382*6dbdd20aSAndroid Build Coastguard Worker constructor(trace: TraceImpl) { 383*6dbdd20aSAndroid Build Coastguard Worker const unregister = trace.tabs.registerDetailsPanel({ 384*6dbdd20aSAndroid Build Coastguard Worker render(selection) { 385*6dbdd20aSAndroid Build Coastguard Worker if (selection.kind === 'area') { 386*6dbdd20aSAndroid Build Coastguard Worker return m(AreaDetailsPanel, {trace}); 387*6dbdd20aSAndroid Build Coastguard Worker } else { 388*6dbdd20aSAndroid Build Coastguard Worker return undefined; 389*6dbdd20aSAndroid Build Coastguard Worker } 390*6dbdd20aSAndroid Build Coastguard Worker }, 391*6dbdd20aSAndroid Build Coastguard Worker }); 392*6dbdd20aSAndroid Build Coastguard Worker 393*6dbdd20aSAndroid Build Coastguard Worker this.trash.use(unregister); 394*6dbdd20aSAndroid Build Coastguard Worker } 395*6dbdd20aSAndroid Build Coastguard Worker 396*6dbdd20aSAndroid Build Coastguard Worker [Symbol.dispose]() { 397*6dbdd20aSAndroid Build Coastguard Worker this.trash.dispose(); 398*6dbdd20aSAndroid Build Coastguard Worker } 399*6dbdd20aSAndroid Build Coastguard Worker} 400*6dbdd20aSAndroid Build Coastguard Worker 401*6dbdd20aSAndroid Build Coastguard Workerfunction getUpidsFromPerfSampleAreaSelection(currentSelection: AreaSelection) { 402*6dbdd20aSAndroid Build Coastguard Worker const upids = []; 403*6dbdd20aSAndroid Build Coastguard Worker for (const trackInfo of currentSelection.tracks) { 404*6dbdd20aSAndroid Build Coastguard Worker if ( 405*6dbdd20aSAndroid Build Coastguard Worker trackInfo?.tags?.kind === PERF_SAMPLES_PROFILE_TRACK_KIND && 406*6dbdd20aSAndroid Build Coastguard Worker trackInfo.tags?.utid === undefined 407*6dbdd20aSAndroid Build Coastguard Worker ) { 408*6dbdd20aSAndroid Build Coastguard Worker upids.push(assertExists(trackInfo.tags?.upid)); 409*6dbdd20aSAndroid Build Coastguard Worker } 410*6dbdd20aSAndroid Build Coastguard Worker } 411*6dbdd20aSAndroid Build Coastguard Worker return upids; 412*6dbdd20aSAndroid Build Coastguard Worker} 413*6dbdd20aSAndroid Build Coastguard Worker 414*6dbdd20aSAndroid Build Coastguard Workerfunction getUtidsFromPerfSampleAreaSelection(currentSelection: AreaSelection) { 415*6dbdd20aSAndroid Build Coastguard Worker const utids = []; 416*6dbdd20aSAndroid Build Coastguard Worker for (const trackInfo of currentSelection.tracks) { 417*6dbdd20aSAndroid Build Coastguard Worker if ( 418*6dbdd20aSAndroid Build Coastguard Worker trackInfo?.tags?.kind === PERF_SAMPLES_PROFILE_TRACK_KIND && 419*6dbdd20aSAndroid Build Coastguard Worker trackInfo.tags?.utid !== undefined 420*6dbdd20aSAndroid Build Coastguard Worker ) { 421*6dbdd20aSAndroid Build Coastguard Worker utids.push(trackInfo.tags?.utid); 422*6dbdd20aSAndroid Build Coastguard Worker } 423*6dbdd20aSAndroid Build Coastguard Worker } 424*6dbdd20aSAndroid Build Coastguard Worker return utids; 425*6dbdd20aSAndroid Build Coastguard Worker} 426