diff --git a/site/frontend/src/pages/detailed-query.ts b/site/frontend/src/pages/detailed-query.ts index f32b35199..414d81e6e 100644 --- a/site/frontend/src/pages/detailed-query.ts +++ b/site/frontend/src/pages/detailed-query.ts @@ -1,443 +1,8 @@ -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; -} - -function fmt_delta(to, delta, is_integral_delta) { - let from = to - delta; - let pct = ((to - from) / from) * 100; - if (from == to) { - pct = 0; - } - let classes; - if (pct > 1) { - classes = "positive"; - } else if (pct < -1) { - classes = "negative"; - } else { - classes = "neutral"; - } - // some arbitrary "small" value - // ignore this because it's not too interesting likely - if (Math.abs(delta) <= 0.05) { - classes = "neutral"; - } - let text; - if (is_integral_delta) { - text = delta.toString(); - } else { - text = delta.toFixed(3); - } - if (pct != Infinity && pct != -Infinity) { - text += `(${pct.toFixed(1)}%)`.padStart(10, " "); - } else { - text += `-`.padStart(10, " "); - } - return `${text}`; -} - -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 `raw`; -} - -function processed_link( - commit: string, - benchmark: string, - scenario: string, - type: string -): string { - const url = processedSelfProfileRelativeUrl( - commit, - benchmark, - scenario, - type - ); - return `${type}`; -} - -// 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 `Perfetto`; -} - -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 `Firefox profiler`; -} - -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 - } run ${state.scenario}`; - if (state.base_commit) { - let self_href = `/detailed-query.html?sort_idx=${state.sort_idx}&commit=${state.commit}&scenario=${state.scenario}&benchmark=${state.benchmark}`; - let base_href = `/detailed-query.html?sort_idx=${state.sort_idx}&commit=${state.base_commit}&scenario=${state.scenario}&benchmark=${state.benchmark}`; - txt += `
diff vs base ${state.base_commit.substring( - 0, - 10 - )}, query info for just base commit`; - txt += `
query info for just this commit`; - } - document.querySelector("#title").innerHTML = txt; - - txt = ""; - if (state.base_commit) { - txt += createDownloadLinks(state, state.base_commit, "base"); - txt += "
"; - } - 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" - : b.endsWith("-doc") - ? "Doc" - : b.endsWith("-debug") - ? "Debug" - : b.endsWith("-check") - ? "Check" - : "???"; - let bench_name = (b) => b.replace(/-[^-]*$/, ""); - let scenario_filter = (s) => - s == "full" - ? "Full" - : s == "incr-full" - ? "IncrFull" - : s == "incr-unchanged" - ? "IncrUnchanged" - : s.startsWith("incr-patched") - ? "IncrPatched" - : "???"; - if (state.base_commit) { - txt += "
"; - txt += `Diff: codegen-schedule`; - txt += - "
Local profile (base): " + - `./target/release/collector profile_local cachegrind - +${state.base_commit} --exact-match ${bench_name( - state.benchmark - )} --profiles - ${profile(state.benchmark)} --scenarios ${scenario_filter( - state.scenario - )}`; - } - txt += - "
Local profile (new): " + - `./target/release/collector profile_local cachegrind - +${state.commit} --exact-match ${bench_name( - state.benchmark - )} --profiles - ${profile(state.benchmark)} --scenarios ${scenario_filter( - state.scenario - )}`; - if (state.base_commit) { - txt += - "
Local profile (diff): " + - `./target/release/collector profile_local cachegrind - +${state.base_commit} --rustc2 +${ - state.commit - } --exact-match ${bench_name(state.benchmark)} --profiles - ${profile(state.benchmark)} --scenarios ${scenario_filter( - state.scenario - )}`; - } - document.querySelector("#raw-urls").innerHTML = txt; - let sort_idx = state.sort_idx; - if (!data.base_profile_delta) { - document.body.classList.add("hide-delta"); - } - let header = document.getElementById("table-header"); - for (let th of header.querySelectorAll("th") as any as HTMLElement[]) { - // Skip non-sortable columns - if (!th.attributes["data-sort-idx"]) { - continue; - } - let idx = th.attributes["data-sort-idx"].value; - if (idx == sort_idx) { - th.setAttribute("data-sorted-by", "desc"); - } else if (idx == -sort_idx) { - th.setAttribute("data-sorted-by", "asc"); - } - let sortedBy = th.attributes["data-sorted-by"]; - let clickState = Object.assign({}, state); - if (sortedBy && sortedBy.value == "desc") { - clickState.sort_idx = -idx; - } else if (sortedBy && sortedBy.value == "asc") { - clickState.sort_idx = idx; - } else { - // start with descending - if (th.attributes["data-default-sort-dir"].value == "1") { - clickState.sort_idx = idx; - } else { - clickState.sort_idx = -idx; - } - } - let inner = th.innerHTML; - th.innerHTML = `${inner}`; - } - - if (!state.scenario.includes("incr-")) { - // No need to show incremental columns if not showing - // incremental data. - document.body.classList.add("hide-incr"); - } - - let table = document.getElementById("primary-table"); - let idx = 0; - for (let element of [data.profile.totals, ...data.profile.query_data]) { - let cur = to_object(element); - let delta = null; - if (data.base_profile_delta) { - if (idx == 0) { - delta = data.base_profile_delta.totals; - } else { - delta = data.base_profile_delta.query_data[idx - 1]; - } - } - let row = document.createElement("tr"); - - function td(row, content, is_delta = false) { - let td = document.createElement("td"); - td.innerHTML = content; - if (is_delta) { - td.classList.add("delta"); - } - row.appendChild(td); - return td; - } - - td(row, cur.label); - if (cur.percent_total_time < 0) { - td(row, "-").setAttribute( - "title", - "No wall-time stat collected for this run" - ); - } else { - let t = td(row, cur.percent_total_time.toFixed(2) + "%"); - if (idx == 0) { - t.innerText += "*"; - t.setAttribute("title", "% of cpu-time stat"); - } - } - td(row, to_seconds(cur.self_time).toFixed(3)); - if (delta) { - td( - row, - fmt_delta( - to_seconds(cur.self_time), - to_seconds(delta.self_time), - false - ), - true - ); - } else { - td(row, "-", true); - } - td(row, cur.invocation_count); - if (delta) { - td( - row, - fmt_delta(cur.invocation_count, delta.invocation_count, true), - true - ); - } else { - td(row, "-", true); - } - td(row, to_seconds(cur.incremental_load_time).toFixed(3)).classList.add( - "incr" - ); - if (delta) { - td( - row, - fmt_delta( - to_seconds(cur.incremental_load_time), - to_seconds(delta.incremental_load_time), - false - ), - true - ).classList.add("incr"); - } else { - td(row, "-", true).classList.add("incr"); - } - table.appendChild(row); - idx += 1; - } - - let artifactTable = document.getElementById("artifact-body"); - - function td(row, content) { - let td = document.createElement("td"); - td.innerHTML = content; - row.appendChild(td); - return td; - } - - for (let [idx, element] of data.profile.artifact_sizes.entries()) { - let row = document.createElement("tr"); - const label = td(row, element.label); - label.style.textAlign = "center"; - td(row, element.bytes); - if ( - data.base_profile_delta && - data.base_profile_delta.artifact_sizes[idx] - ) { - td(row, data.base_profile_delta.artifact_sizes[idx].bytes); - } - artifactTable.appendChild(row); - idx += 1; - } -} - -// https://stackoverflow.com/questions/6234773 -function escapeHtml(unsafe) { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -} - -function to_object(element) { - if (!element.length) { - element = [ - // escape html, to prevent rendering queries like as tags - escapeHtml(element.label), - element.self_time, - element.percent_total_time, - element.number_of_cache_misses, - element.number_of_cache_hits, - element.invocation_count, - element.blocked_time, - element.incremental_load_time, - ]; - } - let [ - label, - self_time, - percent_total_time, - _cache_misses, - _cache_hits, - invocation_count, - _blocked_time, - incremental_load_time, - ] = element; - return { - label, - self_time, - percent_total_time, - invocation_count, - incremental_load_time, - }; -} - -interface Selector { - commit: string; - base_commit: string | null; - benchmark: string; - scenario: string; - sort_idx: string | number; -} - -async function loadData() { - const params = getUrlParams(); - const {commit, base_commit, benchmark, scenario, sort_idx} = params; - const selector: Selector = { - commit, - base_commit: base_commit ?? null, - benchmark, - scenario, - sort_idx: sort_idx ?? "-2", - }; - - const response = await postMsgpack(SELF_PROFILE_DATA_URL, selector); - populate_data(response, selector); -} - -loadData(); +import {createApp} from "vue"; +import WithSuspense from "../components/with-suspense.vue"; +import DetailedQuery from "./detailed-query/page.vue"; + +const app = createApp(WithSuspense, { + component: DetailedQuery, +}); +app.mount("#app"); diff --git a/site/frontend/src/pages/detailed-query/page.vue b/site/frontend/src/pages/detailed-query/page.vue new file mode 100644 index 000000000..904417a97 --- /dev/null +++ b/site/frontend/src/pages/detailed-query/page.vue @@ -0,0 +1,570 @@ + + + + + diff --git a/site/frontend/src/pages/detailed-query/utils.ts b/site/frontend/src/pages/detailed-query/utils.ts new file mode 100644 index 000000000..e8de53195 --- /dev/null +++ b/site/frontend/src/pages/detailed-query/utils.ts @@ -0,0 +1,339 @@ +import { + chromeProfileUrl, + processedSelfProfileRelativeUrl, +} from "../../self-profile"; + +export interface Selector { + commit: string; + base_commit: string | null; + benchmark: string; + scenario: string; +} + +export interface ProfileElement { + label: string; + self_time: number; + percent_total_time: number; + number_of_cache_misses?: number; + number_of_cache_hits?: number; + invocation_count: number; + blocked_time?: number; + incremental_load_time: number; +} + +export interface ArtifactSize { + label: string; + bytes: string; +} + +export interface ProfileData { + totals: ProfileElement; + query_data: ProfileElement[]; + artifact_sizes: ArtifactSize[]; +} + +export interface SelfProfileResponse { + profile: ProfileData; + base_profile_delta?: ProfileData; +} + +export function toSeconds(time: number): number { + return time / 1000000000; +} + +export function createDelta( + to: number, + delta: number, + isIntegralDelta: boolean +): DeltaData { + let from = to - delta; + let pct = ((to - from) / from) * 100; + if (from == to) { + pct = 0; + } + + return { + delta, + from, + percentage: pct, + isIntegral: isIntegralDelta, + }; +} + +export function perfettoProfilerData( + commit: string, + benchmark: string, + scenario: string +): {link: string; traceTitle: string} { + const link = chromeProfileUrl(commit, benchmark, scenario); + const traceTitle = `${benchmark}-${scenario} (${commit})`; + return {link, traceTitle}; +} + +export function createTitleData(selector: Selector | null): { + text: string; + baseHref: string; + selfHref: string; +} { + if (!selector) return {text: "", baseHref: "", selfHref: ""}; + + const state = selector; + let text = `${state.commit.substring(0, 10)}: Self profile results for ${ + state.benchmark + } run ${state.scenario}`; + + let baseHref = ""; + let selfHref = ""; + + if (state.base_commit) { + selfHref = `/detailed-query.html?commit=${state.commit}&scenario=${state.scenario}&benchmark=${state.benchmark}`; + baseHref = `/detailed-query.html?commit=${state.base_commit}&scenario=${state.scenario}&benchmark=${state.benchmark}`; + } + + return {text, baseHref, selfHref}; +} + +interface DownloadLinks { + raw: string; + flamegraph: string; + crox: string; + codegen: string; + perfetto: {link: string; traceTitle: string}; + firefox: string; +} + +export function createDownloadLinksData(selector: Selector | null): { + baseLinks: DownloadLinks | null; + newLinks: DownloadLinks; + diffLink: string; + localCommands: { + base: string; + new: string; + diff: string; + }; +} { + if (!selector) + return { + baseLinks: null, + newLinks: { + raw: "", + flamegraph: "", + crox: "", + codegen: "", + perfetto: {link: "", traceTitle: ""}, + firefox: "", + }, + diffLink: "", + localCommands: {base: "", new: "", diff: ""}, + }; + + const state = selector; + const profile = (b: string) => + b.endsWith("-opt") + ? "Opt" + : b.endsWith("-doc") + ? "Doc" + : b.endsWith("-debug") + ? "Debug" + : b.endsWith("-check") + ? "Check" + : "???"; + const benchName = (b: string) => b.replace(/-[^-]*$/, ""); + const scenarioFilter = (s: string) => + s == "full" + ? "Full" + : s == "incr-full" + ? "IncrFull" + : s == "incr-unchanged" + ? "IncrUnchanged" + : s.startsWith("incr-patched") + ? "IncrPatched" + : "???"; + + const createLinks = (commit: string) => ({ + raw: `/perf/download-raw-self-profile?commit=${commit}&benchmark=${state.benchmark}&scenario=${state.scenario}`, + flamegraph: processedSelfProfileRelativeUrl( + commit, + state.benchmark, + state.scenario, + "flamegraph" + ), + crox: processedSelfProfileRelativeUrl( + commit, + state.benchmark, + state.scenario, + "crox" + ), + codegen: processedSelfProfileRelativeUrl( + commit, + state.benchmark, + state.scenario, + "codegen-schedule" + ), + perfetto: perfettoProfilerData(commit, state.benchmark, state.scenario), + firefox: `https://profiler.firefox.com/from-url/${encodeURIComponent( + chromeProfileUrl(commit, state.benchmark, state.scenario) + )}/marker-chart/?v=5`, + }); + + const baseLinks = state.base_commit ? createLinks(state.base_commit) : null; + const newLinks = createLinks(state.commit); + + const diffLink = state.base_commit + ? `/perf/processed-self-profile?commit=${state.commit}&base_commit=${state.base_commit}&benchmark=${state.benchmark}&scenario=${state.scenario}&type=codegen-schedule` + : ""; + + const localCommands = { + base: state.base_commit + ? `./target/release/collector profile_local cachegrind + +${state.base_commit} --exact-match ${benchName( + state.benchmark + )} --profiles + ${profile(state.benchmark)} --scenarios ${scenarioFilter( + state.scenario + )}` + : "", + new: `./target/release/collector profile_local cachegrind + +${state.commit} --exact-match ${benchName( + state.benchmark + )} --profiles + ${profile(state.benchmark)} --scenarios ${scenarioFilter( + state.scenario + )}`, + diff: state.base_commit + ? `./target/release/collector profile_local cachegrind + +${state.base_commit} --rustc2 +${ + state.commit + } --exact-match ${benchName(state.benchmark)} --profiles + ${profile(state.benchmark)} --scenarios ${scenarioFilter( + state.scenario + )}` + : "", + }; + + return {baseLinks, newLinks, diffLink, localCommands}; +} + +export interface DeltaData { + from: number; + delta: number; + percentage: number; + isIntegral: boolean; +} + +interface TableRowData { + isTotal: boolean; + label: string; + timePercent: {value: number; formatted: string; title: string}; + timeSeconds: number; + timeDelta: DeltaData | null; + executions: number; + executionsDelta: DeltaData | null; + incrementalLoading: number; + incrementalLoadingDelta: DeltaData | null; +} + +export function createTableData( + data: SelfProfileResponse | null +): TableRowData[] { + if (!data) return []; + const result: TableRowData[] = []; + const profile = data.profile; + const delta = data.base_profile_delta; + + // Add totals row first + const totals = profile.totals; + const totalsDelta = delta?.totals; + result.push({ + isTotal: true, + label: totals.label, + timePercent: + totals.percent_total_time < 0 + ? { + value: -1, + formatted: "-", + title: "No wall-time stat collected for this run", + } + : { + value: totals.percent_total_time, + formatted: totals.percent_total_time.toFixed(2) + "%*", + title: "% of cpu-time stat", + }, + timeSeconds: toSeconds(totals.self_time), + timeDelta: totalsDelta + ? createDelta( + toSeconds(totals.self_time), + toSeconds(totalsDelta.self_time), + false + ) + : null, + executions: totals.invocation_count, + executionsDelta: totalsDelta + ? createDelta(totals.invocation_count, totalsDelta.invocation_count, true) + : null, + incrementalLoading: toSeconds(totals.incremental_load_time), + incrementalLoadingDelta: totalsDelta + ? createDelta( + toSeconds(totals.incremental_load_time), + toSeconds(totalsDelta.incremental_load_time), + false + ) + : null, + }); + + // Add query data rows + profile.query_data.forEach((query, idx) => { + const queryDelta = delta?.query_data[idx]; + result.push({ + isTotal: false, + label: query.label, + timePercent: + query.percent_total_time < 0 + ? { + value: -1, + formatted: "-", + title: "No wall-time stat collected for this run", + } + : { + value: query.percent_total_time, + formatted: query.percent_total_time.toFixed(2) + "%", + title: "", + }, + timeSeconds: toSeconds(query.self_time), + timeDelta: queryDelta + ? createDelta( + toSeconds(query.self_time), + toSeconds(queryDelta.self_time), + false + ) + : null, + executions: query.invocation_count, + executionsDelta: queryDelta + ? createDelta(query.invocation_count, queryDelta.invocation_count, true) + : null, + incrementalLoading: toSeconds(query.incremental_load_time), + incrementalLoadingDelta: queryDelta + ? createDelta( + toSeconds(query.incremental_load_time), + toSeconds(queryDelta.incremental_load_time), + false + ) + : null, + }); + }); + + return result; +} + +export function createArtifactData(data: SelfProfileResponse | null): { + name: string; + size: string; + sizeDelta: string; +}[] { + if (!data) return []; + return data.profile.artifact_sizes.map((artifact, idx) => ({ + name: artifact.label, + size: artifact.bytes, + sizeDelta: data?.base_profile_delta?.artifact_sizes[idx]?.bytes || "0", + })); +} diff --git a/site/frontend/templates/pages/detailed-query.html b/site/frontend/templates/pages/detailed-query.html index dbec34f59..69137000d 100644 --- a/site/frontend/templates/pages/detailed-query.html +++ b/site/frontend/templates/pages/detailed-query.html @@ -1,106 +1,9 @@ {% extends "layout.html" %} {% block head %} - + {% endblock %} {% block content %} -
-
-

-
-

Artifact Size

- - - - - - - - - - -
ArtifactSizeSize delta
-

'Time (%)' is the percentage of the cpu-clock time spent on this query (we do not use - wall-time as we want to account for parallelism).

-

Executions do not include cached executions.

- - - - - - - - - - - - - - - -
Query/FunctionTime (%)Time (s)Time deltaExecutionsExecutions delta - Incremental loading (s)Incremental loading delta
-
-
+
{% endblock content %} {% block script %} diff --git a/site/src/api.rs b/site/src/api.rs index 04cd9b811..ba3bc2be2 100644 --- a/site/src/api.rs +++ b/site/src/api.rs @@ -455,7 +455,6 @@ pub mod self_profile { pub benchmark: String, #[serde(alias = "run_name")] pub scenario: String, - pub sort_idx: String, } #[derive(Debug, Clone, Serialize)] diff --git a/site/src/request_handlers/self_profile.rs b/site/src/request_handlers/self_profile.rs index 085adc9a3..db1707451 100644 --- a/site/src/request_handlers/self_profile.rs +++ b/site/src/request_handlers/self_profile.rs @@ -267,66 +267,6 @@ fn get_self_profile_delta( }) } -fn sort_self_profile( - profile: &mut self_profile::SelfProfile, - base_profile_delta: &mut Option, - sort_idx: i32, -) { - let qd = &mut profile.query_data; - let qd_deltas = base_profile_delta.as_mut().map(|bpd| &mut bpd.query_data); - let mut indices: Vec<_> = (0..qd.len()).collect(); - - match sort_idx.abs() { - 1 => indices.sort_by_key(|&i| qd[i].label), - 2 => indices.sort_by_key(|&i| qd[i].self_time), - 3 => indices.sort_by_key(|&i| qd[i].number_of_cache_misses), - 4 => indices.sort_by_key(|&i| qd[i].number_of_cache_hits), - 5 => indices.sort_by_key(|&i| qd[i].invocation_count), - 6 => indices.sort_by_key(|&i| qd[i].blocked_time), - 7 => indices.sort_by_key(|&i| qd[i].incremental_load_time), - 9 => indices.sort_by_key(|&i| { - // convert to displayed percentage - ((qd[i].number_of_cache_hits as f64 / qd[i].invocation_count as f64) * 10_000.0) as u64 - }), - 10 => indices.sort_by(|&a, &b| { - qd[a] - .percent_total_time - .partial_cmp(&qd[b].percent_total_time) - .unwrap() - }), - 11 => { - if let Some(ref deltas) = qd_deltas { - indices.sort_by_key(|&i| deltas[i].self_time); - } - } - 12 => { - if let Some(ref deltas) = qd_deltas { - indices.sort_by_key(|&i| deltas[i].invocation_count); - } - } - 13 => { - if let Some(ref deltas) = qd_deltas { - indices.sort_by_key(|&i| deltas[i].incremental_load_time); - } - } - _ => return, - } - - profile.query_data = if sort_idx < 0 { - indices.iter().map(|&i| qd[i].clone()).rev().collect() - } else { - indices.iter().map(|&i| qd[i].clone()).collect() - }; - - if let Some(deltas) = qd_deltas { - base_profile_delta.as_mut().unwrap().query_data = if sort_idx < 0 { - indices.iter().map(|&i| deltas[i].clone()).rev().collect() - } else { - indices.iter().map(|&i| deltas[i].clone()).collect() - }; - } -} - pub async fn handle_self_profile_raw_download( body: self_profile_raw::Request, ctxt: &SiteCtxt, @@ -516,12 +456,6 @@ pub async fn handle_self_profile( .map_err(|e| format!("invalid run name: {:?}", e))?; let index = ctxt.index.load(); - let sort_idx = body - .sort_idx - .parse::() - .ok() - .ok_or("sort_idx needs to be i32".to_string())?; - let query = selector::CompileBenchmarkQuery::default() .benchmark(selector::Selector::One(bench_name.to_string())) .profile(selector::Selector::One(profile.parse().unwrap())) @@ -573,13 +507,12 @@ pub async fn handle_self_profile( base_self_profile.as_ref().map(|p| &p.profile), ); - let mut base_profile_delta = get_self_profile_delta( + let base_profile_delta = get_self_profile_delta( &self_profile.profile, base_self_profile.as_ref().map(|p| &p.profile), &self_profile.profiling_data, base_self_profile.as_ref().map(|p| &p.profiling_data), ); - sort_self_profile(&mut self_profile.profile, &mut base_profile_delta, sort_idx); Ok(self_profile::Response { base_profile_delta,