Skip to content

Commit ddfd2b2

Browse files
authored
Merge pull request #1757 from Kobzol/perfetto-link
Add links to Perfetto to visualize self-profile query traces
2 parents 77b717d + 6dd9447 commit ddfd2b2

File tree

5 files changed

+235
-65
lines changed

5 files changed

+235
-65
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script setup lang="ts">
2+
import {computed} from "vue";
3+
import {openTraceInPerfetto} from "../perfetto";
4+
import {chromeProfileUrl} from "../self-profile";
5+
import {CompileTestCase} from "../pages/compare/compile/common";
6+
import {ArtifactDescription} from "../pages/compare/types";
7+
8+
const props = defineProps<{
9+
artifact: ArtifactDescription;
10+
testCase: CompileTestCase;
11+
}>();
12+
13+
const link = computed(() => {
14+
return chromeProfileUrl(
15+
props.artifact.commit,
16+
`${props.testCase.benchmark}-${props.testCase.profile.toLowerCase()}`,
17+
props.testCase.scenario
18+
);
19+
});
20+
21+
// This title will appear as the trace name in Perfetto
22+
const traceTitle = computed(() => {
23+
return `${props.testCase.benchmark}-${props.testCase.profile}-${props.testCase.scenario} (${props.artifact.commit})`;
24+
});
25+
</script>
26+
27+
<template>
28+
<a
29+
@click="openTraceInPerfetto(link, traceTitle)"
30+
title="Open the self-profile query trace in Perfetto. You have to wait a bit for the profile to be downloaded after clicking on the link."
31+
><slot></slot
32+
></a>
33+
</template>
34+
35+
<style scoped lang="scss">
36+
a {
37+
cursor: pointer;
38+
}
39+
</style>

site/frontend/src/pages/compare/compile/table/benchmark-detail.vue

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {GraphData, GraphKind, GraphsSelector} from "../../../../graph/data";
1414
import uPlot from "uplot";
1515
import CachegrindCmd from "../../../../components/cachegrind-cmd.vue";
1616
import {COMPILE_DETAIL_RESOLVER} from "./detail-resolver";
17+
import PerfettoLink from "../../../../components/perfetto-link.vue";
1718
1819
const props = defineProps<{
1920
testCase: CompileTestCase;
@@ -327,21 +328,31 @@ onMounted(() => renderGraphs());
327328
</a>
328329
</li>
329330
<li>
330-
<a
331-
:href="graphLink(props.artifact, props.metric, props.testCase)"
332-
target="_blank"
331+
Before:
332+
<a :href="detailedQueryLink(props.baseArtifact)" target="_blank">
333+
self-profile</a
334+
>,
335+
<PerfettoLink
336+
:artifact="props.baseArtifact"
337+
:test-case="props.testCase"
338+
>query trace</PerfettoLink
333339
>
334-
History graph
335-
</a>
336340
</li>
337341
<li>
338-
<a :href="detailedQueryLink(props.baseArtifact)" target="_blank">
339-
Rustc self-profile: baseline commit
340-
</a>
342+
After:
343+
<a :href="detailedQueryLink(props.artifact)" target="_blank"
344+
>self-profile</a
345+
>,
346+
<PerfettoLink :artifact="props.artifact" :test-case="props.testCase"
347+
>query trace</PerfettoLink
348+
>
341349
</li>
342350
<li>
343-
<a :href="detailedQueryLink(props.artifact)" target="_blank">
344-
Rustc self-profile: benchmarked commit
351+
<a
352+
:href="graphLink(props.artifact, props.metric, props.testCase)"
353+
target="_blank"
354+
>
355+
History graph
345356
</a>
346357
</li>
347358
<li>

site/frontend/src/pages/detailed-query.ts

Lines changed: 110 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import {createUrlWithAppendedParams, getUrlParams} from "../utils/navigation";
22
import {postMsgpack} from "../utils/requests";
33
import {SELF_PROFILE_DATA_URL} from "../urls";
4+
import {
5+
chromeProfileUrl,
6+
processedSelfProfileRelativeUrl,
7+
} from "../self-profile";
8+
import {openTraceInPerfetto} from "../perfetto";
49

510
function to_seconds(time) {
611
return time / 1000000000;
@@ -41,6 +46,106 @@ function fmt_delta(to, delta, is_integral_delta) {
4146
)}${delta.toFixed(3)}">${text}</span>`;
4247
}
4348

