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 m from 'mithril'; 16import {LONG, NUM} from '../../trace_processor/query_result'; 17import {getColorForSample} from '../../components/colorizer'; 18import { 19 metricsFromTableOrSubquery, 20 QueryFlamegraph, 21} from '../../components/query_flamegraph'; 22import {DetailsShell} from '../../widgets/details_shell'; 23import {Timestamp} from '../../components/widgets/timestamp'; 24import {Time, time} from '../../base/time'; 25import {Flamegraph, FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph'; 26import {Trace} from '../../public/trace'; 27import {DatasetSliceTrack} from '../../components/tracks/dataset_slice_track'; 28import {SourceDataset} from '../../trace_processor/dataset'; 29 30// TODO(stevegolton): Dedupe this file with instrument_samples_profile_track.ts 31 32export function createProcessPerfSamplesProfileTrack( 33 trace: Trace, 34 uri: string, 35 upid: number, 36) { 37 return new DatasetSliceTrack({ 38 trace, 39 uri, 40 dataset: new SourceDataset({ 41 schema: { 42 id: NUM, 43 ts: LONG, 44 callsiteId: NUM, 45 }, 46 src: ` 47 SELECT 48 p.id, 49 ts, 50 callsite_id as callsiteId, 51 upid 52 FROM perf_sample p 53 JOIN thread using (utid) 54 WHERE callsite_id IS NOT NULL 55 ORDER BY ts 56 `, 57 filter: { 58 col: 'upid', 59 eq: upid, 60 }, 61 }), 62 sliceName: () => 'Perf Sample', 63 colorizer: (row) => getColorForSample(row.callsiteId), 64 detailsPanel: (row) => { 65 const metrics = metricsFromTableOrSubquery( 66 ` 67 ( 68 select 69 id, 70 parent_id as parentId, 71 name, 72 mapping_name, 73 source_file, 74 cast(line_number AS text) as line_number, 75 self_count 76 from _callstacks_for_callsites!(( 77 select p.callsite_id 78 from perf_sample p 79 join thread t using (utid) 80 where p.ts >= ${row.ts} 81 and p.ts <= ${row.ts} 82 and t.upid = ${upid} 83 )) 84 ) 85 `, 86 [ 87 { 88 name: 'Perf Samples', 89 unit: '', 90 columnName: 'self_count', 91 }, 92 ], 93 'include perfetto module linux.perf.samples', 94 [{name: 'mapping_name', displayName: 'Mapping'}], 95 [ 96 { 97 name: 'source_file', 98 displayName: 'Source File', 99 mergeAggregation: 'ONE_OR_NULL', 100 }, 101 { 102 name: 'line_number', 103 displayName: 'Line Number', 104 mergeAggregation: 'ONE_OR_NULL', 105 }, 106 ], 107 ); 108 const serialization = { 109 schema: FLAMEGRAPH_STATE_SCHEMA, 110 state: Flamegraph.createDefaultState(metrics), 111 }; 112 const flamegraph = new QueryFlamegraph(trace, metrics, serialization); 113 return { 114 render: () => renderDetailsPanel(flamegraph, Time.fromRaw(row.ts)), 115 serialization, 116 }; 117 }, 118 }); 119} 120 121export function createThreadPerfSamplesProfileTrack( 122 trace: Trace, 123 uri: string, 124 utid: number, 125) { 126 return new DatasetSliceTrack({ 127 trace, 128 uri, 129 dataset: new SourceDataset({ 130 schema: { 131 id: NUM, 132 ts: LONG, 133 callsiteId: NUM, 134 }, 135 src: ` 136 SELECT 137 p.id, 138 ts, 139 callsite_id as callsiteId, 140 utid 141 FROM perf_sample p 142 WHERE callsite_id IS NOT NULL 143 ORDER BY ts 144 `, 145 filter: { 146 col: 'utid', 147 eq: utid, 148 }, 149 }), 150 sliceName: () => 'Perf Sample', 151 colorizer: (row) => getColorForSample(row.callsiteId), 152 detailsPanel: (row) => { 153 const metrics = metricsFromTableOrSubquery( 154 ` 155 ( 156 select 157 id, 158 parent_id as parentId, 159 name, 160 mapping_name, 161 source_file, 162 cast(line_number AS text) as line_number, 163 self_count 164 from _callstacks_for_callsites!(( 165 select p.callsite_id 166 from perf_sample p 167 where p.ts >= ${row.ts} 168 and p.ts <= ${row.ts} 169 and p.utid = ${utid} 170 )) 171 ) 172 `, 173 [ 174 { 175 name: 'Perf Samples', 176 unit: '', 177 columnName: 'self_count', 178 }, 179 ], 180 'include perfetto module linux.perf.samples', 181 [{name: 'mapping_name', displayName: 'Mapping'}], 182 [ 183 { 184 name: 'source_file', 185 displayName: 'Source File', 186 mergeAggregation: 'ONE_OR_NULL', 187 }, 188 { 189 name: 'line_number', 190 displayName: 'Line Number', 191 mergeAggregation: 'ONE_OR_NULL', 192 }, 193 ], 194 ); 195 const serialization = { 196 schema: FLAMEGRAPH_STATE_SCHEMA, 197 state: Flamegraph.createDefaultState(metrics), 198 }; 199 const flamegraph = new QueryFlamegraph(trace, metrics, serialization); 200 return { 201 render: () => renderDetailsPanel(flamegraph, Time.fromRaw(row.ts)), 202 serialization, 203 }; 204 }, 205 }); 206} 207 208function renderDetailsPanel(flamegraph: QueryFlamegraph, ts: time) { 209 return m( 210 '.flamegraph-profile', 211 m( 212 DetailsShell, 213 { 214 fillParent: true, 215 title: m('.title', 'Perf Samples'), 216 description: [], 217 buttons: [ 218 m( 219 'div.time', 220 `First timestamp: `, 221 m(Timestamp, { 222 ts, 223 }), 224 ), 225 m( 226 'div.time', 227 `Last timestamp: `, 228 m(Timestamp, { 229 ts, 230 }), 231 ), 232 ], 233 }, 234 flamegraph.render(), 235 ), 236 ); 237} 238