Skip to content

Commit

Permalink
[Profiling] fixing TopN functions sorting (#167242)
Browse files Browse the repository at this point in the history
  • Loading branch information
cauemarcondes authored Sep 28, 2023
1 parent 92a92ff commit 261febc
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,82 @@ describe('Functions page', () => {
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(2).contains('libjvm.so');
});

it('Sorting grid', () => {
cy.intercept('GET', '/internal/profiling/topn/functions?*').as('getTopNFunctions');
cy.visitKibana('/app/profiling/functions', { rangeFrom, rangeTo });
cy.wait('@getTopNFunctions');
[
{
columnKey: 'rank',
columnIndex: 1,
highRank: 388,
lowRank: 1,
highValue: 388,
lowValue: 1,
},
{
columnKey: 'samples',
columnIndex: 7,
highRank: 1,
lowRank: 44,
highValue: 28,
lowValue: 1,
},
{
columnKey: 'selfCPU',
columnIndex: 3,
highRank: 1,
lowRank: 44,
highValue: '5.46%',
lowValue: '0.19%',
},
{
columnKey: 'totalCPU',
columnIndex: 4,
highRank: 338,
lowRank: 44,
highValue: '10.33%',
lowValue: '0.19%',
},
{
columnKey: 'annualizedCo2',
columnIndex: 5,
highRank: 1,
lowRank: 44,
highValue: '1.84 lbs / 0.84 kg',
lowValue: '0.07 lbs / 0.03 kg',
},
{
columnKey: 'annualizedDollarCost',
columnIndex: 6,
highRank: 1,
lowRank: 44,
highValue: '$17.37',
lowValue: '$0.62',
},
].forEach(({ columnKey, columnIndex, highRank, highValue, lowRank, lowValue }) => {
cy.get(`[data-test-subj="dataGridHeaderCell-${columnKey}"]`).click();
cy.contains('Sort High-Low').click();
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(1).contains(highRank);
cy.get(firstRowSelector).eq(columnIndex).contains(highValue);

cy.get(`[data-test-subj="dataGridHeaderCell-${columnKey}"]`).click();
cy.contains('Sort Low-High').click();
cy.get(firstRowSelector).eq(1).contains(lowRank);
cy.get(firstRowSelector).eq(columnIndex).contains(lowValue);
});

cy.get(`[data-test-subj="dataGridHeaderCell-frame"]`).click();
cy.contains('Sort Z-A').click();
const firstRowSelector = '[data-grid-row-index="0"] [data-test-subj="dataGridRowCell"]';
cy.get(firstRowSelector).eq(1).contains('1');
cy.get(firstRowSelector).eq(2).contains('vmlinux');

cy.get('[data-test-subj="dataGridHeaderCell-frame"]').click();
cy.contains('Sort A-Z').click();
cy.get(firstRowSelector).eq(1).contains('371');
cy.get(firstRowSelector).eq(2).contains('/');
});
});
73 changes: 41 additions & 32 deletions x-pack/plugins/profiling/public/components/topn_functions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import {
EuiScreenReaderOnly,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { last } from 'lodash';
import { useUiTracker } from '@kbn/observability-shared-plugin/public';
import { getCalleeFunction, TopNFunctions, TopNFunctionSortField } from '@kbn/profiling-utils';
import { last, orderBy } from 'lodash';
import React, { forwardRef, Ref, useMemo, useState } from 'react';
import { GridOnScrollProps } from 'react-window';
import { useUiTracker } from '@kbn/observability-shared-plugin/public';
import { TopNFunctions, TopNFunctionSortField } from '@kbn/profiling-utils';
import { CPULabelWithHint } from '../cpu_label_with_hint';
import { FrameInformationTooltip } from '../frame_information_window/frame_information_tooltip';
import { LabelWithHint } from '../label_with_hint';
Expand Down Expand Up @@ -102,25 +102,50 @@ export const TopNFunctionsGrid = forwardRef(
totalSeconds,
]);

const sortedRows = useMemo(() => {
switch (sortField) {
case TopNFunctionSortField.Frame:
return orderBy(rows, (row) => getCalleeFunction(row.frame), sortDirection);
case TopNFunctionSortField.SelfCPU:
return orderBy(rows, (row) => row.selfCPUPerc, sortDirection);
case TopNFunctionSortField.TotalCPU:
return orderBy(rows, (row) => row.totalCPUPerc, sortDirection);
case TopNFunctionSortField.AnnualizedCo2:
return orderBy(rows, (row) => row.impactEstimates?.selfCPU.annualizedCo2, sortDirection);
case TopNFunctionSortField.AnnualizedDollarCost:
return orderBy(
rows,
(row) => row.impactEstimates?.selfCPU.annualizedDollarCost,
sortDirection
);
default:
return orderBy(rows, sortField, sortDirection);
}
}, [rows, sortDirection, sortField]);

const { columns, leadingControlColumns } = useMemo(() => {
const gridColumns: EuiDataGridColumn[] = [
{
id: TopNFunctionSortField.Rank,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 50 : 90,
displayAsText: i18n.translate('xpack.profiling.functionsView.rankColumnLabel', {
defaultMessage: 'Rank',
}),
},
{
id: TopNFunctionSortField.Frame,
actions: { showHide: false },
displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', {
defaultMessage: 'Function',
}),
},
{
id: TopNFunctionSortField.Samples,
initialWidth: isDifferentialView ? 100 : 200,
schema: 'samples',
schema: 'numeric',
actions: { showHide: false },
display: (
<LabelWithHint
label={i18n.translate('xpack.profiling.functionsView.samplesColumnLabel', {
Expand All @@ -137,6 +162,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.SelfCPU,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<CPULabelWithHint
Expand All @@ -149,6 +176,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.TotalCPU,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<CPULabelWithHint
Expand All @@ -166,6 +195,7 @@ export const TopNFunctionsGrid = forwardRef(
gridColumns.push({
initialWidth: 60,
id: TopNFunctionSortField.Diff,
actions: { showHide: false },
displayAsText: i18n.translate('xpack.profiling.functionsView.diffColumnLabel', {
defaultMessage: 'Diff',
}),
Expand All @@ -176,6 +206,7 @@ export const TopNFunctionsGrid = forwardRef(
gridColumns.push(
{
id: TopNFunctionSortField.AnnualizedCo2,
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
schema: 'numeric',
display: (
Expand All @@ -195,6 +226,8 @@ export const TopNFunctionsGrid = forwardRef(
},
{
id: TopNFunctionSortField.AnnualizedDollarCost,
schema: 'numeric',
actions: { showHide: false },
initialWidth: isDifferentialView ? 100 : 200,
display: (
<LabelWithHint
Expand Down Expand Up @@ -225,7 +258,7 @@ export const TopNFunctionsGrid = forwardRef(
rowCellRender: function RowCellRender({ rowIndex }) {
function handleOnClick() {
trackProfilingEvent({ metric: 'topN_function_details_click' });
setSelectedRow(rows[rowIndex]);
setSelectedRow(sortedRows[rowIndex]);
}
return (
<EuiButtonIcon
Expand All @@ -240,7 +273,7 @@ export const TopNFunctionsGrid = forwardRef(
});
}
return { columns: gridColumns, leadingControlColumns: gridLeadingControlColumns };
}, [isDifferentialView, rows, showDiffColumn, trackProfilingEvent]);
}, [isDifferentialView, sortedRows, showDiffColumn, trackProfilingEvent]);

const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id));

Expand All @@ -249,7 +282,7 @@ export const TopNFunctionsGrid = forwardRef(
columnId,
setCellProps,
}: EuiDataGridCellValueElementProps) {
const data = rows[rowIndex];
const data = sortedRows[rowIndex];
if (data) {
return (
<FunctionRow
Expand All @@ -272,9 +305,8 @@ export const TopNFunctionsGrid = forwardRef(
aria-label="TopN functions"
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={rows.length}
rowCount={sortedRows.length > 100 ? 100 : sortedRows.length}
renderCellValue={RenderCellValue}
inMemory={{ level: 'sorting' }}
sorting={{ columns: [{ id: sortField, direction: sortDirection }], onSort }}
leadingControlColumns={leadingControlColumns}
pagination={{
Expand All @@ -296,29 +328,6 @@ export const TopNFunctionsGrid = forwardRef(
virtualizationOptions={{
onScroll,
}}
schemaDetectors={[
{
type: 'samples',
comparator: (a, b, direction) => {
const aNumber = parseFloat(a.replace(/,/g, ''));
const bNumber = parseFloat(b.replace(/,/g, ''));

if (aNumber < bNumber) {
return direction === 'desc' ? 1 : -1;
}
if (aNumber > bNumber) {
return direction === 'desc' ? -1 : 1;
}
return 0;
},
detector: (a) => {
return 1;
},
icon: '',
sortTextAsc: 'Low-High',
sortTextDesc: 'High-Low',
},
]}
/>
{selectedRow && (
<FrameInformationTooltip
Expand Down
111 changes: 54 additions & 57 deletions x-pack/plugins/profiling/public/components/topn_functions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,65 +70,62 @@ export function getFunctionsRows({
? keyBy(comparisonTopNFunctions.TopN, 'Id')
: {};

return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0)
.slice(0, 100)
.map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];

const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});

const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100;

const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.TotalCount,
totalSeconds,
})
: undefined;

function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});

const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;

return {
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc -
(comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
totalCPUPerc:
totalCPUPerc -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
};
}
}
return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).map((topN, i) => {
const comparisonRow = comparisonDataById?.[topN.Id];

return {
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
diff: calculateDiff(),
};
const scaledSelfCPU = scaleValue({
value: topN.CountExclusive,
scaleFactor: baselineScaleFactor,
});

const totalCPUPerc = (topN.CountInclusive / topNFunctions.TotalCount) * 100;
const selfCPUPerc = (topN.CountExclusive / topNFunctions.TotalCount) * 100;

const impactEstimates =
totalSeconds > 0
? calculateImpactEstimates({
countExclusive: topN.CountExclusive,
countInclusive: topN.CountInclusive,
totalSamples: topNFunctions.TotalCount,
totalSeconds,
})
: undefined;

function calculateDiff() {
if (comparisonTopNFunctions && comparisonRow) {
const comparisonScaledSelfCPU = scaleValue({
value: comparisonRow.CountExclusive,
scaleFactor: comparisonScaleFactor,
});

const scaledDiffSamples = scaledSelfCPU - comparisonScaledSelfCPU;

return {
rank: topN.Rank - comparisonRow.Rank,
samples: scaledDiffSamples,
selfCPU: comparisonRow.CountExclusive,
totalCPU: comparisonRow.CountInclusive,
selfCPUPerc:
selfCPUPerc - (comparisonRow.CountExclusive / comparisonTopNFunctions.TotalCount) * 100,
totalCPUPerc:
totalCPUPerc -
(comparisonRow.CountInclusive / comparisonTopNFunctions.TotalCount) * 100,
};
}
}

return {
rank: topN.Rank,
frame: topN.Frame,
samples: scaledSelfCPU,
selfCPUPerc,
totalCPUPerc,
selfCPU: topN.CountExclusive,
totalCPU: topN.CountInclusive,
impactEstimates,
diff: calculateDiff(),
};
});
}

export function calculateBaseComparisonDiff({
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/profiling/scripts/test/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ function runTests() {

return childProcess.spawnSync('node', spawnArgs, {
cwd: e2eDir,
env: { ...process.env, CYPRESS_CLI_ARGS: JSON.stringify(cypressCliArgs) },
env: {
...process.env,
CYPRESS_CLI_ARGS: JSON.stringify(cypressCliArgs),
NODE_OPTIONS: '--openssl-legacy-provider',
},
encoding: 'utf8',
stdio: 'inherit',
});
Expand Down

0 comments on commit 261febc

Please sign in to comment.