49+
function raw_self_profile_link(
50+
commit: string,
51+
benchmark: string,
52+
scenario: string
53+
): string {
54+
const url = `/perf/download-raw-self-profile?commit=${commit}&benchmark=${benchmark}&scenario=${scenario}`;
55+
return `<a href="${url}">raw</a>`;
56+
}
57+
58+
function processed_link(
59+
commit: string,
60+
benchmark: string,
61+
scenario: string,
62+
type: string
63+
): string {
64+
const url = processedSelfProfileRelativeUrl(
65+
commit,
66+
benchmark,
67+
scenario,
68+
type
69+
);
70+
return `<a href="${url}">${type}</a>`;
71+
}
72+
73+
// FIXME: remove this hack and use the PerfettoLink component once this page is
74+
// converted to Vue.
75+
function perfetto_profiler_link(
76+
id: string,
77+
commit: string,
78+
benchmark: string,
79+
scenario: string
80+
): string {
81+
let link = chromeProfileUrl(commit, benchmark, scenario);
82+
let traceTitle = `${benchmark}-${scenario} (${commit})`;
83+
document.addEventListener("click", (event) => {
84+
if ((event.target as HTMLElement).id === id) {
85+
openTraceInPerfetto(link, traceTitle);
86+
}
87+
});
88+
return `<a href="#" id="${id}">Perfetto</a>`;
89+
}
90+
91+
function firefox_profiler_link(
92+
commit: string,
93+
benchmark: string,
94+
scenario: string
95+
): string {
96+
let crox_url = encodeURIComponent(
97+
chromeProfileUrl(commit, benchmark, scenario)
98+
);
99+
let ff_url = `https://profiler.firefox.com/from-url/${crox_url}/marker-chart/?v=5`;
100+
return `<a href="${ff_url}">Firefox profiler</a>`;
101+
}
102+
103+
function createDownloadLinks(
104+
state: Selector,
105+
commit: string,
106+
label: string
107+
): string {
108+
const raw = raw_self_profile_link(
109+
state.commit,
110+
state.benchmark,
111+
state.scenario
112+
);
113+
const flamegraph_link = processed_link(
114+
commit,
115+
state.benchmark,
116+
state.scenario,
117+
"flamegraph"
118+
);
119+
const crox_link = processed_link(
120+
commit,
121+
state.benchmark,
122+
state.scenario,
123+
"crox"
124+
);
125+
const codegen = processed_link(
126+
commit,
127+
state.benchmark,
128+
state.scenario,
129+
"codegen-schedule"
130+
);
131+
const perfetto = perfetto_profiler_link(
132+
`perfetto-${label}`,
133+
commit,
134+
state.benchmark,
135+
state.scenario
136+
);
137+
const firefox = firefox_profiler_link(
138+
state.base_commit,
139+
state.benchmark,
140+
state.scenario
141+
);
142+
143+
return `Download/view ${raw}, ${flamegraph_link}, ${crox_link}, ${codegen} (${perfetto}, ${firefox}) results for ${commit.substring(
144+
0,
145+
10
146+
)} (${label} commit)`;
147+
}
148+
44149
function populate_data(data, state: Selector) {
45150
let txt = `${state.commit.substring(0, 10)}: Self profile results for ${
46151
state.benchmark
@@ -55,65 +160,15 @@ function populate_data(data, state: Selector) {
55160
txt += `<br><a href="${self_href}">query info for just this commit</a>`;
56161
}
57162
document.querySelector("#title").innerHTML = txt;
58-
let dl_link = (commit, bench, run) => {
59-
let url = `/perf/download-raw-self-profile?commit=${commit}&benchmark=${bench}&scenario=${run}`;
60-
return `<a href="${url}">raw</a>`;
61-
};
62-
let processed_url = (commit, bench, run, ty) => {
63-
return `/perf/processed-self-profile?commit=${commit}&benchmark=${bench}&scenario=${run}&type=${ty}`;
64-
};
65-
let processed_link = (commit, {benchmark, scenario}, ty) => {
66-
let url = processed_url(commit, benchmark, scenario, ty);
67-
return `<a href="${url}">${ty}</a>`;
68-
};
69-
let processed_crox_url = (commit, bench, run) => {
70-
let crox_url =
71-
window.location.origin + processed_url(commit, bench, run, "crox");
72-
return encodeURIComponent(crox_url);
73-
};
74-
let firefox_profiler_link = (commit, bench, run) => {
75-
let crox_url = processed_crox_url(commit, bench, run);
76-
let ff_url = `https://profiler.firefox.com/from-url/${crox_url}/marker-chart/?v=5`;
77-
return `<a href="${ff_url}">Firefox profiler</a>`;
78-
};
163+
79164
txt = "";
80165
if (state.base_commit) {
81-
txt += `Download/view
82-
${dl_link(
83-
state.base_commit,
84-
state.benchmark,
85-
state.scenario
86-
)},
87-
${processed_link(state.base_commit, state, "flamegraph")},
88-
${processed_link(state.base_commit, state, "crox")},
89-
${processed_link(
90-
state.base_commit,
91-
state,
92-
"codegen-schedule"
93-
)}
94-
(${firefox_profiler_link(
95-
state.base_commit,
96-
state.benchmark,
97-
state.scenario
98-
)})
99-
results for ${state.base_commit.substring(
100-
0,
101-
10
102-
)} (base commit)`;
166+
txt += createDownloadLinks(state, state.base_commit, "base");
103167
txt += "<br>";
104168
}
105-
txt += `Download/view
106-
${dl_link(state.commit, state.benchmark, state.scenario)},
107-
${processed_link(state.commit, state, "flamegraph")},
108-
${processed_link(state.commit, state, "crox")},
109-
${processed_link(state.commit, state, "codegen-schedule")}
110-
(${firefox_profiler_link(
111-
state.commit,
112-
state.benchmark,
113-
state.scenario
114-
)})
115-
results for ${state.commit.substring(0, 10)} (new commit)`;
116-
// TODO: use the Cachegrind Vue components once this page is refactored to Vue
169+
txt += createDownloadLinks(state, state.commit, "new");
170+
171+
// FIXME: use the Cachegrind Vue components once this page is refactored to Vue
117172
let profile = (b) =>
118173
b.endsWith("-opt")
119174
? "Opt"

site/frontend/src/perfetto.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Code for displaying post-processed self-profile traces in perfetto.dev.
2+
// The process of linking to perfetto is explained at
3+
// https://perfetto.dev/docs/visualization/deep-linking-to-perfetto-ui.
4+
// This code is adapted from https://llvm-compile-time-tracker.com/compare.php
5+
const ORIGIN = "https://ui.perfetto.dev";
6+
7+
export async function openTraceInPerfetto(traceUrl: string, title: string) {
8+
const resp = await fetch(traceUrl);
9+
const blob = await resp.blob();
10+
const arrayBuffer = await blob.arrayBuffer();
11+
openTrace(arrayBuffer, title);
12+
}
13+
14+
function openTrace(arrayBuffer: ArrayBuffer, title: string) {
15+
const win = window.open(ORIGIN);
16+
if (win === null) {
17+
return;
18+
}
19+
20+
const timer = setInterval(() => win.postMessage("PING", ORIGIN), 50);
21+
22+
const onMessageHandler = (evt) => {
23+
if (evt.data !== "PONG") return;
24+
25+
// We got a PONG, the UI is ready.
26+
window.clearInterval(timer);
27+
window.removeEventListener("message", onMessageHandler);
28+
29+
win.postMessage(
30+
{
31+
perfetto: {
32+
buffer: arrayBuffer,
33+
title,
34+
},
35+
},
36+
ORIGIN
37+
);
38+
};
39+
40+
window.addEventListener("message", onMessageHandler);
41+
}

site/frontend/src/self-profile.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Returns the URL to a measureme self-profile data, processed into the
2+
// Chrome profiler format.
3+
export function chromeProfileUrl(
4+
commit: string,
5+
benchmarkAndProfile: string,
6+
scenario: string
7+
): string {
8+
const relativeUrl = processedSelfProfileRelativeUrl(
9+
commit,
10+
benchmarkAndProfile,
11+
scenario,
12+
"crox"
13+
);
14+
return window.location.origin + relativeUrl;
15+
}
16+
17+
export function processedSelfProfileRelativeUrl(
18+
commit: string,
19+
benchmarkAndProfile: string,
20+
scenario: string,
21+
type: string
22+
): string {
23+
return `/perf/processed-self-profile?commit=${commit}&benchmark=${benchmarkAndProfile}&scenario=${scenario}&type=${type}`;
24+
}

0 commit comments

Comments
 (0)