Skip to content

Commit

Permalink
ui: add support for contextual columns in QueryFlamegraph
Browse files Browse the repository at this point in the history
This prepares for showing extra contextual properties in the
flamegraph which are not just the name/value (e.g. the mapping name,
line number etc).

Change-Id: I537d4541c22164c24237b32b492c8aad424a0a4d
  • Loading branch information
LalitMaganti committed Jul 22, 2024
1 parent e127b95 commit fdfe6df
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 49 deletions.
96 changes: 64 additions & 32 deletions src/trace_processor/perfetto_sql/stdlib/viz/flamegraph.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.

include perfetto module graphs.scan;
INCLUDE PERFETTO MODULE graphs.scan;
INCLUDE PERFETTO MODULE metasql.column_list;

-- For each frame in |tab|, returns a row containing the result of running
-- all the filtering operations over that frame's name.
Expand All @@ -24,7 +25,8 @@ CREATE PERFETTO MACRO _viz_flamegraph_prepare_filter(
show_from_frame Expr,
hide_frame Expr,
pivot Expr,
impossible_stack_bits Expr
impossible_stack_bits Expr,
grouping _ColumnNameList
)
RETURNS TableOrSubquery
AS (
Expand All @@ -33,7 +35,8 @@ AS (
IIF($hide_stack, $impossible_stack_bits, $show_stack) AS stackBits,
$show_from_frame As showFromFrameBits,
$hide_frame = 0 AS showFrame,
$pivot AS isPivot
$pivot AS isPivot,
HASH(name, _metasql_unparenthesize_column_list!($grouping)) AS groupingHash
FROM $tab
ORDER BY id
);
Expand Down Expand Up @@ -168,12 +171,17 @@ AS (
ORDER BY id
);

CREATE PERFETTO MACRO _viz_flamegraph_s_prefix(col ColumnName)
RETURNS _SqlFragment AS s.$col;

-- Propogates the cumulative value of the pivot nodes to the roots
-- and computes the "fingerprint" of the path.
CREATE PERFETTO MACRO _viz_flamegraph_upwards_hash(
source TableOrSubquery,
filtered TableOrSubquery,
accumulated TableOrSubquery
accumulated TableOrSubquery,
grouping _ColumnNameList,
grouped _ColumnNameList
)
RETURNS TableOrSubquery
AS (
Expand All @@ -185,7 +193,7 @@ AS (
inits AS (
SELECT
f.id,
HASH(-1, s.name) AS hash,
HASH(-1, s.groupingHash) AS hash,
NULL AS parentHash,
-1 AS depth,
a.cumulativeValue
Expand All @@ -202,6 +210,8 @@ AS (
g.parentHash,
g.depth,
s.name,
_metasql_map_join_column_list!($grouping, _viz_flamegraph_s_prefix),
_metasql_map_join_column_list!($grouped, _viz_flamegraph_s_prefix),
f.value,
g.cumulativeValue
FROM _graph_scan!(
Expand All @@ -211,7 +221,7 @@ AS (
(
SELECT
t.id,
HASH(t.hash, x.name) AS hash,
HASH(t.hash, x.groupingHash) AS hash,
t.hash AS parentHash,
t.depth - 1 AS depth,
t.cumulativeValue
Expand All @@ -228,31 +238,36 @@ AS (
CREATE PERFETTO MACRO _viz_flamegraph_downwards_hash(
source TableOrSubquery,
filtered TableOrSubquery,
accumulated TableOrSubquery
accumulated TableOrSubquery,
grouping _ColumnNameList,
grouped _ColumnNameList
)
RETURNS TableOrSubquery
AS (
WITH edges AS (
SELECT parentId AS source_node_id, id AS dest_node_id
FROM $filtered
WHERE parentId IS NOT NULL
),
inits AS (
SELECT
f.id,
HASH(1, s.name) AS hash,
NULL AS parentHash,
1 AS depth
FROM $filtered f
JOIN $source s USING (id)
WHERE f.parentId IS NULL
)
WITH
edges AS (
SELECT parentId AS source_node_id, id AS dest_node_id
FROM $filtered
WHERE parentId IS NOT NULL
),
inits AS (
SELECT
f.id,
HASH(1, s.groupingHash) AS hash,
NULL AS parentHash,
1 AS depth
FROM $filtered f
JOIN $source s USING (id)
WHERE f.parentId IS NULL
)
SELECT
g.id,
g.hash,
g.parentHash,
g.depth,
s.name,
_metasql_map_join_column_list!($grouping, _viz_flamegraph_s_prefix),
_metasql_map_join_column_list!($grouped, _viz_flamegraph_s_prefix),
f.value,
a.cumulativeValue
FROM _graph_scan!(
Expand All @@ -262,7 +277,7 @@ AS (
(
SELECT
t.id,
HASH(t.hash, x.name) AS hash,
HASH(t.hash, x.groupingHash) AS hash,
t.hash AS parentHash,
t.depth + 1 AS depth
FROM $table t
Expand All @@ -275,10 +290,18 @@ AS (
ORDER BY hash
);

CREATE PERFETTO MACRO _viz_flamegraph_merge_grouped(
col ColumnName
)
RETURNS _SqlFragment
AS IIF(COUNT() = 1, $col, NULL) AS $col;

-- Converts a table of hashes and paretn hashes into ids and parent
-- ids, grouping all hashes together.
CREATE PERFETTO MACRO _viz_flamegraph_merge_hashes(
hashed TableOrSubquery
hashed TableOrSubquery,
grouping _ColumnNameList,
grouped _ColumnNameList
)
RETURNS TableOrSubquery
AS (
Expand All @@ -292,6 +315,11 @@ AS (
) AS parentId,
depth,
name,
-- The grouping columns should be passed through as-is because the
-- hash took them into account: we would not merged any nodes where
-- the grouping columns were different.
_metasql_unparenthesize_column_list!($grouping),
_metasql_map_join_column_list!($grouped, _viz_flamegraph_merge_grouped),
SUM(value) AS value,
SUM(cumulativeValue) AS cumulativeValue
FROM $hashed c
Expand Down Expand Up @@ -327,7 +355,9 @@ AS (
-- parents to their children.
CREATE PERFETTO MACRO _viz_flamegraph_global_layout(
merged TableOrSubquery,
layout TableOrSubquery
layout TableOrSubquery,
grouping _ColumnNameList,
grouped _ColumnNameList
)
RETURNS TableOrSubquery
AS (
Expand All @@ -343,12 +373,14 @@ AS (
WHERE h.parentId IS NULL
)
SELECT
h.id,
IFNULL(h.parentId, -1) AS parentId,
IIF(h.name = '', 'unknown', h.name) AS name,
h.value AS selfValue,
h.cumulativeValue,
h.depth,
s.id,
IFNULL(s.parentId, -1) AS parentId,
IIF(s.name = '', 'unknown', s.name) AS name,
_metasql_map_join_column_list!($grouping, _viz_flamegraph_s_prefix),
_metasql_map_join_column_list!($grouped, _viz_flamegraph_s_prefix),
s.value AS selfValue,
s.cumulativeValue,
s.depth,
g.xStart,
g.xEnd
FROM _graph_scan!(
Expand All @@ -364,6 +396,6 @@ AS (
JOIN $layout w USING (id)
)
) g
JOIN $merged h USING (id)
JOIN $merged s USING (id)
ORDER BY depth, xStart
);
83 changes: 66 additions & 17 deletions ui/src/core/query_flamegraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ import {Monitor} from '../base/monitor';
import {featureFlags} from './feature_flags';
import {uuidv4Sql} from '../base/uuid';

interface QueryFlamegraphMetric {
export interface QueryFlamegraphColumn {
// The name of the column in SQL.
readonly name: string;

// The human readable name describing the contents of the column.
readonly displayName: string;
}

export interface QueryFlamegraphMetric {
// The human readable name of the metric: will be shown to the user to change
// between metrics.
readonly name: string;
Expand All @@ -43,8 +51,26 @@ interface QueryFlamegraphMetric {
readonly dependencySql?: string;

// A single SQL statement which returns the columns `id`, `parentId`, `name`
// and `selfValue`
// `selfValue`, all columns specified by `unaggregatableProperties` and
// `aggregatableProperties`.
readonly statement: string;

// Additional contextual columns containing data which should not be merged
// between sibling nodes, even if they have the same name.
//
// Examples include the mapping that a name comes from, the heap graph root
// type etc.
//
// Note: the name is always unaggregatable and should not be specified here.
readonly unaggregatableProperties?: ReadonlyArray<QueryFlamegraphColumn>;

// Additional contextual columns containing data which will be displayed to
// the user if there is no merging. If there is merging, currently the value
// will not be shown.
//
// TODO(lalitm): reconsider the decision to show nothing, instead maybe show
// the top 5 options etc.
readonly aggregatableProperties?: ReadonlyArray<QueryFlamegraphColumn>;
}

// Given a table and columns on those table (corresponding to metrics),
Expand Down Expand Up @@ -124,26 +150,25 @@ export class QueryFlamegraph implements m.ClassComponent<QueryFlamegraphAttrs> {
}

private async fetchData(attrs: QueryFlamegraphAttrs) {
const {statement, dependencySql} = assertExists(
const metric = assertExists(
attrs.metrics.find((metric) => metric.name === this.selectedMetricName),
);
const engine = attrs.engine;
const filters = this.filters;
this.queryLimiter.schedule(async () => {
this.data = await computeFlamegraphTree(
engine,
dependencySql,
statement,
filters,
);
this.data = await computeFlamegraphTree(engine, metric, filters);
});
}
}

async function computeFlamegraphTree(
engine: Engine,
dependencySql: string | undefined,
sql: string,
{
dependencySql,
statement,
unaggregatableProperties,
aggregatableProperties,
}: QueryFlamegraphMetric,
{showStack, hideStack, showFromFrame, hideFrame, pivot}: FlamegraphFilters,
) {
// Pivot also essentially acts as a "show stack" filter so treat it like one.
Expand Down Expand Up @@ -180,6 +205,12 @@ async function computeFlamegraphTree(

const pivotFilter = pivot === undefined ? '0' : `name like '%${pivot}%'`;

const unagg = unaggregatableProperties ?? [];
const agg = aggregatableProperties ?? [];

const groupingColumns = `(${(unagg.length === 0 ? ['groupingColumn'] : unagg).join()})`;
const groupedColumns = `(${(agg.length === 0 ? ['groupedColumn'] : agg).join()})`;

if (dependencySql !== undefined) {
await engine.query(dependencySql);
}
Expand All @@ -196,13 +227,23 @@ async function computeFlamegraphTree(
`
select *
from _viz_flamegraph_prepare_filter!(
(${sql}),
(
select
id,
parentId,
name,
value,
${(unagg.length === 0 ? ['0 as groupingColumn'] : unagg).join()},
${(agg.length === 0 ? ['0 as groupedColumn'] : agg).join()}
FROM (${statement})
),
(${showStackFilter}),
(${hideStackFilter}),
(${showFromFrameFilter}),
(${hideFrameFilter}),
(${pivotFilter}),
${1 << showStackAndPivot.length}
${1 << showStackAndPivot.length},
${groupingColumns}
)
`,
),
Expand Down Expand Up @@ -244,14 +285,18 @@ async function computeFlamegraphTree(
from _viz_flamegraph_downwards_hash!(
_flamegraph_source_${uuid},
_flamegraph_filtered_${uuid},
_flamegraph_accumulated_${uuid}
_flamegraph_accumulated_${uuid},
${groupingColumns},
${groupedColumns}
)
union all
select *
from _viz_flamegraph_upwards_hash!(
_flamegraph_source_${uuid},
_flamegraph_filtered_${uuid},
_flamegraph_accumulated_${uuid}
_flamegraph_accumulated_${uuid},
${groupingColumns},
${groupedColumns}
)
order by hash
`,
Expand All @@ -264,7 +309,9 @@ async function computeFlamegraphTree(
`
select *
from _viz_flamegraph_merge_hashes!(
_flamegraph_hash_${uuid}
_flamegraph_hash_${uuid},
${groupingColumns},
${groupedColumns}
)
`,
),
Expand All @@ -285,7 +332,9 @@ async function computeFlamegraphTree(
select *
from _viz_flamegraph_global_layout!(
_flamegraph_merged_${uuid},
_flamegraph_layout_${uuid}
_flamegraph_layout_${uuid},
${groupingColumns},
${groupedColumns}
)
`);
const it = res.iter({
Expand Down

0 comments on commit fdfe6df

Please sign in to comment.