1-- 2-- Copyright 2024 The Android Open Source Project 3-- 4-- Licensed under the Apache License, Version 2.0 (the "License"); 5-- you may not use this file except in compliance with the License. 6-- You may obtain a copy of the License at 7-- 8-- https://www.apache.org/licenses/LICENSE-2.0 9-- 10-- Unless required by applicable law or agreed to in writing, software 11-- distributed under the License is distributed on an "AS IS" BASIS, 12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13-- See the License for the specific language governing permissions and 14-- limitations under the License. 15 16-- sqlformat file off 17-- The case sensitivity of this file matters so don't format it which 18-- changes sensitivity. 19 20INCLUDE PERFETTO MODULE graphs.scan; 21 22CREATE PERFETTO MACRO _viz_flamegraph_hash_coalesce(col ColumnName) 23RETURNS Expr AS IFNULL($col, 0); 24 25-- For each frame in |tab|, returns a row containing the result of running 26-- all the filtering operations over that frame's name. 27CREATE PERFETTO MACRO _viz_flamegraph_prepare_filter( 28 tab TableOrSubquery, 29 show_stack Expr, 30 hide_stack Expr, 31 show_from_frame Expr, 32 hide_frame Expr, 33 pivot Expr, 34 impossible_stack_bits Expr, 35 grouping _ColumnNameList 36) 37RETURNS TableOrSubquery 38AS ( 39 SELECT 40 *, 41 IIF($hide_stack, $impossible_stack_bits, $show_stack) AS stackBits, 42 $show_from_frame As showFromFrameBits, 43 $hide_frame = 0 AS showFrame, 44 $pivot AS isPivot, 45 HASH( 46 name, 47 __intrinsic_token_apply!(_viz_flamegraph_hash_coalesce, $grouping) 48 ) AS groupingHash 49 FROM $tab 50 ORDER BY id 51); 52 53-- Walks the forest from root to leaf and performs the following operations: 54-- 1) removes frames which were filtered out 55-- 2) make any pivot nodes become the roots 56-- 3) computes whether the stack as a whole should be retained or not 57CREATE PERFETTO MACRO _viz_flamegraph_filter_frames( 58 source TableOrSubquery, 59 show_from_frame_bits Expr 60) 61RETURNS TableOrSubquery 62AS ( 63 WITH edges AS ( 64 SELECT parentId AS source_node_id, id AS dest_node_id 65 FROM $source 66 WHERE parentId IS NOT NULL 67 ), 68 inits AS ( 69 SELECT 70 id, 71 IIF( 72 showFrame AND showFromFrameBits = $show_from_frame_bits, 73 id, 74 NULL 75 ) AS filteredId, 76 NULL AS filteredParentId, 77 NULL AS filteredUnpivotedParentId, 78 IIF( 79 showFrame, 80 showFromFrameBits, 81 0 82 ) AS showFromFrameBits, 83 IIF( 84 showFrame AND showFromFrameBits = $show_from_frame_bits, 85 stackBits, 86 0 87 ) AS stackBits 88 FROM $source 89 WHERE parentId IS NULL 90 ) 91 SELECT 92 g.filteredId AS id, 93 g.filteredParentId AS parentId, 94 g.filteredUnpivotedParentId AS unpivotedParentId, 95 g.stackBits, 96 SUM(t.value) AS value 97 FROM _graph_scan!( 98 edges, 99 inits, 100 (filteredId, filteredParentId, filteredUnpivotedParentId, showFromFrameBits, stackBits), 101 ( 102 SELECT 103 t.id, 104 IIF( 105 x.showFrame AND (t.showFromFrameBits | x.showFromFrameBits) = $show_from_frame_bits, 106 t.id, 107 t.filteredId 108 ) AS filteredId, 109 IIF( 110 x.showFrame AND (t.showFromFrameBits | x.showFromFrameBits) = $show_from_frame_bits, 111 IIF(x.isPivot, NULL, t.filteredId), 112 t.filteredParentId 113 ) AS filteredParentId, 114 IIF( 115 x.showFrame AND (t.showFromFrameBits | x.showFromFrameBits) = $show_from_frame_bits, 116 t.filteredId, 117 t.filteredParentId 118 ) AS filteredUnpivotedParentId, 119 IIF( 120 x.showFrame, 121 (t.showFromFrameBits | x.showFromFrameBits), 122 t.showFromFrameBits 123 ) AS showFromFrameBits, 124 IIF( 125 x.showFrame AND (t.showFromFrameBits | x.showFromFrameBits) = $show_from_frame_bits, 126 (t.stackBits | x.stackBits), 127 t.stackBits 128 ) AS stackBits 129 FROM $table t 130 JOIN $source x USING (id) 131 ) 132 ) g 133 JOIN $source t USING (id) 134 WHERE filteredId IS NOT NULL 135 GROUP BY filteredId 136 ORDER BY filteredId 137); 138 139-- Walks the forest from leaves to root and does the following: 140-- 1) removes nodes whose stacks are filtered out 141-- 2) computes the cumulative value for each node (i.e. the sum of the self 142-- value of the node and all descendants). 143CREATE PERFETTO MACRO _viz_flamegraph_accumulate( 144 filtered TableOrSubquery, 145 showStackBits Expr 146) 147RETURNS TableOrSubquery 148AS ( 149 WITH edges AS ( 150 SELECT id AS source_node_id, parentId AS dest_node_id 151 FROM $filtered 152 WHERE parentId IS NOT NULL 153 ), inits AS ( 154 SELECT f.id, f.value AS cumulativeValue 155 FROM $filtered f 156 LEFT JOIN $filtered c ON c.parentId = f.id 157 WHERE c.id IS NULL AND f.stackBits = $showStackBits 158 ) 159 SELECT id, cumulativeValue 160 FROM _graph_aggregating_scan!( 161 edges, 162 inits, 163 (cumulativeValue), 164 ( 165 SELECT 166 x.id, 167 x.childValue + IIF( 168 t.stackBits = $showStackBits, 169 t.value, 170 0 171 ) AS cumulativeValue 172 FROM ( 173 SELECT id, SUM(cumulativeValue) AS childValue 174 FROM $table 175 GROUP BY id 176 ) x 177 JOIN $filtered t USING (id) 178 ) 179 ) 180 ORDER BY id 181); 182 183CREATE PERFETTO MACRO _viz_flamegraph_s_prefix(col ColumnName) 184RETURNS Expr AS s.$col; 185 186-- Propogates the cumulative value of the pivot nodes to the roots 187-- and computes the "fingerprint" of the path. 188CREATE PERFETTO MACRO _viz_flamegraph_upwards_hash( 189 source TableOrSubquery, 190 filtered TableOrSubquery, 191 accumulated TableOrSubquery, 192 grouping _ColumnNameList, 193 grouped _ColumnNameList 194) 195RETURNS TableOrSubquery 196AS ( 197 WITH edges AS ( 198 SELECT id AS source_node_id, unpivotedParentId AS dest_node_id 199 FROM $filtered 200 WHERE unpivotedParentId IS NOT NULL 201 ), 202 inits AS ( 203 SELECT 204 f.id, 205 HASH(-1, s.groupingHash) AS hash, 206 NULL AS parentHash, 207 -1 AS depth, 208 a.cumulativeValue 209 FROM $filtered f 210 JOIN $source s USING (id) 211 JOIN $accumulated a USING (id) 212 WHERE s.isPivot AND a.cumulativeValue > 0 213 ) 214 SELECT 215 g.id, 216 g.hash, 217 g.parentHash, 218 g.depth, 219 s.name, 220 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouping), 221 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouped), 222 f.value, 223 g.cumulativeValue 224 FROM _graph_scan!( 225 edges, 226 inits, 227 (hash, parentHash, depth, cumulativeValue), 228 ( 229 SELECT 230 t.id, 231 HASH(t.hash, x.groupingHash) AS hash, 232 t.hash AS parentHash, 233 t.depth - 1 AS depth, 234 t.cumulativeValue 235 FROM $table t 236 JOIN $source x USING (id) 237 ) 238 ) g 239 JOIN $source s USING (id) 240 JOIN $filtered f USING (id) 241); 242 243-- Computes the "fingerprint" of the path by walking from the laves 244-- to the root. 245CREATE PERFETTO MACRO _viz_flamegraph_downwards_hash( 246 source TableOrSubquery, 247 filtered TableOrSubquery, 248 accumulated TableOrSubquery, 249 grouping _ColumnNameList, 250 grouped _ColumnNameList, 251 showDownward Expr 252) 253RETURNS TableOrSubquery 254AS ( 255 WITH 256 edges AS ( 257 SELECT parentId AS source_node_id, id AS dest_node_id 258 FROM $filtered 259 WHERE parentId IS NOT NULL 260 ), 261 inits AS ( 262 SELECT 263 f.id, 264 HASH(1, s.groupingHash) AS hash, 265 NULL AS parentHash, 266 1 AS depth 267 FROM $filtered f 268 JOIN $source s USING (id) 269 WHERE f.parentId IS NULL AND $showDownward 270 ) 271 SELECT 272 g.id, 273 g.hash, 274 g.parentHash, 275 g.depth, 276 s.name, 277 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouping), 278 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouped), 279 f.value, 280 a.cumulativeValue 281 FROM _graph_scan!( 282 edges, 283 inits, 284 (hash, parentHash, depth), 285 ( 286 SELECT 287 t.id, 288 HASH(t.hash, x.groupingHash) AS hash, 289 t.hash AS parentHash, 290 t.depth + 1 AS depth 291 FROM $table t 292 JOIN $source x USING (id) 293 ) 294 ) g 295 JOIN $source s USING (id) 296 JOIN $filtered f USING (id) 297 JOIN $accumulated a USING (id) 298 ORDER BY hash 299); 300 301CREATE PERFETTO MACRO _col_list_id(a ColumnName) 302RETURNS Expr AS $a; 303 304-- Converts a table of hashes and paretn hashes into ids and parent 305-- ids, grouping all hashes together. 306CREATE PERFETTO MACRO _viz_flamegraph_merge_hashes( 307 hashed TableOrSubquery, 308 grouping _ColumnNameList, 309 grouped_agged_exprs _ColumnNameList 310) 311RETURNS TableOrSubquery 312AS ( 313 SELECT 314 _auto_id AS id, 315 ( 316 SELECT p._auto_id 317 FROM $hashed p 318 WHERE p.hash = c.parentHash 319 LIMIT 1 320 ) AS parentId, 321 depth, 322 name, 323 -- The grouping columns should be passed through as-is because the 324 -- hash took them into account: we would not merged any nodes where 325 -- the grouping columns were different. 326 __intrinsic_token_apply!(_col_list_id, $grouping), 327 __intrinsic_token_apply!(_col_list_id, $grouped_agged_exprs), 328 SUM(value) AS value, 329 SUM(cumulativeValue) AS cumulativeValue 330 FROM $hashed c 331 GROUP BY hash 332); 333 334-- Performs a "layout" of nodes in the flamegraph relative to their 335-- siblings. 336CREATE PERFETTO MACRO _viz_flamegraph_local_layout( 337 merged TableOrSubquery 338) 339RETURNS TableOrSubquery 340AS ( 341 WITH partial_layout AS ( 342 SELECT 343 id, 344 cumulativeValue, 345 SUM(cumulativeValue) OVER win AS xEnd 346 FROM $merged 347 WHERE cumulativeValue > 0 348 WINDOW win AS ( 349 PARTITION BY parentId, depth 350 ORDER BY cumulativeValue DESC 351 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 352 ) 353 ) 354 SELECT id, xEnd - cumulativeValue as xStart, xEnd 355 FROM partial_layout 356 ORDER BY id 357); 358 359-- Walks the graph from root to leaf, propogating the layout of 360-- parents to their children. 361CREATE PERFETTO MACRO _viz_flamegraph_global_layout( 362 merged TableOrSubquery, 363 layout TableOrSubquery, 364 grouping _ColumnNameList, 365 grouped _ColumnNameList 366) 367RETURNS TableOrSubquery 368AS ( 369 WITH edges AS ( 370 SELECT parentId AS source_node_id, id AS dest_node_id 371 FROM $merged 372 WHERE parentId IS NOT NULL 373 ), 374 inits AS ( 375 SELECT h.id, 1 AS rootDistance, l.xStart, l.xEnd 376 FROM $merged h 377 JOIN $layout l USING (id) 378 WHERE h.parentId IS NULL 379 ) 380 SELECT 381 s.id, 382 IFNULL(s.parentId, -1) AS parentId, 383 IIF(s.name = '', 'unknown', s.name) AS name, 384 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouping), 385 __intrinsic_token_apply!(_viz_flamegraph_s_prefix, $grouped), 386 s.value AS selfValue, 387 s.cumulativeValue, 388 p.cumulativeValue AS parentCumulativeValue, 389 s.depth, 390 g.xStart, 391 g.xEnd 392 FROM _graph_scan!( 393 edges, 394 inits, 395 (rootDistance, xStart, xEnd), 396 ( 397 SELECT 398 t.id, 399 t.rootDistance + 1 as rootDistance, 400 t.xStart + w.xStart AS xStart, 401 t.xStart + w.xEnd AS xEnd 402 FROM $table t 403 JOIN $layout w USING (id) 404 ) 405 ) g 406 JOIN $merged s USING (id) 407 LEFT JOIN $merged p ON s.parentId = p.id 408 ORDER BY rootDistance, xStart 409); 410