mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Profiling] fixing TopN functions sorting (#167242)

This commit is contained in:
parent
92a92fff67
commit
261febcf31
4 changed files with 178 additions and 90 deletions
|
@ -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('/');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
@ -102,10 +102,33 @@ 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',
|
||||
|
@ -113,6 +136,7 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
},
|
||||
{
|
||||
id: TopNFunctionSortField.Frame,
|
||||
actions: { showHide: false },
|
||||
displayAsText: i18n.translate('xpack.profiling.functionsView.functionColumnLabel', {
|
||||
defaultMessage: 'Function',
|
||||
}),
|
||||
|
@ -120,7 +144,8 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
{
|
||||
id: TopNFunctionSortField.Samples,
|
||||
initialWidth: isDifferentialView ? 100 : 200,
|
||||
schema: 'samples',
|
||||
schema: 'numeric',
|
||||
actions: { showHide: false },
|
||||
display: (
|
||||
<LabelWithHint
|
||||
label={i18n.translate('xpack.profiling.functionsView.samplesColumnLabel', {
|
||||
|
@ -137,6 +162,8 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
},
|
||||
{
|
||||
id: TopNFunctionSortField.SelfCPU,
|
||||
schema: 'numeric',
|
||||
actions: { showHide: false },
|
||||
initialWidth: isDifferentialView ? 100 : 200,
|
||||
display: (
|
||||
<CPULabelWithHint
|
||||
|
@ -149,6 +176,8 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
},
|
||||
{
|
||||
id: TopNFunctionSortField.TotalCPU,
|
||||
schema: 'numeric',
|
||||
actions: { showHide: false },
|
||||
initialWidth: isDifferentialView ? 100 : 200,
|
||||
display: (
|
||||
<CPULabelWithHint
|
||||
|
@ -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',
|
||||
}),
|
||||
|
@ -176,6 +206,7 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
gridColumns.push(
|
||||
{
|
||||
id: TopNFunctionSortField.AnnualizedCo2,
|
||||
actions: { showHide: false },
|
||||
initialWidth: isDifferentialView ? 100 : 200,
|
||||
schema: 'numeric',
|
||||
display: (
|
||||
|
@ -195,6 +226,8 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
},
|
||||
{
|
||||
id: TopNFunctionSortField.AnnualizedDollarCost,
|
||||
schema: 'numeric',
|
||||
actions: { showHide: false },
|
||||
initialWidth: isDifferentialView ? 100 : 200,
|
||||
display: (
|
||||
<LabelWithHint
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -249,7 +282,7 @@ export const TopNFunctionsGrid = forwardRef(
|
|||
columnId,
|
||||
setCellProps,
|
||||
}: EuiDataGridCellValueElementProps) {
|
||||
const data = rows[rowIndex];
|
||||
const data = sortedRows[rowIndex];
|
||||
if (data) {
|
||||
return (
|
||||
<FunctionRow
|
||||
|
@ -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={{
|
||||
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
return topNFunctions.TopN.filter((topN) => topN.CountExclusive > 0).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 {
|
||||
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({
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue