Skip to content

Add links to Perfetto to visualize self-profile query traces #1757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions site/frontend/src/components/perfetto-link.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import {computed} from "vue";
import {openTraceInPerfetto} from "../perfetto";
import {chromeProfileUrl} from "../self-profile";
import {CompileTestCase} from "../pages/compare/compile/common";
import {ArtifactDescription} from "../pages/compare/types";

const props = defineProps<{
artifact: ArtifactDescription;
testCase: CompileTestCase;
}>();

const link = computed(() => {
return chromeProfileUrl(
props.artifact.commit,
`${props.testCase.benchmark}-${props.testCase.profile.toLowerCase()}`,
props.testCase.scenario
);
});

// This title will appear as the trace name in Perfetto
const traceTitle = computed(() => {
return `${props.testCase.benchmark}-${props.testCase.profile}-${props.testCase.scenario} (${props.artifact.commit})`;
});
</script>

<template>
<a
@click="openTraceInPerfetto(link, traceTitle)"
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."
><slot></slot
></a>
</template>

<style scoped lang="scss">
a {
cursor: pointer;
}
</style>
31 changes: 21 additions & 10 deletions site/frontend/src/pages/compare/compile/table/benchmark-detail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {GraphData, GraphKind, GraphsSelector} from "../../../../graph/data";
import uPlot from "uplot";
import CachegrindCmd from "../../../../components/cachegrind-cmd.vue";
import {COMPILE_DETAIL_RESOLVER} from "./detail-resolver";
import PerfettoLink from "../../../../components/perfetto-link.vue";

const props = defineProps<{
testCase: CompileTestCase;
Expand Down Expand Up @@ -327,21 +328,31 @@ onMounted(() => renderGraphs());
</a>
</li>
<li>
<a
:href="graphLink(props.artifact, props.metric, props.testCase)"
target="_blank"
Before:
<a :href="detailedQueryLink(props.baseArtifact)" target="_blank">
self-profile</a
>,
<PerfettoLink
:artifact="props.baseArtifact"
:test-case="props.testCase"
>query trace</PerfettoLink
>
History graph
</a>
</li>
<li>
<a :href="detailedQueryLink(props.baseArtifact)" target="_blank">
Rustc self-profile: baseline commit
</a>
After:
<a :href="detailedQueryLink(props.artifact)" target="_blank"
>self-profile</a
>,
<PerfettoLink :artifact="props.artifact" :test-case="props.testCase"
>query trace</PerfettoLink
>
</li>
<li>
<a :href="detailedQueryLink(props.artifact)" target="_blank">
Rustc self-profile: benchmarked commit
<a
:href="graphLink(props.artifact, props.metric, props.testCase)"
target="_blank"
>
History graph
</a>
</li>
<li>
Expand Down
165 changes: 110 additions & 55 deletions site/frontend/src/pages/detailed-query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {createUrlWithAppendedParams, getUrlParams} from "../utils/navigation";
import {postMsgpack} from "../utils/requests";
import {SELF_PROFILE_DATA_URL} from "../urls";
import {
chromeProfileUrl,
processedSelfProfileRelativeUrl,
} from "../self-profile";
import {openTraceInPerfetto} from "../perfetto";

function to_seconds(time) {
return time / 1000000000;
Expand Down Expand Up @@ -41,6 +46,106 @@ function fmt_delta(to, delta, is_integral_delta) {
)} ≈ ${delta.toFixed(3)}">${text}</span>`;
}

function raw_self_profile_link(
commit: string,
benchmark: string,
scenario: string
): string {
const url = `/perf/download-raw-self-profile?commit=${commit}&benchmark=${benchmark}&scenario=${scenario}`;
return `<a href="${url}">raw</a>`;
}

function processed_link(
commit: string,
benchmark: string,
scenario: string,
type: string
): string {
const url = processedSelfProfileRelativeUrl(
commit,
benchmark,
scenario,
type
);
return `<a href="${url}">${type}</a>`;
}

// FIXME: remove this hack and use the PerfettoLink component once this page is
// converted to Vue.
function perfetto_profiler_link(
id: string,
commit: string,
benchmark: string,
scenario: string
): string {
let link = chromeProfileUrl(commit, benchmark, scenario);
let traceTitle = `${benchmark}-${scenario} (${commit})`;
document.addEventListener("click", (event) => {
if ((event.target as HTMLElement).id === id) {
openTraceInPerfetto(link, traceTitle);
}
});
return `<a href="#" id="${id}">Perfetto</a>`;
}

function firefox_profiler_link(
commit: string,
benchmark: string,
scenario: string
): string {
let crox_url = encodeURIComponent(
chromeProfileUrl(commit, benchmark, scenario)
);
let ff_url = `https://profiler.firefox.com/from-url/${crox_url}/marker-chart/?v=5`;
return `<a href="${ff_url}">Firefox profiler</a>`;
}

function createDownloadLinks(
state: Selector,
commit: string,
label: string
): string {
const raw = raw_self_profile_link(
state.commit,
state.benchmark,
state.scenario
);
const flamegraph_link = processed_link(
commit,
state.benchmark,
state.scenario,
"flamegraph"
);
const crox_link = processed_link(
commit,
state.benchmark,
state.scenario,
"crox"
);
const codegen = processed_link(
commit,
state.benchmark,
state.scenario,
"codegen-schedule"
);
const perfetto = perfetto_profiler_link(
`perfetto-${label}`,
commit,
state.benchmark,
state.scenario
);
const firefox = firefox_profiler_link(
state.base_commit,
state.benchmark,
state.scenario
);

return `Download/view ${raw}, ${flamegraph_link}, ${crox_link}, ${codegen} (${perfetto}, ${firefox}) results for ${commit.substring(
0,
10
)} (${label} commit)`;
}

function populate_data(data, state: Selector) {
let txt = `${state.commit.substring(0, 10)}: Self profile results for ${
state.benchmark
Expand All @@ -55,65 +160,15 @@ function populate_data(data, state: Selector) {
txt += `<br><a href="${self_href}">query info for just this commit</a>`;
}
document.querySelector("#title").innerHTML = txt;
let dl_link = (commit, bench, run) => {
let url = `/perf/download-raw-self-profile?commit=${commit}&benchmark=${bench}&scenario=${run}`;
return `<a href="${url}">raw</a>`;
};
let processed_url = (commit, bench, run, ty) => {
return `/perf/processed-self-profile?commit=${commit}&benchmark=${bench}&scenario=${run}&type=${ty}`;
};
let processed_link = (commit, {benchmark, scenario}, ty) => {
let url = processed_url(commit, benchmark, scenario, ty);
return `<a href="${url}">${ty}</a>`;
};
let processed_crox_url = (commit, bench, run) => {
let crox_url =
window.location.origin + processed_url(commit, bench, run, "crox");
return encodeURIComponent(crox_url);
};
let firefox_profiler_link = (commit, bench, run) => {
let crox_url = processed_crox_url(commit, bench, run);
let ff_url = `https://profiler.firefox.com/from-url/${crox_url}/marker-chart/?v=5`;
return `<a href="${ff_url}">Firefox profiler</a>`;
};

txt = "";
if (state.base_commit) {
txt += `Download/view
${dl_link(
state.base_commit,
state.benchmark,
state.scenario
)},
${processed_link(state.base_commit, state, "flamegraph")},
${processed_link(state.base_commit, state, "crox")},
${processed_link(
state.base_commit,
state,
"codegen-schedule"
)}
(${firefox_profiler_link(
state.base_commit,
state.benchmark,
state.scenario
)})
results for ${state.base_commit.substring(
0,
10
)} (base commit)`;
txt += createDownloadLinks(state, state.base_commit, "base");
txt += "<br>";
}
txt += `Download/view
${dl_link(state.commit, state.benchmark, state.scenario)},
${processed_link(state.commit, state, "flamegraph")},
${processed_link(state.commit, state, "crox")},
${processed_link(state.commit, state, "codegen-schedule")}
(${firefox_profiler_link(
state.commit,
state.benchmark,
state.scenario
)})
results for ${state.commit.substring(0, 10)} (new commit)`;
// TODO: use the Cachegrind Vue components once this page is refactored to Vue
txt += createDownloadLinks(state, state.commit, "new");

// FIXME: use the Cachegrind Vue components once this page is refactored to Vue
let profile = (b) =>
b.endsWith("-opt")
? "Opt"
Expand Down
41 changes: 41 additions & 0 deletions site/frontend/src/perfetto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Code for displaying post-processed self-profile traces in perfetto.dev.
// The process of linking to perfetto is explained at
// https://perfetto.dev/docs/visualization/deep-linking-to-perfetto-ui.
// This code is adapted from https://llvm-compile-time-tracker.com/compare.php
const ORIGIN = "https://ui.perfetto.dev";

export async function openTraceInPerfetto(traceUrl: string, title: string) {
const resp = await fetch(traceUrl);
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, title);
}

function openTrace(arrayBuffer: ArrayBuffer, title: string) {
const win = window.open(ORIGIN);
if (win === null) {
return;
}

const timer = setInterval(() => win.postMessage("PING", ORIGIN), 50);

const onMessageHandler = (evt) => {
if (evt.data !== "PONG") return;

// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener("message", onMessageHandler);

win.postMessage(
{
perfetto: {
buffer: arrayBuffer,
title,
},
},
ORIGIN
);
};

window.addEventListener("message", onMessageHandler);
}
24 changes: 24 additions & 0 deletions site/frontend/src/self-profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Returns the URL to a measureme self-profile data, processed into the
// Chrome profiler format.
export function chromeProfileUrl(
commit: string,
benchmarkAndProfile: string,
scenario: string
): string {
const relativeUrl = processedSelfProfileRelativeUrl(
commit,
benchmarkAndProfile,
scenario,
"crox"
);
return window.location.origin + relativeUrl;
}

export function processedSelfProfileRelativeUrl(
commit: string,
benchmarkAndProfile: string,
scenario: string,
type: string
): string {
return `/perf/processed-self-profile?commit=${commit}&benchmark=${benchmarkAndProfile}&scenario=${scenario}&type=${type}`;
}