tp: add support for aggregate selection of perf samples
Change-Id: Ie4e81ea40947a014c4e8803e6685cac216c244e3
diff --git a/Android.bp b/Android.bp
index 11c9b2a..f6fb6ed 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13178,6 +13178,7 @@
"src/trace_processor/perfetto_sql/stdlib/linux/memory/general.sql",
"src/trace_processor/perfetto_sql/stdlib/linux/memory/high_watermark.sql",
"src/trace_processor/perfetto_sql/stdlib/linux/memory/process.sql",
+ "src/trace_processor/perfetto_sql/stdlib/linux/perf/samples.sql",
"src/trace_processor/perfetto_sql/stdlib/pkvm/hypervisor.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/casts.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql",
diff --git a/BUILD b/BUILD
index 24543ae..210c6c0 100644
--- a/BUILD
+++ b/BUILD
@@ -2708,6 +2708,14 @@
],
)
+# GN target: //src/trace_processor/perfetto_sql/stdlib/linux/perf:perf
+perfetto_filegroup(
+ name = "src_trace_processor_perfetto_sql_stdlib_linux_perf_perf",
+ srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/linux/perf/samples.sql",
+ ],
+)
+
# GN target: //src/trace_processor/perfetto_sql/stdlib/linux:linux
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_linux_linux",
@@ -2843,6 +2851,7 @@
":src_trace_processor_perfetto_sql_stdlib_linux_cpu_utilization_utilization",
":src_trace_processor_perfetto_sql_stdlib_linux_linux",
":src_trace_processor_perfetto_sql_stdlib_linux_memory_memory",
+ ":src_trace_processor_perfetto_sql_stdlib_linux_perf_perf",
":src_trace_processor_perfetto_sql_stdlib_pkvm_pkvm",
":src_trace_processor_perfetto_sql_stdlib_prelude_prelude",
":src_trace_processor_perfetto_sql_stdlib_sched_sched",
diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn
index 7c4dd47..e0fd42f 100644
--- a/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn
@@ -19,5 +19,6 @@
deps = [
"cpu",
"memory",
+ "perf",
]
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/perf/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/linux/perf/BUILD.gn
new file mode 100644
index 0000000..ca58438
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/linux/perf/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("perf") {
+ sources = [ "samples.sql" ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/perf/samples.sql b/src/trace_processor/perfetto_sql/stdlib/linux/perf/samples.sql
new file mode 100644
index 0000000..593542e
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/linux/perf/samples.sql
@@ -0,0 +1,39 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the 'License');
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an 'AS IS' BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+CREATE PERFETTO MACRO _perf_callsites_for_samples(samples TableOrSubquery)
+RETURNS TableOrSubquery
+AS
+(
+ WITH cs AS MATERIALIZED (
+ SELECT callsite_id, COUNT() cnt FROM $samples GROUP BY 1
+ )
+ SELECT
+ c.id,
+ c.parent_id AS parentId,
+ COALESCE(f.deobfuscated_name, f.name, '[unknown]') AS name,
+ IFNULL((SELECT cnt FROM cs WHERE cs.callsite_id = c.id), 0) AS self_count
+ FROM graph_reachable_dfs!(
+ (
+ SELECT
+ c.id AS source_node_id,
+ c.parent_id AS dest_node_id
+ FROM stack_profile_callsite c
+ ),
+ (SELECT callsite_id AS node_id FROM cs)
+ ) g
+ JOIN stack_profile_callsite c ON c.id = g.node_id
+ JOIN stack_profile_frame f ON c.frame_id = f.id
+);
diff --git a/ui/src/core_plugins/perf_samples_profile/index.ts b/ui/src/core_plugins/perf_samples_profile/index.ts
index 8124c1b..cf1703f 100644
--- a/ui/src/core_plugins/perf_samples_profile/index.ts
+++ b/ui/src/core_plugins/perf_samples_profile/index.ts
@@ -109,23 +109,15 @@
...metricsFromTableOrSubquery(
`
(
- with agg_callsites as (
- select p.callsite_id, count() as cnt
+ select *
+ from _perf_callsites_for_samples!((
+ select p.callsite_id
from perf_sample p
join thread t using (utid)
where p.ts >= ${leftTs}
and p.ts <= ${rightTs}
and t.upid = ${upid}
- group by p.callsite_id
- )
- select
- c.id,
- c.parent_id as parentId,
- ifnull(f.deobfuscated_name, f.name) as name,
- ifnull(cnt, 0) as self_count
- from stack_profile_callsite c
- join stack_profile_frame f on c.frame_id = f.id
- left join agg_callsites a on c.id = a.callsite_id
+ ))
)
`,
[
@@ -135,6 +127,7 @@
columnName: 'self_count',
},
],
+ 'INCLUDE PERFETTO MODULE linux.perf.samples',
),
],
};
diff --git a/ui/src/frontend/aggregation_tab.ts b/ui/src/frontend/aggregation_tab.ts
index f8889bb..4587aa6 100644
--- a/ui/src/frontend/aggregation_tab.ts
+++ b/ui/src/frontend/aggregation_tab.ts
@@ -27,10 +27,16 @@
LegacyFlamegraphDetailsPanel,
FlamegraphSelectionParams,
} from './legacy_flamegraph_panel';
-import {ProfileType, TrackState} from '../common/state';
+import {AreaSelection, ProfileType, TrackState} from '../common/state';
import {assertExists} from '../base/logging';
import {Monitor} from '../base/monitor';
import {PERF_SAMPLES_PROFILE_TRACK_KIND} from '../core/track_kinds';
+import {
+ QueryFlamegraph,
+ QueryFlamegraphAttrs,
+ USE_NEW_FLAMEGRAPH_IMPL,
+ metricsFromTableOrSubquery,
+} from '../core/query_flamegraph';
interface View {
key: string;
@@ -41,7 +47,8 @@
class AreaDetailsPanel implements m.ClassComponent {
private readonly monitor = new Monitor([() => globals.state.selection]);
private currentTab: string | undefined = undefined;
- private flamegraphSelection?: FlamegraphSelectionParams;
+ private flamegraphAttrs?: QueryFlamegraphAttrs;
+ private legacyFlamegraphSelection?: FlamegraphSelectionParams;
private getCurrentView(): string | undefined {
const types = this.getViews().map(({key}) => key);
@@ -62,18 +69,12 @@
}
private getViews(): View[] {
- const views = [];
+ const views: View[] = [];
- this.flamegraphSelection = this.computeFlamegraphSelection();
- if (this.flamegraphSelection !== undefined) {
- views.push({
- key: 'flamegraph_selection',
- name: 'Flamegraph Selection',
- content: m(LegacyFlamegraphDetailsPanel, {
- cache: globals.areaFlamegraphCache,
- selection: this.flamegraphSelection,
- }),
- });
+ if (USE_NEW_FLAMEGRAPH_IMPL.get()) {
+ this.addFlamegraphView(views);
+ } else {
+ this.addLegacyFlamegraphView(views);
}
for (const [key, value] of globals.aggregateDataStore.entries()) {
@@ -155,7 +156,78 @@
);
}
- private computeFlamegraphSelection() {
+ private addFlamegraphView(views: View[]) {
+ this.flamegraphAttrs = this.computeFlamegraphAttrs();
+ if (this.flamegraphAttrs === undefined) {
+ return;
+ }
+ views.push({
+ key: 'flamegraph_selection',
+ name: 'Flamegraph Selection',
+ content: m(QueryFlamegraph, this.flamegraphAttrs),
+ });
+ }
+
+ private computeFlamegraphAttrs() {
+ const currentSelection = globals.state.selection;
+ if (currentSelection.kind !== 'area') {
+ return undefined;
+ }
+ if (!this.monitor.ifStateChanged()) {
+ // If the selection has not changed, just return a copy of the last seen
+ // attrs.
+ return this.flamegraphAttrs;
+ }
+ const upids = getUpidsFromAreaSelection(currentSelection);
+ if (upids.length === 0) {
+ return undefined;
+ }
+ return {
+ engine: assertExists(this.getCurrentEngine()),
+ metrics: [
+ ...metricsFromTableOrSubquery(
+ `
+ (
+ select *
+ from _perf_callsites_for_samples!((
+ select p.callsite_id
+ from perf_sample p
+ join thread t using (utid)
+ where p.ts >= ${currentSelection.start}
+ and p.ts <= ${currentSelection.end}
+ and t.upid in (${upids.join(',')})
+ ))
+ )
+ `,
+ [
+ {
+ name: 'Perf Samples',
+ unit: '',
+ columnName: 'self_count',
+ },
+ ],
+ 'INCLUDE PERFETTO MODULE linux.perf.samples',
+ ),
+ ],
+ };
+ }
+
+ private addLegacyFlamegraphView(views: View[]) {
+ this.legacyFlamegraphSelection = this.computeLegacyFlamegraphSelection();
+ if (this.legacyFlamegraphSelection === undefined) {
+ return;
+ }
+ views.push({
+ key: 'flamegraph_selection',
+ name: 'Flamegraph Selection',
+ content: m(LegacyFlamegraphDetailsPanel, {
+ cache: globals.areaFlamegraphCache,
+ selection: this.legacyFlamegraphSelection,
+ }),
+ });
+ }
+
+ private computeLegacyFlamegraphSelection() {
const currentSelection = globals.state.selection;
if (currentSelection.kind !== 'area') {
return undefined;
@@ -163,17 +235,9 @@
if (!this.monitor.ifStateChanged()) {
// If the selection has not changed, just return a copy of the last seen
// selection.
- return this.flamegraphSelection;
+ return this.legacyFlamegraphSelection;
}
- const upids = [];
- for (const trackId of currentSelection.tracks) {
- const track: TrackState | undefined = globals.state.tracks[trackId];
- const trackInfo = globals.trackManager.resolveTrackInfo(track?.uri);
- if (trackInfo?.kind !== PERF_SAMPLES_PROFILE_TRACK_KIND) {
- continue;
- }
- upids.push(assertExists(trackInfo.upid));
- }
+ const upids = getUpidsFromAreaSelection(currentSelection);
if (upids.length === 0) {
return undefined;
}
@@ -184,6 +248,12 @@
upids,
};
}
+
+ private getCurrentEngine() {
+ const engineId = globals.getCurrentEngine()?.id;
+ if (engineId === undefined) return undefined;
+ return globals.engines.get(engineId);
+ }
}
export class AggregationsTabs implements Disposable {
@@ -207,3 +277,16 @@
this.trash.dispose();
}
}
+
+function getUpidsFromAreaSelection(currentSelection: AreaSelection) {
+ const upids = [];
+ for (const trackId of currentSelection.tracks) {
+ const track: TrackState | undefined = globals.state.tracks[trackId];
+ const trackInfo = globals.trackManager.resolveTrackInfo(track?.uri);
+ if (trackInfo?.kind !== PERF_SAMPLES_PROFILE_TRACK_KIND) {
+ continue;
+ }
+ upids.push(assertExists(trackInfo.upid));
+ }
+ return upids;
+}